Repository: coin-or/CppAD Branch: main Commit: 5c27de281a69 Files: 1472 Total size: 8.1 MB Directory structure: gitextract_tk_667wh/ ├── .circleci/ │ └── config.yml ├── .github/ │ └── workflows/ │ ├── conda/ │ │ └── conda-env.yml │ ├── conda-bash.yml │ ├── conda-windows-clang.yml │ ├── conda-windows-eigen.yml │ ├── conda-windows-v142.yml │ ├── ubuntu-macos.yml │ └── windows-msys2.yml ├── .gitignore ├── .readthedocs.yaml ├── .travis.yml ├── CMakeLists.txt ├── COPYING ├── appendix/ │ ├── addon.xrst │ ├── appendix.xrst │ ├── bib.xrst │ ├── deprecated/ │ │ ├── compare_change.xrst │ │ ├── deprecated.xrst │ │ ├── fun_deprecated.xrst │ │ ├── include_deprecated.xrst │ │ └── omp_alloc.xrst │ ├── directory.xrst │ ├── faq.xrst │ ├── glossary.xrst │ ├── license.xrst │ ├── numeric_ad.xrst │ ├── whats_new/ │ │ ├── 2003.xrst │ │ ├── 2004.xrst │ │ ├── 2005.xrst │ │ ├── 2006.xrst │ │ ├── 2007.xrst │ │ ├── 2008.xrst │ │ ├── 2009.xrst │ │ ├── 2010.xrst │ │ ├── 2011.xrst │ │ ├── 2012.xrst │ │ ├── 2013.xrst │ │ ├── 2014.xrst │ │ ├── 2015.xrst │ │ ├── 2016.xrst │ │ ├── 2017.xrst │ │ ├── 2018.xrst │ │ ├── 2019.xrst │ │ ├── 2020.xrst │ │ ├── 2021.xrst │ │ ├── 2022.xrst │ │ ├── 2023.xrst │ │ ├── 2024.xrst │ │ ├── 2025.xrst │ │ ├── 2026.xrst │ │ └── whats_new.xrst │ └── wish_list.xrst ├── appveyor.yml ├── authors ├── batch_edit.sed ├── bin/ │ ├── appveyor.sh │ ├── build.bat │ ├── check_addon.sh │ ├── check_all.sh │ ├── check_copy.sh │ ├── check_define.sh │ ├── check_deprecated.sh │ ├── check_doxygen.sh │ ├── check_example.sh │ ├── check_if.sh │ ├── check_include_def.sh │ ├── check_include_file.sh │ ├── check_include_xrst.sh │ ├── check_invisible.sh │ ├── check_nominmax.sh │ ├── check_op_code.sh │ ├── check_sort.sh │ ├── check_tab.sh │ ├── check_tempfile.sh │ ├── check_trace.sh │ ├── check_user_def.py │ ├── check_version.sh │ ├── colpack.sh │ ├── deprecate_xam.sh │ ├── dev_settings.sh │ ├── dos_build.bat │ ├── doxyfile.sh │ ├── get_adolc.sh │ ├── get_colpack.sh │ ├── get_cppadcg.sh │ ├── get_fadbad.sh │ ├── get_ipopt.sh │ ├── get_optional.sh │ ├── get_sacado.sh │ ├── git_commit.sh │ ├── grep_and_sed.sh │ ├── group_list.sh │ ├── master_revert.sh │ ├── new_release.sh │ ├── no_bitwise.sh │ ├── package.sh │ ├── run_cmake.sh │ ├── run_configure.sh │ ├── run_doxygen.sh │ ├── run_xrst.sh │ ├── sort.sh │ ├── speed_branch.sh │ ├── speed_diff.sh │ ├── speed_new.sh │ ├── speed_package.sh │ ├── tag_month.sh │ ├── test_install.sh │ ├── test_multi_thread.sh │ ├── test_one.sh.in │ ├── trace.sh │ ├── travis.sh │ ├── twine.sh │ └── valgrind.sh ├── bug/ │ ├── boost_lu.sh │ ├── clang_simple.sh │ ├── cmake_target.sh │ ├── cppad_cg.sh │ ├── doxy_member.sh │ ├── eigen_shadow.sh │ ├── gcc_complex.sh │ ├── numeric_limit.sh │ ├── pow.sh │ ├── sparsity.sh │ ├── std_vector.sh │ ├── subgraph.sh │ ├── template.sh │ ├── test_install.sh │ └── vec_itr_speed.sh ├── cmake/ │ ├── add_check_executable.cmake │ ├── add_to_list.cmake │ ├── assert.cmake │ ├── assert_value_in_set.cmake │ ├── command_line_arg.cmake │ ├── compile_source_test.cmake │ ├── cppad_uninstall.cmake │ ├── dos_path_to_unix.cmake │ ├── eigen_info.cmake │ ├── pkgconfig_info.cmake │ ├── prefix_info.cmake │ ├── print_variable.cmake │ ├── run_source_test.cmake │ └── set_compile_flags.cmake ├── configure ├── cppad_ipopt/ │ ├── CMakeLists.txt │ ├── example/ │ │ ├── CMakeLists.txt │ │ ├── example.cpp │ │ ├── get_started.cpp │ │ ├── ode1.xrst │ │ ├── ode2.xrst │ │ ├── ode_check.cpp │ │ ├── ode_check.hpp │ │ ├── ode_fast.hpp │ │ ├── ode_fast_check.cpp │ │ ├── ode_problem.hpp │ │ ├── ode_run.hpp │ │ ├── ode_simple.hpp │ │ ├── ode_simple_check.cpp │ │ └── test.sh.in │ ├── speed/ │ │ ├── CMakeLists.txt │ │ ├── ode_speed.cpp │ │ ├── speed.cpp │ │ └── test.sh.in │ ├── src/ │ │ ├── CMakeLists.txt │ │ ├── cppad_ipopt_nlp.cpp │ │ ├── cppad_ipopt_nlp.hpp │ │ ├── fun_record.hpp │ │ ├── hes_fg_map.cpp │ │ ├── hes_fg_map.hpp │ │ ├── jac_g_map.cpp │ │ ├── jac_g_map.hpp │ │ ├── sparse_map2vec.cpp │ │ ├── sparse_map2vec.hpp │ │ ├── vec_fun_pattern.cpp │ │ └── vec_fun_pattern.hpp │ └── test/ │ ├── CMakeLists.txt │ ├── k_gt_one.cpp │ ├── multiple_solution.cpp │ ├── retape_k1_l1.cpp │ ├── retape_k1_l2.cpp │ ├── test.sh.in │ └── test_more.cpp ├── cppad_lib/ │ ├── CMakeLists.txt │ ├── code_gen_fun.cpp │ ├── cpp_graph_op.cpp │ ├── cppad_colpack.cpp │ ├── csrc_writer.cpp │ ├── json_lexer.cpp │ ├── json_parser.cpp │ ├── json_writer.cpp │ ├── link_dll_lib.cpp │ └── temp_file.cpp ├── epl-2.0.txt ├── example/ │ ├── CMakeLists.txt │ ├── abs_normal/ │ │ ├── CMakeLists.txt │ │ ├── abs_eval.cpp │ │ ├── abs_eval.hpp │ │ ├── abs_eval.xrst │ │ ├── abs_min_linear.cpp │ │ ├── abs_min_linear.hpp │ │ ├── abs_min_linear.xrst │ │ ├── abs_min_quad.cpp │ │ ├── abs_min_quad.hpp │ │ ├── abs_min_quad.xrst │ │ ├── abs_normal.cpp │ │ ├── abs_normal.xrst │ │ ├── abs_print_mat.hpp │ │ ├── get_started.cpp │ │ ├── lp_box.cpp │ │ ├── lp_box.hpp │ │ ├── lp_box.xrst │ │ ├── min_nso_linear.cpp │ │ ├── min_nso_linear.hpp │ │ ├── min_nso_linear.xrst │ │ ├── min_nso_quad.cpp │ │ ├── min_nso_quad.hpp │ │ ├── min_nso_quad.xrst │ │ ├── qp_box.cpp │ │ ├── qp_box.hpp │ │ ├── qp_box.xrst │ │ ├── qp_interior.cpp │ │ ├── qp_interior.hpp │ │ ├── qp_interior.xrst │ │ ├── simplex_method.cpp │ │ ├── simplex_method.hpp │ │ ├── simplex_method.xrst │ │ └── talk.tex │ ├── atomic_four/ │ │ ├── CMakeLists.txt │ │ ├── atomic_four.cpp │ │ ├── atomic_four.xrst │ │ ├── bilinear.cpp │ │ ├── dynamic.cpp │ │ ├── forward.cpp │ │ ├── get_started.cpp │ │ ├── lin_ode/ │ │ │ ├── CMakeLists.txt │ │ │ ├── forward.cpp │ │ │ ├── lin_ode.cpp │ │ │ ├── rev_depend.cpp │ │ │ ├── reverse.cpp │ │ │ └── sparsity.cpp │ │ ├── mat_mul/ │ │ │ ├── CMakeLists.txt │ │ │ ├── forward.cpp │ │ │ ├── identical_zero.cpp │ │ │ ├── mat_mul.cpp │ │ │ ├── rev_depend.cpp │ │ │ ├── reverse.cpp │ │ │ └── sparsity.cpp │ │ ├── norm_sq.cpp │ │ └── vector/ │ │ ├── CMakeLists.txt │ │ ├── add.cpp │ │ ├── div.cpp │ │ ├── hes_sparsity.cpp │ │ ├── jac_sparsity.cpp │ │ ├── mul.cpp │ │ ├── neg.cpp │ │ ├── rev_depend.cpp │ │ ├── sub.cpp │ │ └── vector.cpp │ ├── atomic_three/ │ │ ├── CMakeLists.txt │ │ ├── atomic_three.cpp │ │ ├── atomic_three.xrst │ │ ├── base2ad.cpp │ │ ├── dynamic.cpp │ │ ├── forward.cpp │ │ ├── get_started.cpp │ │ ├── hes_sparsity.cpp │ │ ├── jac_sparsity.cpp │ │ ├── mat_mul.cpp │ │ ├── norm_sq.cpp │ │ ├── reciprocal.cpp │ │ ├── rev_depend.cpp │ │ ├── reverse.cpp │ │ └── tangent.cpp │ ├── atomic_two/ │ │ ├── CMakeLists.txt │ │ ├── atomic_two.cpp │ │ ├── eigen_cholesky.cpp │ │ ├── eigen_mat_inv.cpp │ │ └── eigen_mat_mul.cpp │ ├── chkpoint_two/ │ │ ├── CMakeLists.txt │ │ ├── base2ad.cpp │ │ ├── chkpoint_two.cpp │ │ ├── compare.cpp │ │ ├── dynamic.cpp │ │ ├── get_started.cpp │ │ └── ode.cpp │ ├── compare_change/ │ │ ├── CMakeLists.txt │ │ └── compare_change.cpp │ ├── cppad_code_gen/ │ │ ├── CMakeLists.txt │ │ ├── cppad_code_gen.cpp │ │ ├── file.cpp │ │ ├── function.cpp │ │ ├── jac_as_fun.cpp │ │ ├── jacobian.cpp │ │ ├── sparse_jac_as_fun.cpp │ │ └── sparse_jacobian.cpp │ ├── general/ │ │ ├── CMakeLists.txt │ │ ├── abort_recording.cpp │ │ ├── acos.cpp │ │ ├── acosh.cpp │ │ ├── ad_assign.cpp │ │ ├── ad_ctor.cpp │ │ ├── ad_fun.cpp │ │ ├── ad_in_c.cpp │ │ ├── ad_input.cpp │ │ ├── ad_output.cpp │ │ ├── add.cpp │ │ ├── add_eq.cpp │ │ ├── asin.cpp │ │ ├── asinh.cpp │ │ ├── atan.cpp │ │ ├── atan2.cpp │ │ ├── atanh.cpp │ │ ├── azmul.cpp │ │ ├── base2ad.cpp │ │ ├── base2vec_ad.cpp │ │ ├── base_alloc.hpp │ │ ├── base_require.cpp │ │ ├── bender_quad.cpp │ │ ├── bool_fun.cpp │ │ ├── capacity_order.cpp │ │ ├── change_param.cpp │ │ ├── check_for_nan.cpp │ │ ├── compare.cpp │ │ ├── complex_poly.cpp │ │ ├── con_dyn_var.cpp │ │ ├── cond_exp.cpp │ │ ├── cos.cpp │ │ ├── cosh.cpp │ │ ├── div.cpp │ │ ├── div_eq.cpp │ │ ├── eigen_array.cpp │ │ ├── eigen_det.cpp │ │ ├── equal_op_seq.cpp │ │ ├── erf.cpp │ │ ├── erfc.cpp │ │ ├── exp.cpp │ │ ├── expm1.cpp │ │ ├── fabs.cpp │ │ ├── for_one.cpp │ │ ├── for_two.cpp │ │ ├── forward.cpp │ │ ├── forward_dir.cpp │ │ ├── forward_order.cpp │ │ ├── fun_assign.cpp │ │ ├── fun_check.cpp │ │ ├── fun_property.cpp │ │ ├── function_name.cpp │ │ ├── general.cpp │ │ ├── hes_lagrangian.cpp │ │ ├── hes_lu_det.cpp │ │ ├── hes_minor_det.cpp │ │ ├── hes_times_dir.cpp │ │ ├── hessian.cpp │ │ ├── independent.cpp │ │ ├── integer.cpp │ │ ├── interface2c.cpp │ │ ├── interp_onetape.cpp │ │ ├── interp_retape.cpp │ │ ├── jac_lu_det.cpp │ │ ├── jac_minor_det.cpp │ │ ├── jacobian.cpp │ │ ├── log.cpp │ │ ├── log10.cpp │ │ ├── log1p.cpp │ │ ├── lu_ratio.cpp │ │ ├── lu_vec_ad.cpp │ │ ├── lu_vec_ad.hpp │ │ ├── lu_vec_ad_ok.cpp │ │ ├── mul.cpp │ │ ├── mul_eq.cpp │ │ ├── mul_level.cpp │ │ ├── mul_level_adolc.cpp │ │ ├── mul_level_adolc_ode.cpp │ │ ├── mul_level_ode.cpp │ │ ├── near_equal_ext.cpp │ │ ├── new_dynamic.cpp │ │ ├── num_limits.cpp │ │ ├── number_skip.cpp │ │ ├── numeric_type.cpp │ │ ├── ode_stiff.cpp │ │ ├── opt_val_hes.cpp │ │ ├── pow.cpp │ │ ├── pow_nan.cpp │ │ ├── print_for.cpp │ │ ├── rev_checkpoint.cpp │ │ ├── rev_one.cpp │ │ ├── rev_two.cpp │ │ ├── reverse_one.cpp │ │ ├── reverse_three.cpp │ │ ├── reverse_two.cpp │ │ ├── sign.cpp │ │ ├── sin.cpp │ │ ├── sinh.cpp │ │ ├── sqrt.cpp │ │ ├── stack_machine.cpp │ │ ├── sub.cpp │ │ ├── sub_eq.cpp │ │ ├── tan.cpp │ │ ├── tanh.cpp │ │ ├── tape_index.cpp │ │ ├── taylor_ode.cpp │ │ ├── unary_minus.cpp │ │ ├── unary_plus.cpp │ │ ├── value.cpp │ │ ├── var2par.cpp │ │ └── vec_ad.cpp │ ├── get_started/ │ │ ├── CMakeLists.txt │ │ └── get_started.cpp │ ├── graph/ │ │ ├── CMakeLists.txt │ │ ├── add_op.cpp │ │ ├── atom4_op.cpp │ │ ├── atom_op.cpp │ │ ├── azmul_op.cpp │ │ ├── cexp_op.cpp │ │ ├── comp_op.cpp │ │ ├── discrete_op.cpp │ │ ├── div_op.cpp │ │ ├── graph.cpp │ │ ├── mul_op.cpp │ │ ├── pow_op.cpp │ │ ├── print_graph.cpp │ │ ├── print_op.cpp │ │ ├── sub_op.cpp │ │ ├── sum_op.cpp │ │ ├── switch_var_dyn.cpp │ │ └── unary_op.cpp │ ├── ipopt_solve/ │ │ ├── CMakeLists.txt │ │ ├── get_started.cpp │ │ ├── ipopt_solve.cpp │ │ ├── ode_inverse.cpp │ │ ├── retape.cpp │ │ └── test.sh.in │ ├── jit/ │ │ ├── CMakeLists.txt │ │ ├── atomic.cpp │ │ ├── compare_change.cpp │ │ ├── compile.cpp │ │ ├── dynamic.cpp │ │ ├── get_started.cpp │ │ ├── jit.cpp │ │ └── jit.xrst │ ├── json/ │ │ ├── CMakeLists.txt │ │ ├── add_op.cpp │ │ ├── atom4_op.cpp │ │ ├── atom_op.cpp │ │ ├── azmul_op.cpp │ │ ├── cexp_op.cpp │ │ ├── comp_op.cpp │ │ ├── discrete_op.cpp │ │ ├── div_op.cpp │ │ ├── from_json.cpp │ │ ├── get_started.cpp │ │ ├── json.cpp │ │ ├── mul_op.cpp │ │ ├── pow_op.cpp │ │ ├── print_op.cpp │ │ ├── sparse.cpp │ │ ├── sub_op.cpp │ │ ├── sum_op.cpp │ │ ├── to_json.cpp │ │ └── unary_op.cpp │ ├── multi_thread/ │ │ ├── CMakeLists.txt │ │ ├── bthread/ │ │ │ ├── CMakeLists.txt │ │ │ ├── a11c_bthread.cpp │ │ │ ├── get_started.cpp │ │ │ └── team_bthread.cpp │ │ ├── harmonic.cpp │ │ ├── harmonic.hpp │ │ ├── harmonic.xrst │ │ ├── multi_atomic_three.cpp │ │ ├── multi_atomic_three.hpp │ │ ├── multi_atomic_three.xrst │ │ ├── multi_atomic_two.cpp │ │ ├── multi_atomic_two.hpp │ │ ├── multi_atomic_two.xrst │ │ ├── multi_chkpoint_one.cpp │ │ ├── multi_chkpoint_one.hpp │ │ ├── multi_chkpoint_one.xrst │ │ ├── multi_chkpoint_two.cpp │ │ ├── multi_chkpoint_two.hpp │ │ ├── multi_chkpoint_two.xrst │ │ ├── multi_newton.cpp │ │ ├── multi_newton.hpp │ │ ├── multi_newton.xrst │ │ ├── openmp/ │ │ │ ├── CMakeLists.txt │ │ │ ├── a11c_openmp.cpp │ │ │ ├── get_started.cpp │ │ │ └── team_openmp.cpp │ │ ├── pthread/ │ │ │ ├── CMakeLists.txt │ │ │ ├── a11c_pthread.cpp │ │ │ ├── get_started.cpp │ │ │ └── team_pthread.cpp │ │ ├── sthread/ │ │ │ ├── CMakeLists.txt │ │ │ ├── a11c_sthread.cpp │ │ │ ├── get_started.cpp │ │ │ └── team_sthread.cpp │ │ ├── team_example.cpp │ │ ├── team_example.hpp │ │ ├── team_thread.hpp │ │ ├── template/ │ │ │ └── get_started.xrst │ │ └── thread_test.cpp │ ├── optimize/ │ │ ├── CMakeLists.txt │ │ ├── compare_op.cpp │ │ ├── conditional_skip.cpp │ │ ├── cumulative_sum.cpp │ │ ├── forward_active.cpp │ │ ├── nest_conditional.cpp │ │ ├── optimize.cpp │ │ ├── optimize_twice.cpp │ │ ├── print_for.cpp │ │ └── reverse_active.cpp │ ├── print_for/ │ │ ├── CMakeLists.txt │ │ ├── print_for.cpp │ │ └── test.sh │ ├── sparse/ │ │ ├── CMakeLists.txt │ │ ├── colpack_hes.cpp │ │ ├── colpack_hessian.cpp │ │ ├── colpack_jac.cpp │ │ ├── colpack_jacobian.cpp │ │ ├── conj_grad.cpp │ │ ├── dependency.cpp │ │ ├── for_hes_sparsity.cpp │ │ ├── for_jac_sparsity.cpp │ │ ├── for_sparse_hes.cpp │ │ ├── for_sparse_jac.cpp │ │ ├── rc_sparsity.cpp │ │ ├── rev_hes_sparsity.cpp │ │ ├── rev_jac_sparsity.cpp │ │ ├── rev_sparse_hes.cpp │ │ ├── rev_sparse_jac.cpp │ │ ├── sparse.cpp │ │ ├── sparse2eigen.cpp │ │ ├── sparse_hes.cpp │ │ ├── sparse_hessian.cpp │ │ ├── sparse_jac_for.cpp │ │ ├── sparse_jac_rev.cpp │ │ ├── sparse_jacobian.cpp │ │ ├── sparse_sub_hes.cpp │ │ ├── sparsity_sub.cpp │ │ ├── sub_sparse_hes.cpp │ │ ├── subgraph_hes2jac.cpp │ │ ├── subgraph_jac_rev.cpp │ │ ├── subgraph_reverse.cpp │ │ └── subgraph_sparsity.cpp │ ├── utility/ │ │ ├── CMakeLists.txt │ │ ├── check_numeric_type.cpp │ │ ├── check_simple_vector.cpp │ │ ├── cppad_vector.cpp │ │ ├── dll_lib.cpp │ │ ├── error_handler.cpp │ │ ├── index_sort.cpp │ │ ├── lu_factor.cpp │ │ ├── lu_invert.cpp │ │ ├── lu_solve.cpp │ │ ├── nan.cpp │ │ ├── near_equal.cpp │ │ ├── ode_err_control.cpp │ │ ├── ode_err_maxabs.cpp │ │ ├── ode_gear.cpp │ │ ├── ode_gear_control.cpp │ │ ├── poly.cpp │ │ ├── pow_int.cpp │ │ ├── romberg_mul.cpp │ │ ├── romberg_one.cpp │ │ ├── rosen_34.cpp │ │ ├── runge45_1.cpp │ │ ├── runge_45.cpp │ │ ├── set_union.cpp │ │ ├── simple_vector.cpp │ │ ├── sparse_rc.cpp │ │ ├── sparse_rcv.cpp │ │ ├── thread_alloc.cpp │ │ ├── to_string.cpp │ │ ├── utility.cpp │ │ └── vector_bool.cpp │ └── valvector/ │ ├── CMakeLists.txt │ ├── ad_join.cpp │ ├── ad_split.cpp │ ├── ad_sum.cpp │ ├── assign.cpp │ ├── azmul.cpp │ ├── base_require.cpp │ ├── binary_op.cpp │ ├── compare_op.cpp │ ├── compound_op.cpp │ ├── condexp.cpp │ ├── ctor.cpp │ ├── element.cpp │ ├── get_started.cpp │ ├── llsq_obj.cpp │ ├── output.cpp │ ├── pow.cpp │ ├── resize.cpp │ ├── size.cpp │ ├── sum.cpp │ ├── unary_math.cpp │ ├── unary_op.cpp │ └── valvector.cpp ├── include/ │ └── cppad/ │ ├── CMakeLists.txt │ ├── base_require.hpp │ ├── configure.hpp.in │ ├── core/ │ │ ├── abort_recording.hpp │ │ ├── abs.hpp │ │ ├── abs_normal_fun.hpp │ │ ├── ad.hpp │ │ ├── ad_assign.hpp │ │ ├── ad_binary.hpp │ │ ├── ad_ctor.hpp │ │ ├── ad_fun.hpp │ │ ├── ad_fun.xrst │ │ ├── ad_io.hpp │ │ ├── ad_to_string.hpp │ │ ├── ad_type.hpp │ │ ├── ad_valued.hpp │ │ ├── add.hpp │ │ ├── add_eq.hpp │ │ ├── arithmetic.hpp │ │ ├── atan2.hpp │ │ ├── atomic/ │ │ │ ├── atomic.xrst │ │ │ ├── four/ │ │ │ │ ├── atomic.hpp │ │ │ │ ├── atomic.xrst │ │ │ │ ├── call.hpp │ │ │ │ ├── ctor.hpp │ │ │ │ ├── devel/ │ │ │ │ │ ├── devel.xrst │ │ │ │ │ ├── hes_sparsity.hpp │ │ │ │ │ └── jac_sparsity.hpp │ │ │ │ ├── for_type.hpp │ │ │ │ ├── forward.hpp │ │ │ │ ├── hes_sparsity.hpp │ │ │ │ ├── jac_sparsity.hpp │ │ │ │ ├── rev_depend.hpp │ │ │ │ └── reverse.hpp │ │ │ ├── one/ │ │ │ │ └── atomic.hpp │ │ │ ├── three/ │ │ │ │ ├── afun.hpp │ │ │ │ ├── atomic.hpp │ │ │ │ ├── atomic.xrst │ │ │ │ ├── ctor.hpp │ │ │ │ ├── for_type.hpp │ │ │ │ ├── forward.hpp │ │ │ │ ├── hes_sparsity.hpp │ │ │ │ ├── jac_sparsity.hpp │ │ │ │ ├── rev_depend.hpp │ │ │ │ └── reverse.hpp │ │ │ └── two/ │ │ │ ├── afun.hpp │ │ │ ├── atomic.hpp │ │ │ ├── clear.hpp │ │ │ ├── ctor.hpp │ │ │ ├── for_sparse_hes.hpp │ │ │ ├── for_sparse_jac.hpp │ │ │ ├── forward.hpp │ │ │ ├── option.hpp │ │ │ ├── rev_depend.hpp │ │ │ ├── rev_sparse_hes.hpp │ │ │ ├── rev_sparse_jac.hpp │ │ │ └── reverse.hpp │ │ ├── azmul.hpp │ │ ├── base2ad.hpp │ │ ├── base_complex.hpp │ │ ├── base_cond_exp.hpp │ │ ├── base_double.hpp │ │ ├── base_float.hpp │ │ ├── base_hash.hpp │ │ ├── base_limits.hpp │ │ ├── base_std_math.hpp │ │ ├── base_to_string.hpp │ │ ├── bender_quad.hpp │ │ ├── bool_fun.hpp │ │ ├── bool_valued.hpp │ │ ├── capacity_order.hpp │ │ ├── check_for_nan.hpp │ │ ├── chkpoint_one/ │ │ │ ├── chkpoint_one.hpp │ │ │ ├── ctor.hpp │ │ │ ├── for_sparse_jac.hpp │ │ │ ├── forward.hpp │ │ │ ├── rev_sparse_hes.hpp │ │ │ ├── rev_sparse_jac.hpp │ │ │ ├── reverse.hpp │ │ │ ├── set_hes_sparse_bool.hpp │ │ │ ├── set_hes_sparse_set.hpp │ │ │ ├── set_jac_sparse_bool.hpp │ │ │ └── set_jac_sparse_set.hpp │ │ ├── chkpoint_two/ │ │ │ ├── chk_fun.xrst │ │ │ ├── chkpoint_two.hpp │ │ │ ├── ctor.hpp │ │ │ ├── dynamic.hpp │ │ │ ├── for_type.hpp │ │ │ ├── forward.hpp │ │ │ ├── hes_sparsity.hpp │ │ │ ├── jac_sparsity.hpp │ │ │ ├── rev_depend.hpp │ │ │ └── reverse.hpp │ │ ├── compare.hpp │ │ ├── compound_assign.hpp │ │ ├── con_dyn_var.hpp │ │ ├── cond_exp.hpp │ │ ├── convert.hpp │ │ ├── cppad_assert.hpp │ │ ├── dependent.hpp │ │ ├── discrete/ │ │ │ ├── devel.xrst │ │ │ ├── discrete.hpp │ │ │ └── user.xrst │ │ ├── div.hpp │ │ ├── div_eq.hpp │ │ ├── drivers.hpp │ │ ├── epsilon.hpp │ │ ├── equal_op_seq.hpp │ │ ├── for_hes_sparsity.hpp │ │ ├── for_jac_sparsity.hpp │ │ ├── for_one.hpp │ │ ├── for_sparse_hes.hpp │ │ ├── for_sparse_jac.hpp │ │ ├── for_two.hpp │ │ ├── forward/ │ │ │ ├── compare_change.xrst │ │ │ ├── devel.xrst │ │ │ ├── forward.hpp │ │ │ ├── forward_dir.xrst │ │ │ ├── forward_one.xrst │ │ │ ├── forward_order.xrst │ │ │ ├── forward_two.xrst │ │ │ ├── forward_zero.xrst │ │ │ └── size_order.xrst │ │ ├── fun_check.hpp │ │ ├── fun_construct.hpp │ │ ├── fun_eval.hpp │ │ ├── fun_property.xrst │ │ ├── function_name.xrst │ │ ├── graph/ │ │ │ ├── cpp_ad_graph.xrst │ │ │ ├── cpp_graph.hpp │ │ │ ├── cpp_graph.xrst │ │ │ ├── from_graph.hpp │ │ │ ├── from_json.hpp │ │ │ ├── graph_op_enum.hpp │ │ │ ├── json_ad_graph.xrst │ │ │ ├── json_graph_op.xrst │ │ │ ├── to_graph.hpp │ │ │ └── to_json.hpp │ │ ├── hash_code.hpp │ │ ├── hessian.hpp │ │ ├── identical.hpp │ │ ├── independent/ │ │ │ ├── devel.xrst │ │ │ ├── independent.hpp │ │ │ └── user.xrst │ │ ├── integer.hpp │ │ ├── jacobian.hpp │ │ ├── lu_ratio.hpp │ │ ├── mul.hpp │ │ ├── mul_eq.hpp │ │ ├── near_equal_ext.hpp │ │ ├── new_dynamic.hpp │ │ ├── num_skip.hpp │ │ ├── numeric_limits.hpp │ │ ├── omp_max_thread.hpp │ │ ├── opt_val_hes.hpp │ │ ├── optimize.hpp │ │ ├── ordered.hpp │ │ ├── parallel_ad.hpp │ │ ├── pow.hpp │ │ ├── print_for.hpp │ │ ├── rev_hes_sparsity.hpp │ │ ├── rev_jac_sparsity.hpp │ │ ├── rev_one.hpp │ │ ├── rev_sparse_hes.hpp │ │ ├── rev_sparse_jac.hpp │ │ ├── rev_two.hpp │ │ ├── reverse.hpp │ │ ├── sign.hpp │ │ ├── sparse.hpp │ │ ├── sparse_hes.hpp │ │ ├── sparse_hessian.hpp │ │ ├── sparse_jac.hpp │ │ ├── sparse_jacobian.hpp │ │ ├── standard_math.hpp │ │ ├── std_math_11.hpp │ │ ├── sub.hpp │ │ ├── sub_eq.hpp │ │ ├── subgraph_jac_rev.hpp │ │ ├── subgraph_reverse.hpp │ │ ├── subgraph_sparsity.hpp │ │ ├── tape_link.hpp │ │ ├── testvector.hpp │ │ ├── to_csrc.hpp │ │ ├── unary_minus.hpp │ │ ├── unary_plus.hpp │ │ ├── undef.hpp │ │ ├── user_ad.hpp │ │ ├── value.hpp │ │ ├── var2par.hpp │ │ ├── vec_ad/ │ │ │ ├── user.xrst │ │ │ └── vec_ad.hpp │ │ └── zdouble.hpp │ ├── cppad.hpp │ ├── example/ │ │ ├── atomic_four/ │ │ │ ├── lin_ode/ │ │ │ │ ├── base_solver.hpp │ │ │ │ ├── for_type.hpp │ │ │ │ ├── forward.hpp │ │ │ │ ├── get.hpp │ │ │ │ ├── hes_sparsity.hpp │ │ │ │ ├── implement.xrst │ │ │ │ ├── jac_sparsity.hpp │ │ │ │ ├── lin_ode.hpp │ │ │ │ ├── lin_ode.xrst │ │ │ │ ├── rev_depend.hpp │ │ │ │ ├── reverse.hpp │ │ │ │ ├── reverse_2.xrst │ │ │ │ └── set.hpp │ │ │ ├── mat_mul/ │ │ │ │ ├── base_mat_mul.hpp │ │ │ │ ├── for_type.hpp │ │ │ │ ├── forward.hpp │ │ │ │ ├── get.hpp │ │ │ │ ├── hes_sparsity.hpp │ │ │ │ ├── implement.xrst │ │ │ │ ├── jac_sparsity.hpp │ │ │ │ ├── mat_mul.hpp │ │ │ │ ├── mat_mul.xrst │ │ │ │ ├── rev_depend.hpp │ │ │ │ ├── reverse.hpp │ │ │ │ └── set.hpp │ │ │ └── vector/ │ │ │ ├── add_op.hpp │ │ │ ├── div_op.hpp │ │ │ ├── for_type.hpp │ │ │ ├── forward_op.hpp │ │ │ ├── hes_sparsity.hpp │ │ │ ├── implement.xrst │ │ │ ├── jac_sparsity.hpp │ │ │ ├── mul_op.hpp │ │ │ ├── neg_op.hpp │ │ │ ├── rev_depend.hpp │ │ │ ├── reverse_op.hpp │ │ │ ├── sub_op.hpp │ │ │ ├── vector.hpp │ │ │ └── vector.xrst │ │ ├── atomic_three/ │ │ │ └── mat_mul.hpp │ │ ├── atomic_two/ │ │ │ ├── eigen_cholesky.hpp │ │ │ ├── eigen_mat_inv.hpp │ │ │ └── eigen_mat_mul.hpp │ │ ├── base_adolc.hpp │ │ ├── code_gen_fun.hpp │ │ ├── cppad_eigen.hpp │ │ ├── eigen_plugin.hpp │ │ └── valvector/ │ │ ├── class.hpp │ │ ├── split_join.hpp │ │ └── sum.hpp │ ├── ipopt/ │ │ ├── solve.hpp │ │ ├── solve_callback.hpp │ │ └── solve_result.hpp │ ├── local/ │ │ ├── ad_tape.hpp │ │ ├── atom_state.hpp │ │ ├── atomic_index.hpp │ │ ├── color_general.hpp │ │ ├── color_symmetric.hpp │ │ ├── cppad_colpack.hpp │ │ ├── declare_ad.hpp │ │ ├── define.hpp │ │ ├── graph/ │ │ │ ├── cpp_graph_itr.hpp │ │ │ ├── cpp_graph_itr.xrst │ │ │ ├── cpp_graph_op.hpp │ │ │ ├── csrc_writer.hpp │ │ │ ├── json_lexer.hpp │ │ │ ├── json_lexer.xrst │ │ │ ├── json_parser.hpp │ │ │ └── json_writer.hpp │ │ ├── hash_code.hpp │ │ ├── independent.hpp │ │ ├── is_pod.hpp │ │ ├── is_pod.hpp.in │ │ ├── op_code_dyn.hpp │ │ ├── op_code_var.hpp │ │ ├── optimize/ │ │ │ ├── cexp_info.hpp │ │ │ ├── csum_op_info.hpp │ │ │ ├── csum_stacks.hpp │ │ │ ├── extract_option.hpp │ │ │ ├── get_cexp_info.hpp │ │ │ ├── get_dyn_previous.hpp │ │ │ ├── get_op_previous.hpp │ │ │ ├── get_op_usage.hpp │ │ │ ├── get_par_usage.hpp │ │ │ ├── hash_code.hpp │ │ │ ├── match_op.hpp │ │ │ ├── optimize_run.hpp │ │ │ ├── record_csum.hpp │ │ │ ├── record_pv.hpp │ │ │ ├── record_vp.hpp │ │ │ ├── record_vv.hpp │ │ │ ├── size_pair.hpp │ │ │ └── usage.hpp │ │ ├── play/ │ │ │ ├── addr_enum.hpp │ │ │ ├── atom_op_info.hpp │ │ │ ├── dyn_player.hpp │ │ │ ├── player.hpp │ │ │ ├── random_iterator.hpp │ │ │ ├── random_setup.hpp │ │ │ ├── sequential_iterator.hpp │ │ │ └── subgraph_iterator.hpp │ │ ├── pod_vector.hpp │ │ ├── record/ │ │ │ ├── comp_op.hpp │ │ │ ├── cond_exp.hpp │ │ │ ├── dyn_recorder.hpp │ │ │ ├── put_dyn_atomic.hpp │ │ │ ├── put_var_atomic.hpp │ │ │ ├── put_var_vecad.hpp │ │ │ └── recorder.hpp │ │ ├── set_get_in_parallel.hpp │ │ ├── sparse/ │ │ │ ├── dev_sparse.xrst │ │ │ ├── internal.hpp │ │ │ ├── list_setvec.hpp │ │ │ ├── pack_setvec.hpp │ │ │ ├── pack_setvec.xrst │ │ │ ├── setvector.xrst │ │ │ ├── size_setvec.hpp │ │ │ └── svec_setvec.hpp │ │ ├── std_set.hpp │ │ ├── subgraph/ │ │ │ ├── arg_variable.hpp │ │ │ ├── entire_call.hpp │ │ │ ├── get_rev.hpp │ │ │ ├── info.hpp │ │ │ ├── init_rev.hpp │ │ │ └── sparsity.hpp │ │ ├── sweep/ │ │ │ ├── call_atomic.hpp │ │ │ ├── dev_sweep.xrst │ │ │ ├── dynamic.hpp │ │ │ ├── for_hes.hpp │ │ │ ├── for_jac.hpp │ │ │ ├── forward_0.hpp │ │ │ ├── forward_any.hpp │ │ │ ├── forward_dir.hpp │ │ │ ├── rev_hes.hpp │ │ │ ├── rev_jac.hpp │ │ │ ├── reverse.hpp │ │ │ └── template/ │ │ │ └── forward_sweep.xrst │ │ ├── temp_file.hpp │ │ ├── utility/ │ │ │ ├── cppad_vector_itr.hpp │ │ │ └── vector_bool.hpp │ │ ├── val_graph/ │ │ │ ├── base_op.hpp │ │ │ ├── binary_op.hpp │ │ │ ├── call_atomic.hpp │ │ │ ├── call_op.hpp │ │ │ ├── cexp_op.hpp │ │ │ ├── comp_op.hpp │ │ │ ├── compress.hpp │ │ │ ├── con_op.hpp │ │ │ ├── csum_op.hpp │ │ │ ├── cumulative.hpp │ │ │ ├── dead_code.hpp │ │ │ ├── dis_op.hpp │ │ │ ├── dyn_type.hpp │ │ │ ├── enable_parallel.hpp │ │ │ ├── fold_con.hpp │ │ │ ├── fun2val.hpp │ │ │ ├── op2arg_index.hpp │ │ │ ├── op_enum2class.hpp │ │ │ ├── op_hash_table.hpp │ │ │ ├── op_iterator.hpp │ │ │ ├── option.hpp │ │ │ ├── pri_op.hpp │ │ │ ├── print_op.hpp │ │ │ ├── record.hpp │ │ │ ├── record_new.hpp │ │ │ ├── renumber.hpp │ │ │ ├── rev_depend.hpp │ │ │ ├── summation.hpp │ │ │ ├── tape.hpp │ │ │ ├── unary_op.hpp │ │ │ ├── val2fun.hpp │ │ │ ├── val_graph.xrst │ │ │ ├── val_optimize.hpp │ │ │ ├── val_type.hpp │ │ │ ├── var_type.hpp │ │ │ └── vector_op.hpp │ │ └── var_op/ │ │ ├── abs_op.hpp │ │ ├── acos_op.hpp │ │ ├── acosh_op.hpp │ │ ├── add_op.hpp │ │ ├── asin_op.hpp │ │ ├── asinh_op.hpp │ │ ├── atan_op.hpp │ │ ├── atanh_op.hpp │ │ ├── atomic_op.hpp │ │ ├── binary_op.xrst │ │ ├── cexp_op.hpp │ │ ├── compare_op.hpp │ │ ├── cos_op.hpp │ │ ├── cosh_op.hpp │ │ ├── cskip_op.hpp │ │ ├── csum_op.hpp │ │ ├── dis_op.hpp │ │ ├── div_op.hpp │ │ ├── erf_op.hpp │ │ ├── exp_op.hpp │ │ ├── expm1_op.hpp │ │ ├── load_op.hpp │ │ ├── log1p_op.hpp │ │ ├── log_op.hpp │ │ ├── mul_op.hpp │ │ ├── neg_op.hpp │ │ ├── one_var.hpp │ │ ├── par_op.hpp │ │ ├── pow_op.hpp │ │ ├── pri_op.hpp │ │ ├── prototype_op.hpp │ │ ├── sign_op.hpp │ │ ├── sin_op.hpp │ │ ├── sinh_op.hpp │ │ ├── sqrt_op.hpp │ │ ├── store_op.hpp │ │ ├── sub_op.hpp │ │ ├── tan_op.hpp │ │ ├── tanh_op.hpp │ │ ├── template/ │ │ │ ├── atomic_op.xrst │ │ │ ├── forward_dir.xrst │ │ │ ├── forward_op.xrst │ │ │ └── reverse_op.xrst │ │ ├── two_var.hpp │ │ ├── unary_op.xrst │ │ ├── var_op.hpp │ │ └── zmul_op.hpp │ ├── speed/ │ │ ├── det_33.hpp │ │ ├── det_by_lu.hpp │ │ ├── det_by_minor.hpp │ │ ├── det_grad_33.hpp │ │ ├── det_of_minor.hpp │ │ ├── mat_sum_sq.hpp │ │ ├── ode_evaluate.hpp │ │ ├── sparse_hes_fun.hpp │ │ ├── sparse_jac_fun.hpp │ │ └── uniform_01.hpp │ ├── utility/ │ │ ├── check_numeric_type.hpp │ │ ├── check_simple_vector.hpp │ │ ├── create_dll_lib.hpp │ │ ├── elapsed_seconds.hpp │ │ ├── error_handler.hpp │ │ ├── index_sort.hpp │ │ ├── link_dll_lib.hpp │ │ ├── lu_factor.hpp │ │ ├── lu_invert.hpp │ │ ├── lu_solve.hpp │ │ ├── memory_leak.hpp │ │ ├── nan.hpp │ │ ├── near_equal.hpp │ │ ├── ode_err_control.hpp │ │ ├── ode_gear.hpp │ │ ├── ode_gear_control.hpp │ │ ├── omp_alloc.hpp │ │ ├── poly.hpp │ │ ├── pow_int.hpp │ │ ├── romberg_mul.hpp │ │ ├── romberg_one.hpp │ │ ├── rosen_34.hpp │ │ ├── runge_45.hpp │ │ ├── set_union.hpp │ │ ├── sparse2eigen.hpp │ │ ├── sparse_rc.hpp │ │ ├── sparse_rcv.hpp │ │ ├── speed_test.hpp │ │ ├── test_boolofvoid.hpp │ │ ├── thread_alloc.hpp │ │ ├── time_test.hpp │ │ ├── to_string.hpp │ │ ├── track_new_del.hpp │ │ ├── vector.hpp │ │ ├── vector_bool.hpp │ │ └── xrst/ │ │ ├── cppad_vector.xrst │ │ ├── dev_cppad_vector.xrst │ │ ├── dev_utility.xrst │ │ ├── dev_vector_bool.xrst │ │ └── utility.xrst │ ├── utility.hpp │ └── wno_conversion.hpp ├── introduction/ │ ├── CMakeLists.txt │ ├── exp_2.cpp │ ├── exp_2.hpp │ ├── exp_2.xrst │ ├── exp_2_cppad.cpp │ ├── exp_2_for0.cpp │ ├── exp_2_for1.cpp │ ├── exp_2_for2.cpp │ ├── exp_2_rev1.cpp │ ├── exp_2_rev2.cpp │ ├── exp_eps.cpp │ ├── exp_eps.hpp │ ├── exp_eps.xrst │ ├── exp_eps_cppad.cpp │ ├── exp_eps_for0.cpp │ ├── exp_eps_for1.cpp │ ├── exp_eps_for2.cpp │ ├── exp_eps_rev1.cpp │ ├── exp_eps_rev2.cpp │ └── introduction.cpp ├── pkgconfig/ │ ├── CMakeLists.txt │ ├── cppad-uninstalled.pc.in │ └── cppad.pc.in ├── readme.md ├── speed/ │ ├── CMakeLists.txt │ ├── add_test.sh │ ├── adolc/ │ │ ├── CMakeLists.txt │ │ ├── adolc_alloc_mat.hpp │ │ ├── adolc_usrparms.sh │ │ ├── alloc_mat.cpp │ │ ├── det_lu.cpp │ │ ├── det_minor.cpp │ │ ├── mat_mul.cpp │ │ ├── ode.cpp │ │ ├── poly.cpp │ │ ├── sparse_hessian.cpp │ │ ├── sparse_jacobian.cpp │ │ └── speed_adolc.xrst │ ├── cppad/ │ │ ├── CMakeLists.txt │ │ ├── det_lu.cpp │ │ ├── det_minor.cpp │ │ ├── mat_mul.cpp │ │ ├── ode.cpp │ │ ├── poly.cpp │ │ ├── sparse_hessian.cpp │ │ ├── sparse_jacobian.cpp │ │ └── speed_cppad.xrst │ ├── cppad_jit/ │ │ ├── CMakeLists.txt │ │ ├── det_lu.cpp │ │ ├── det_minor.cpp │ │ ├── mat_mul.cpp │ │ ├── ode.cpp │ │ ├── poly.cpp │ │ ├── sparse_hessian.cpp │ │ ├── sparse_jacobian.cpp │ │ └── speed_cppad_jit.xrst │ ├── cppadcg/ │ │ ├── CMakeLists.txt │ │ ├── det_lu.cpp │ │ ├── det_minor.cpp │ │ ├── mat_mul.cpp │ │ ├── ode.cpp │ │ ├── poly.cpp │ │ ├── sparse_hessian.cpp │ │ ├── sparse_jacobian.cpp │ │ └── speed_cppadcg.xrst │ ├── dev_speed.xrst │ ├── double/ │ │ ├── CMakeLists.txt │ │ ├── det_lu.cpp │ │ ├── det_minor.cpp │ │ ├── mat_mul.cpp │ │ ├── ode.cpp │ │ ├── poly.cpp │ │ ├── sparse_hessian.cpp │ │ ├── sparse_jacobian.cpp │ │ └── speed_double.xrst │ ├── example/ │ │ ├── CMakeLists.txt │ │ ├── det_by_lu.cpp │ │ ├── det_by_minor.cpp │ │ ├── det_of_minor.cpp │ │ ├── elapsed_seconds.cpp │ │ ├── example.cpp │ │ ├── mat_sum_sq.cpp │ │ ├── ode_evaluate.cpp │ │ ├── sparse_hes_fun.cpp │ │ ├── sparse_jac_fun.cpp │ │ ├── speed_program.cpp │ │ ├── speed_test.cpp │ │ └── time_test.cpp │ ├── fadbad/ │ │ ├── CMakeLists.txt │ │ ├── det_lu.cpp │ │ ├── det_minor.cpp │ │ ├── mat_mul.cpp │ │ ├── ode.cpp │ │ ├── poly.cpp │ │ ├── sparse_hessian.cpp │ │ ├── sparse_jacobian.cpp │ │ └── speed_fadbad.xrst │ ├── main.cpp │ ├── profile/ │ │ ├── CMakeLists.txt │ │ └── gprof.sed.in │ ├── sacado/ │ │ ├── CMakeLists.txt │ │ ├── det_lu.cpp │ │ ├── det_minor.cpp │ │ ├── mat_mul.cpp │ │ ├── ode.cpp │ │ ├── poly.cpp │ │ ├── sparse_hessian.cpp │ │ ├── sparse_jacobian.cpp │ │ └── speed_sacado.xrst │ ├── speed.xrst │ ├── speed_utility.xrst │ ├── src/ │ │ ├── CMakeLists.txt │ │ ├── dev_link.xrst │ │ ├── dev_sparse_hessian.xrst │ │ ├── link.xrst │ │ ├── link_det_lu.cpp │ │ ├── link_det_minor.cpp │ │ ├── link_mat_mul.cpp │ │ ├── link_ode.cpp │ │ ├── link_poly.cpp │ │ ├── link_sparse_hessian.cpp │ │ ├── link_sparse_hessian.hpp │ │ ├── link_sparse_jacobian.cpp │ │ └── link_sparse_jacobian.hpp │ └── xpackage/ │ ├── CMakeLists.txt │ ├── det_lu.cpp │ ├── det_minor.cpp │ ├── mat_mul.cpp │ ├── ode.cpp │ ├── poly.cpp │ ├── sparse_hessian.cpp │ ├── sparse_jacobian.cpp │ └── speed_xpackage.xrst ├── test_more/ │ ├── CMakeLists.txt │ ├── compare_c/ │ │ ├── CMakeLists.txt │ │ ├── c/ │ │ │ └── CMakeLists.txt │ │ ├── cpp/ │ │ │ └── CMakeLists.txt │ │ └── det_by_minor.c │ ├── cppad_for_tmb/ │ │ ├── CMakeLists.txt │ │ ├── cppad_for_tmb.cpp │ │ ├── implicit_ctor.cpp │ │ ├── multi_atomic_three.cpp │ │ ├── multi_atomic_two.cpp │ │ ├── multi_chkpoint_one.cpp │ │ ├── multi_chkpoint_two.cpp │ │ └── prefer_reverse.cpp │ ├── debug_rel/ │ │ ├── CMakeLists.txt │ │ ├── debug.cpp │ │ ├── debug_rel.cpp │ │ └── release.cpp │ ├── deprecated/ │ │ ├── CMakeLists.txt │ │ ├── atomic_two/ │ │ │ ├── CMakeLists.txt │ │ │ ├── atomic_sparsity.cpp │ │ │ ├── atomic_two.cpp │ │ │ ├── base2ad.cpp │ │ │ ├── for_sparse_hes.cpp │ │ │ ├── for_sparse_jac.cpp │ │ │ ├── forward.cpp │ │ │ ├── get_started.cpp │ │ │ ├── mat_mul.cpp │ │ │ ├── mat_mul.hpp │ │ │ ├── norm_sq.cpp │ │ │ ├── reciprocal.cpp │ │ │ ├── rev_sparse_hes.cpp │ │ │ ├── rev_sparse_jac.cpp │ │ │ ├── reverse.cpp │ │ │ ├── set_sparsity.cpp │ │ │ └── tangent.cpp │ │ ├── chkpoint_one/ │ │ │ ├── CMakeLists.txt │ │ │ ├── chkpoint_one.cpp │ │ │ ├── extended_ode.cpp │ │ │ ├── get_started.cpp │ │ │ ├── mul_level.cpp │ │ │ └── ode.cpp │ │ ├── deprecated.cpp │ │ ├── old_mat_mul.cpp │ │ ├── old_mat_mul.hpp │ │ ├── old_reciprocal.cpp │ │ ├── old_tan.cpp │ │ ├── old_usead_1.cpp │ │ ├── old_usead_2.cpp │ │ ├── omp_alloc.cpp │ │ ├── track_new_del.cpp │ │ └── zdouble.cpp │ └── general/ │ ├── CMakeLists.txt │ ├── abs_normal.cpp │ ├── acos.cpp │ ├── acosh.cpp │ ├── add.cpp │ ├── add_eq.cpp │ ├── add_zero.cpp │ ├── adfun.cpp │ ├── alloc_openmp.cpp │ ├── asin.cpp │ ├── asinh.cpp │ ├── assign.cpp │ ├── atan.cpp │ ├── atan2.cpp │ ├── atanh.cpp │ ├── atomic_four.cpp │ ├── atomic_three.cpp │ ├── azmul.cpp │ ├── base2ad.cpp │ ├── base_adolc.cpp │ ├── base_alloc.cpp │ ├── base_complex.cpp │ ├── bool_sparsity.cpp │ ├── check_simple_vector.cpp │ ├── chkpoint_one.cpp │ ├── chkpoint_two.cpp │ ├── compare.cpp │ ├── compare_change.cpp │ ├── cond_exp.cpp │ ├── cond_exp_ad.cpp │ ├── cond_exp_rev.cpp │ ├── copy.cpp │ ├── cos.cpp │ ├── cosh.cpp │ ├── cpp_graph.cpp │ ├── cppad_eigen.cpp │ ├── cppad_vector.cpp │ ├── dbl_epsilon.cpp │ ├── dependency.cpp │ ├── div.cpp │ ├── div_eq.cpp │ ├── div_zero_one.cpp │ ├── eigen_mat_inv.cpp │ ├── erf.cpp │ ├── exp.cpp │ ├── expm1.cpp │ ├── extern_value.cpp │ ├── extern_value.hpp │ ├── fabs.cpp │ ├── for_hes_sparsity.cpp │ ├── for_hess.cpp │ ├── for_jac_sparsity.cpp │ ├── forward.cpp │ ├── forward_dir.cpp │ ├── forward_order.cpp │ ├── from_base.cpp │ ├── fun_check.cpp │ ├── general.cpp │ ├── hes_sparsity.cpp │ ├── ipopt_solve.cpp │ ├── jacobian.cpp │ ├── json_graph.cpp │ ├── local/ │ │ ├── is_pod.cpp │ │ ├── json_lexer.cpp │ │ ├── json_parser.cpp │ │ ├── temp_file.cpp │ │ └── vector_set.cpp │ ├── log.cpp │ ├── log10.cpp │ ├── log1p.cpp │ ├── mul.cpp │ ├── mul_cond_rev.cpp │ ├── mul_cskip.cpp │ ├── mul_eq.cpp │ ├── mul_level.cpp │ ├── mul_zdouble.cpp │ ├── mul_zero_one.cpp │ ├── near_equal_ext.cpp │ ├── neg.cpp │ ├── new_dynamic.cpp │ ├── num_limits.cpp │ ├── ode_err_control.cpp │ ├── optimize.cpp │ ├── parameter.cpp │ ├── poly.cpp │ ├── pow.cpp │ ├── pow_int.cpp │ ├── print_for.cpp │ ├── rev_sparse_jac.cpp │ ├── rev_two.cpp │ ├── reverse.cpp │ ├── romberg_one.cpp │ ├── rosen_34.cpp │ ├── runge_45.cpp │ ├── simple_vector.cpp │ ├── sin.cpp │ ├── sin_cos.cpp │ ├── sinh.cpp │ ├── sparse_hessian.cpp │ ├── sparse_jac_work.cpp │ ├── sparse_jacobian.cpp │ ├── sparse_sub_hes.cpp │ ├── sparse_vec_ad.cpp │ ├── sqrt.cpp │ ├── std_math.cpp │ ├── sub.cpp │ ├── sub_eq.cpp │ ├── sub_zero.cpp │ ├── subgraph_1.cpp │ ├── subgraph_2.cpp │ ├── subgraph_hes2jac.cpp │ ├── tan.cpp │ ├── to_csrc.cpp │ ├── to_string.cpp │ ├── value.cpp │ ├── vec_ad.cpp │ ├── vec_ad_par.cpp │ └── vec_unary.cpp ├── typos.toml ├── user_guide.xrst ├── uw_copy_040507.html ├── val_graph/ │ ├── CMakeLists.txt │ ├── atomic_xam.hpp │ ├── binary_xam.cpp │ ├── call_xam.cpp │ ├── cexp_xam.cpp │ ├── comp_xam.cpp │ ├── compress_xam.cpp │ ├── con_xam.cpp │ ├── csum_xam.cpp │ ├── cumulative_xam.cpp │ ├── dead_xam.cpp │ ├── dis_xam.cpp │ ├── fold_con_xam.cpp │ ├── fun2val_xam.cpp │ ├── pri_xam.cpp │ ├── renumber_xam.cpp │ ├── summation_xam.cpp │ ├── test/ │ │ ├── ad_double.cpp │ │ ├── fold.cpp │ │ ├── fun2val.cpp │ │ ├── nan.cpp │ │ ├── opt_call.cpp │ │ ├── optimize.cpp │ │ ├── val2fun.cpp │ │ └── val_optimize.cpp │ ├── unary_xam.cpp │ ├── val2fun_xam.cpp │ ├── val_graph.cpp │ └── vec_xam.cpp ├── xrst/ │ ├── base_require/ │ │ ├── base_example.xrst │ │ ├── base_identical.xrst │ │ ├── base_member.xrst │ │ └── base_ordered.xrst │ ├── det_33_hpp.xrst │ ├── det_by_lu_hpp.xrst │ ├── det_by_minor_hpp.xrst │ ├── det_grad_33_hpp.xrst │ ├── det_of_minor_hpp.xrst │ ├── devel/ │ │ ├── devel.xrst │ │ ├── dynamic.xrst │ │ └── whats_new/ │ │ └── 2024.xrst │ ├── example.xrst │ ├── example_list.xrst │ ├── install/ │ │ ├── adolc.xrst │ │ ├── cmake.xrst │ │ ├── cmake_check.xrst │ │ ├── colpack_prefix.xrst │ │ ├── cppadcg.xrst │ │ ├── download.xrst │ │ ├── eigen.xrst │ │ ├── fadbad_prefix.xrst │ │ ├── install.xrst │ │ ├── ipopt.xrst │ │ ├── sacado_prefix.xrst │ │ └── testvector.xrst │ ├── introduction.xrst │ ├── lu_det_and_solve.xrst │ ├── lu_factor_hpp.xrst │ ├── lu_invert_hpp.xrst │ ├── lu_solve_hpp.xrst │ ├── mat_sum_sq_hpp.xrst │ ├── mul_level.xrst │ ├── multi_thread.xrst │ ├── numeric_type.xrst │ ├── ode_evaluate.xrst │ ├── poly_hpp.xrst │ ├── preprocessor.xrst │ ├── reverse/ │ │ ├── reverse_any.xrst │ │ ├── reverse_one.xrst │ │ └── reverse_two.xrst │ ├── simple_vector.xrst │ ├── sparse_hes_fun.xrst │ ├── sparse_jac_fun.xrst │ ├── theory/ │ │ ├── acos_forward.xrst │ │ ├── acos_reverse.xrst │ │ ├── asin_forward.xrst │ │ ├── asin_reverse.xrst │ │ ├── atan_forward.xrst │ │ ├── atan_reverse.xrst │ │ ├── cholesky.xrst │ │ ├── erf_forward.xrst │ │ ├── erf_reverse.xrst │ │ ├── exp_forward.xrst │ │ ├── exp_reverse.xrst │ │ ├── forward_theory.xrst │ │ ├── log_forward.xrst │ │ ├── log_reverse.xrst │ │ ├── pow_forward.xrst │ │ ├── pow_reverse.xrst │ │ ├── research.xrst │ │ ├── reverse_identity.xrst │ │ ├── reverse_theory.xrst │ │ ├── sin_cos_forward.xrst │ │ ├── sin_cos_reverse.xrst │ │ ├── sqrt_forward.xrst │ │ ├── sqrt_reverse.xrst │ │ ├── tan_forward.xrst │ │ ├── tan_reverse.xrst │ │ ├── taylor_ode.xrst │ │ └── theory.xrst │ ├── thread_alloc.xrst │ └── uniform_01_hpp.xrst └── xrst.toml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .circleci/config.yml ================================================ # https://circleci.com/docs/hello-world/ version: 2.1 jobs: hello-job: docker: - image: cimg/node:17.2.0 # the primary container, where your job's commands are run steps: - checkout # check out the code in the project directory - run: echo "hello world" # run the `echo` command workflows: my-workflow: jobs: - hello-job ================================================ FILE: .github/workflows/conda/conda-env.yml ================================================ name: cppad channels: - conda-forge - nodefaults dependencies: - boost - python ================================================ FILE: .github/workflows/conda-bash.yml ================================================ name: conda-bash on: pull_request: push: branches: - master - 'stable/*' env: BUILD_TYPE: Release jobs: build: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: # os: ["ubuntu-latest", "macos-latest"] 2024-04-26 macos-latest failing # Error: No installed conda 'base' environment found ...; see os: ["ubuntu-latest", "macos-latest", "windows-latest"] steps: - uses: actions/checkout@v4 - uses: conda-incubator/setup-miniconda@v3 with: activate-environment: cppad auto-update-conda: true - name: Install cmake and pkgconfig shell: bash -l {0} run: | conda activate cppad conda install -y cmake conda install -y pkgconfig - name: Build CppAD shell: bash -l {0} run: | conda activate cppad if [ "$RUNNER_OS" == "macOS" ] then generator='Unix Makefiles' export SDKROOT=$(xcrun --sdk macosx --show-sdk-path) n_job=$(sysctl -n hw.ncpu) elif [ "$RUNNER_OS" == "Windows" ] then generator='Visual Studio 17 2022' n_job=1 else generator='Unix Makefiles' n_job=$(nproc) fi echo "RUNNER_OS=$RUNNER_OS, n_job=$n_job, generator=$generator" conda activate cppad echo $CONDA_PREFIX mkdir build cd build cmake .. \ -G "$generator" \ -D CMAKE_INSTALL_PREFIX=$CONDA_PREFIX \ -D CMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} cmake --build . \ --config ${{env.BUILD_TYPE}} \ --target check install \ --parallel $n_job ================================================ FILE: .github/workflows/conda-windows-clang.yml ================================================ name: conda-windows-clang on: pull_request: push: branches: - master - 'stable/*' env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE: Release jobs: build: runs-on: ${{ matrix.os }} strategy: fail-fast: true matrix: name: [windows-latest-clang-cl] include: - name: windows-latest-clang-cl os: windows-2019 compiler: clang-cl steps: - uses: actions/checkout@v4 - name: Checkout submodules run: | git submodule update --init - uses: conda-incubator/setup-miniconda@v3 env: ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' with: activate-environment: cppad environment-file: .github/workflows/conda/conda-env.yml python-version: 3.7 - name: Install cmake , pkgconfig, and update conda run: | conda install cmake -c main conda install pkgconfig -c conda-forge - name: Build CppAD shell: cmd /C CALL {0} env: ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' run: | :: unset extra Boost envs set Boost_ROOT= set BOOST_ROOT_1_69_0= set BOOST_ROOT_1_72_0= set PATH=%PATH:C:\hostedtoolcache\windows\Boost\1.72.0;=% :: Create build directory mkdir build pushd build :: Configure # wd6316 is same as gnu -Wno-bitwise-instead-of-logical # https://learn.microsoft.com/en-us/cpp/code-quality/c6316 cmake ^ -G "Visual Studio 16 2019" -T "ClangCl" ^ -DCMAKE_GENERATOR_PLATFORM=x64 ^ -DCMAKE_INSTALL_PREFIX=%CONDA_PREFIX%\Library ^ -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ^ -Dcppad_cxx_flags="/wd6316" ^ -Dcppad_static_lib=TRUE ^ .. :: Build cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --target check install ================================================ FILE: .github/workflows/conda-windows-eigen.yml ================================================ name: conda-windows-eigen on: push: branches: - master - 'stable/*' jobs: build: runs-on: windows-2019 steps: - uses: actions/checkout@v4 - uses: conda-incubator/setup-miniconda@v3 with: activate-environment: conda_env - name: Use conda to install eigen and pkgconfig shell: cmd /C CALL {0} run: | echo conda install --yes eigen pkgconfig conda install --yes eigen pkgconfig - name: Create and check Eigen link, check for eigen3.pc shell: cmd /C CALL {0} run: | mklink /d %CONDA_PREFIX%\Library\include\Eigen^ %CONDA_PREFIX%\Library\include\eigen3\Eigen echo dir %CONDA_PREFIX%\Library\include\Eigen\Core dir %CONDA_PREFIX%\Library\include\Eigen\Core echo dir %CONDA_PREFIX%\Library\share\pkgconfig\eigen3.pc dir %CONDA_PREFIX%\Library\share\pkgconfig\eigen3.pc - name: Build and test CppAD with Eigen shell: cmd /C CALL {0} run: | echo Use vcvarsall.bat to get proper version of cmake call "%programfiles(x86)%\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64 echo where camke where cmake echo mkdir build mkdir build echo cd build cd build echo set PKG_CONFIG_PATH=%CONDA_PREFIX%\Library\share\pkgconfig set PKG_CONFIG_PATH=%CONDA_PREFIX%\Library\share\pkgconfig echo run cmake cmake ^ -D CMAKE_CXX_COMPILER=cl ^ -D CMAKE_C_COMPILER=cl ^ -D CMAKE_BUILD_TYPE=release ^ -G "NMake Makefiles" ^ -D cppad_static_lib=TRUE ^ -D cppad_cxx_flags="/MP /EHs /EHc /std:c++17 /Zc:__cplusplus" ^ .. REM Build and run tests. cmake --build . --target check --parallel 4 ================================================ FILE: .github/workflows/conda-windows-v142.yml ================================================ name: conda-windows-v142 on: pull_request: push: branches: - master - 'stable/*' env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE: Release jobs: build: runs-on: ${{ matrix.os }} strategy: fail-fast: true matrix: name: [windows-latest] include: - name: windows-latest os: windows-2019 steps: - uses: actions/checkout@v4 - name: Checkout submodules run: | git submodule update --init - uses: conda-incubator/setup-miniconda@v3 env: ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' with: activate-environment: cppad environment-file: .github/workflows/conda/conda-env.yml python-version: 3.7 - name: Install cmake , pkgconfig and update conda run: | conda install cmake -c main conda install pkgconfig -c conda-forge - name: Build CppAD shell: cmd /C CALL {0} env: ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' run: | :: unset extra Boost envs set Boost_ROOT= set BOOST_ROOT_1_69_0= set BOOST_ROOT_1_72_0= set PATH=%PATH:C:\hostedtoolcache\windows\Boost\1.72.0;=% :: Create build directory mkdir build pushd build :: Configure cmake ^ -G "Visual Studio 16 2019" -T "v142" -DCMAKE_GENERATOR_PLATFORM=x64 ^ -DCMAKE_INSTALL_PREFIX=%CONDA_PREFIX%\Library ^ -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ^ -Dcppad_static_lib=TRUE ^ .. :: Build, check and Install # https://gitlab.kitware.com/cmake/cmake/-/issues/20564 # --parallel 1 works # --parallel 4 does not work cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --target check install --parallel 1 ================================================ FILE: .github/workflows/ubuntu-macos.yml ================================================ name: ubuntu-macos on: pull_request: push: branches: - master - 'stable/*' jobs: run_tests: runs-on: ${{ matrix.os }} strategy: matrix: os: [ ubuntu-latest, macos-latest ] steps: - name: Check out repository code uses: actions/checkout@v4 - name: set debug_which run: | mkdir build cd build set +e random_03=$(expr $RANDOM % 4) set -e case $random_03 in 0) debug_which='debug_all' ;; 1) debug_which='debug_even' ;; 2) debug_which='debug_odd' ;; 3) debug_which='debug_none' ;; esac echo "$debug_which" > debug_which - name: run cmake run: | cd build debug_which=$(cat debug_which) echo "cmake -D cppad_debug_which=$debug_which" .. cmake -D cppad_debug_which=$debug_which .. - name: run make check run: | if [ "$RUNNER_OS" == "macOS" ] then export SDKROOT=$(xcrun --sdk macosx --show-sdk-path) fi if which nproc >& /dev/null then n_job=$(nproc) else n_job=$(sysctl -n hw.ncpu) fi cd build echo "make -j $n_job check" make -j $n_job check - run: echo "job.status = ${{ job.status }}" ================================================ FILE: .github/workflows/windows-msys2.yml ================================================ name: windows-msys2 on: pull_request: push: branches: - master - 'stable/*' jobs: build: runs-on: windows-latest defaults: run: shell: msys2 {0} steps: - uses: actions/checkout@v4 - uses: msys2/setup-msys2@v2 with: msystem: MINGW64 update: true install: git mingw-w64-x86_64-cc mingw-w64-x86_64-cmake make - name: CppAD check run: | # # Create build directory mkdir build pushd build # # configure cmake -G "MSYS Makefiles" -D cppad_static_lib=TRUE .. # # check cmake --build . --target check --parallel 1 ================================================ FILE: .gitignore ================================================ # ---------------------------------------------------------------------------- # specific files # # # cmake configure files /bin/test_one.sh /include/cppad/configure.hpp # # create by bin/test_one.sh /test_one.cpp /test_one.exe /test_one.exe.dSYM/ # # git commit script /git_commit.sh # ---------------------------------------------------------------------------- # specific directories # # buld directories /build/ /build2/ # # for building external packages /external/ # # for caching git changes /new/ /new.*/ # ---------------------------------------------------------------------------- # specific extensions in top direcotry /*.err /*.log /*.tmp # ---------------------------------------------------------------------------- # vim swap files in any directory *.swp # ---------------------------------------------------------------------------- # temp files in any directory temp temp.* ================================================ FILE: .readthedocs.yaml ================================================ # !! EDITS TO THIS FILE ARE LOST DURING UPDATES BY xrst.git/bin/dev_tools.sh !! # {xrst_comment_ch #} # # Example Read the Docs Configuration # ################################### # # Read the Docs Specification # *************************** # ``_ # # This Example File # ***************** # {xrst_literal} # version: 2 build: os: "ubuntu-22.04" tools: python: "3.10" # # The readthedocs commands feature is still in beta commands: # # xrst - pip install xrst # # This is only necessary if you need the most recent testing verison - pip uninstall -y xrst - pip install --index-url https://test.pypi.org/simple/ xrst # # This command prints the version of xrst that is being used - xrst --version # # build/html # This comamnd builds the xrst user documentation. # Suppress spell warnings because readthdocs uses a different dictionry. - xrst --page_source --html_theme furo --group_list default app --index_page_name user_guide --suppress_spell_warnings # # _readthedocs/html # The directory that Read the Docs uploads when commands is used. - mkdir _readthedocs - rm -r build/html/.doctrees - cp -r build/html _readthedocs/html ================================================ FILE: .travis.yml ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # sudo: false language: cpp compiler: gcc script: # bin/travis.sh (make|test_one) target1 target2 ... # bin/travis.sh make check install uninstall notifications: email: recipients: bradbell@seanet.com on_success: change # send e-mail what success status changes on_failure: always # always send e-mail when a test fails ================================================ FILE: CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-26 Bradley M. Bell # ---------------------------------------------------------------------------- # ============================================================================= # Some constants # ============================================================================= # # Set the minimum required version of cmake for this project. # see https://cmake.org//pipermail/cmake/2013-January/053213.html CMAKE_MINIMUM_REQUIRED(VERSION 3.10) # # Use the version of BoostConfig.cmake provided by boost. IF( POLICY CMP0167 ) CMAKE_POLICY(SET CMP0167 NEW) ENDIF( POLICY CMP0167 ) # # Only interpret if() arguments as variables or keywords when unquoted. IF( POLICY CMP0054 ) CMAKE_POLICY(SET CMP0054 NEW) ENDIF( POLICY CMP0054 ) # # cppad_version is used by version.sh to get the version number. SET(cppad_version "20260408") SET(cppad_url "https://coin-or.github.io/CppAD" ) SET(cppad_description "Differentiation of C++ Algorithms" ) IF( NOT DEFINED CMAKE_BUILD_TYPE) SET(CMAKE_BUILD_TYPE "NOTFOUND") ENDIF( NOT DEFINED CMAKE_BUILD_TYPE) # # Set name of this project and create the variables # cppad_BINARY_DIR and cppad_SOURCE_DIR. # project(projectname [CXX] [C] [Java]) PROJECT(cppad) # # Add the include sub-directory to the list of C++ preprocessor # include directories for the entire project. The SYSTEM flag is not included # so warnings will be geenreated for this directory. INCLUDE_DIRECTORIES( ${cppad_SOURCE_DIR}/include ) # # ============================================================================= # Some system cmake language extensions # ============================================================================= # CHECK_CXX_SOURCE_COMPILES(source variable) # Checks whether the code given in source will compile, link and run and # return zero status. You can set # CMAKE_REQUIRED_LIBRARIES, CMAKE_REQUIRED_FLAGS and CMAKE_REQUIRED_INCLUDES # accordingly if additional libraries or compiler flags are required. INCLUDE(CheckCXXSourceCompiles) # ============================================================================ # Some local cmake language # ============================================================================ # assert_value_in_set(value element_1 ... element_n) INCLUDE(cmake/assert_value_in_set.cmake) # # dos_path_to_unix(dos_path unix_path) INCLUDE(cmake/dos_path_to_unix.cmake) # # add_to_list(variable_list constant_value) INCLUDE(cmake/add_to_list.cmake) # # command_line_arg(variable default type description) INCLUDE(cmake/command_line_arg.cmake) # # prefix_info(package description) INCLUDE(cmake/prefix_info.cmake) # # compile_source_test(source variable) INCLUDE(cmake/compile_source_test.cmake) # # assert(variable) INCLUDE(cmake/assert.cmake) # # print_variable(variable) INCLUDE(cmake/print_variable.cmake) # # set_compile_flags( program_name debug_which source_list) INCLUDE(cmake/set_compile_flags.cmake) # # pkgconfig_info(name, system) INCLUDE(cmake/pkgconfig_info.cmake) # # eigen_info() INCLUDE(cmake/eigen_info.cmake) # # add_check_executable(parent_target short_name) INCLUDE(cmake/add_check_executable.cmake) # ============================================================================= # command line arguments # ============================================================================= # Arguments that are no longer used # IF( debug_which ) MESSAGE(FATAL_ERROR "Using debug_which, use cppad_debug_which or CMAKE_BUILD_TYPE instead" ) ENDIF( debug_which ) # IF( cppad_sparse_list ) MESSAGE(FATAL_ERROR "cppad_sparse_list has been removed from cmake command" ) ENDIF( cppad_sparse_list ) # IF( cppad_deprecated ) MESSAGE(FATAL_ERROR "cppad_deprecated has been removed from cmake command" ) ENDIF( cppad_deprecated ) # IF( cmake_install_prefix ) MESSAGE(FATAL_ERROR "cmake_install_prefix has been changed to cppad_prefix" ) ENDIF( cmake_install_prefix ) # IF( cmake_install_postfix ) MESSAGE(FATAL_ERROR "cmake_install_postfix has been changed to cppad_postfix" ) ENDIF( cmake_install_postfix ) # FOREACH(pkg adolc eigen ipopt cppadcg) IF( ${pkg}_prefix ) MESSAGE(FATAL_ERROR "-D ${pkg}_prefix=value has been changed to -D include_${pkg}=true" ) ENDIF( ${pkg}_prefix ) ENDFOREACH(pkg adolc eigen ipopt cppadcg) # IF( cmake_needs_dot_slash ) MESSAGE(FATAL_ERROR "cmake_needs_dot_slash was removed; see release notes for 2023-06-01" ) ENDIF( cmake_needs_dot_slash ) # IF( include_eigen ) MESSAGE(FATAL_ERROR "include_eigen was removed; see release notes for 2024-10-02" ) ENDIF( ) # ----------------------------------------------------------------------------- # Current Arguments # # cmake_install_datadir command_line_arg(cmake_install_datadir share STRING "directory, below prefix, where cmake installs cppad data files" ) # # cmake_install_docdir command_line_arg(cmake_install_docdir NOTFOUND STRING "directory, below prefix, where cmake installs cppad documentation files" ) # # cmake_install_includedirs command_line_arg(cmake_install_includedirs include STRING "directories, below prefix, where cmake installs include files" ) # # cmake_install_libdirs command_line_arg(cmake_install_libdirs "lib;lib64" STRING "directories, below prefix, where cmake installs library files" ) # # cmake_defined_ok command_line_arg(cmake_defined_ok TRUE BOOL "If false, check that some symbols only get defined once" "If true, you can use CMakeCache.txt to store previous settings" ) # # cppad_prefix command_line_arg(cppad_prefix "${CMAKE_INSTALL_PREFIX}" PATH "cppad install prefix" ) # # cppad_postfix command_line_arg(cppad_postfix NOTFOUND STRING "cppad install postfix" ) # # cppad_cxx_flags command_line_arg(cppad_cxx_flags "" STRING "compile flags used with cppad (besides debug, release, and profile flags)" ) # # cppad_link_flags command_line_arg(cppad_link_flags "" STRING "additional linker flags)" ) # # cppad_profile_flag command_line_arg(cppad_profile_flag NOTFOUND STRING "compile flag used to compile and link a profile version of a program" ) # # cppad_testvector command_line_arg(cppad_testvector cppad STRING "Namespace of vector used for testing, one of: boost, cppad, eigen, std" ) assert_value_in_set(cppad_testvector boost cppad eigen std) # # cppad_max_num_threads command_line_arg(cppad_max_num_threads 48 STRING "maximum number of threads that CppAD can use use" ) IF( "${cppad_max_num_threads}" LESS "4" ) MESSAGE(FATAL_ERROR "cppad_max_num_threads is not an integer greater than or equal 4" ) ENDIF( "${cppad_max_num_threads}" LESS "4" ) # # cppad_tape_id_type command_line_arg(cppad_tape_id_type "unsigned int" STRING "type used to identify different tapes, size must be <= sizeof(size_t)" ) # # cppad_tape_addr_type command_line_arg(cppad_tape_addr_type "unsigned int" STRING "type used to identify variables on one tape, size must be <= sizeof(size_t)" ) # # cppad_debug_and_release command_line_arg(cppad_debug_and_release TRUE BOOL "If true the cppad library and tests will be able to mix debug and release" ) # # cppad_static_lib STRING( REGEX MATCH "^MSYS" is_msys "${CMAKE_SYSTEM_NAME}" ) STRING( REGEX MATCH "^CYGWIN" is_cygwin "${CMAKE_SYSTEM_NAME}" ) STRING( REGEX MATCH "^Windows" is_windows "${CMAKE_SYSTEM_NAME}" ) IF( is_msys OR is_cygwin OR is_windows ) SET(default_cppad_static_lib TRUE) ELSE( ) SET(default_cppad_static_lib FALSE) ENDIF( ) # # cppad_static_lib command_line_arg(cppad_static_lib ${default_cppad_static_lib} BOOL "If true (false) the cppad library will be static (shared)" ) # ---------------------------------------------------------------------------- # # Ensure c++11 support SET(CMAKE_REQUIRED_DEFINITIONS "") SET(CMAKE_REQUIRED_FLAGS "${cppad_cxx_flags}") SET(CMAKE_REQUIRED_INCLUDES "") SET(CMAKE_REQUIRED_LIBRARIES "") SET(source " int main(void) { static_assert( __cplusplus >= 201103 , \"c++11 is supported\" ); return 0; }" ) compile_source_test(${cmake_defined_ok} "${source}" minimal_cplusplus ) IF( NOT minimal_cplusplus ) MESSAGE(STATUS "setting C++ standard to 2011") SET(CMAKE_CXX_STANDARD 11) SET(CMAKE_CXX_STANDARD_REQUIRED ON) ENDIF( NOT minimal_cplusplus ) # # use_cplusplus_2014_ok SET(source " int main(void) { static_assert( __cplusplus >= 201302L , \"c++17 is supported\" ); return 0; }" ) compile_source_test(${cmake_defined_ok} "${source}" use_cplusplus_2014_ok ) # # use_cplusplus_2017_ok SET(source " int main(void) { static_assert( __cplusplus >= 201703L , \"c++17 is supported\" ); return 0; }" ) compile_source_test(${cmake_defined_ok} "${source}" use_cplusplus_2017_ok ) # ---------------------------------------------------------------------------- # # cppad_debug_and_release_01 IF (cppad_debug_and_release ) SET(cppad_debug_and_release_01 1 ) ELSE (cppad_debug_and_release ) SET(cppad_debug_and_release_01 0 ) ENDIF ( ) # # cppad_debug_which # CMAKE_BUILD_TYPE SET(debug_even_or_odd FALSE) IF( NOT ${cppad_debug_which} STREQUAL "" ) assert_value_in_set( cppad_debug_which debug_even debug_odd debug_all debug_none ) ENDIF( NOT ${cppad_debug_which} STREQUAL "" ) IF( CMAKE_BUILD_TYPE ) IF( NOT ${cppad_debug_which} STREQUAL "" ) print_variable(CMAKE_BUILD_TYPE) print_variable(cppad_debug_which) MESSAGE(FATAL_ERROR "Both CMAKE_BUILD_TYPE and cppad_debug_which specified" ) ENDIF( ) STRING( TOLOWER "${CMAKE_BUILD_TYPE}" cmake_build_type_lower ) IF(cmake_build_type_lower MATCHES debug) SET(cppad_debug_which "debug_all") ELSEIF(cmake_build_type_lower MATCHES release) SET(cppad_debug_which "debug_none") ELSEIF(cmake_build_type_lower MATCHES relwithdebInfo) SET(cppad_debug_which "debug_none") ELSEIF(cmake_build_type_lower MATCHES minsizerel) SET(cppad_debug_which "debug_none") ELSE( ) MESSAGE(FATAL_ERROR "Unknown CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}" ) ENDIF( ) ELSE( ) # CMAKE_BUILD_TYPE IF( "${cppad_debug_which}" STREQUAL debug_all) SET(CMAKE_BUILD_TYPE Debug) ELSEIF( "${cppad_debug_which}" STREQUAL debug_none) SET(CMAKE_BUILD_TYPE Release) ELSEIF( "${cppad_debug_which}" STREQUAL debug_odd) SET(CMAKE_BUILD_TYPE Debug) SET(debug_even_or_odd TRUE) ELSEIF( "${cppad_debug_which}" STREQUAL debug_even) SET(CMAKE_BUILD_TYPE Release) SET(debug_even_or_odd TRUE) ENDIF( "${cppad_debug_which}" STREQUAL debug_all) ENDIF( ) # CMAKE_BUILD_TYPE IF( debug_even_or_odd ) IF ( NOT cppad_debug_and_release ) MESSAGE(FATAL_ERROR "cppad_debug_and_release is false and cppad_debug_which = ${cppad_debug_which}" ) ENDIF ( NOT cppad_debug_and_release ) IF( ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" ) MESSAGE(FATAL_ERROR "Cannot have cppad_debug_which equal ${cppad_debug_which} on Windows." ) ENDIF( ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" ) ENDIF( debug_even_or_odd ) print_variable(CMAKE_SYSTEM_NAME) print_variable(cppad_debug_which) print_variable(CMAKE_BUILD_TYPE) MESSAGE(STATUS "make check: available") # ----------------------------------------------------------------------------- # CMAKE_INSTALL_PREFIX SET(CMAKE_INSTALL_PREFIX "${cppad_prefix}" CACHE PATH "value copied from cppad_prefix" FORCE ) # ----------------------------------------------------------------------------- # Optional package information # cppad_has_{package}, {package}_LINK_LIBRARIES, package INCLUDE_DIRECTORIES # # system_include SET(system_include TRUE) # # package = eigen eigen_info() # # package = adolc pkgconfig_info(adolc ${system_include}) # # package = ipopt pkgconfig_info(ipopt ${system_include}) # # package = colpack prefix_info(colpack ${system_include} ) # # package = sacado prefix_info(sacado ${system_include} ) # # package = fadbad prefix_info(fadbad ${system_include} ) # # check sacado IF( ${cppad_has_sacado} ) IF( NOT ${use_cplusplus_2017_ok} ) MESSAGE(FATAL_ERROR "sacado_prefix is defined but c++17 not supported") ENDIF( ) ENDIF( ) # # package = cppadcg SET( include_cppadcg FALSE CACHE BOOL "include cppadcg" ) IF( include_cppadcg ) # # cppad_has_cppadcg SET(cppad_has_cppadcg 1) # # Assume bin/get_cppadcg.sh puts include files in this directory SET(cppadcg_include_dir "${CMAKE_BINARY_DIR}/prefix/include" ) print_variable(cppadcg_include_dir) INCLUDE_DIRECTORIES( SYSTEM ${cppadcg_include_dir}) ELSE( ) # cppad_has_cppadcg SET(cppad_has_cppadcg 0) ENDIF( include_cppadcg ) # # package = xrst SET( include_doc FALSE CACHE BOOL "include documentation") IF( include_doc ) # # xrst_path FIND_PROGRAM( xrst_path xrst ) IF( "${xrst_path}" STREQUAL "xrst_path-NOTFOUND" ) MESSAGE(FATAL_ERROR "include_doc is true and cannot find xrst in system path" ) ENDIF( ) # # processor_count INCLUDE(ProcessorCount) ProcessorCount(processor_count) IF( processor_count EQUAL 0 ) SET(processor_count 1) ENDIF( ) # # see # https://discourse.cmake.org/t/how-to-install-a-generated-directory/1267 # # doc_dir SET(doc_dir "${CMAKE_SOURCE_DIR}/build/html") # # doc_user ADD_CUSTOM_TARGET(doc_user ALL COMMAND xrst --group_list default app --local_toc --html_theme sphinx_rtd_theme --number_jobs ${processor_count} --index_page_name user_guide --suppress_spell_warnings --config_file "${CMAKE_SOURCE_DIR}/xrst.toml" ) # # doc_dev ADD_CUSTOM_TARGET(doc_dev COMMAND xrst --group_list default app dev --local_toc --html_theme sphinx_rtd_theme --number_jobs ${processor_count} --index_page_name user_guide --suppress_spell_warnings --config_file "${CMAKE_SOURCE_DIR}/xrst.toml" ) ENDIF( include_doc ) # ============================================================================= # cppad_lib # Perhaps in the future cppad_lib will depend on cmake header only flag ? SET( cppad_lib "cppad_lib" ) LINK_DIRECTORIES( ${cppad_BINARY_DIR}/cppad_lib ) # ============================================================================= # colpack_libs # IF( cppad_has_colpack ) SET( colpack_libs "ColPack" ) ELSE( cppad_has_colpack ) SET( colpack_libs "" ) ENDIF( cppad_has_colpack ) # ============================================================================= # automated system configuration # ============================================================================= # CMAKE_CXX_FLAGS IF( "${cppad_debug_which}" STREQUAL "debug_all" ) print_variable(CMAKE_CXX_FLAGS_DEBUG) ELSEIF( "${cppad_debug_which}" STREQUAL "debug_none" ) print_variable(CMAKE_CXX_FLAGS_RELEASE) ELSE( "${cppad_debug_which}" ) print_variable(CMAKE_CXX_FLAGS_DEBUG) print_variable(CMAKE_CXX_FLAGS_RELEASE) ENDIF( "${cppad_debug_which}" STREQUAL "debug_all" ) # ----------------------------------------------------------------------------- # cppad_abs_includedir, cppad_abs_libdir, cppad_abs_datadir, cppad_abs_docdir # # for dir_types = includedirs, libdirs, datadir, docdir FOREACH(dir_types includedirs libdirs datadir docdir) # set dir_type = dir_types with "dirs" -> "dir" STRING(REGEX REPLACE "dirs" "dir" dir_type ${dir_types}) # # set dir_name to first directory in cmake_install_${dir_types} SET(dir_name NOTFOUND) FOREACH(dir ${cmake_install_${dir_types}}) IF( NOT dir_name ) SET(dir_name ${dir}) ENDIF( NOT dir_name ) ENDFOREACH(dir ${cmake_install_${dir_types}}) # # set cppad_abs_${dir_type} SET(cppad_abs_${dir_type} "${cppad_prefix}/${dir_name}" ) # # check if we need to add a postfix to it IF( cppad_postfix ) SET(cppad_abs_${dir_type} ${cppad_abs_${dir_type}}/${cppad_postfix} ) ENDIF( cppad_postfix ) ENDFOREACH(dir_types includedirs libdirs datadir docdir) # ---------------------------------------------------------------------------- # https://gitlab.kitware.com/cmake/community/-/wikis/doc/cmake/RPATH-handling # # CMAKE_SKIP_BUILD_RPATH # use, i.e. don't skip the full RPATH for the build tree SET(CMAKE_SKIP_BUILD_RPATH FALSE) # # CMAKE_BUILD_WITH_INSTALL_RPATH # when building shared libraries, don't use the install RPATH already # (but later on when installing) SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) # # CMAKE_INSTAL_RPATH_USE_LINK_PATH # add the automatically determined parts of the RPATH # which point to directories outside the build tree to the install RPATH SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) # # isSystemDir # is this RPATH choice a system directory LIST(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${cppad_abs_libdir}" isSystemDir ) # # CMAKE_INSTALL_RPATH # the RPATH to be used when installing, but only if it's not a system directory SET(CMAKE_INSTALL_RPATH "") if("${isSystemDir}" STREQUAL "-1") SET(CMAKE_INSTALL_RPATH "${cppad_abs_libdir}") endif("${isSystemDir}" STREQUAL "-1") print_variable( CMAKE_INSTALL_RPATH ) # ----------------------------------------------------------------------------- # cppad_link_flags_has_m32 STRING( FIND "${cppad_link_flags}" "-m32" index) IF( "${index}" STREQUAL "-1" ) SET(cppad_link_flags_has_m32 0) ELSE ( ) SET(cppad_link_flags_has_m32 1) ENDIF( ) # ----------------------------------------------------------------------------- # OpenMP_CXX_FOUND, OpenMP_CXX_FLAGS IF( cppad_link_flags_has_m32 ) SET(OpenMP_CXX_FOUND FALSE) MESSAGE(STATUS "Skipping OpenMP because -m32 is in cppad_link_flags" ) ELSEIF( ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" ) SET(OpenMP_CXX_FOUND FALSE) MESSAGE(STATUS "Skipping OpenMP on Darwin (Mac) systems. 2DO: fix this" ) ELSE( ) FIND_PACKAGE(OpenMP) ENDIF( ) # ----------------------------------------------------------------------------- # Boost_Found, cppad_has_boost # Only need components for object libraries, not include libraries. IF( cppad_link_flags_has_m32 ) MESSAGE(STATUS "Skipping Boost because -m32 is in cppad_link_flags" ) SET( Boost_FOUND 0) SET(cppad_has_boost 0) ELSE ( ) SET(CMAKE_REQUIRED_FLAGS "") # 2DO: why is this necessary with clang ? FIND_PACKAGE(Boost COMPONENTS thread) SET(CMAKE_REQUIRED_FLAGS "${cppad_cxx_flags}") ENDIF( ) IF ( Boost_FOUND ) SET(cppad_has_boost 1) # # Extract the Boost prefix from Boost_INCLUDE_DIRS # # convert to using unix directory separator dos_path_to_unix("${Boost_INCLUDE_DIRS}" boost_include_dirs) # # convert to just one directory STRING(REGEX REPLACE "([^ ]+).*" "\\1" boost_include_dir "${boost_include_dirs}" ) # # extract part before last backslash STRING(REGEX REPLACE "([^ ]*)/[^/ ]*" "\\1" boost_prefix "${boost_include_dir}" ) print_variable(boost_prefix) # # add boost include directories FOREACH(dir ${cmake_install_includedirs}) IF( IS_DIRECTORY ${boost_prefix}/${dir} ) INCLUDE_DIRECTORIES( ${boost_prefix}/${dir} ) MESSAGE(STATUS "Found ${boost_prefix}/${dir}") ENDIF( IS_DIRECTORY ${boost_prefix}/${dir} ) ENDFOREACH( dir ) # # add boost link directories FOREACH(dir ${cmake_install_libdirs}) IF( IS_DIRECTORY ${boost_prefix}/${dir} ) LINK_DIRECTORIES( ${boost_prefix}/${dir} ) MESSAGE(STATUS "Found ${boost_prefix}/${dir}") ENDIF( IS_DIRECTORY ${boost_prefix}/${dir} ) ENDFOREACH( dir ) ENDIF ( Boost_FOUND ) # ----------------------------------------------------------------------------- # ipopt_LIBRARIES and ipopt_LIBRARY_DIRS IF( cppad_has_ipopt ) # # Set the system environment variable PKG_CONFIG_PATH FOREACH(dir ${cmake_install_libdirs}) IF(EXISTS "${ipopt_prefix}/${dir}/pkgconfig/ipopt.pc") SET( ENV{PKG_CONFIG_PATH} ${ipopt_prefix}/${dir}/pkgconfig ) ENDIF(EXISTS "${ipopt_prefix}/${dir}/pkgconfig/ipopt.pc") ENDFOREACH(dir) # # pkg_check_modules( [REQUIRED] []*) # ipopt_LIBRARIES ... only the libraries (w/o the '-l') # ipopt_LIBRARY_DIRS ... the paths of the libraries (w/o the '-L') pkg_check_modules(ipopt ipopt) IF( NOT ipopt_FOUND ) MESSAGE(FATAL_ERROR "For all directories dir in cmake_install_libdirs, cannot find the file ipopt_prefix/dir/pkgconfig/ipopt.pc where ipopt_prefix = ${ipopt_prefix} cmake_install_libdirs = ${cmake_install_libdirs} " ) ENDIF( NOT ipopt_FOUND ) ENDIF( cppad_has_ipopt ) # ============================================================================= # Currently building tests as normal executables # ============================================================================= # The CMakeLists.txt file in the specified source directory is processed # before the current input file continues beyond this command. # add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL]) # # # is_cppad_lib_dynamic IF( cppad_static_lib ) SET(is_cppad_lib_dynamic 0) ELSE( ) SET(is_cppad_lib_dynamic 1) ENDIF( ) print_variable(is_cppad_lib_dynamic) # # CMAKE_EXE_LINKER_FLAGS SET(CMAKE_EXE_LINKER_FLAGS "${cppad_link_flags}" ) # # Initialize list of tests as empty SET(check_depends "") # # directories with no check depends entries ADD_SUBDIRECTORY(include/cppad) ADD_SUBDIRECTORY(pkgconfig) ADD_SUBDIRECTORY(cppad_lib) # IF( NOT ( "${check_depends}" STREQUAL "" ) ) MESSAGE(FATAL_ERROR "Error in CMakeLists.txt scripts") ENDIF( NOT ( "${check_depends}" STREQUAL "" ) ) # # directories with check depends entries ADD_SUBDIRECTORY(val_graph) ADD_SUBDIRECTORY(example) ADD_SUBDIRECTORY(introduction) ADD_SUBDIRECTORY(test_more) ADD_SUBDIRECTORY(speed) IF( cppad_has_ipopt ) ADD_SUBDIRECTORY(cppad_ipopt) ENDIF( cppad_has_ipopt ) # # check, test ADD_CUSTOM_TARGET(check DEPENDS ${check_depends}) ADD_CUSTOM_TARGET(test DEPENDS ${check_depends}) # ============================================================================ # Copy a file to another location and modify its contents. # configure_file(InputFile OutputFile [COPYONLY] [ESCAPE_QUOTES] [@ONLY]) CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/bin/test_one.sh.in ${CMAKE_CURRENT_SOURCE_DIR}/bin/test_one.sh ) # ============================================================================= # install procedure # ============================================================================= # install(DIRECTORY dirs... DESTINATION # [FILE_PERMISSIONS permissions...] # [DIRECTORY_PERMISSIONS permissions...] # [USE_SOURCE_PERMISSIONS] [OPTIONAL] # [CONFIGURATIONS [Debug|Release|...]] # [COMPONENT ] [FILES_MATCHING] # [[PATTERN | REGEX ] # [EXCLUDE] [PERMISSIONS permissions...]] [...] # ) # Note a trailing / in the source directory name drops the source directory # name during the copy. # # During install copy all the cppad include files to # ${cppad_abs_includedir}/cppad INSTALL( DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/cppad/" DESTINATION ${cppad_abs_includedir}/cppad FILES_MATCHING PATTERN "*.hpp" PATTERN "xrst" EXCLUDE ) # # During install copy doc direcrory to cppad_abs_docdir/cppad IF ( cmake_install_docdir AND include_doc ) INSTALL( DIRECTORY "${CMAKE_SOURCE_DIR}/build/html/" DESTINATION ${cppad_abs_docdir}/cppad ) ENDIF ( cmake_install_docdir AND include_doc ) # ============================================================================= # uninstall procedure # ============================================================================= ADD_CUSTOM_TARGET(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cppad_uninstall.cmake) ================================================ FILE: COPYING ================================================ ----------------------------------------------------------------------------- CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-18 Bradley M. Bell CppAD is distributed under the terms of the Eclipse Public License Version 2.0. This Source Code may also be made available under the following Secondary License when the conditions for such availability set forth in the Eclipse Public License, Version 2.0 are satisfied: GNU General Public License, Version 2.0 or later. ----------------------------------------------------------------------------- ================================================ FILE: appendix/addon.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin addon app} {xrst_spell adcomp addons cg ext includedir libcppad libdir py pycppad tmb } CppAD Addons ############ Name **** Each CppAD addon has a short name which we denote by *name* below, a longer name *longer* and a *description* : .. list-table:: :widths: auto * - *name* - *longer* - *description* * - `tmb `_ - ``adcomp`` - An R Interface to CppAD with Random Effects Modeling Utilities * - `cg `_ - ``CppADCodeGen`` - C++ Source Code Generation of CppAD Derivative Calculations * - `mixed `_ - ``cppad_mixed`` - A C++ Interface to Random Effects Laplace Approximation * - `cppad_py `_ - ``cppad_py`` - A Python Interface to CppAD * - `swig `_ - ``cppad_swig`` - A C++ AD Library with a Swig Interface to Perl, Octave, and Python (no longer being developed) * - `pycppad `_ - ``pycppad`` - A Python Interface to CppAD (no longer being developed) Include Files ************* If *includedir* is the directory where the include files are installed, the file *includedir* / ``include/cppad/`` *name* . ``hpp`` and the directory *includedir* / ``include/cppad/`` *name* are reserved for use by the *name* addon. Library Files ************* If *libdir* is the directory where CppAD library files are installed, files with the name | |tab| *libdir* / ``libcppad_`` *name* . *ext* | |tab| *libdir* / ``libcppad_`` *name* _ *anything* . *ext* where *anything* and *ext* are arbitrary, are reserved for use by the *name* addon. Preprocessor Symbols ******************** C++ preprocessor symbols that begin with ``CPPAD_`` *NAME* _ where *NAME* is a upper-case version of *name* , are reserved for use by the *name* addon. Namespace ********* The C++ namespace ``CppAD::`` *name* is reserved for use by the *name* addon. {xrst_end addon} ================================================ FILE: appendix/appendix.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin appendix app} Appendix ######## Contents ******** {xrst_toc_table appendix/faq.xrst appendix/directory.xrst appendix/glossary.xrst appendix/bib.xrst appendix/wish_list.xrst appendix/whats_new/whats_new.xrst appendix/deprecated/deprecated.xrst test_more/compare_c/CMakeLists.txt appendix/numeric_ad.xrst appendix/addon.xrst appendix/license.xrst } {xrst_end appendix} ================================================ FILE: appendix/bib.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin Bib app} {xrst_spell abramowitz andreas bjarne flannery fortran griewank rosenbrock shampine stegun stroustrup teukolsky vetterling vol } Bibliography ############ Abramowitz and Stegun ********************* Handbook of Mathematical Functions, Dover, New York. The C++ Programming Language **************************** Bjarne Stroustrup, The C++ Programming Language, Special ed., AT&T, 2000 Evaluating Derivatives ********************** Evaluating Derivatives: Principles and Techniques of Algorithmic Differentiation, Andreas Griewank, SIAM, Philadelphia, 2000 Numerical Recipes ***************** Numerical Recipes in Fortran: The Art of Scientific Computing, Second Edition, William H. Press, William T. Vetterling, Saul, A. Teukolsky, Brian R. Flannery, Cambridge University Press, 1992 Shampine, L.F. ************** Implementation of Rosenbrock Methods, ACM Transactions on Mathematical Software, Vol. 8, No. 2, June 1982. {xrst_end Bib} ================================================ FILE: appendix/deprecated/compare_change.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin CompareChange app} Comparison Changes During Zero Order Forward Mode ################################################# Syntax ****** | *c* = *f* . ``CompareChange`` () See Also ******** :ref:`FunCheck-name` Deprecated 2015-01-20 ********************* This routine has been deprecated, use :ref:`compare_change-name` instead. Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* . This function may be not agree with the algorithm that was used to create the corresponding AD of *Base* :ref:`operation sequence` because of changes in AD :ref:`comparison` results. The ``CompareChange`` function can be used to detect these changes. f * The object *f* has prototype ``const ADFun`` < *Base* > *f* c * The result *c* has prototype ``size_t`` *c* It is the number of ``AD`` < *Base* > :ref:`comparison` operations, corresponding to the previous call to :ref:`Forward-name` *f* . ``Forward`` (0, *x* ) that have a different result from when *F* was created by taping an algorithm. Discussion ********** If *c* is not zero, the boolean values resulting from some of the :ref:`comparison operations` corresponding to *x* are different from when the AD of *Base* :ref:`operation sequence` was created. In this case, you may want to re-tape the algorithm with the :ref:`independent variables` equal to the values in *x* (so AD operation sequence properly represents the algorithm for this value of independent variables). On the other hand, re-taping the AD operation sequence usually takes significantly more time than evaluation using :ref:`forward_zero-name` . If the functions values have not changed (see :ref:`FunCheck-name` ) it may not be worth re-taping a new AD operation sequence. {xrst_end CompareChange} ================================================ FILE: appendix/deprecated/deprecated.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin deprecated app} {xrst_spell colpack } CppAD Deprecated API Features ############################# Contents ******** {xrst_toc_table appendix/deprecated/include_deprecated.xrst appendix/deprecated/fun_deprecated.xrst appendix/deprecated/compare_change.xrst include/cppad/core/omp_max_thread.hpp include/cppad/utility/track_new_del.hpp appendix/deprecated/omp_alloc.xrst include/cppad/utility/memory_leak.hpp include/cppad/core/epsilon.hpp cppad_ipopt/src/cppad_ipopt_nlp.hpp include/cppad/core/atomic/one/atomic.hpp include/cppad/core/atomic/two/atomic.hpp example/multi_thread/multi_atomic_two.xrst include/cppad/core/chkpoint_one/chkpoint_one.hpp example/multi_thread/multi_chkpoint_one.xrst include/cppad/core/zdouble.hpp configure } Name Changes ************ .. list-table:: :widths: auto * - ``CppADCreateUnaryBool`` - see Deprecated 2007-07-31 in :ref:`bool_fun-title` * - ``CppADCreateDiscrete`` - see Deprecated 2007-07-31 in :ref:`bool_fun-title` * - ``nan`` ( *zero* ) - :ref:`nan@nan(zero)` * - ``colpack.star`` coloring - see :ref:`sparse_hes` and :ref:`sparse_hessian` {xrst_end deprecated} ================================================ FILE: appendix/deprecated/fun_deprecated.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin fun_deprecated app} ADFun Object Deprecated Member Functions ######################################## Syntax ****** | *f* . ``Dependent`` ( *y* ) | *o* = *f* . ``Order`` () | *m* = *f* . ``Memory`` () | *s* = *f* . ``Size`` () | *t* = *f* . ``taylor_size`` () | *u* = *f* . ``use_VecAD`` () | *v* = *f* . ``size_taylor`` () | *w* = *f* . ``capacity_taylor`` () Purpose ******* The ``ADFun`` < *Base* > functions documented here have been deprecated; i.e., they are no longer approved of and may be removed from some future version of CppAD. Dependent ********* A recording of and AD of *Base* :ref:`operation sequence` is started by a call of the form ``Independent`` ( *x* ) If there is only one such recording at the current time, you can use *f* . ``Dependent`` ( *y* ) in place of *f* . ``Dependent`` ( *x* , *y* ) See :ref:`Dependent-name` for a description of this operation. Deprecated 2007-08-07 ===================== This syntax was deprecated when CppAD was extended to allow for more than one ``AD`` < *Base* > recording to be active at one time. This was necessary to allow for multiple threading applications. Order ***** The result *o* has prototype ``size_t`` *o* and is the order of the previous forward operation using the function *f* . This is the highest order of the :ref:`Taylor coefficients` that are currently stored in *f* . Deprecated 2006-03-31 ===================== Zero order corresponds to function values being stored in *f* . In the future, we would like to be able to erase the function values so that *f* uses less memory. In this case, the return value of ``Order`` would not make sense. Use :ref:`size_order-name` to obtain the number of Taylor coefficients currently stored in the ADFun object *f* (which is equal to the order plus one). Memory ****** The result ``size_t`` *m* and is the number of memory units (``sizeof`` ) required for the information currently stored in *f* . This memory is returned to the system when the destructor for *f* is called. Deprecated 2006-03-31 ===================== It used to be the case that an ADFun object just kept increasing its buffers to the maximum size necessary during its lifetime. It would then return the buffers to the system when its destructor was called. This is no longer the case, an ADFun object now returns memory when it no longer needs the values stored in that memory. Thus the ``Memory`` function is no longer well defined. Size **** The result *s* has prototype ``size_t`` *s* and is the number of variables in the operation sequence plus the following: one for a phantom variable with tape address zero, one for each component of the domain that is a parameter. The amount of work and memory necessary for computing function values and derivatives using *f* is roughly proportional to *s* . Deprecated 2006-04-03 ===================== There are other sizes attached to an ADFun object, for example, the number of operations in the sequence. In order to avoid confusion with these other sizes, use :ref:`fun_property@size_var` to obtain the number of variables in the operation sequence. taylor_size *********** The result *t* has prototype ``size_t`` *t* and is the number of Taylor coefficient orders currently calculated and stored in the ADFun object *f* . Deprecated 2006-06-17 ===================== This function has been replaced by :ref:`size_order-name` . use_VecAD ********* The result *u* has prototype ``bool`` *u* If it is true, the AD of *Base* :ref:`operation sequence` stored in *f* contains :ref:`VecAD::reference>` operands. Otherwise *u* is false. Deprecated 2006-04-08 ===================== You can instead use *u* = *f* . ``size_VecAD`` () > 0 size_taylor *********** The result *v* has prototype ``size_t`` *v* and is the number of Taylor coefficient orders currently calculated and stored in the ADFun object *f* . Deprecated 2014-03-18 ===================== This function has been replaced by :ref:`size_order-name` . capacity_taylor *************** The result *w* has prototype ``size_t`` *w* and is the number of Taylor coefficient orders currently allocated in the ADFun object *f* . Deprecated 2014-03-18 ===================== This function has been replaced by :ref:`capacity_order-name` . {xrst_end fun_deprecated} ================================================ FILE: appendix/deprecated/include_deprecated.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin include_deprecated app} Deprecated Include Files ######################## Deprecated 2015-11-30 ********************* The :ref:`utility-name` individual include files have been deprecated; e.g., :: # include You must instead use :: # include or you can include individual utility files; e.g., :: # include Deprecated 2006-12-17 ********************* The following is a list of deprecated include file names and the corresponding names that should be used. For example, if your program uses the deprecated preprocessor command :: # include you must change it to the command :: # include The before 2015-11-30 column contains name used for the include file between 2006-12-17 and 2015-11-30 (see discussion above). .. csv-table:: :widths: auto **Deprecated**,**Before 2015-11-30**,**Documentation** CppAD/CheckNumericType.h,cppad/check_numeric_type.hpp,:ref:`CheckNumericType-name` CppAD/CheckSimpleVector.h,cppad/check_simple_vector.hpp,:ref:`CheckSimpleVector-name` CppAD/CppAD.h,cppad/cppad.hpp,:ref:`user_guide-name` CppAD/CppAD_vector.h,cppad/vector.hpp,:ref:`CppAD_vector-name` CppAD/ErrorHandler.h,cppad/error_handler.hpp,:ref:`ErrorHandler-name` CppAD/LuFactor.h,cppad/lu_factor.hpp,:ref:`LuFactor-name` CppAD/LuInvert.h,cppad/lu_invert.hpp,:ref:`LuInvert-name` CppAD/LuSolve.h,cppad/lu_solve.hpp,:ref:`LuSolve-name` CppAD/NearEqual.h,cppad/near_equal.hpp,:ref:`NearEqual-name` CppAD/OdeErrControl.h,cppad/ode_err_control.hpp,:ref:`OdeErrControl-name` CppAD/OdeGear.h,cppad/ode_gear.hpp,:ref:`OdeGear-name` CppAD/OdeGearControl.h,cppad/ode_gear_control.hpp,:ref:`OdeGearControl-name` CppAD/Poly.h,cppad/poly.hpp,:ref:`Poly-name` CppAD/PowInt.h,cppad/pow_int.hpp,:ref:`pow_int-name` CppAD/RombergMul.h,cppad/romberg_mul.hpp,:ref:`RombergMul-name` CppAD/RombergOne.h,cppad/romberg_one.hpp,:ref:`RombergOne-name` CppAD/Rosen34.h,cppad/rosen_34.hpp,:ref:`Rosen34-name` CppAD/Runge45.h,cppad/runge_45.hpp,:ref:`Runge45-name` CppAD/SpeedTest.h,cppad/speed_test.hpp,:ref:`SpeedTest-name` CppAD/TrackNewDel.h,cppad/track_new_del.hpp,:ref:`track_new_del-name` {xrst_end include_deprecated} ================================================ FILE: appendix/deprecated/omp_alloc.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin omp_alloc app} A Quick OpenMP Memory Allocator Used by CppAD ############################################# Syntax ****** | ``# include `` Purpose ******* The C++ ``new`` and ``delete`` operators are thread safe, but this means that a thread may have to wait for a lock on these operations. Once memory is obtained for a thread, the ``omp_alloc`` memory allocator keeps that memory :ref:`omp_available-name` for the thread so that it can be re-used without waiting for a lock. All the CppAD memory allocations use this utility. The :ref:`omp_free_available-name` function should be used to return memory to the system (once it is no longer required by a thread). Include ******* The routines in sections below are defined by ``cppad/omp_alloc.hpp`` . This file is included by ``cppad/cppad.hpp`` , but it can also be included separately with out the rest of the ``CppAD`` . Deprecated 2011-08-23 ********************* Use :ref:`thread_alloc-name` instead. Contents ******** {xrst_toc_table include/cppad/utility/omp_alloc.hpp } {xrst_end omp_alloc} */ ================================================ FILE: appendix/directory.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin directory app} {xrst_spell chkpoint ipopt jit json omh pkgconfig } Directory Structure ################### A brief description of each of the CppAD directories is provided below: Distribution Directory ********************** The following table lists the sub-directories of the :ref:`download@Distribution Directory` : .. list-table:: :widths: auto * - ``bin`` - Scripts used for CppAD development. * - ``bug`` - Used to create a simple CppAD bug report or test. * - ``build`` - Used to build the libraries, examples, and tests. * - ``cmake`` - cmake macro files. * - ``cppad_ipopt`` - Example and tests for the deprecated cppad_ipopt library. * - ``cppad_lib`` - Source code corresponding to the CppAD library. * - ``example`` - Source code for the CppAD examples. * - ``external`` - Used to build optional packages; see :ref:`get_optional.sh-name` * - ``include`` - The CppAD include directory. * - ``introduction`` - Source code for the CppAD introduction. * - ``omh`` - Contains files that are only used for documentation. * - ``pkgconfig`` - Contains the CppAD pkg-config information. * - ``speed`` - The CppAD speed tests. * - ``test_more`` - Tests that are not part of the documentation. Example Directory ***************** The following table lists the sub-directories of the ``example`` directory. .. list-table:: :widths: auto * - ``abs_normal`` - examples using :ref:`abs_normal` representation of non-smooth functions. * - ``atomic_four`` - :ref:`atomic_four-title` function examples. * - ``atomic_three`` - :ref:`atomic_three-title` function examples. * - ``atomic_two`` - :ref:`atomic_two-title` examples not yet converted to use ``atomic_four`` . * - ``chkpoint_two`` - :ref:`chkpoint_two-title` examples * - ``code_gen_fun`` - :ref:`code_gen_fun-title` examples * - ``general`` - general purpose examples. * - ``get_started`` - a good place to get started using CppAD. * - ``ipopt_solve`` - :ref:`ipopt_solve-title` examples. * - ``jit`` - :ref:`example_jit-title` * - ``json`` - :ref:`json_ad_graph-title` examples * - ``multi_thread`` - CppAD :ref:`multi_threading` examples. * - ``optimize`` - examples using the :ref:`optimize-name` operation. * - ``print_for`` - examples that used the :ref:`PrintFor-name` operation. * - ``sparse`` - examples using :ref:`sparsity_patterns` and :ref:`sparse_derivatives` . * - ``utility`` - example using the CppAD :ref:`utilities` . {xrst_end directory} ================================================ FILE: appendix/faq.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin Faq app} {xrst_spell github } Frequently Asked Questions and Answers ###################################### Assignment and Independent ************************** Why does the code sequence | |tab| ``Independent`` ( *u* ); | |tab| *v* = *u* [0]; behave differently from the code sequence | |tab| *v* = *u* [0]; | |tab| ``Independent`` ( *u* ); Before the call to :ref:`Independent-name` , *u* [0] is a :ref:`glossary@Parameter` and after the call it is a variable. Thus in the first case, *v* is a variable and in the second case it is a parameter. Bugs **** What should I do if I suspect that there is a bug in CppAD ? #. The first step is to check currently open `issues `_ on github. If it is an open issue, and you want to hurry it along, you can add a comment to the effect that it is holding you up. #. The next step is to search the :ref:`whats_new-name` sections for mention of a related bug fix between the date of the version you are using and the current date. It the bug has been fixed, obtain a more recent release that has the fix and see if that works for you. #. The next step is to create a simple demonstration of the bug; see the file ``bug/template.sh`` for a template that you can edit for that purpose. The smaller the program, the better the bug report. #. The next step is open a new issue on github and provide your simple example so that the problem can be reproduced. CompareChange ************* If you attempt to use the :ref:`CompareChange-name` function when ``NDEBUG`` is true, you will get an error message stating that ``CompareChange`` is not a member of the :ref:`ADFun-name` template class. Complex Types ************* Which of the following complex types is better: | |tab| ``AD< std::complex<`` *Base* > > | |tab| ``std::complex< AD<`` *Base* > > The :ref:`complex abs function` is differentiable with respect to its real and imaginary parts, but it is not complex differentiable. Thus one would prefer to use ``std::complex< AD<`` *Base* > > On the other hand, the C++ standard only specifies ``std::complex<`` *Type* > where *Type* is ``float`` , ``double`` , or ``lone double`` . The effect of instantiating the template complex for any other type is unspecified. Exceptions ********** Why, in all the examples, do you pass back a boolean variable instead of throwing an exception ? The examples are also used to test the correctness of CppAD and to check your installation. For these two uses, it is helpful to run all the tests and to know which ones failed. The actual code in CppAD uses the :ref:`ErrorHandler-name` utility to signal exceptions. Specifications for redefining this action are provided. Independent Variables ********************* Is it possible to evaluate the same tape recording with different values for the independent variables ? Yes (see :ref:`forward_zero-name` ). Matrix Inverse ************** Is it possible to differentiate (with respect to the matrix elements) the computation of the inverse of a matrix where the computation of the inverse uses pivoting ? LuSolve ======= The example routine :ref:`LuSolve-name` can be used to do this because the inverse is a special case of the solution of linear equations. The examples :ref:`jac_lu_det.cpp-name` and :ref:`hes_lu_det.cpp-name` use LuSolve to compute derivatives of the determinant with respect to the components of the matrix. Atomic Operation ================ One can also do this by making the inversion of the matrix an atomic operation; e.g., see :ref:`atomic_two_eigen_mat_inv.cpp-name` . Mode: Forward or Reverse ************************ When evaluating derivatives, one always has a choice between forward and reverse mode. How does one decide which mode to use ? In general, the best mode depends on the number of domain and range components in the function that your are differentiating. Each call to :ref:`Forward-name` computes the derivative of all the range directions with respect to one domain direction. Each call to :ref:`Reverse-name` computes the derivative of one range direction with respect to all the domain directions. The times required for (speed of) calls ``Forward`` and ``Reverse`` are about equal. The :ref:`fun_property@Parameter` function can be used to quickly determine that some range directions have derivative zero. Namespace ********* Test Vector Preprocessor Symbol =============================== Why do you use ``CPPAD_TESTVECTOR`` instead of a namespace for the CppAD :ref:`testvector-name` class ? The preprocessor symbol ``CPPAD_TESTVECTOR`` ( see :ref:`testvector-name` ) determines which :ref:`SimpleVector-name` template class is used for extensive testing. The default definition for ``CPPAD_TESTVECTOR`` is the :ref:`CppAD::vector` template class, but it can be changed. Note that all the preprocessor symbols that are defined or used by CppAD begin with either ``CPPAD`` (some old deprecated symbols begin with ``CppAD`` ). Speed ***** How do I get the best speed performance out of CppAD ? NDEBUG ====== You should compile your code with optimization, without debugging, and with the preprocessor symbol ``NDEBUG`` defined. (The :ref:`speed_cppad-name` tests do this.) Note that defining ``NDEBUG`` will turn off all of the error checking and reporting that is done using :ref:`ErrorHandler-name` . Optimize ======== It is also possible that preforming a tape :ref:`optimization` will improve the speed of evaluation more than the time required for the optimization. Memory Allocation ================= You may also increase execution speed by calling ``hold_memory`` with :ref:`ta_hold_memory@value` equal to true. Tape Storage: Disk or Memory **************************** Does CppAD store the tape on disk or in memory ? CppAD uses memory to store a different tape for recording operations for each ``AD`` < *Base* > type that is used. If you have a very large number calculations that are recorded on a tape, the tape will keep growing to hold the necessary information. Eventually, virtual memory may be used to store the tape and the calculations may slow down because of necessary disk access. {xrst_end Faq} ================================================ FILE: appendix/glossary.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin glossary app} {xrst_spell is is } Glossary ######## AD Function *********** Given an :ref:`ADFun-name` object *f* there is a corresponding AD of *Base* :ref:`operation sequence` . This operation sequence defines a function :math:`F : \B{R}^n \rightarrow \B{R}^m` where :math:`\B{R}` is the space corresponding to objects of type *Base* (usually the real numbers), *n* is the size of the :ref:`fun_property@Domain` space, and *m* is the size of the :ref:`fun_property@Range` space. We refer to :math:`F` as the AD function corresponding to the operation sequence stored in the object *f* . (See the :ref:`FunCheck discussion` for possible differences between :math:`F(x)` and the algorithm that defined the operation sequence.) AD of Base ********** An object is called an AD of *Base* object its type is either ``AD`` < *Base* > (see the default and copy :ref:`constructors` or ``VecAD`` < *Base* >:: ``reference`` (see :ref:`VecAD-name` ) for some *Base* type. AD Type Above Base ****************** If *Base* is a type, an AD type above *Base* is the following sequence of types: ``AD`` < *Base* > , ``AD< AD<`` *Base* > > , ``AD< AD< AD<`` *Base* > > > , ... Base Function ************* A function :math:`f : \B{R} \rightarrow \B{R}` is referred to as a *Base* function, if *Base* is a C++ type that represent elements of the domain and range space of *f* . Base Type ********* If *x* is an ``AD`` < *Base* > object, *Base* is referred to as the base type for *x* . Elementary Vector ***************** The *j*-th elementary vector :math:`e^j \in \B{R}^m` is defined by .. math:: e_i^j = \left\{ \begin{array}{ll} 1 & {\rm if} \; i = j \\ 0 & {\rm otherwise} \end{array} \right. Operation ********* Atomic ====== An atomic *Type* operation is an operation that has a *Type* result and is not made up of other more basic operations. Sequence ======== A sequence of atomic *Type* operations is called a *Type* operation sequence. A sequence of atomic :ref:`glossary@AD of Base` operations is referred to as an AD of *Base* operation sequence. The abbreviated notation operation sequence is often used when it is not necessary to specify the type. Dependent ========= Suppose that *x* and *y* are *Type* objects and the result of *x* < *y* has type ``bool`` (where *Type* is not the same as ``bool`` ). If one executes the following code | |tab| ``if`` ( *x* < *y* ) | |tab| |tab| *y* = ``cos`` ( *x* ); | |tab| ``else`` | |tab| |tab| *y* = ``sin`` ( *x* ); the choice above depends on the value of *x* and *y* and the two choices result in a different *Type* operation sequence. In this case, we say that the *Type* operation sequence depends on *x* and *y* . Independent =========== Suppose that *i* and *n* are ``size_t`` objects, and *x* [ *i* ] , *y* are *Type* objects, where *Type* is different from ``size_t`` . The *Type* sequence of operations corresponding to | |tab| *y* = *Type* (0); | |tab| ``for`` ( *i* = 0; *i* < *n* ; *i* ++) | |tab| |tab| *y* += *x* [ *i* ]; does not depend on the value of *x* or *y* . In this case, we say that the *Type* operation sequence is independent of *y* and the elements of *x* . Parameter ********* Constant ======== An ``AD`` < *Base* > object *u* is a constant parameter if its value does not depend on the value of the :ref:`Independent-name` variable vector or the :ref:`Independent@dynamic` parameter vector for an :ref:`active tape` . If *u* is a constant parameter, :ref:`Constant(u)` returns true, :ref:`Parameter(u)` returns true, :ref:`Dynamic(u)` returns false, and :ref:`Variable(u)` returns false. Dynamic ======= An ``AD`` < *Base* > object *u* is a dynamic parameter if its value does not depend on the value of the :ref:`Independent-name` variable vector, but its value does depend on the :ref:`Independent@dynamic` parameter vector, for an :ref:`active tape` . If *u* is a dynamic parameter, the function :ref:`Dynamic(u)` returns true :ref:`Parameter(u)` returns true, :ref:`Constant(u)` returns false, and :ref:`Variable(u)` returns false. Row-major Representation ************************ A :ref:`SimpleVector-name` *v* is a row-major representation of a matrix :math:`M \in \B{R}^{m \times n}` if *v* . ``size`` () == *m* * *n* and for :math:`i = 0 , \ldots , m-1`, :math:`j = 0 , \ldots , n-1` .. math:: M_{i,j} = v[ i \times n + j ] Sparsity Pattern **************** Suppose that :math:`A \in \B{R}^{m \times n}` is a sparse matrix. CppAD has several ways to specify the elements of :math:`A` that are possible non-zero. Row and Column Index Vectors ============================ A pair of non-negative integer vectors :math:`r`, :math:`c` are a sparsity pattern for :math:`A` if for every non-zero element :math:`A_{i,j}`, there is a :math:`k` such that :math:`i = r_k` and :math:`j = c_k`. Furthermore, for every :math:`\ell \neq k`, either :math:`r_\ell \neq r_k` or :math:`c_\ell \neq c_k`. Boolean Vector ============== A boolean vector :math:`b`, of length :math:`m \times n`, is a sparsity pattern for :math:`A` if for every non-zero element :math:`A_{i,j}`, :math:`b_{i \times n + j}` is true. Vector of Sets ============== A vector of sets :math:`s` of positive integers, of length :math:`m`, is a sparsity pattern for :math:`A` if for every non-zero element :math:`A_{i,j}`, :math:`j \in s_i`. Tape **** Active ====== A new tape is created and becomes active after each call of the form (see :ref:`Independent-name` ) ``Independent`` ( *x* ) All operations that depend on the elements of *x* are recorded on this active tape. Inactive ======== The :ref:`operation sequence` stored in a tape can be transferred to a function object using the syntax | |tab| ``ADFun`` < *Base* > *f* ( *x* , *y* ) | |tab| *f* . ``Dependent`` ( *x* , *y* ) see :ref:`fun_construct-name` . After such a transfer, the tape becomes inactive. The tape becomes inactive, without storing the operation sequence, after a call to :ref:`abort_recording-name` . Independent Variable ==================== While the tape is active, we refer to the elements of *x* as the independent variables for the tape. When the tape becomes inactive, the corresponding objects become :ref:`constants` . Variables ========= While the tape is active, we use the term variables for any scalar whose value depends on the independent variables for the tape. When the tape becomes inactive, the corresponding objects become :ref:`constants` . Taylor Coefficient ****************** Suppose :math:`X : \B{R} \rightarrow \B{R}^n` is a is :math:`p` times continuously differentiable function in some neighborhood of zero. For :math:`k = 0 , \ldots , p`, we use the column vector :math:`x^{(k)} \in \B{R}^n` for the *k*-th order Taylor coefficient corresponding to :math:`X` which is defined by .. math:: x^{(k)} = \frac{1}{k !} \Dpow{k}{t} X(0) It follows that .. math:: X(t) = x^{(0)} + x^{(1)} t + \cdots + x^{(p)} t^p + R(t) where the remainder :math:`R(t)` divided by :math:`t^p` converges to zero and :math:`t` goes to zero. Variable ******** An ``AD`` < *Base* > object *u* is a variable if its value depends on an independent variable vector for a currently :ref:`active tape` . If *u* is a variable, :ref:`Variable(u)` returns true, :ref:`Constant(u)` returns false, :ref:`Dynamic(u)` returns false, and :ref:`Parameter(u)` returns false. For example, directly after the code sequence | |tab| ``Independent`` ( *x* ); | |tab| ``AD`` *u* = *x* [0]; the ``AD`` object *u* is currently a variable. Directly after the code sequence | |tab| ``Independent`` ( *x* ); | |tab| ``AD`` *u* = *x* [0]; | |tab| *u* = 5; *u* is currently a :ref:`glossary@Parameter@Constant` parameter, not a dynamic parameter or a variable. Note that we often drop the word currently and just refer to an ``AD`` < *Base* > object as a variable or parameter. {xrst_end glossary} ================================================ FILE: appendix/license.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin License app} Your License for the CppAD Software ################################### Your License ************ {xrst_literal COPYING } Eclipse Public License Version 2.0 ********************************** {xrst_literal epl-2.0.txt } {xrst_end License} ================================================ FILE: appendix/numeric_ad.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin numeric_ad app} Some Numerical AD Utilities ########################### The routines listed below are numerical utilities that are designed to work with CppAD in particular. Contents ******** {xrst_toc_table include/cppad/core/bender_quad.hpp include/cppad/core/opt_val_hes.hpp include/cppad/core/lu_ratio.hpp } {xrst_end numeric_ad} ================================================ FILE: appendix/whats_new/2003.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2003 app} {xrst_spell ddouble def dodds faq geq hess indvar ini leq mak valarray } Release Notes for 2003 ###################### mm-dd ***** 12-24 ===== Some references to ``double`` should have been references to the :ref:`glossary@Base Type` (in reverse mode and in the ``Grad/`` and ``Hess`` functions). This has been fixed. 12-22 ===== The preprocessor symbol ``WIN32`` was being used to determine if one was using Microsoft's C++ compiler. This symbol is predefined by the `MinGW `_ version of the GNU C++ compiler and hence CppAD had errors during installation using MinGW. This has been fixed by using the preprocessor symbol ``_MSC_VER`` to determine if one is using the Microsoft C++ compiler. 12-14 ===== The extended system solvers ``OdeOne`` and ``OdeTwo`` have been removed from the distribution. In addition, the interface to the ODE solvers have been simplified. 12-13 ===== Remove the ``CppADCreateTape`` macro and have the tapes created and grow automatically. 12-12 ===== The old method where one directly accesses the tape has been removed and the following functions are no longer available: | |tab| |tab| ``size_t`` *TapeName* . ``Independent`` ( ``AD`` < *Base* > & *indvar* ) | |tab| |tab| ``size_t`` *TapeName* . ``Record`` ( ``size_t`` *order* ) | |tab| |tab| ``size_t`` *TapeName* . ``Stop`` ( ``void`` ) | |tab| |tab| ``bool Dependent`` ( ``const AD`` < *Base* > & *var* ) ``const`` | |tab| |tab| ``bool`` *TapeName* . ``Dependent`` ( ``const AD`` < *Base* > & *var* ) ``const`` | |tab| |tab| ``size_t`` *TapeName* . ``Total`` ( ``void`` ) ``const`` | |tab| |tab| ``size_t`` *TapeName* . ``Required`` ( ``void`` ) ``const`` | |tab| |tab| ``size_t`` *TapeName* . ``Erase`` ( ``void`` ) | |tab| |tab| ``TapeState`` *TapeName* . ``State`` ( ``void`` ) ``const`` | |tab| |tab| ``size_t`` *TapeName* . ``Order`` ( ``void`` ) ``const`` | |tab| |tab| ``size_t`` *TapeName* . ``Required`` ( ``void`` ) ``const`` | |tab| |tab| ``bool Parameter`` ( ``CppADvector< AD<`` *Base* > > & *u* ) | |tab| |tab| *TapeName* . ``Forward`` ( *indvar* ) | |tab| |tab| *TapeName* . ``Reverse`` ( *var* ) | |tab| |tab| *TapeName* . ``Partial`` ( *var* ) | |tab| |tab| *TapeName* . ``ForwardTwo`` ( *indvar* ) | |tab| |tab| *TapeName* . ``ReverseTwo`` ( *var* ) | |tab| |tab| *TapeName* . ``PartialTwo`` ( *var* ) 12-10 ===== The change on :ref:`2003@mm-dd@12-01` make the taping process simpler if one does not directly access ``CppADCreateTape`` . The :ref:`examples` were changed to not use *TapeName* . The following examples were skipped because they document the functions that access *TapeName* : ``DefFun.cpp`` , ``For.cpp`` , ``for_two.cpp`` , ``Rev.cpp`` , and ``rev_two.cpp`` . 12-05 ===== There was a bug in *f* . ``Rev`` and *f* . ``RevTwo`` and when two dependent variables were always equal and shared the same location in the tape. This has been fixed. The ODE Example was changed to tape the solution (and not use ``OdeOne`` or ``OdeTwo`` ). This is simpler to use and the resulting speed tests gave much faster results. 12-01 ===== The following function has been added: ``void Independent`` ( ``const CppADvector`` < *Base* > & *x* ) which will declare the independent variables and begin recording ``AD`` < *Base* > operations (see :ref:`Independent-name` ). The :ref:`ADFun-name` constructor was modified so that it stops the recording and erases that tape as well as creates the :ref:`ADFun-name` object. In addition, the tape no longer needs to be specified in the constructor. 11-21 ===== Add ``StiffZero`` to set of ODE solvers. 11-20 ===== The ``AbsGeq`` and ``LeqZero`` in :ref:`LuSolve-name` were changed to template functions so they could have default definitions in the case where the ``<=`` and ``>=`` operators are defined. This made the ``double`` and ``AD`` use of ``LuSolve`` simpler because the user need not worry about these functions. On the other hand, it made the ``std::complex`` and ``AD`` use of ``LuSolve`` more complex. The member function names for the *fun* argument to ODE were changed from *fun* . ``f`` to *fun* . ``Ode`` and from *fun* . ``g`` to *fun* . ``Ode_ini`` . 11-16 ===== The table of contents was reorganized to provide a better grouping of the documentation. The :ref:`LuSolve-name` utility is now part of the distribution and not just an example; i.e., it is automatically included by ``cppad.hpp`` . 11-15 ===== The ODE solver was modified so that it can be used with any type (not just an AD type. This was useful for the speed testing. It is also useful for determining how the integrator steps should be before starting the tape. The template argument *Type* was changed to *Base* where ever it was the :ref:`glossary@Base Type` of an AD class. 11-14 ===== An ``speed_cppad/OdeSpeed.cpp/`` test was added and some changes were made to the ODE interface in order to make it faster. The most significant change was in the specifications for the ODE function object *fun* . 11-12 ===== The user defined unary function example ``example/UnaryFun.cpp`` was incorrect. It has been corrected and extended. 11-11 ===== The :ref:`CppAD::vector` template class is now used where the ``std::vector`` template class was previously used. You can replace the ``CppAD::vector`` class with a vector template class of your choosing during the :ref:`Install-name` procedure. 11-06 ===== The documentation for :ref:`taping derivative calculations` was improved as well as the corresponding example. In order to make this simpler, the example tape name ``DoubleTape`` was changed to ``ADdoubleTape`` (and the other example tape names were also changed). 11-04 ===== The ODE utility was changed from an example to part of the distribution. In addition, it was extended so that it now supports taping the solution of the differential equations (case *order* equal zero) or solving the extended set of differential equations for both first and second derivatives (cases *order* equal one and two). In addition, an initial condition that depends on the parameter values is also allowed. 11-02 ===== It is now legal to differentiate a :ref:`glossary@Parameter` with respect to an :ref:`glossary@Tape@Independent Variable` (parameter derivatives are always equal to zero). This is an extension of the ``Reverse`` , ``Partial`` , ``ReverseTwo`` , and ``PartialTwo`` functions. 10-21 ===== All the ``CppAD`` include files, except ``cppad.hpp`` were moved into an ``include`` subdirectory. 10-16 ===== The :ref:`ADFun-name` template class was added so that one can save a tape recording and use it as a differentiable function. The ``ADFun`` functions supports directional derivatives in both :ref:`Forward-name` and :ref:`Reverse-name` mode where as the tape only supports partial derivatives. 10-14 ===== The ``sqrt`` function was added to the :ref:`unary_standard_math-name` functions. In addition, a definition of the power function for the types ``float`` and ``double`` was automatically included in the ``CppAD`` namespace. The :ref:`Value-name` function was changed so that it can be called when the tape is in the Empty state. 10-10 ===== The ``atan`` function was added to the :ref:`unary_standard_math-name` functions. 10-06 ===== In the notation below, *zero* and *one* are parameters that are exactly equal to zero and one. If the variables *z* and *x* were related in any of the following ways, they share can share the same record on the tape because they will have the same derivatives. | |tab| *z* = *x* + *zero* *z* = *x* * *one* | |tab| *z* = *zero* + *x* *z* = *one* * *x* | |tab| *z* = *x* ``-`` *zero* *z* = *x* / *one* Furthermore, in the following cases, the result *z* is a parameter (equal to zero) and need not be recorded in the tape: | |tab| *z* = *x* * *zero* *z* = *zero* / *x* | |tab| *z* = *zero* * *x* The :ref:`arithmetic operators` were all checked to make sure they did not add to the tape in these special cases. The total record count for the program in the Example directory was 552 before this change and 458 after. 10-05 ===== The process of converting the tape to operators was completed. In order to make this conversion, the binary user defined functions were removed. (Bob Goddard suggested a very nice way to keep the unary functions.) Another significant change was made to the user interface during this procedure, the standard math library functions are now part of the CppAD distribution and not defined by the user. The function *TapeName* . ``Total`` was added to make it easy to track how many tape records are used by the test suite. This will help with future optimization of the CppAD recording process. There was a bug (found by `Mike Dodds `_) in the error checking of the *TapeName.Erase* function. If ``Erase`` was called twice in a row, and ``NDEBUG`` was false during compilation, the program would abort. This has been fixed. 09-30 ===== A process of changing the tape from storing partial derivatives to storing operators has been started. This will make the tape smaller and it will enable the computation of higher derivatives with out having to tape the tape (see :ref:`mul_level-name` ). The Add, Subtract, Multiply and Divide operators have been converted. The user defined functions are presenting some difficulties, so this process has not yet been completed. There was a bug in reverse mode when an dependent variable was exactly equal to an independent variable. In this case, it was possible for it to be located before other of the independent variables on the tape. These other independent variable partials were not initialized to zero before the reverse calculation and hence had what ever value was left by the previous mode calculation. This has been fixed and the ``Eq.cpp`` example has been changed to test for this case. The following tape functions were changed to be declared ``const`` because they do not modify the tape in any way: ``State`` , ``Order`` , ``Required`` , ``Dependent`` , and :ref:`con_dyn_var@Parameter` . 09-20 ===== The functions ``Grad`` and ``Hess`` were changed to use function objects instead of function pointers. 09-19 ===== The higher order constructors (in standard valarray) were removed from the ODE example in order to avoid memory allocation of temporaries (and hence increase speed). In addition, the function objects in the ODE examples were changed to be ``const`` . 09-18 ===== An ordinary differential equation solver was added. In addition, the extended system to differentiate the solution was included. 09-15 ===== The linked list of AD variables was not being maintained correctly by the AD destructor. This was fixed by have the destructor use ``RemoveFromVarList`` to remove variables from the list. (``RemoveFromVarList`` is a private AD member function not visible to the user.) 09-14 ===== There is a new Faq question about evaluating derivatives at multiple values for the :ref:`Faq@Independent Variables` . 09-13 ===== An example that uses ``AD< AD >`` to compute higher derivatives was added. The name ``GaussEliminate`` was changed to :ref:`LuSolve-name` to better reflect the solution method. 09-06 ===== Changed the :ref:`get_started.cpp-name` and :ref:`complex_poly.cpp-name` examples so they use a template function with both base type and AD type arguments. (The resulting code is simpler and a good use of templates.) 09-05 ===== A :ref:`getting started` example was added and the organization of the :ref:`Examples` was changed. 09-04 ===== The ``AbsOfDoubleNotDefine`` flag is no longer used and it was removed from the Windows :ref:`Install-name` instructions. The 03-09-03 distribution did not have the proper date attached to it. The distribution script has been changed so that attaching the proper date is automated (i.e., this should not happen again). A :ref:`faq-title` section was started. 09-03 ===== Added the :ref:`Value-name` function which returns the :ref:`glossary@Base Type` value corresponding to an AD object. 08-23 ===== A new version of Cygwin was installed on the development system (this may affect the timing tests reported in this document). In addition, :ref:`LuSolve-name` was changed to use back substitution instead of reduction to an identity matrix. This reduced the number of floating point operations corresponding to evaluation of the determinant. The following results correspond to the speed test of DetLu on a 9 by 9 matrix: .. list-table:: :widths: auto * - **Version** - **double Rate** - **AD Rate** - **Gradient Rate** - **Hessian Rate** - **Tape Length** * - 03-08-20 - 8,524 - 5,278 - 4,260 - 2,450 - 532 * - 03-08-23 - 7,869 - 4,989 - 4,870 - 2,637 - 464 08-22 ===== The :ref:`unary minus` operator was added to the AD operations. 08-19 ===== The standard math function examples were extended to include the complex case. The :ref:`LuSolve-name` routine what changed to use ``std::vector<`` *Base* > & arguments in place of *Base* * arguments. This removes the need to use ``new`` and ``delete`` with ``LuSolve`` . When testing the speed of the change to using standard vector, it was noticed that the LuSolve routine was much slower. (see times for 03-08-16 below). This was do to computing the determinant instead of the log of the determinant. Converting back to the log of the determinant regained the high speeds. The following results correspond to the speed test of DetLu on a 9 by 9 matrix: .. list-table:: :widths: auto * - **Version** - **double Rate** - **AD Rate** - **Gradient Rate** - **Hessian Rate** - **Tape Length** * - 03-08-16 - 9,509 - 5,565 - 3,587 - 54 - 537 * - 03-08-19 - 8,655 - 5,313 - 4,307 - 2,495 - 532 08-17 ===== The macro ``CppADTapeOverflow`` was added so that CppAD can check for tape overflow even in the ``NDEBUG`` preprocessor flag is defined. 08-16 ===== The :ref:`LuSolve-name` routine was extended to handle complex arguments. Because the complex absolute value function is nowhere differentiable, this required the allowing for user defined :ref:`boolean valued functions with AD arguments` . The examples :ref:`lu_solve.cpp-name` and ``GradLu.cpp`` were converted to a complex case. 08-11 ===== The routine :ref:`LuSolve-name` was made more efficient so that it is more useful as a tool for differentiating linear algebra calculations. The following results correspond to the speed test of DetLu on a 9 by 9 matrix: .. list-table:: :widths: auto * - **Version** - **double Rate** - **AD Rate** - **Gradient Rate** - **Hessian Rate** - **Tape Length** * - 03-08-10 - 49,201 - 7,787 - 2,655 - 1,809 - 824 * - 03-08-11 - 35,178 - 12,681 - 4,521 - 2,541 - 540 In addition the corresponding test case :ref:`lu_solve.cpp-name` was changed to a Hilbert matrix case. 08-10 ===== A :ref:`complex polynomial` example was added. The documentation and type conversion in :ref:`LuSolve-name` was improved. The absolute value function was removed from the examples because some systems do not yet properly support ``double abs`` ( ``double`` *x* ) , 08-07 ===== Because the change to the multiplication operator had such a large positive effect, all of the :ref:`arithmetic operators` were modified to reduce the amount of information in the tape (where possible). 08-06 ===== During Lu factorization, certain elements of the matrix are know to be zero or one and do not depend on the variables. The :ref:`multiplication` operator was modified to take advantage of this fact. This reduced the size of the tape and increased the speed for the calculation of the gradient and Hessian for the Lu determinant test of a 5 by 5 matrix as follows: .. list-table:: :widths: auto * - **Version** - **Tape Length** - **Gradient Rate** - **Hessian Rate** * - 03-08-05 - 176 - 11,362 - 1,149 * - 03-08-06 - 167 - 12,780 - 10,625 08-05 ===== Fixed a mistake in the calculation of the sign of the determinant in the :ref:`LuSolve-name` example. 08-04 ===== Added a the compiler flag :: AbsOfDoubleNotDefined to the make files so that it could be removed on systems where the function ``double abs`` ( ``double`` *x* ) was defined in ``math.h`` . 08-03 ===== The ``Grad`` and ``Hess`` functions were modified to handle the case where the function does not depend on the independent variables. The :ref:`LuSolve-name` example was added to show how on can differentiate linear algebra calculations. In addition, it was used to add another set of :ref:`speed tests` . The standard Math functions were added both as examples of defining atomic operations and to support mathematical operations for the ``AD`` case. The :ref:`\<\<` operator was added to the ``AD`` template class for output to streams. 08-01 ===== The :ref:`compound assignment` operators were added to the ``AD`` template class. The name of the ``Speed/SpeedTest`` program was changed to :ref:`Speed/Speed` . In addition, ``Speed/SpeedRun`` was changed to ``Speed/SpeedTest`` . 07-30 ===== The :ref:`assignment` operator was changed so the it returns a reference to the target. This allows for statements of the form *x* = *y* = *z* ; i.e., multiple assignments. 07-29 ===== If the :ref:`AD copy constructor` or :ref:`assignment` operator used an :ref:`glossary@Tape@Independent Variable` for its source value, the result was also an independent variable. This has been fixed so that the result is a dependent variable in these cases. 07-26 ===== The ``AD`` < *Base* > data structure was changed to include a doubly linked list of variables. This enabled the :ref:`AD copy constructor` and :ref:`assignment` operator to create multiple references to the same place in the tape. This reduced the size of the tape and increased the speed for the calculation of the gradient and Hessian for the determinant of a 5 by 5 matrix as follows: .. list-table:: :widths: auto * - **Version** - **Tape Length** - **Gradient Rate** - **Hessian Rate** * - 03-07-22 - 1668 - 1,363 - 53 * - 03-07-26 - 436 - 3,436 - 213 07-22 ===== The facility was added so that the user can define binary functions together with their derivatives. (This facility has been removed because it is better to define binary functions using AD variables.) The Windows version make file directive ``/I ..\..`` in ``example\Example.mak`` and ``Speed\Speed.mak`` was changed to ``/I ..`` (as it should have been). 07-20 ===== The facility was added so that the user can define unary functions, together with their derivatives. For example, the standard math functions such as :ref:`exp` are good candidates for such definitions. (This feature has been replaced by and the standard math functions are now part of the AD types, see :ref:`AD-name` .) The first Alpha for the Windows :ref:`installation` was released. 07-18 ===== Computing the determinant of a minor of a matrix :ref:`det_of_minor-name` was documented as a realistic example using CppAD. 07-16 ===== Fixed some non-standard constructions that caused problems with the installation on other machines. Compiled and ran the tests under Microsoft Windows. (The Windows release should not take much more work.) 07-14 ===== First Alpha release of CppAD and is being released under the :ref:`Gnu Public License` . It is intended for use by a Unix system. A Microsoft release is intended in the near future. {xrst_end 2003} ================================================ FILE: appendix/whats_new/2004.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2004 app} {xrst_spell aclocal autoconf automake dep deps dodds dsp executables ext faq filename hess mak makefile nmake retape ublas usr yy } Release Notes for 2004 ###################### mm-dd ***** 12-11 ===== The documentation for the CppAD error macros was improved. The package title in :ref:`user_guide-name` was changed. The documentation for :ref:`CppAD::vector` was improved and the corresponding source code ``cppad/vector.hpp`` was included. 12-09 ===== The :ref:`LuSolve-name` and ``OdeRunge`` source code was modified to make the more in line with the introduction to C++ AD book (``OdeRunge`` has been replaced by :ref:`Runge45-name` ). In addition, the examples ``OdeRunge.cpp`` and :ref:`lu_solve.cpp-name` were modified to make the simpler. (The more complex version of ``OdeRunge.cpp`` was moved to the ``TestMore`` directory.) 12-03 ===== The :ref:`Poly-name` documentation and source code were modified to make them more in line with the introduction to C++ AD book. 11-17 ===== Changing to Autoconf and Automake on :ref:`2004@mm-dd@08-24` mistakenly forgot the ``-Wall`` compiler switch (all warnings). This has been added and the corresponding warnings have been fixed. 11-16 ===== The 11-15 Debug version would not compile under Visual C++ version 7.0 because a declaration of ``LessThanOrZero`` was missing. This has been fixed. 11-15 ===== The :ref:`ForOne-name` and :ref:`RevOne-name` easy to use :ref:`Drivers-name` were added. 11-14 ===== The notation in the :ref:`ADFun-name` sections was changed to make the :ref:`Forward-name` and :ref:`Reverse-name` routines easier to use. 11-13 ===== The Taylor coefficient vector and matrix notation was folded into just :ref:`Taylor coefficients` . 11-12 ===== If ``NDEBUG`` is not defined during compile time, all ``AD`` < *Base* > :ref:`comparison` operations are checked during :ref:`zero order` forward mode calculations. The :ref:`CompareChange-name` function returns the number of comparison operations that have changed. 11-10 ===== The :ref:`get_started.cpp-name` example was changed to use the :ref:`Jacobian-name` driver. In addition, more index entries, that point to the :ref:`easy to use drivers` , were added. 11-04 ===== The Microsoft Visual Studio project file ``example/Example.dsp/`` was missing some new examples that needed to be linked in the install windows procedure. This has been fixed. 11-02 ===== The :ref:`unix installation` required the user to touch the files to get the dates in proper order. This is no longer necessary. 11-01 ===== Some of the dependency directories and files, for example ``PrintFor/.deps`` and ``PrintFor/.deps/PrintFor.Po`` had an extra ``?`` at the end of their names. This seems to have been fixed by using a newer version of the autoconf and automake tools. 10-29 ===== Add the example and test :ref:`simple_vector.cpp-name` to the :ref:`SimpleVector-name` documentation. The specifications for :ref:`preprocessor symbols` state that all the CppAD preprocessor symbols begin with ``CppAD`` (so they do not conflict with other packages). Some preprocessor symbols in the file ``cppad/config.h`` did began with ``WITH_`` . This has been fixed. 10-28 ===== The examples :ref:`hes_lu_det.cpp-name` , :ref:`hes_minor_det.cpp-name` , :ref:`jac_lu_det.cpp-name` , and :ref:`jac_minor_det.cpp-name` used the negative of a ``size_t`` value. The value has been changed to an ``int`` . The :ref:`CppAD::vector` template class was converted into a library routine so it can be used separately from the rest of CppAD. 10-27 ===== The :ref:`PrintFor-name` example was moved to its own directory because the conversion from VC 6.0 to VC 7.0 projects did not work when there were multiple executables in one project file. The :ref:`install-name` instructions were modified to reflect this change. 10-21 ===== One declaration (for the :ref:`Value-name` function) was missing from the file ``cppad/local/Declare.h`` . This has been added and CppAD should now compile and run under both Microsoft VC 6.0 and 7.0. 10-19 ===== The current version of CppAD has a problem compiling under Microsoft Visual C++ version 7.0 (it compiles and works under version 6.0). The problem appears to be due to a closer agreement between VC 7.0 and the C++ standard for declaring templates functions as friends. Some friend declarations were removed and others were made more specific in order to migrate the a version that will compile and run using VC 7.0. 10-16 ===== The example :ref:`compare.cpp-name` displayed the text from :ref:`bool_fun.cpp-name` by mistake. This has been fixed. The :ref:`Compare-name` operators have been extended to work with ``int`` operands. 10-06 ===== The test ``TapeDetLu`` was added to ``speed_cppad/DetLuSpeed.cpp`` and ``TapeDetMinor`` was added to ``speed_cppad/DetMinorSpeed.cpp`` . These tests just tape the calculations without computing any derivatives. Using this, and the other tests, one can to separate the taping time from the derivative calculation time. The windows installation steps do not build a ``config.h`` file. Hence a default ``config.h`` file was added to the distribution for use with Microsoft Visual Studio. The ``Distribute`` section of the developer documentation was brought up to date. Links to the ADOLC and FADBAD download pages were added to the :ref:`unix installation` instructions. 09-29 ===== The include files for the :ref:`utilities` are now included by the root file ``cppad/cppad.hpp`` . They can still be included individually with out the rest of the CppAD package. 09-26 ===== The routine ``OdeRunge`` was modified so that it will now integrate functions of a complex arguments. This was done by removing all uses of greater than and less than comparisons were removed. (``OdeRunge`` has been replaced by :ref:`Runge45-name` ). The changes on :ref:`2004@mm-dd@09-21` did not fix all the file date and time problems; i.e., automake was still running in response to the :ref:`unix installation` ``make`` command. 09-23 ===== There was a reference to *B* that should have been *X* in the description of the :ref:`LuSolve@X` argument of ``LuSolve`` . This has been fixed. 09-21 ===== The :ref:`CondExp-name` function has been modified so that it works properly for ``AD< AD<`` *Base* > > types; i.e., it now works for multiple levels of taping. The date of the files ``aclocal.m4`` and ``config.h.in`` were later than the date of top level ``Makefile.am`` . This caused the ``make`` command during the :ref:`unix installation` to try to run ``autoconf`` and this did not work on systems with very old versions of ``autoconf`` . This has been fixed. 09-13 ===== The examples that are specific to an operation were moved to be below that operation in the documentation tree. For example :ref:`add.cpp-name` is below :ref:`ad_binary-name` in the documentation tree. 09-10 ===== The version released on 04-09-09 did not have the new file ``PrintFor.h`` in ``cppad/local`` . This has been fixed. The *Base* type requirements were simplified. The :ref:`Unix installation` instructions were modified so just one make command was executed at the top level. This was necessary because the order of the makes is now important (as previously suggested, the makes did not work properly). 09-09 ===== The :ref:`PrintFor-name` function was added so that users can debug the computation of function values at arguments that are different from those used when taping. 09-07 ===== In the :ref:`Unix installation` instructions place ``./`` in front of current directory program names; for example, ``./GetStarted`` instead of ``GetStarted`` (because some unix systems do not have the current directory in the default executable path). 09-04 ===== A library containing the :ref:`SpeedTest-name` and :ref:`NearEqual-name` object files was added to the distribution. All of the include files of the form < ``cppad/library/`` *name* . ``h>`` were moved to < ``cppad/`` *name* . ``h>`` . 09-02 ===== Some more messages were added to the output of ``configure`` during the :ref:`Unix installation` . The suggested compression program during Windows installation was changed from `7-zip `_ to `WinZip `_. 08-27 ===== The error messages printed by the default version of the CppAD error macros had ``YY-MM-DD`` in place of the date for the current version. This has been fixed. All the correctness tests are now compiled with the ``-g`` command line option (the speed tests are still compiled with ``-O2 -DNDEBUG`` ). The :ref:`installation instructions` for Unix and Windows were split into separate pages. 08-25 ===== The :ref:`installation` now automates the replacement of :ref:`CppAD::vector` by either the ``std::vector`` or ``boost::numeric::ublas::vector`` . 08-24 ===== This date marks the first release that uses the Gnu tools Autoconf and Automake. This automates the building of the make files for the :ref:`installation` and is the standard way to distribute open source software. This caused some organizational changes, for example, the :ref:`GetStarted` example now has its own directory and the distribution directory is named ``cppad-`` *yy* ``-`` *mm* ``-`` *dd* where *yy* ``-`` *mm* ``-`` *dd* is the year, month and date of the distribution. (Note the distribution directory is different from the directory where CppAD is finally installed.) 08-12 ===== Move ``OdeExplicit`` into the ``cppad/library/`` directory. In addition, change it so that the vector type was a template argument; i.e., works for any type of vector (not just ``CppADvector`` ). 07-31 ===== Move :ref:`LuSolve-name` into the ``cppad/library/`` directory. In addition, change it so that the vector type was a template argument; i.e., works for any type of vector (not just ``CppADvector`` ). 07-08 ===== The file ``cppad/example/NearEqual.h`` has been moved to ``cppad/example/NearEqualExt.h`` because it contains extensions of the :ref:`NearEqual-name` routine to ``AD`` types. 07-07 ===== The ``double`` and ``std::complex`` cases for the :ref:`NearEqual-name` routine arguments has been moved to the general purpose :ref:`utilities` . 07-03 ===== The CppAD error macros names ``CppADExternalAssert`` and ``CppADInternalAssert`` were changed to ``CppADUsageError`` and ``CppADUnknownError`` . The :ref:`SpeedTest-name` routine was changed to use ``CppADUsageError`` instead of a C assert. 07-02 ===== The :ref:`SpeedTest-name` output was improved so that the columns of values line up. Previously, this was not the case when the number of digits in the size changed. 06-29 ===== Added code to trap and report memory allocation errors during ``new`` operations. 06-25 ===== A discussion of the order dependence of the :ref:`assignment` operator and the :ref:`independent function` was added to the :ref:`Faq` . In addition, a similar discussion was added to the documentation for the :ref:`Independent-name` function. The definition of a :ref:`glossary@Parameter` and :ref:`glossary@Variable` were changed to reflect that fact that these are time dependent (current) properties of an ``AD`` < *Base* > object. 06-12 ===== All of the :ref:`arithmetic operators` (except for the unary operators) can now accept ``int`` arguments. The documentation for these arguments has been changed to reflect this. In addition, the corresponding test cases have been changed to test this and to test high order derivative cases. The old versions of these tests were moved into the ``cppad/Test`` directory. 06-04 ===== The :ref:`atan2-name` function was added. 06-03 ===== The ``asin`` and ``acos`` :ref:`unary_standard_math-name` functions were added. There was a bug the reverse mode theory and calculation of derivatives of :ref:`sqrt-name` for fourth and higher orders. This has been fixed. In addition, the following examples have been changed so that they test derivative up to fifth order: :ref:`asin` , :ref:`atan` , :ref:`cos` , :ref:`exp` , :ref:`log` , :ref:`sin` , :ref:`sqrt` . 06-01 ===== There was a bug in the :ref:`atan-name` function :ref:`forward mode` calculations for Taylor coefficient orders greater than two. This has been fixed. 05-30 ===== The :ref:`sin` and :ref:`cos` examples were changed so that they tested higher order derivatives. 05-29 ===== The forward mode recursion formulas for each of the :ref:`standard math functions` has been split into separate sections. A roman (instead of italic) font was used for the name of for the name of each of the standard math functions in the assumption statements below the section for the standard math functions. For example, :math:`\sin(x)` instead of :math:`sin(x)`. 05-26 ===== In the documentation for :ref:`Poly-name` , the reference to ``example/Poly.h`` was corrected to ``cppad/library/Poly.h`` . In the documentation for :ref:`SpeedTest-name` , the reference to ``Lib/SpeedTest.h`` was corrected to ``cppad/library/SpeedTest.h`` . In addition, the example case was corrected. In :ref:`Reverse-name` , the definition for :math:`U(t, u)` had :math:`t^p-1` where it should have had :math:`t^{p-1}`. This has been fixed. 05-25 ===== The special case where the second argument to the :ref:`pow-name` function is an ``int`` has been added. 05-14 ===== Change all of the include syntax # ``include`` " *filename* " to the syntax # ``include`` < *filename* > so that examples and other use better reflect how one would use CppAD after it was installed in a standard include directory; for example ``/usr/local/include/cppad`` . The user documentation was moved from the directory ``cppad/User`` to the directory ``cppad/Doc`` . The directory ``cppad/Lib`` was moved to ``cppad/library`` to reflect that fact that it is not what one expects in a standard ``lib`` directory or a standard ``include`` directory. 05-12 ===== The string ``YY-MM-DD`` in the preprocessor symbol ``CppADVersion`` was not being replaced by the current date during distribution. This resulted in the ``CppADExternalAssert`` macro printing ``YY-MM-DD`` where is should have printed the date of distribution. This has been fixed. All of the include commands of the form | |tab| # ``include`` " ``include/`` *name* . ``h`` " | |tab| # ``include`` " ``lib/`` *name* . ``h`` " have been changed to the form | |tab| # ``include`` " ``cppad/include/`` *name* . ``h`` " | |tab| # ``include`` " ``cppad/lib/`` *name* . ``h`` " This will avoid mistakenly loading a file from another package that is in the set of directories being searched by the compiler. It is therefore necessary to specify that the directory above the ``CppAD`` directory be searched by the compiler. For example, if ``CppAD`` is in ``/usr/local/cppad`` , you must specify that ``/usr/local`` be searched by the compiler. Note that if ``/usr/local/cppad/`` is no longer searched, you will have to change :: # include "cppad.hpp" to:: # include "cppad/cppad.hpp" The window ``nmake`` file ``Speed/Speed.mak`` was out of date. This has been fixed. 05-09 ===== Move :ref:`Poly-name` and :ref:`SpeedTest-name` into the ``cppad/Lib`` directory and the ``CppAD`` namespace. 05-07 ===== The :ref:`divide operator tests` were extended to include a second order derivative calculation using reverse mode. The :ref:`Poly-name` routine was modified to be more efficient in the derivative case. In addition, it was changed to use an arbitrary vector for the coefficients (not just a ``CppADvector`` ). 05-04 ===== A reloading of the data base caused the files ``include/atan.h`` and ``include/cos.h`` to be mistakenly started with lower case letters. These have been moved to ``include/Atan.h`` and ``include/Cos.h`` respectively. 05-03 ===== The :ref:`Reverse-name` mode calculations for :ref:`conditional expressions` were mistakenly left out. This has been fixed. 04-29 ===== The unary functions, such as :ref:`sin-name` and :ref:`cos-name` , were not defined for elements of an :ref:`VecAD-name` vector. This has been fixed. 04-28 ===== The operator :ref:`\<\<` was added to the default ``test_vector`` template class. A FADBAD correctness and speed comparison with CppAD was added. 04-25 ===== Factor out common sub-expressions in order to make :ref:`lu_vec_ad.cpp-name` faster. Convert description from C++ Automatic Differentiation to C++ Algorithmic Differentiation. 04-24 ===== The :ref:`VecAD-name` element class is no longer a derived class of the :ref:`AD-name` class. This enabled a decrease in tape memory and an increase in the speed for :ref:`VecAD-name` operations. The :ref:`log10-name` function was added. 04-22 ===== Add :ref:`CondExp-name` and use it to speed up :ref:`lu_vec_ad.cpp-name` . 04-21 ===== Use :ref:`abs-name` to speed up :ref:`lu_vec_ad.cpp-name` . 04-20 ===== The :ref:`absolute value` function was added. The value *n* for ``OdeExplicit`` and ``OdeImplicit`` is deduced from the argument *x0* and is not passed as a separate argument. This documentation has been fixed to this effect. 04-19 ===== The :ref:`+=` operator did not function correctly when the left hand operand was a :ref:`glossary@Parameter` and the right hand operand was a variable (found by `Mike Dodds `_). This has been fixed. 04-09 ===== Adding special operators for using parameters to index ``VecAD`` objects increased the speed and reduced the memory requirements (by about 20%) for the :ref:`VecAD-name` case in the ``speed_cppad/LuSolveSpeed.cpp/`` test. The :ref:`VecAD-name` objects are not being handled correctly by the :ref:`Reverse-name` function. The ``VecAD`` test was extended to demonstrate the problem and the problem was fixed (it is now part of ``TestMore/VecAD`` ). 04-08 ===== The example :ref:`lu_vec_ad_ok.cpp-name` uses :ref:`VecAD-name` to executes different pivoting operations during the solution of linear equations with out having to retape. The speed test ``speed_cppad/LuSolveSpeed.cpp/`` has been added. It shows that the initial implementation of :ref:`VecAD-name` is slow (and uses a lot of memory.) In fact, it is faster to use :ref:`LuSolve-name` and retape for each set of equations than it is to use :ref:`lu_vec_ad.cpp-name` and not have to retape. This test will help us improve the speed of :ref:`lu_vec_ad.cpp-name` . 04-07 ===== There were bugs in the assignment to :ref:`VecAD-name` elements during taping that have been fixed. In addition, an example of tapping the pivoting operations in an :ref:`Lu factorization` has been added. 04-03 ===== Added ``size_t`` indexing to the :ref:`VecAD-name` class. Fixed a bug connected to the :ref:`VecAD-name` class and erasing the tape. 04-02 ===== Some memory savings is done with regard to equal parameter values being stored in the tape. There was a bug in this logic when parameter in an ``AD< AD<`` *Base* > > class had values that were variables in the ``AD`` < *Base* > class. This has been fixed. 04-01 ===== The name of the class that tapes indexing operations was changed from ``ADVec`` to :ref:`VecAD-name` . This class was extended so that the value of elements in these vectors can be variables (need not be :ref:`parameters` ). 03-30 ===== Do some simple searching of the parameter table during taping avoid multiple copies of parameters on tape (use less tape memory). 03-28 ===== The version :ref:`ADVec` , a vector class that tapes indexing operations, is now available. It is currently restricted by the fact that all the values in the vector must be :ref:`parameters` . 03-25 ===== The internal taping structure has been changed to have variable length instructions. This is to save memory on the tape. In addition, it may help in the implementation of the vector class that tracks indexing. (A now functioning version of this class is described in :ref:`VecAD-name` .) 03-18 ===== A change was made to the way parameter values are stored on the tape. This resulted in a significant savings in the amount of memory required. 03-17 ===== Change the return type for :ref:`SpeedTest-name` from ``const char *`` to ``std::string`` . The memory required for the largest test cases was added to the :ref:`speed_cppad-name` tests output. 03-15 ===== The comparison between ADOLC and CppAD for the ``DetLuADOLC.cpp/`` example was returning an error (because it was checking for exact equality of calculated derivatives instead of nearly equal). This has been fixed. 03-12 ===== The user defined unary functions were removed and the user defined :ref:`discrete functions` were added. These discrete functions add the capability of conditional expressions (alternate calculations) being included in an :ref:`ADFun-name` object. 03-11 ===== The classes :ref:`det_by_minor-name` and :ref:`det_by_lu-name` were added and used these to simplify the examples that compute determinants. 03-09 ===== The routines ``Grad`` and ``Hess`` have been removed. You should use :ref:`Jacobian-name` and :ref:`Hessian-name` instead. 03-07 ===== The driver routines :ref:`Hessian-name` and :ref:`RevTwo-name` has been added. These to compute specialized subsets of the second order partials. Documentation errors in :ref:`ForTwo-name` and :ref:`Reverse-name` were fixed. The :ref:`example-name` documentation was reorganized. 03-06 ===== The driver :ref:`ForTwo-name` has been added. It uses forward mode to compute a subset of the second order partials. Split all of the "example" and "test" index entries that come from ``include/cppad/example/`` * . ``cpp`` into sorted subheadings. 03-05 ===== The ``Grad`` routine, which only computed first derivatives of scalar valued functions, has been replaced by the :ref:`Jacobian-name` routine which computes the derivative of vector valued functions. 03-04 ===== The bug reported on :ref:`2004@mm-dd@02-17` was present in all the operators. These have all been fixed and tests for all the operators have been added to the ``cppad/Test`` directory. The :ref:`f.Parameter()` function was added so that one can count how many components of the range space depend on the value of the domain space components. This helps when deciding whether to use forward or reverse mode. 03-03 ===== Special operators were added to distinguish the cases where one of the operands is a :ref:`glossary@Parameter` . This reduced the amount of branching that is necessary when executing :ref:`Forward-name` and :ref:`Reverse-name` calculations. The :ref:`Independent-name` and :ref:`fun_property@Parameter` functions were moved below :ref:`ADFun-name` in the documentation. 03-01 ===== The DetLuADOLC.cpp, DetLu case was added to the ADOLC comparison tests. 02-29 ===== Under certain optimization flag values, and on certain systems, an error was reported by the ADOLC correctness comparison. It turned out that CppAD was not initializing a particular index when debugging was turned off. This has been fixed. 02-28 ===== A set of routines for comparing CppAD with ADOLC has been added to the distribution. In addition, documentation for compiling and linking the :ref:`Examples` and :ref:`Speed Tests` has been added. 02-21 ===== If you use the user defined unary atomic functions there is a restriction on the order of the derivatives that can be calculated. This restriction was documented in the user defined unary function :ref:`Forward-name` and :ref:`Reverse-name` . (These unary functions were removed on :ref:`2004@mm-dd@03-12` .) 02-20 ===== A user interface to arbitrary order :ref:`reverse mode` calculations was implemented. In addition, the :ref:`ADFun-name` member functions ``Rev`` and ``RevTwo`` were removed because it is easier to use the uniform syntax below: .. list-table:: :widths: auto * - **Old Syntax** - **Uniform Syntax** * - *r1* = *f* . ``Rev`` ( *v* ) - *r1* = *f* . ``Reverse`` (1, *v* ) * - *q1* = *f* . ``RevTwo`` ( *v* ) - *r2* = *f* . ``Reverse`` (2, *v* ) * - - *q1* [ *i* ] == *r2* [2 * *i* + 1] The :ref:`Theory-name` section has been completely changed so that it corresponds to the arbitrary order calculations. (Some of this change was made when the arbitrary forward mode interface was added on :ref:`04-02-15<2004@mm-dd@02-15>` . The directory ``cppad/Test`` has been added. It contains tests cases that are not intended as examples. 02-17 ===== There was a bug in the way CppAD handled the parameters zero and one when they were variables on a lower level tape; i.e. x might be a parameter on an ``AD< AD<`` *Base* > > tape and a its value might be a variable on the ``AD`` < *Base* > tape. This bug in the multiply and divide routines has been fixed. There was a bug that is some cases reported a divide by zero error when the numerator was zero. This has been fixed. 02-16 ===== A bug in :ref:`Forward-name` prevented the calculation of derivatives with higher order than two. In addition, this checking for user errors in the use of ``Forward`` was also faulty. This has been fixed. The Microsoft project file ``example\Example.dsp`` was out of date. This has been fixed. The example that :ref:`tapes derivative calculations` has been changed to an application of :ref:`Taylor's method` for solving ordinary differential equations. 02-15 ===== A user interface to arbitrary order :ref:`forward mode` calculations was implemented. In addition, the :ref:`ADFun-name` member functions ``Arg`` , ``For`` and ``ForTwo`` were removed because it is easier to use the uniform syntax below: .. list-table:: :widths: auto * - **Old Syntax** - **Uniform Syntax** * - *v0* = *f* . ``Arg`` ( *u0* ) - *v0* = *f* . ``Forward`` (0, *u0* ) * - *v1* = *f* . ``For`` ( *u1* ) - *v1* = *f* . ``Forward`` (1, *u1* ) * - *v2* = *f* . ``For`` ( *u2* ) - *v2* = *f* . ``Forward`` (1, *u2* ) 02-12 ===== All of the derivative calculations are now done using arbitrary order Taylor arithmetic routines. The :ref:`Theory-name` section was changed to document this method of calculation. 02-01 ===== The definition of a :ref:`glossary@Taylor Coefficient` was changed to include the factorial factor. This change was also made to the output specifications for the ``FunForTwo`` routine. 01-29 ===== There were some bugs in the ``FunArg`` function that were fixed. #. If one of the dependent variables was a :ref:`glossary@Parameter` ``FunArg`` did not set it's value properly. (All its derivatives are zero and this was handled properly.) #. The user defined unary functions were not computed correctly. The specifications for the usage and unknown CppAD error macros were modified so that they could be used with out side effects. 01-28 ===== Some corrections and improvements were made to the documentation including: ``CppADvector`` was placed before its use, a reference to ``Ode_ind`` and ``Ode_dep`` was fixed in ``OdeImplicit`` . 01-22 ===== The specifications for the routine ``FunForTwo`` was changed to use :ref:`Taylor coefficients` . This makes the interface to CppAD closer to the interface for `ADOLC `_. {xrst_end 2004} ================================================ FILE: appendix/whats_new/2005.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2005 app} {xrst_spell dlib dsp dsw dussault etc gz kutta maxabs neg pearce pkzip scur sln typedef valarray vcproj yy } Release Notes for 2005 ###################### mm-dd ***** 12-24 ===== Fix a memory leak that could occur during the :ref:`ForSparseJac-name` calculations. 12-23 ===== The buffers that are used to do :ref:`RevSparseJac-name` and :ref:`RevSparseHes-name` calculations are now freed directly after use. The TrackNewDel.cpp example was missing from the Windows install examples and testing project file. This has been fixed. 12-22 ===== The buffer that is are used to do :ref:`Reverse-name` mode calculations is now freed directly after use. This reduces the memory requirements attached to an :ref:`ADFun-name` object. 12-20 ===== Buffers that are used to store the tape information corresponding to the ``AD`` < *Base* > type are now freed when the corresponding :ref:`ADFun-name` object is constructed. This reduces memory requirements and actually had better results with the :ref:`speed_cppad-name` tests. The :ref:`speed_cppad-name` test program now outputs the version of CppAD at the top (to help when comparing output between different versions). 12-19 ===== The :ref:`track_new_del-name` routines were added for track memory allocation and deletion with ``new[]`` and ``delete[]`` . This is in preparation for making CppAD more efficient in it's use of memory. The bug mentioned on :ref:`2005@mm-dd@12-01` resurfaced and the corresponding routine was changed as follows: :: static local::ADTape *Tape(void) { // If we return &tape, instead of creating and returning ptr, // there seems to be a bug in g++ with -O2 option. static local::ADTape tape; static local::ADTape *ptr = &tape; return ptr; } 12-16 ===== The :ref:`NearEqual-name` function documentation for the relative error case was changed to | *x* ``-`` *y* | <= *r* * ( | *x* | + | *y* | ) so that there is no problem with division by zero when *x* and *y* are zero (the code was changed to that form also). The ``std::abs`` function replaced the direct computation of the complex norms (for the complex case in ``NearEqual`` ). In addition, more extensive testing was done in :ref:`near_equal.cpp-name` . 12-15 ===== Extend :ref:`NearEqual-name` and :ref:`near_equal_ext-name` to cover more cases while converting them from, a library function in ``lib/CppADlib.a`` and an utility in ``example/NearEqualExt.h`` , to a template functions in ``cppad/near_equal.hpp`` and ``cppad/local/NearEqualExt.h`` . This is another step along the way of removing the entire ``CppADlib.a`` library. The change on :ref:`2005@mm-dd@12-14` broke the Microsoft project files ``example/Example.sln`` and ``TestMore/TestMore.sln`` used during CppAD installation on Windows. This has been fixed. Move ``lib/SpeedTest.cpp`` to ``cppad/speed_test.hpp`` . This was the last change necessary in order to remove the CppAD library, so remove all commands related to building and linking ``CppADlib.a`` . The corresponding entry has been removed from the :ref:`wish_list-name` . One of the entries in the :ref:`wish_list-name` corresponded to the :ref:`Integer-name` function. It has also been removed (because it is already implemented). 12-14 ===== Extend :ref:`erf-name` to cover more cases while converting it from a function in ``lib/CppADlib.a`` to a template function in ``cppad/local/Erf.h`` . This is one step along the way of removing the entire ``CppADlib.a`` library. 12-11 ===== Group routines that extend the domain for which an :ref:`ADFun-name` object is useful into the ExtendDomain section. Add an example of a C callable routine that computes derivatives using CppAD (see :ref:`interface2c.cpp-name` ). 12-08 ===== Split out :ref:`LuFactor-name` with the *ratio* argument to a separate function called :ref:`LuRatio-name` . This needed to be done because :ref:`LuRatio-name` is more restrictive and should not be part of the general template :ref:`utilities` . 12-07 ===== Improve :ref:`CheckSimpleVector-name` so that it tests element assignment. Change :ref:`check_simple_vector.cpp-name` so that it provides and example and test of a case where a simple vector returns a type different from the element type and the element assignment returns ``void`` . 12-06 ===== The specifications for a :ref:`SimpleVector-name` template class were extended so that the return type of an element access is not necessarily the same as the type of the elements. This enables us to include ``std::vector`` which packs multiple elements into a single storage location and returns a special type on element access (not the same as ``bool`` ). To be more specific, if *x* is a ``std::vector`` object and *i* has type ``size_t`` , *x* [ *i* ] does not have type ``bool`` . Add a Home icon, that links to the `CppAD home page `_, to the top left of the navigation frame (left frame) for each documentation section. 12-05 ===== The :ref:`RevSparseHes-name` reverse mode Hessian sparsity calculation has been added. The definition of a :ref:`glossary@Sparsity Pattern` has been corrected to properly correspond to the more efficient form mentioned under :ref:`2005<2005@mm-dd@11-20>` below. The dates in this file used to correspond to local time for when the change was checked into the subversion repository. From now on the dates in this file will correspond to the first version of CppAD where the change appears; i.e., the date in the unix and windows download file names ``CppAD-`` *yy* ``-`` *mm* ``-`` *dd* . 12-03 ===== There was a bug in the :ref:`RevSparseJac-name` reverse mode sparsity patterns when used with :ref:`VecAD-name` calculations. This bug was fixed and the calculations were made more efficient (fewer true entries). 12-02 ===== There was a bug in the :ref:`ForSparseJac-name` forward mode sparsity patterns when used with :ref:`VecAD-name` calculations. This bug was fixed and the calculations were made more efficient (fewer true entries). 12-01 ===== The speed test of :ref:`lu_vec_ad.cpp-name` has been reinstated. It appears that there is some sort of bug in the gcc compiler with the -O2 option whereby the following member function :: static local::ADTape *Tape(void) { static local::ADTape tape; return &tape; } (in ``cppad/local/AD.h`` ) would sometimes return a null value (during :ref:`VecAD-name` operations). A speed improvement in ``cppad/local/ExtendBuffer.h`` seems to prevent this problem. This fix is not well understood; i.e., we should watch to see if this problem reoccurs. The source code for :ref:`lu_vec_ad_ok.cpp-name` was mistakenly used for ``speed_cppad/LuSolveSpeed.cpp`` . This has been fixed. 11-23 ===== The speed test of :ref:`lu_vec_ad.cpp-name` has been commented out because it sometimes generates a segmentation fault. Here is an explanation: If *X* is a ``AD`` < *Base* > object, *y* is a *Base* object, *X* [ *y* ] uses pointer from the element back to the original vector. Optimizing compilers might reorder operations so that the vector is destroyed before the object is used. This can be avoided by changing the syntax for :ref:`VecAD-name` objects to use ``set`` and ``get`` member functions. 11-22 ===== A much better :ref:`example` for using :ref:`VecAD-name` vectors has been provided. In addition, a bug in the computation of derivatives using ``VecAD`` vectors has been fixed. CppAD now checks that the domain dimension during :ref:`Independent-name` and the range dimension during :ref:`ADFun-name` (provided that ``-DNDEBUG`` is not defined). If either of these is zero, the ``CppADUsageError`` macro is invoked. 11-20 ===== The sparsity pattern routines :ref:`ForSparseJac-name` and :ref:`RevSparseJac-name` have been modified so that they are relative to the Jacobian at a single argument value. This enables us to return more efficient :ref:`sparsity patterns` . An extra :ref:`exception::reference@Exceptions>` has been added to the use of :ref:`VecAD-name` elements. This makes ``VecAD`` some what more efficient. 11-19 ===== Improve the output messages generated during execution of the :ref:`configure@Configure` command. Put a try and catch block around all of the uses of ``new`` so that if a memory allocation error occurs, it will generate a ``CppADUsageError/`` message. The :ref:`get_started.cpp-name` example has been simplified so that it is easier to understand. 11-15 ===== Fix a memory leak in both the :ref:`ForSparseJac-name` and :ref:`RevSparseJac-name` calculations. 11-12 ===== Add reverse mode :ref:`Jacobian sparsity` calculation. 11-09 ===== Add prototype documentation for :ref:`LuSolve@logdet` in the :ref:`LuSolve-name` function. Add the optional *ratio* argument to the :ref:`LuFactor-name` routine. (This has since been moved to a separate routine called :ref:`LuRatio-name` .) 11-07 ===== Remove some blank lines from the example files listed directly below (under 11-06). Comments for computing the entire Jacobian :ref:`ForSparseJac@Entire Sparsity Pattern` was added. 11-06 ===== The cases of ``std::vector`` , ``std::valarray`` , and ``CppAD::vector`` were folded into the standard example and tests format for the following cases: :ref:`rev_two.cpp-name` , :ref:`rev_one.cpp-name` , ``Reverse.cpp`` , :ref:`hessian.cpp-name` , :ref:`jacobian.cpp-name` , :ref:`forward.cpp-name` , :ref:`for_two.cpp-name` , :ref:`for_one.cpp-name` , ``Fun.cpp`` (``Fun.cpp`` has since been replaced by :ref:`independent.cpp-name` , ``Reverse.cpp`` has since been replaced by :ref:`reverse_one.cpp-name` and ``rev_checkpoint.cpp`` ). 11-01 ===== Add forward mode :ref:`Jacobian sparsity` calculation. 10-20 ===== Add :ref:`sparsity patterns` to the wish list. 10-18 ===== The Unix install :ref:`configure@Configure` command was missing the ``--`` before of the ``prefix`` command line argument. 10-14 ===== The template class :ref:`CppAD_vector-name` uses a try/catch block during the allocation of memory (for error reporting). This may be slow down memory allocation and hence it is now replaced by simple memory allocation when the preprocessor variable ``NDEBUG`` is defined. The specialization of ``CppAD::vector`` was moved to :ref:`CppAD_vector@vectorBool` so that ``CppAD::vector`` does not pack one bit per value (which can be slow to access). 10-12 ===== Change the :ref:`configure@Configure` script so that compilation of the :ref:`get_started.cpp-name` and :ref:`print_for_cout.cpp-name` examples are optional. One of the dates in the Unix installation extraction discussion was out of date. This has been fixed. 10-06 ===== Change the Unix install ``configure`` script so that is reports information using the same order and notation as its :ref:`documentation` . Some compiler errors in the :ref:`ode_gear_control.cpp-name` and :ref:`ode_stiff.cpp-name` examples were fixed. 09-29 ===== Add a specialization to :ref:`CppAD_vector-name` for the ``CppAD::vector`` case. A test for the ``push_back`` member function as well as a :ref:`CheckSimpleVector-name` test has been added to :ref:`cppad_vector.cpp-name` . The source code for this template vector class, ``cppad/vector.hpp`` , has been removed from the documentation. 09-27 ===== Add the :ref:`configure@prefix_dir` and *postfix_dir* ( *postfix_dir* has since been removed) options to the ``configure`` command line. This gives the user more control over the location where CppAD is installed. 09-24 ===== The stiff Ode routines, :ref:`OdeGear-name` and :ref:`OdeGearControl-name` , were added to the :ref:`utilities` . A comparison various Ode solvers on a stiff problem :ref:`ode_stiff.cpp-name` was added. In addition, ``OdeGear`` and ``OdeGearControl`` were added to the :ref:`utilities` and the library was reorganized. 09-20 ===== The Microsoft compiler project files ``example/Example.vcproj`` and ``TestMore/TestMore.vcproj`` were not up to date. This has been fixed. In addition, the example :ref:`numeric_type.cpp-name` has been added. Make the building of the ``Example`` , ``TestMore`` , and ``Speed`` , directories optional during the :ref:`configure@Configure` command. The :ref:`Unix installation instructions` were overhauled to make the larger set of options easy to understand. 09-14 ===== Added the :ref:`NumericType-name` concept and made the following library routines require this concept for their floating point template parameter type: :ref:`LuSolve-name` , :ref:`LuFactor-name` , :ref:`RombergOne-name` , :ref:`RombergMul-name` , :ref:`Runge45-name` , :ref:`Rosen34-name` , and :ref:`OdeErrControl-name` . This is more restrictive than the previous requirements for these routines but it enables future changes to the implementation of these routines (for optimization purposes) with out affecting their specifications. 09-09 ===== Add the :ref:`unary_plus-name` operator and move the ``Neg`` examples and tests to :ref:`unary_minus-name` . 09-07 ===== Change name of distribution files from ``CppAD.unix.tar.gz`` and ``CppAD.dos.tar.gz`` to ``CppAD-`` *yy* ``-`` *mm* ``-`` *dd* . ``tar.gz`` and ``CppAD-`` *yy* ``-`` *mm* ``-`` *dd* . ``zip`` (the * . ``zip`` file uses pkzip compression). 08-30 ===== The *maxabs* argument has been added to the :ref:`OdeErrControl-name` function so that it can be used with relative errors where components of the ODE solution may be zero (some of the time). In addition, some of the rest of the OdeErrControl documentation has been improved. The documentation for replacing defaults in CppAD error macros has been improved. 08-24 ===== Changed ``Romberg`` to :ref:`RombergOne-name` and added :ref:`RombergMul-name` . In addition, added missing entries to :ref:`list_all_examples-name` and reorganized :ref:`utilities` . 08-20 ===== Backed out addition of ``Romberg`` integration routine (at this point uncertain of the interface that is most useful in the context of AD.) 08-19 ===== Added a ``Romberg`` integration routine for where the argument types are template parameters (for use with AD types). 08-15 ===== The Microsoft project files ``example/Example.vcproj`` and ``TestMore/TestMore.vcproj`` were missing some necessary routines. In addition, ``Speed/Speed.vcproj`` was generating a warning. This has been fixed. 08-14 ===== An :ref:`Integer-name` conversion function as been added. The :ref:`value.cpp-name` example has been improved and the old example has been moved into the ``TestMore`` directory. 08-13 ===== The :ref:`unary_standard_math-name` functions ``sinh`` , and ``cosh`` have been added. In addition, more correctness testing has been added for the ``sin`` and ``cos`` functions. The :ref:`OdeErrControl-name` routine could lock in an infinite loop. This has been fixed and a test case has been added to check for this problem. 08-07 ===== The :ref:`conditional expression` function has been changed from just ``CondExp`` to ``CondExpLt`` , ``CondExpLe`` , ``CondExpEq`` , ``CondExpGe`` , ``CondExpGt`` . This should make code with conditional expressions easier to understand. In addition, it should reduce the number of tape operations because one need not create as many temporaries to do comparisons with. The old ``CondExp`` function has been deprecated. 07-21 ===== Remove unnecessary no-op that was left in tape for the :ref:`unary_standard_math-name` functions ``acos`` , ``asin`` , ``atan`` , ``cos`` . Improve the index entries in the documentation that corresponds to the ``cppad/local`` directory source code. 07-19 ===== The :ref:`wish_list-name` and ``Bugs`` information were moved out of this section and into their own separate sections (the Bugs section has been removed; see the ``bug`` subdirectory instead). A discussion of :ref:`VecAD speed and memory` was added as well as an entry in the :ref:`wish_list-name` to make it more efficient. 07-15 ===== The ``BOOST_DIR`` and ``CPP_ERROR_WARN`` :ref:`configure@Configure` options were not properly implemented for compiling the ``lib`` sub-directory. This has been fixed. Some compiler warnings in the file ``lib/ErrFun.cpp`` , which computes the :ref:`erf-name` function, have been fixed. 07-11 ===== The :ref:`CppAD_vector@push_back` function has been added to the ``CppAD::vector`` template class. It appears that the ``TestMore/Runge45.cpp`` file was missing an include of ``example/NearEqualExt.h`` . This has been fixed. 07-08 ===== The documentation for :ref:`Forward-name` and :ref:`Reverse-name` has been improved. 07-05 ===== The :ref:`rosen_34.cpp-name` example mixed the :ref:`CppAD::vector` and ``CppADvector`` vector types. This caused the compilation of the examples to fail when ``CppADvector`` was defined as something other than ``CppAD::vector`` (found by Jon Pearce). This has been fixed. The :ref:`CheckSimpleVector-name` run time code has been improved so that it is only run once per case that is being checked. Simple Vector concept checking (:ref:`CheckSimpleVector-name` ) was added to the routines: :ref:`ForOne-name` , :ref:`ForTwo-name` , :ref:`Forward-name` , :ref:`ADFun-name` , :ref:`Hessian-name` , :ref:`Independent-name` , :ref:`Jacobian-name` , :ref:`RevOne-name` , :ref:`RevTwo-name` , and :ref:`Reverse-name` . 07-04 ===== Simple Vector concept checking (:ref:`CheckSimpleVector-name` ) was added to the routines: :ref:`LuFactor-name` , :ref:`LuSolve-name` , :ref:`LuInvert-name` , :ref:`OdeErrControl-name` , :ref:`Runge45-name` , and :ref:`Rosen34-name` . The previous version of the routine :ref:`OdeErrControl-name` was mistakenly in the global namespace. It has been moved to the ``CppAD`` namespace (where all the other :ref:`utilities` routines are). The previous distribution (version 05-07-02) was missing the file ``cppad/local/Default.h`` . This has been fixed. 07-03 ===== Added :ref:`CheckSimpleVector-name` , a C++ concept checking utility that checks if a vector type has all the necessary conditions to be a :ref:`SimpleVector-name` class with a specific element type. 07-02 ===== Version 7 of Microsoft's C++ compiler supports the standard declaration for a friend template function. Version 6 did not and CppAD used macros to substitute the empty string for ```` , ``< AD >`` , and ``< VecAD >`` in these declarations. These macro substitutions have been removed because Version 6 of Microsoft's C++ compiler is no longer supported by CppAD. The copy base section was split into the default constructor and the construction for the base type. The construction from base type has been extended to include any type that is convertible to the base type. As a special case, this provides the previous wish list item of a constructor from an arbitrary *Base* to a ``AD< AD<`` *Base* > > , ``AD< AD< AD<`` *Base* > > > etc. 07-01 ===== The permissions were set as executable for many of the no-executable files in the distribution; for example, the ``README`` , file. This has been fixed. 06-25 ===== Some improvements were made to the README, AUTHORS, COPYING, and INSTALL files. In addition, the file ``UWCopy040507.html`` which contains the University of Washington's copyright policy (see Section 2) was added to the distribution. 06-24 ===== The ``List2Vector`` example utility is no longer used and has been removed. 06-18 ===== CppAD is now supported by Microsoft Visual C++ version 7 or higher. The version 6 project files * . ``dsw`` and * . ``dsp`` have been replaced by the version 7 project files * . ``sln`` and * . ``vcproj`` . 06-14 ===== A new :ref:`CondExp example` has been added and the old :ref:`CondExp-name` example has been moved to the ``TestMore`` directory (it is now only a test). 06-13 ===== The changes made on 06-06 do not run under Microsoft Visual C++ version 6.0 (even though they are within the C++ standard). Preliminary testing under version 7 indicates that Microsoft has fixed this problem in later versions of their C++ compiler. 06-06 ===== Converted the routines :ref:`Forward-name` and :ref:`Reverse-name` to allow for any :ref:`SimpleVector-name` instead of just ``CppADvector`` . In addition, separated the syntax of the function call from the prototype for each of the arguments. This was also done for all the easy to use :ref:`Drivers-name` as well as the :ref:`Independent-name` function and the :ref:`ADFun-name` constructor. Add a section containing a list of :ref:`all the examples` . 05-19 ===== A significant improvement in speed was obtained by moving the buffer extension to a separate function and then inline the rest of putting operators in the tape. For example, here is part of the speed test output before this change: :: Tape of Expansion by Minors Determinant: Length = 350, Memory = 6792 size = 5 rate = 230 size = 4 rate = 1,055 size = 3 rate = 3,408 size = 2 rate = 7,571 size = 1 rate = 13,642 and here is the same output after this change: :: Tape of Expansion by Minors Determinant: Length = 350, Memory = 6792 size = 5 rate = 448 size = 4 rate = 2,004 size = 3 rate = 5,761 size = 2 rate = 10,221 size = 1 rate = 14,734 Note that your results will vary depending on operating system and machine. 05-18 ===== Change name of ``OdeControl`` to :ref:`OdeErrControl-name` and improve its documentation. Correct the syntax for the :ref:`con_dyn_var@Parameter` and :ref:`con_dyn_var@Variable` functions. 05-16 ===== Change :ref:`OdeErrControl-name` to have method return its order instead of having a separate argument to ``OdeErrControl`` . Add the argument *scur* to ``OdeErrControl`` , improve ``OdeErrControl`` choice of step size and documentation. 05-12 ===== Using profiling, the :ref:`multiplication operator` was show to take a significant amount of time. It was reorganized in order to make it faster. The profiling indicated an improvement so that same change was made to the :ref:`ad_binary-name` and :ref:`compound_assign-name` operators. 05-06 ===== The documentation for :ref:`SimpleVector-name` and :ref:`NearEqual-name` were changed to use more syntax (what the user enters) and simpler prototypes (the compiler oriented description of the arguments). In addition, exercises were added at the end of the :ref:`SimpleVector-name` , :ref:`CppAD_vector-name` , and :ref:`NearEqual-name` documentation. There was a undesired divide by zero case in the file ``TestMore/VecUnary.cpp`` that just happened to work in corresponding :ref:`NearEqual-name` check. The ``NearEqual`` routine has been changed to return false if either of the values being compared is infinite or not a number. In addition, the divide by zero has been removed from the ``TestMore/VecUnary.cpp`` test. 05-01 ===== The doubly linked list was also removed from the :ref:`VecAD-name` internal data structure because this method of coding is simpler and it makes it more like the rest of CppAD. 04-21 ===== The profiling indicated that the destructor for an AD object was using a significant amount of time. The internal data structure of an AD object had a doubly linked list that pointed to the current variables and this was modified when an AD object was destroyed. In order to speed AD operations in general, the internal data structure of an AD object has been changed so that this list is no longer necessary (a tape id number is used in its place) During the process above, the function :ref:`con_dyn_var@Variable` was added. 04-20 ===== Add profiling to the speed tests. 04-19 ===== Remove an extra (not necessary) semi-colon from the file ``cppad/local/Operator.h`` . 03-26 ===== The new routine :ref:`OdeErrControl-name` does automatic step size control for the ODE solvers. 03-23 ===== The routine :ref:`Rosen34-name` is an improved stiff integration method that has an optional error estimate in the calling sequence. You must change all your calls to ``OdeImplicit`` to use ``Rosen34`` (but do not need to change other arguments because error estimate is optional). 03-22 ===== The routine :ref:`Runge45-name` is an improved Runge-Kutta method that has an optional error estimate in the calling sequence. You must change all your calls to ``OdeRunge`` to use ``Runge45`` (but do not need to change other arguments because error estimate is optional). 03-09 ===== Some extra semi-colons (empty statements) were generating warnings on some compilers. The ones that occurred after the macros ``CppADStandardMathBinaryFun`` , ``CppADCompareMember`` , ``CppADBinaryMember`` , and ``CppADFoldBinaryOperator`` have been removed. 03-04 ===== An new multiple level of AD example :ref:`mul_level-name` was added. 03-01 ===== An option that specifies error and warning :ref:`flags` for all the C++ compile commands, was added to the :ref:`Unix installation instructions` . 02-24 ===== The routine :ref:`LuSolve-name` was split into :ref:`LuFactor-name` and :ref:`LuInvert-name` . This enables one to efficiently solve equations where the matrix does not change and the right hand side for one equation depends on the left hand side for a previous equation. An extra requirement was added to the :ref:`SimpleVector-name` template class. There must be a typedef for ``value_type`` which is the type of elements in the vector Under Mandrake Linux 10.1, some template friend declarations were failing because the corresponding operations were not declared before being indicated as friends (found by `Jean-Pierre Dussault `_). This has been fixed. 01-08 ===== The :ref:`erf-name` function was added. The implementation of this function used conditional expressions (:ref:`CondExp-name` ) and some times the expression that was not valid in a region caused division by zero. For this reason, the check and abort on division by zero has been removed. {xrst_end 2005} ================================================ FILE: appendix/whats_new/2006.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2006 app} {xrst_spell aclocal autoconf automake avector delem div dw fy gprof kasper kristensen makefile memcheck of of omh rpm sed speedtest tautschnig usr valgrind yy yyyymmdd } Release Notes for 2006 ###################### mm-dd ***** 12-24 ===== Move ``exp_eps_ad`` to ``exp_eps_cppad`` and add exercises to the following sections: :ref:`exp_eps_rev1-name` , :ref:`exp_eps_cppad-name` . Add operation sequence indices to help track operations in :ref:`exp_eps_for0-name` , :ref:`exp_eps_for1-name` , :ref:`exp_eps_rev1-name` . 12-23 ===== Add exercises to the following sections: :ref:`get_started.cpp-name` , :ref:`exp_eps-name` , :ref:`exp_eps_for0-name` , and :ref:`exp_eps_for1-name` . 12-22 ===== Move :ref:`get_started.cpp-name` below the :ref:`introduction-name` directory. Move the exponential example to the subdirectory ``introduction/exp_apx`` and change the ``--with-Introduction`` unix configure option to build both the :ref:`get_started.cpp-name` and :ref:`exp_apx.cpp-name` example programs. (The ``--with-GetStarted`` configure command line option has been removed.) 12-21 ===== Add the :ref:`source code for Poly` to the documentation and include :ref:`Poly-name` in the in the :ref:`speed_utility-name` section. The :ref:`get_started.cpp-name` section has been moved into the :ref:`Introduction-name` and :ref:`get_started.cpp@Exercises` were added to that section. In addition some sections has switched position between the top level :ref:`user_guide-name` and the :ref:`appendix-name` . 12-19 ===== Reorganize so that the source code is below the corresponding routine in the documentation tree (instead of at the same level) for the following routines: :ref:`det_by_minor-name` , :ref:`det_grad_33-name` , :ref:`uniform_01-name` , :ref:`det_of_minor-name` , :ref:`det_by_lu-name` , :ref:`LuInvert-name` , :ref:`LuFactor-name` , :ref:`LuSolve-name` . Separate the specifications for the source in :ref:`speed_utility-name` and add cross reference to the following routine specification and implementations: :ref:`det_by_minor-name` , :ref:`det_grad_33-name` , :ref:`uniform_01-name` , :ref:`det_of_minor-name` , :ref:`det_by_lu-name` , :ref:`LuInvert-name` , :ref:`LuFactor-name` , :ref:`LuSolve-name` . 12-18 ===== Make the :ref:`speed-name` source code easier to read. Change the speed test output name ``det_poly`` to ``poly`` (as it should have been). 12-17 ===== The speed test :ref:`det_grad_33-name` was missing its documentation (this has been fixed). In addition, the titles and indexing for the speed test documentation has been improved. Add to the specifications that each repeated test corresponds to a different matrix in :ref:`det_lu` and :ref:`det_minor` . In addition, modify all the speed tests so that they abide by this rule. Change some references from the deprecated name ``CppAD.h`` to new name ``cppad.hpp`` . Change :ref:`adolc_det_minor.cpp-name` and :ref:`cppad_det_minor.cpp-name` to tape once and reuse operation sequence for each repeated matrix in the test. Add the :ref:`poly` speed test for all three packages. In addition, correct a missing include in :ref:`poly-name` routine. 12-15 ===== The wish list item to simplify and better organize the speed tests been completed: .. csv-table:: :widths: auto :ref:`speed/`,template functions that are speed tested ``speed/example``,example usage of speed template functions :ref:`speed/adolc`,Adolc drivers for the template functions :ref:`speed/cppad`,CppAD drivers for the template functions :ref:`speed/fadbad`,Fadbad drivers for the template functions speed/profile,profiling version of CppAD drivers 12-13 ===== Next step for the speed wish list item: remove ``speed_cppad`` from the documentation and replace it by ``speed/cppad`` , see :ref:`speed_cppad-name` for the new CppAD speed test routines. 12-12 ===== Started the speed wish list item by move the ``adolc`` director to ``speed/adolc`` and ``fadbad`` to ``speed/fadbad`` . 12-11 ===== Started the speed wish list item by creating the ``speed/example`` directory and moving the relevant examples from ``example/`` * . ``cpp`` and ``speed_example/`` * . ``cpp`` to ``speed/example/`` * . ``cpp`` . In addition, the relevant include files have been moved from ``example/`` * . ``hpp`` to ``speed/`` * . ``hpp`` . A new :ref:`speed_test-name` routine was added to the library. 12-10 ===== The :ref:`pow-name` function was changed to be a an ``AD`` < *Base* > :ref:`atomic_base` operation. This function used to return a ``nan`` if *x* is negative because it was implemented as ``pow`` ( *x* , *y* ) = ``exp`` ( ``log`` ( *x* ) * *y* ) This has been fixed so that the function and its derivatives are now calculated properly when *x* is less than zero. The :ref:`pow-name` documentation was improved and the :ref:`pow.cpp-name` example was changed to test more cases and to use the same variable names as in the documentation. 12-09 ===== A speed wish list item was added to the wish list. The prototype for ``int`` arguments in binary operations (for example :ref:`addition` ) was documented as ``const int &`` but was actually just plain ``int`` . This has been fixed. (Later changed to ``double`` .) 12-07 ===== Fix bug in the subversion installation instructions; see `bug report `_. The some of the automatically generated ``makefile.in`` files had an improper license statement in the GPL license version. This has been fixed. 12-05 ===== Add the unix installation --with_Documentation option and remove the *postfix_dir* option. Create a fixed :ref:`whats_new-name` section above the section for each particular year. Also improve the CppAD distribution ``README`` file. 12-03 ===== The include file directory ``CppAD`` was changed to be all lower case; i.e., ``cppad`` . If you are using a Unix system, see :ref:`include_deprecated-name` . This completes the following :ref:`wish_list-name` items (which were removed): #. File and directory names should only contain lowercase letters, numbers underscores and possibly one period. The leading character must be alphabetic. #. C++ header files should have the ``.hpp`` extension. 12-02 ===== Put explanation of version numbering in the download instructions. Correct some file name references under the Windows heading in :ref:`speed_cppad-name` . 12-01 ===== All of the ``Makefile.am`` and ``Makefile`` files were changed to lower case; i.e., ``makefile.am`` and ``makefile`` . Fix compiler warning while compiling ``cppad/RombergOne/`` (mistake occurred during :ref:`11-20<2006@mm-dd@11-29>` change). 11-30 ===== Cygwin packages, and other system packages, should not have a dash in the version number. See `cygwin package file naming `_ . or, to quote the `rpm file naming convention `_ *The only restriction placed on the version is that* *it cannot contain a dash "-".* As per the acceptable package naming conventions for cygwin, CppAD version numbering has be changed from *yy* ``-`` *mm* ``-`` *dd* format to *yyyymmdd* ; i.e. ``cppad-06-11-30`` was changed to ``cppad-20061130`` . 11-29 ===== There was a problem using :ref:`RombergOne-name` with floating point types other than ``double`` . This has been fixed. 11-28 ===== The :ref:`installation` download files were not being built because ``Makefile.am`` referenced ``Doc`` when it should have referenced ``doc`` . This has been fixed. 11-23 ===== A Version Numbering entry was added to the :ref:`wish_list-name` (this was completed on :ref:`2006@mm-dd@11-30` ). 11-18 ===== The example routine that computes determinants using expansion by minors ``DetOfMinor`` was changed to :ref:`det_of_minor-name` , in preparation for more formal speed comparisons with other packages. To be specific, its documentation was improved, its dependence on the rest of CppAD was removed (it no longer includes CppAD.h ). 11-12 ===== The :ref:`general.cpp-name` and ``test_more/test_more.cpp`` programs were changed to print out the number of tests that passed or failed instead of just "All the tests passed" or "At least one of the tests failed". The windows project files for examples and testing should have been changes to use lower case file names on as part of the 11-08 change below. This has been fixed. 11-08 ===== Move the ``Example`` directory to ``example`` and change all its files to use lower case names. 11-06 ===== Move the ``TestMore`` directory to ``test_more`` and change all its files to use lower case names. 11-05 ===== Remove references in the :ref:`speed_cppad-name` tests to the ``Memory`` and ``Size`` functions because they have been :ref:`deprecated` . Correct some references to ``var_size`` that should have been :ref:`fun_property@size_var` . 11-04 ===== Put text written to standard output in the documentation for the :ref:`get_started.cpp` and ``print_for.cpp`` examples. (Now documentation can be built from a subversion checkout with out needing to execute automake.) The ``PrintFor.cpp`` and ``speedtest.cpp`` examples were missing in :ref:`list_all_examples-name` (which has been fixed). Move the ``Speed`` directory to ``speed`` and change all its files to use lower case names. 11-02 ===== The ``print_for`` directory was referenced as ``PrintFor`` in the root CppAD ``Makefile.am`` this has been fixed. The documentation for the Adolc helper routines ``AllocVec`` and ``AllocMat`` were not being included. This has been fixed. Move the ``GetStarted`` directory to ``get_started`` and change all its files to use lower case names. 11-01 ===== Move the ``PrintFor`` directory to ``print_for`` and change all its files to use lower case names. 10-31 ===== Move the ``SpeedExample`` directory to ``speed_cppad_example`` and change all its files to use lower case names. 10-29 ===== Move the ``Adolc`` directory to ``adolc`` and change all its files to use lower case names. Change all the file in the ``omh`` directory to use lower case names. The file ``Makefile.am`` in the distribution directory had the CPL copyright message in the GPL version. This has been fixed. 10-28 ===== The copyright message in the script files ``example/OneTest`` and ``TestMore/OneTest`` were GPL (in the CPL distribution). This has been fixed by moving them to ``example/OneTest.sh`` and ``TestMore/OneTest.sh`` so that the distribution automatically edits the copyright message. 10-27 ===== Change :ref:`hes_lagrangian.cpp-name` example so that it computes the Lagrangian two ways. One is simpler and the other can be used to avoid re-taping operation sequence. 10-26 ===== Change :ref:`hes_lagrangian.cpp-name` example so that it modifies the independent variable vector between the call to :ref:`Independent-name` and the ``ADFun`` < *Base* > :ref:`constructor` . 10-25 ===== A subversion install procedure was added to the documentation. Fix definition of preprocessor symbol ``PACKAGE_STRING`` in ``Speed/Speed.cpp`` (broken by change on 10-18). Added the example :ref:`hes_lagrangian.cpp-name` which computes the Hessian of a Lagrangian. 10-18 ===== Document and fix possible conflicts for :ref:`preprocessor symbols` that do not begin with ``CppAD`` or ``CPPAD_`` . Include a default value for the file ``cppad/config.h`` in the subversion repository. 10-16 ===== Fix bug when using :ref:`OdeErrControl-name` with the type ``AD< AD >`` . 10-10 ===== Add the :ref:`Var2Par-name` function so it is possible to obtain the :ref:`Value-name` of a variable. Move the ``Discrete.cpp`` example to :ref:`tape_index.cpp-name` . Fix the Microsoft project file so that the Windows install examples and testing works properly (it was missing the :ref:`stack_machine.cpp-name` example). 09-30 ===== These changes were grouped together because it took a while for Coin-Or to review the dual licensing version and because it was not possible to get the nightly build changed: #. Change shell scripts to use \*.sh extension. #. Two versions, one with CPL and other with GPL license. #. Change subversion version of CppAD from GPL to CPL copyright. #. Change all files in cppad/local to use lower case and \*.hpp extension. #. CppAD_vector.h was generating a warning on version 4 of gcc. This have been fixed. #. Change the preprocessor ``# define`` commands in ``cppad/local/\*.hpp`` to use upper case names. #. Add the :ref:`stack_machine.cpp-name` example. 08-17 ===== Some error message occurred while executing :: valgrind --tool=memcheck example/example valgrind --tool=memcheck TestMore/TestMore These were not really bugs, but they have been fixed to avoid this conflict between CppAD and `valgrind `_. 07-14 ===== Make some improvements were made to the :ref:`Introduction-name` , :ref:`exp_eps.hpp-name` and :ref:`exp_eps_rev1-name` sections. 07-12 ===== Use a drop down menu for the navigation links, instead of a separate frame for the navigation links, for each section in the documentation. 06-29 ===== Newer versions of the gcc compiler generated an error because :ref:`erf-name` was using :ref:`CondExp-name` before it was defined. This was found by Kasper Kristensen and his fix has been included in the CppAD distribution. 06-22 ===== The :ref:`ADFun-name` operation *f* ( *x* , *y* ) no longer executes a zero order :ref:`Forward-name` operation when a new operation sequence is stored in *f* . In addition, the syntax for this operation was changed to *f* . ``Dependent`` ( *y* ) (see :ref:`Dependent-name` ). {xrst_comment ------------------------------------------------------ } 06-19 ===== The changes listed under 06-17 and 06-18 were made in the branches/ADFun branch of the CppAD subversion repository. They did not get merged into the trunk and become part of the distribution until 06-19. This accomplished the following goal, which was removed from the :ref:`wish_list-name` : "We would like to be able to erase the function values so that :ref:`ADFun-name` objects use less memory. We may even want to erase the AD operation sequence so that :ref:`ADFun-name` objects use even less memory and can be used for a subsequent AD operation sequence." #. 06-17: Added :ref:`capacity_order-name` which can be used to control the amount of memory used to store :ref:`Forward-name` results. Also :ref:`deprecated` ``taylor_size`` , and defined :ref:`size_order-name` in its place. #. 06-18: Added the :ref:`ADFun default constructor` and the ability to :ref:`store a new operation sequence` in an ``ADFun`` object with out having to use ``ADFun`` pointers together with ``new`` and ``delete`` . {xrst_comment ------------------------------------------------------ } 06-17 ===== The location where the distribution files are stored has changed and this broke the Download Current Version links for the unix and windows installation. This has been fixed. The compiling instructions for the :ref:`speed_cppad-name` routines have been improved. The :ref:`Value-name` function has been extended to allow for :ref:`glossary@Parameter` arguments even if the corresponding tape is in the Recording state. The :ref:`BenderQuad-name` documentation and example have been improved by changing *Vector* to *BAvector* to emphasize that it corresponds to a vector of *Base* objects. 06-15 ===== Change :ref:`BenderQuad-name` to use *Base* instead of ``AD`` < *Base* > where every possible. This allows for more calculations to be done in the base type; i.e., is more efficient. 06-09 ===== Add a size check (size one) for the :ref:`function value argument, g` in ``BenderQuad`` . 06-07 ===== Some major changes were made to the notation in :ref:`get_started.cpp-name` (to make it easier to start using CppAD). In the :ref:`Introduction-name` example, :math:`exp_eps` was changed to :math:`{\rm exp\_eps}`. 06-05 ===== Change :ref:`BenderQuad-name` :math:`F_y (x, y)` to :math:`H(x,y)` so applies in a more general setting. This was another change to the ``BenderQuad`` interface, *fun* . ``fy`` was changed to *fun* . ``h`` . 06-02 ===== Newer versions of the gcc compiler generated a warning for possible use of an uninitialized pointer. This was found by Michael Tautschnig and his fix has been included in the CppAD distribution. 05-31 ===== The interface to :ref:`BenderQuad-name` has been changed. Now all the function evaluation routines are member functions of one class object. This makes it easy for them to share common data. 05-29 ===== Change statement of command syntax to be in the same browser frame as the command documentation (for all the commands with a syntax statement). Now when a user links to a specific heading in a command's documentation, the syntax for that command is automatically included. Before the user needed to follow another link to see to the command syntax. 05-27 ===== Added :ref:`BenderQuad-name` for computing the Hessian of Bender's reduced objective function. Added special specifications for ``resize(0)`` to :ref:`CppAD_vector-name` . 05-03 ===== The g++ (GCC) 4.1.0 (Red Hat 4.1.0-3) compiler reported an error because certain functions were used before being defined (version 3.4.4 did not complain about this). This has been fixed. 04-29 ===== Change all of the example and test driver programs so that they return error codes; i.e., zero for no error and one for an error. Add more discussion and a reference for a ``gcc 3.4.4 -O2`` bug (since been removed). 04-28 ===== Improve the :ref:`get_started.cpp-name` example and move it so that it is visible at the too level of the documentation. 04-26 ===== The programs in :ref:`Introduction-name` have been converted to automated test that return true or false with the driver program :ref:`Introduction` . 04-25 ===== Add an :ref:`Introduction-name` section to the documentation (replaces old example that was part of the :ref:`Theory-name` section). 04-19 ===== A discussion was added near the end of the :ref:`FunCheck-name` documentation. And the cross references to the :ref:`CompareChange-name` discussion were changed to the FunCheck discussion. An operation sequence entry was added to the :ref:`wish_list-name` . 04-18 ===== The new definitions for :ref:`glossary@AD of Base` and :ref:`operation sequence` have been used throughout the documentation. Add the :ref:`FunCheck-name` section for checking that a sequence of operations is as intended. 04-17 ===== The documentation for :ref:`SpeedTest-name` and :ref:`Poly-name` was improved. Definitions were added for an atomic :ref:`glossary@Operation` and for an operation sequence being dependent and independent of the values of specific operands. The definition of AD sequence of operations was made abstract and moved to the glossary as :ref:`Type operation sequence` . 04-15 ===== The :ref:`mul_level-name` example was moved from :ref:`ADFun-name` to :ref:`General-name` . The documentation for :ref:`SpeedTest-name` was improved. 04-14 ===== Documentation and examples were improved for the following routines: :ref:`ForTwo-name` , :ref:`RevTwo-name` . In addition, the computation in ``RevTwo`` was made more efficient (it used to possibly calculate some first order partials that were not used). 04-13 ===== Documentation and examples were improved for the following routines: :ref:`Jacobian-name` , :ref:`ForOne-name` , :ref:`RevOne-name` , and :ref:`Hessian-name` . 04-08 ===== In the case where :ref:`fun_deprecated@use_VecAD` is true, the :ref:`ForSparseJac-name` calculation in only for the current independent variable values. In this case, the sparsity pattern can be (and has been) made more efficient; i.e., fewer true values (because it only applies to the current :ref:`forward_zero-name` ). The conversion from :ref:`VecAD@VecAD\::reference` to :ref:`AD-name` gave a compile error (this has been fixed). Code example for this fix :: VecAD V(1); AD zero = 0; V[zero] = 1.; static_cast< AD > ( V[zero] ); 04-06 ===== The :ref:`ForSparseJac-name` , :ref:`RevSparseJac-name` , :ref:`RevSparseHes-name` sparsity results are now valid for all independent variable values (if the AD operation sequence does no use any ``VecAD`` < *Base* > operands). In addition, the ``ForSparseJac`` , :ref:`RevSparseJac-name` and :ref:`RevSparseHes-name` documentation and examples were improved. The :ref:`useVecAD` member function was added to :ref:`ADFun-name` objects. The ``var_size`` member function was changed to :ref:`fun_property@size_var` (this is not backward compatible, but ``var_size`` was just added on :ref:`2006@mm-dd@04-03` ). 04-05 ===== The documentation and example for :ref:`CompareChange-name` were improved and moved to be part of the :ref:`Forward-name` section. 04-04 ===== The documentation and examples for :ref:`Reverse-name` were improved and split into :ref:`reverse_one-name` and :ref:`reverse_any-name` . 04-03 ===== Create separate sections for the :ref:`zero` and :ref:`forward_one-name` first order case of :ref:`Forward-name` mode. The ADFun :ref:`fun_deprecated@Size` member function has been deprecated (use :ref:`size_order-name` instead). The :ref:`Reverse-name` member function is now declared, and documented as, ``const`` ; i.e., it does not effect the state of the ADFun object. Change the examples that use :ref:`Reverse-name` to use the same return value notation as the documentation; i.e., ``dw`` . 04-02 ===== The member functions of :ref:`ADFun-name` that return properties of AD of *Base* :ref:`operation sequence` have been grouped into the :ref:`fun_property-name` section. In addition, the :ref:`fun_property.cpp-name` example has been added. The :ref:`CompareChange-name` function documentation was improved and moved to a separate section. Group the documentation for the :ref:`ADFun-name` member functions that evaluate functions and derivative values. This organization has since been changed. Remove the old ``Fun.cpp`` example and extend :ref:`independent.cpp-name` so that it demonstrates using different choices for the :ref:`SimpleVector-name` type. 04-01 ===== Move the :ref:`ADFun Constructor` to its own separate section, improve its documentation, and use :ref:`independent.cpp-name` for its example. The following member functions of :ref:`ADFun-name` have been :ref:`deprecated` : ``Order`` , ``Memory`` . The wish list entry for Memory usage was updated on 04-01. The request was implemented on :ref:`2006@mm-dd@06-19` and the entry was removed from the wish list. 03-31 ===== Add examples for the :ref:`Parameter, Variable` and :ref:`Independent-name` functions. Move the :ref:`con_dyn_var@Parameter` and :ref:`con_dyn_var@Variable` functions from the :ref:`ADFun-name` section to the :ref:`AD-name` section. In the examples for the :ref:`AD-name` sections, refer to the range space vector instead of the dependent variable vector because some of the components may not be :ref:`variables` . 03-30 ===== Move the :ref:`LuRatio-name` section below :ref:`lu_det_and_solve-name` . Move the definition of an AD of *Base* :ref:`operation sequence` from the glossary to the :ref:`AD-name` section. Improve the definition of tape state. Add mention of taping to :ref:`Erf-name` , :ref:`bool_fun-name` , :ref:`near_equal_ext-name` ,and :ref:`Pow-name` . Change the definition for :ref:`VecAD@VecAD\::reference` so that it stands out of the text better. 03-29 ===== Mention the :ref:`VecAD@VecAD\::reference` case in documentation and examples for :ref:`abs-name` , :ref:`atan2-name` , :ref:`erf-name` , and :ref:`pow-name` . Fix a bug derivative computation for ``abs`` ( *x* ) when *x* had type ``AD< AD >`` and *x* had value zero. Fix a bug using non-zero AD indices for :ref:`VecAD-name` vectors while the tape is in the empty state. Extend :ref:`erf-name` to include ``float`` , ``double`` , and ``VecAD`` < *Base* >:: ``reference`` . 03-28 ===== Mention the :ref:`VecAD@VecAD\::reference` case in documentation and examples for :ref:`unary_plus-name` , :ref:`unary_minus-name` , :ref:`ad_binary-name` , :ref:`compound_assign-name` , and :ref:`unary_standard_math-name` 03-27 ===== Extend and improve the :ref:`VecAD exceptions::reference@Exceptions>` . Mention the :ref:`VecAD@VecAD\::reference` case and generally improve :ref:`addition` documentation and examples. 03-26 ===== Improve documentation and examples for :ref:`VecAD-name` and change its element type from ``VecADelem`` < *Base* > to ``VecAD_reference`` < *Base* > (so that it looks more like :ref:`VecAD@VecAD\::reference` ). Mention the :ref:`VecAD@VecAD\::reference` case and generally improve :ref:`Value-name` , :ref:`ad_output-name` and :ref:`assignment` documentation and examples. Extend :ref:`Integer-name` and :ref:`PrintFor-name` to include the :ref:`VecAD@VecAD\::reference` case (and mention in documentation and examples). 03-24 ===== Move :ref:`VecAD-name` and :ref:`LuRatio-name` from the old ExtendDomain section to :ref:`AD-name` . 03-23 ===== Improve documentation and examples for :ref:`CondExp-name` and :ref:`Discrete-name` . Move both of these sections from ExtendDomain to :ref:`ADValued-name` . 03-22 ===== The documentation sections under :ref:`AD-name` have been organized into a new set of sub-groups. 03-18 ===== The documentation and example for :ref:`PrintFor-name` have been improved. The sections below :ref:`AD-name` in the documentation have been organized into subgroups. 03-17 ===== The documentation and examples have been improved for the following functions: :ref:`bool_fun-name` , and :ref:`near_equal_ext-name` . 03-16 ===== Improve the documentation and example for the :ref:`pow-name` function. This includes splitting out and generalizing the integer case :ref:`pow_int-name` . The copies of the ``atan2`` function were included in the CppAD namespace for the ``float`` and ``double`` types. 03-15 ===== Improve the introduction to CppAD on the top level page of the documentation. 03-11 ===== The file ``cppad/local/MathOther.h`` had a file name case error that prevented the documentation from building and tests from running (except under Cygwin which is not really case sensitive). This has been fixed. The term AD of *Base* :ref:`operation sequence` has been defined. It will be used to improve the user's understanding of exactly how an :ref:`ADFun-name` object is related to the C++ algorithm. 03-10 ===== The math functions that are not under :ref:`unary_standard_math-name` have been grouped under ``MathOther`` . The documentation and examples have been improved for the following functions: :ref:`abs-name` , :ref:`atan2-name` . 03-09 ===== The examples :ref:`cos.cpp-name` , :ref:`cosh.cpp-name` , :ref:`exp.cpp-name` , :ref:`log.cpp-name` , :ref:`log10.cpp-name` , :ref:`sin.cpp-name` , :ref:`sinh.cpp-name` , :ref:`sqrt.cpp-name` have been improved. 03-07 ===== The ``tan`` function has been added to CppAD. The examples :ref:`Acos.cpp-name` , :ref:`Asin.cpp-name` and :ref:`atan.cpp-name` have been improved. 03-05 ===== The AD standard math unary functions documentation has been grouped together with improved documentation in :ref:`unary_standard_math-name` . 02-28 ===== The :ref:`ad_output-name` and :ref:`Abs-name` documentation and example have been improved. Minor improvements were also made to the :ref:`lu_vec_ad.cpp-name` documentation. 02-25 ===== The :ref:`Compare-name` documentation and example have been improved. 02-24 ===== The documentation and examples have been improved for the following sections: :ref:`division` , :ref:`-=` , :ref:`\*=` , and :ref:`/=` . 02-23 ===== The :ref:`multiplication` documentation and example have been improved. 02-21 ===== The :ref:`subtraction` documentation and example have been improved. There was a bug :ref:`RevTwo-name` that was not detected by the :ref:`rev_two.cpp-name` test. This bug was reported by `Kasper Kristensen `_ A test was added ``TestMore/rev_two.cpp`` that detects this problem and the problem has been fixed. 02-15 ===== The :ref:`+=` documentation and example have been improved. 02-14 ===== The :ref:`addition` documentation and example have been improved. 02-13 ===== Combine the old binary operator and compound assignment documentation into :ref:`Arithmetic-name` documentation. The documentation and examples have been improved for the following sections: :ref:`assignment` , :ref:`unary_plus-name` , :ref:`unary_minus-name` . 02-11 ===== The documentation and examples have been improved for the following sections: :ref:`ad_ctor-name` , :ref:`ad_ctor-name` and :ref:`ad_assign-name` , and :ref:`Value-name` . 02-10 ===== This is the beginning of a pass to improve the documentation: The documentation sections The CopyBase (formerly FromBase and now part of :ref:`ad_ctor-name` and :ref:`ad_assign-name` ) and :ref:`AD copy constructor` (formerly Copy) documentation has been modified. Some of the error messaging during :ref:`ADFun-name` construction has been improved. 02-04 ===== There was a read memory access past the end of an array in :ref:`CppAD::vector::push_back` . This has been fixed and in addition :ref:`track_new_del-name` is now used to do and check the allocation in ``CppAD::vector`` . The routines :ref:`Runge45-name` and :ref:`Rosen34-name` had static vectors to avoid recalculation on each call. These have been changed to be plain vectors to avoid memory leak detection by :ref:`track_new_del@TrackCount` . 01-20 ===== Add software guidelines to the wish list. 01-18 ===== Improve the definition for :ref:`parameters` and :ref:`variables` . Remove unnecessary reference to parameter and variable in documentation for :ref:`Independent-name` . 01-08 ===== The aclocal program is part of the automake and autoconf system. It often generates warnings of the form: | |tab| / ``usr/share/aclocal/`` ...: ``warning: underquoted definition of`` | |tab| ... The shell script file ``FixAclocal`` , which attempts to fix these warnings, was added to the distribution. 01-07 ===== Change CppAD error handler from using the macros defined in ``cppad/CppADError.h`` to using a class defined in :ref:`include/cppad/utility/error_handler.hpp` . The macros ``CppADUnknownError`` and ``CppADUsageError`` have been deprecated (they are temporarily still available in the file ``cppad/local/CppADError.h`` ). 01-02 ===== Add the sed script ``Speed/gprof.sed`` to aid in the display of the profiling output. Make the following source code files easier to understand: ``Add.h`` , ``Sub.h`` , ``Mul.h`` , ``Div.h`` (in the directory ``cppad/local`` ). 01-05 ===== Make the following source code files easier to understand: ``RevSparseHes.h`` , ``Reverse.h`` , ``Fun.h`` , ``Forward.h`` , ``ForSparseJac.h`` , ``RevSparseJac.h`` (in the directory ``cppad/local`` ). {xrst_end 2006} ================================================ FILE: appendix/whats_new/2007.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2007 app} {xrst_spell adouble apx autoconf automake executables gettimeofday ginac intro inv isnan kipp makefile makefiles omh openmp rpm tarballs tay tgz threadprivate valgrind vcproj yyyymmdd } Release Notes for 2007 ###################### mm-dd ***** 12-29 ===== License conversions missed the copyright message at the top in the following special cases: ``makefile.am`` , ``makefile.in`` , and ``omh/appendix/license.omh`` . 12-25 ===== The :ref:`install-name` instructions have been improved. 12-21 ===== The --with_Documentation option on the ``configure`` command line caused an error on some systems because it attempted to copy to many files. This has been fixed by copying the directory instead of the individual files. 12-08 ===== By mistake, the documentation :ref:`License-name` statement for the GPL distribution was the same as for the CPL distribution. This has been fixed. 12-05 ===== Change the name of the spec file from ``cppad-`` *yyyymmdd* . ``spec`` to ``cppad.spec`` . 12-04 ===== Add the capability for the RPM spec file to use a different prefix directory. 12-03 ===== This is the first version with the rpm spec file ``cppad.spec`` . 12-02 ===== Add the ``DESTDIR`` = *directory* option on the :ref:`configure@make install` command line. 11-29 ===== The :ref:`unary_standard_math-name` function ``sqrt`` did not link properly when *Base* was ``AD`` . This has been fixed. 11-23 ===== The routines ``nan`` and ``isnan`` were failing for some systems because they use ``nan`` and or ``isnan`` as preprocessor symbols. This has been fixed; see :ref:`nan@Include@Macros` . In addition, the example and test :ref:`nan.cpp-name` has been added. 11-18 ===== Speed tests for ``tape_values`` branch were not better than trunk, so the good parts of that branch (but not all) were merged into the trunk. The interface specifications for :ref:`base type requirements` have been changed so that CppAD would compile with ``gcc 4.1.1`` (which requires more definitions before use in template functions). This changed of requirements is demonstrated by the :ref:`base_complex.hpp-name` and :ref:`base_adolc.hpp-name` examples. The problem with newer C++ compilers requiring more definitions before use also required the user to know about ``float`` and ``double`` definitions for the standard math functions in the CppAD namespace; see :ref:`base_std_math-name` . The ``example/test_one.sh`` and ``test_more/test_one.sh`` scripts were modified so that one only need specify the test file name (does not also need the test routine name). Some of the test routine declarations were changed from *name* () to *name* ( ``void`` ) to make this possible. The program ``test_more/test_more`` was changed to always report the memory leak test results (same as ``example/example`` ). The :ref:`PrintFor-name` function was putting an unused variable in the tape. This has been fixed. 11-06 ===== Added the ``-DRAD_EQ_ALIAS`` compiler flag to the :ref:`Sacado speed tests` . In addition, compiler flag documentation was included for Sacado and all the other speed tests. 11-05 ===== MS project files have been added for running the :ref:`cppad` and :ref:`double` speed tests. 11-04 ===== The ``cppad/config.h`` file was not compatible with the Windows install procedure and the Windows project's could not find a certain include file. This has been fixed. The :ref:`unix install` procedure has been modified so that the one configure flag ``--with-Speed`` builds all the possible executables related to the speed testing. 11-03 ===== Improve the :ref:`speed_main-name` documentation and output (as well as the title for other sections under :ref:`speed-name` ). The subversion copy of the :ref:`configure@Configure` script was not executable. This has been fixed. 11-02 ===== The instructions for downloading the current version using ``subversion`` have changed. The user should now directly edit the file :: trunk/configure in order to set the correct date for the installation and to build the corresponding documentation. The :ref:`speed-name` section has been slightly reorganized (the main program and utilities have been separated). Add :ref:`speed_double-name` for testing the speed of evaluating functions in ``double`` as apposed to gradients using AD types. 11-01 ===== The instructions for downloading the current version using subversion have changed. The user must now execute the command :: ./build.sh version in order to set the correct version number for her (or his) installation. Add the return status for all the correctness tests to the documentation; see ``make test`` . 10-30 ===== The download instructions did not update current version number and this broke the links to the current tarballs. This has been fixed. The documentation for :ref:`det_by_minor-name` and :ref:`det_by_lu-name` has been improved. The order of the elements in :ref:`det_of_minor-name` has been corrected (they were transposed but this did not really matter because determinants of transposes are equal). The makefiles in the distribution have been changed so that one can run configure from a directory other than the distribution directory. 10-27 ===== A ``subversion`` method for downloading CppAD has been added. The installation was broken on some systems because the :ref:`configure@Configure` command tried to run the ``autoconf`` and ``automake`` programs. This has been fixed by adding ``AM_MAINTAINER_MODE`` to the ``autoconf`` input file. Extend the ``subversion`` methods to include a full installation and old versions. 10-23 ===== The :ref:`configure@cxx_flags` environment variable has been changed from ``CPP_ERROR_WARN`` to ``CXX_FLAGS`` . The command ``configure --help`` now prints a description of the environment variables ``ADOLC_DIR`` , ``FADBAD_DIR`` , ``SACADO_DIR`` , ``BOOST_DIR`` , and ``CXX_FLAGS`` . In addition, if the environment variables ``POSTFIX_DIR`` or ``CPP_ERROR_WARN`` are used, an message is printed saying that are not longer valid. 10-22 ===== The correctness checks and speed test wrappers were moved from the individual package directories to :ref:`speed_main-name` . This way they do not have to be reproduced for each package. This makes it easier to add a new package, but it requires the prototype for ``compute_`` *test_name* to be the same for all packages. The `Sacado `_ package was added to the list of :ref:`speed-name` tests. In addition, the discussion about how to run each of the speed tests was corrected to include the *seed* argument. The *postfix_dir* option was removed on :ref:`2006-12-05<2006@mm-dd@12-05>` but it was not removed from the :ref:`configure@Configure` documentation. This has been fixed. The routine :ref:`CheckSimpleVector-name` was changed. It used to require conversion of the form *Scalar* ( *i* ) where *i* was ``0`` or ``1`` . This does not work with when *Scalar* is ``Sacado::Tay::Taylor`` . This requirement has been changed (see :ref:`CheckSimpleVector@Restrictions` ) to support of *x* = *i* where *x* has type *Scalar* and *i* has type ``int`` . Fix include directives in :ref:`speed_fadbad-name` programs ``det_lu`` , ``det_minor`` , and ``poly`` , to use ``FADBAD++`` instead of ``Fadbad++`` directory. Add ``ADOLC_DIR`` , ``FADBAD_DIR`` , ``SACADO_DIR`` , and ``BOOST_DIR`` to the :ref:`configure@Configure` help string. 10-16 ===== Add *seed* argument and improve :ref:`speed_main-name` documentation. 10-13 ===== Fix the title in :ref:`adolc_det_lu.cpp-name` . Add the package name to each test case result printed by :ref:`speed_main-name` . 10-05 ===== Added and example using complex calculations for a function that is not complex differentiable ``not_complex_ad.cpp`` . (This example has been removed; see :ref:`complex FAQ` .) 10-02 ===== Extend the :ref:`pow-name` function to work for any case where one argument is ``AD`` < *Base* > and the other is ``double`` (as do the binary operators). 09-06 ===== If the :ref:`method.step` function returned ``nan`` (not a number), it was possible for :ref:`OdeErrControl-name` to drop into an infinite loop. This has been fixed. 08-09 ===== Let user detect and handle the case where an ODE initial vector *xi* contains not a number ``nan`` (see :ref:`Runge45-name` , :ref:`Rosen34-name` , and :ref:`OdeErrControl-name` ). Use the ``||`` operation instead of ``|`` operator in the ``nan`` function (The Ginac library seems to use an alias for the type ``bool`` and does not have ``|`` defined for this alias). The file ``test_more/ode_err_control.cpp`` was using the wrong include file name since the change on 08/07. This has been fixed. 08-07 ===== Sometimes an ODE solver takes to large a step and this results in invalid values for the variables being integrated. The ODE solvers :ref:`Runge45-name` and :ref:`Rosen34-name` have been modified to abort and return :ref:`nan-name` when it is returned by the differential equation evaluation. The solver :ref:`OdeErrControl-name` have been modified to try smaller steps when this happens. Fix an :ref:`fun_construct@Sequence Constructor` referenced to ``Dependent`` in documentation (was using the :ref:`fun_deprecated-name` one argument syntax). Add comment about mixing debug and non-debug versions of CppAD in :ref:`track_new_del@TrackDelVec` error message. 07-30 ===== ``CppADCreateBinaryBool`` and ``CppADCreateUnaryBool`` have been replaced by ``CPPAD_BOOL_BINARY`` and ``CPPAD_BOOL_UNARY`` respectively. In addition, the :ref:`wish_list-name` item for conversion of all preprocessor macros to upper case been completed and removed. 07-29 ===== The preprocessor macros ``CppADUsageError`` and ``CppADUnknownError`` have been replaced by ``CPPAD_ASSERT_KNOWN`` and ``CPPAD_ASSERT_UNKNOWN`` respectively. The meaning for these macros has been included in the :ref:`cppad_assert-name` section. In addition, the *known* argument to :ref:`ErrorHandler-name` was wrong for the unknown case. The :ref:`wish_list-name` item for conversion of all preprocessor macros to upper case has been changes (to an item that was previous missing). 07-28 ===== The preprocessor macro ``CPPAD_DISCRETE_FUNCTION`` was defined as a replacement for ``CppADCreateDiscrete`` which has been deprecated. 07-26 ===== Merge in changes made in ``branches/test_vector`` . #. 07-26: Change all occurrences of ``CppADvector`` , in the files ``test_more/`` * . ``cpp`` and ``speed/`` * /* . ``cpp`` , where changed to ``CPPAD_TEST_VECTOR`` . All occurrences of the ``CppADvector`` in the documentation were edited to reflect that fact that it has been deprecated. The documentation index and search for deprecated items has been improved. #. 07-25: Deprecate the preprocessor symbol ``CppADvector`` and start changing it to ``CPPAD_TEST_VECTOR`` . Change all occurrences of ``CppADvector`` , in the ``example/`` * . ``cpp`` files, to ``CPPAD_TEST_VECTOR`` . 07-23 ===== The :ref:`track_new_del-name` macros ``CppADTrackNewVec`` , ``CppADTrackDelVec`` , and ``CppADTrackExtend`` have been deprecated. The new macros names to use are ``CPPAD_TRACK_NEW_VEC`` , ``CPPAD_TRACK_DEL_VEC`` , and ``CPPAD_TRACK_EXTEND`` respectively. This item has been removed from the software guidelines section of the wish list. The member variable entry in the software guideline wish list item has be brought up to date. 07-22 ===== Minor improvements to the :ref:`mul_level_adolc_ode.cpp-name` example. 07-21 ===== #. The ``openmp/run.sh`` example programs ``example_a11c.cpp`` , ``openmp_newton_example.cpp`` , and ``sum_i_inv.cpp`` have been changed so that they run on more systems (are C++ standard compliant). #. :ref:`base_require-name` : The ``IdenticalEqual`` function, in the :ref:`base_require-name` specification, was changed to ``IdenticalEqualCon`` (note the :ref:`base_require@API Warning` in the *Base* requirement specifications). #. Implementation of the :ref:`base requirements` for complex types were moved into the :ref:`base_complex.hpp-name` example. 07-20 ===== The download for CppAD was still broken. It turned out that the copyright message was missing from the file :ref:`base_adolc.hpp-name` and this stopped the creation of the download files. This has been fixed. In addition, the automated testing procedure has been modified so that missing copyright messages and test program failures will be more obvious in the test log. 07-19 ===== The download for CppAD has been broken since the example ``mul_level_adolc_ode.cpp`` was added because the ``example/example`` program was failing. This has been fixed. 07-18 ===== A realistic example using Adolc with CppAD :ref:`mul_level_adolc_ode.cpp-name` was added. The documentation for :ref:`track_new_del-name` was improved. 07-14 ===== Add a discussion at the beginning of :ref:`mul_level_ode.cpp-name` example (and improve the notation used in the example). 07-13 ===== Separate the include file :ref:`base_adolc.hpp-name` from the :ref:`mul_level_adolc.cpp-name` example so that it can be used by other examples. 06-22 ===== Add :ref:`mul_level_adolc.cpp-name` , an example that demonstrates using ``adouble`` and for the :ref:`Base` type. The :ref:`get_started.cpp-name` example did not build when the ``--with-Introduction`` and ``BOOST_DIR`` options were included on the :ref:`configure@Configure` command line. In fact, some of the :ref:`speed-name` tests also had compilation errors when ``BOOST_DIR`` was include in the configure command. This has been fixed. There was a namespace reference missing in the files that could have caused compilation errors in the files ``speed/cppad/det_minor.cpp`` and ``speed/cppad/det_lu.cpp`` . This has been fixed. 06-20 ===== The MS project ``test_more/test_more.vcproj`` would not build because the file ``test_more/fun_check.cpp`` was missing; this has been fixed. In addition, fix warnings generated by the MS compiler when compiling the ``test_more/test_more.cpp`` file. Add a section defining the :ref:`Base type requirements` . Remove the *Base* type restrictions from the :ref:`Faq-name` . Make all the prototype for the default Base types agree with the specifications in the Base type requirements. Fix the description of the ``tan`` function in :ref:`unary_standard_math-name` . 06-14 ===== The routine :ref:`Rosen34-name` ( :ref:`Runge45-name` ) had a division of a ``size_t`` ( ``int`` ) by a *Scalar* , where *Scalar* was any :ref:`NumericType-name` . Such an operation may not be valid for a particular numeric type. This has been fixed by explicitly converting the ``size_t`` to an ``int`` , then converting the ``int`` to a *Scalar* , and then preforming the division. (The conversion of an ``int`` to any numeric type must be valid.) 05-26 ===== If the *Base* type is not ``double`` , the :ref:`compound assignment` operators did not always allow for ``double`` operands. For example, if *x* had type ``AD< AD >`` *x* += .5; would slice the value ``.5`` to an ``int`` and then convert it to an ``AD< AD >`` . This has been fixed. This slicing has also been fixed in the :ref:`assignment` operation. In addition, the assignment and copy operations have been grouped together in the documentation; see :ref:`ad_ctor-name` and :ref:`ad_assign-name` . 05-25 ===== Document usage of ``double`` with binary arithmetic operators, and combine all those operators into one section (:ref:`ad_binary-name` ). The documentation for all the :ref:`compound assignment` operators has been grouped together. In addition, a compound assignment wish list item has been added (it was completed and removed with the :ref:`2007@mm-dd@05-26` update.) 05-24 ===== Suppose that *op* is a binary operation and we have *left* *op* *right* where one of the operands was ``AD< AD >`` and the other operand was ``double`` . There was a bug in this case that caused the ``double`` operand to be converted to ``int`` before being converted to ``AD< AD >`` . This has been fixed. 05-22 ===== The Microsoft examples and testing project file ``example/example.vcproj`` was missing a reference to the source code file ``example/reverse_two.cpp`` . This has been fixed. 05-08 ===== Reverse mode does not work with the :ref:`pow-name` function when the base is less than or equal zero and the exponent is an integer. For this reason, the :ref:`pow_int-name` function is no longer deprecated (and is used by CppAD when the exponent has type ``int`` ). 05-05 ===== Third and fourth order derivatives were included in the routine ``test_more/sqrt.cpp`` that tests square roots. The return value descriptions were improved for the introduction examples: :ref:`exp_2_for1` , :ref:`exp_2_for2` , :ref:`exp_eps_for1` , and :ref:`exp_eps_for2` . The summation index in :ref:`sqrt_reverse-name` was changed from :math:`k` to :math:`\ell` to make partial differentiation with respect to :math:`z^{(k)}` easier to understand. In addition, a sign error was corrected near the end of :ref:`sqrt_reverse-name` . The dimension for the notation :math:`X` in :ref:`reverse_identity-name` was corrected. The word mega was added to the spelling exception list for ``openmp/run.sh`` . 04-19 ===== Improve connection from :ref:`reverse_identity-name` theorem to :ref:`reverse_any-name` calculations. Improve the ``openmp/run.sh`` script. It now runs all the test cases at once in addition to including multiple number of thread cases for each test. Add the ``sum_i_inv_time.cpp`` OpenMP example case. There was a typo in the :ref:`forward_order@Second Order` discussion (found by Kipp Martin). It has been fixed. 04-17 ===== Add a paragraph to :ref:`reverse_identity-name` explaining how it relates to :ref:`reverse_any-name` calculations. Add description of :ref:`first` and :ref:`reverse_any@Second Order` results in :ref:`reverse_any-name` . 04-14 ===== Simplify the :ref:`Reverse-name` mode documentation by creating a separate :ref:`reverse_two-name` section for second order reverse, making major changes to the description in :ref:`reverse_any-name` , and creating a third order example :ref:`rev_checkpoint.cpp-name` for reverse mode calculations. Improve the :ref:`reverse_identity-name` proof. 04-11 ===== Merge in changes made in ``branches/intro`` . #. 04-11: Add :ref:`exp_eps_rev2-name` and its verification routine :ref:`exp_eps_rev2.cpp-name` . #. 04-10: Finished off :ref:`exp_2_rev2-name` and added :ref:`exp_2_rev2.cpp-name` which verifies its calculations. Added second order calculations to :ref:`exp_2_cppad-name` . Added :ref:`exp_eps_for2-name` and its verification routine. #. 04-07: Added a preliminary version of :ref:`exp_2_rev2-name` (does not yet have verification or exercises). #. 04-06: Fixed a problem with the Microsoft Visual Studio project file ``introduction/exp_apx/exp_apx.vcproj`` (it did not track the file name changes of the form ``exp_apx/exp_2_for`` to ``exp_apx/exp_2_for1`` on 04-05). Added :ref:`exp_2_for2-name` to introduction. #. 04-05: Use order expansions in introduction; e.g., the :ref:`exp_2_for2@Second Order Expansion` for the :ref:`exp_2-name` example. 03-31 ===== Merge in changes made in ``branches/intro`` and remove the corresponding Introduction item from the wish list: #. 03-31: Create the a simpler exponential approximation in the :ref:`introduction-name` called :ref:`exp_2-name` which has a different program variable for each variable in the operation sequence. Simplify the :ref:`exp_eps-name` approximation using the :math:`v_1 , \ldots , v_7` notation so that variables directly correspond to index in operation sequence (as with the :ref:`exp_2-name` example). #. 03-30: The Microsoft project file ``introduction/exp_apx/exp_apx.vcproj`` was referencing ``exp_apx_ad.cpp`` which no longer exists. It has been changed to reference ``exp_apx_cppad.cpp`` which is the new name for that file. 03-29 ===== Fixed entries in this file where the year was mistakenly used for the month. To be more specific, 07 ``-`` *dd* was changed to 03 ``-`` *dd* for some of the entries directly below. Corrected some places where ``CppAD`` was used in stead of ``Adolc`` in the :ref:`adolc_poly.cpp-name` documentation. Added an Introduction and :ref:`wish_list@Tracing` entry to the wish list. (The Introduction item was completed on :ref:`2007@mm-dd@03-31` .) 03-20 ===== Example A.1.1c, ``example_a11c.cpp`` , from the OpenMP 2.5 standards document, was added to the tests that can be run using ``openmp/run.sh`` . 03-15 ===== Included the changes from openmp branch so that so CppAD does not use the OpenMP ``threadprivate`` command (some systems do not support this command). #. 03-15: Add command line arguments to ``openmp_newton_example.cpp`` , and modified ``openmp/run.sh`` to allow for more flexible testing. #. 03-14: Fixed some Microsoft compiler warnings by explicitly converting from ``size_t`` to ``int`` . In the Microsoft compiler case, the ``cppad/config.h`` file had the wrong setting of ``GETTIMEOFDAY`` . The setting is now overridden (and always false) when the ``_MSC_VER`` preprocessor symbol is defined. Some minor changes were made in an effort to speed up the multi-threading case. #. 03-13: Started a new openmp branch and created a version of CppAD that does not use the OpenMP ``threadprivate`` command (not supported on some systems). 03-09 ===== Included the changes from openmp branch so that OpenMP can be used with CppAD, see :ref:`omp_max_thread-name` . The changes below were made in the openmp branch and transferred to the trunk on 03-09. #. 03-28: The conditional include commands were missing on some include files; for example :: # ifndef CPPAD_BENDER_QUAD_HPP # define CPPAD_BENDER_QUAD_HPP was missing at the beginning of the :ref:`BenderQuad-name` include file. This has been fixed. The ``speed_test`` routines :ref:`speed_test@Timing` was changed to use ``gettimeofday`` if it is available. (``gettimeofday`` measures wall clock time which is better in a multi-threading environment). Added the user multi-threading interface :ref:`omp_max_thread-name` along with its examples which are distributed in the directory ``openmp`` . The ``speed/`` * . ``hpp`` files have been moved to ``include/cppad/speed/`` * . ``hpp`` and the corresponding wish list item has been removed. The multiple tapes with the same base type wish list item have been removed (it's purpose was multi-threading which has been implemented). #. 02-27: The :ref:`speed-name` include files are currently being distributed above the ``cppad`` include directory. A fix this wish list item has been added. Multiple active tapes required a lot of multi-threading access management for the tapes. This was made simpler (and faster) by having at most one tape per thread. #. 02-22: The include command in the :ref:`speed_test-name` documentation was :: # include but it should have been :: # include This has been fixed. #. 02-17: An entry about optimizing the operation sequence in an :ref:`ADFun` object was added to the :ref:`wish_list-name` . Change the argument syntax for :ref:`Dependent-name` and deprecate the :ref:`old Dependent syntax` . #. 02-16: Added ``VecAD`` < *Base* > as a valid argument type for the :ref:`con_dyn_var@Parameter` and :ref:`con_dyn_var@Variable` functions. In addition, :ref:`VecAD@Base Indexing` is was extended to be allowed during taping so long as the VecAD object is a parameter. #. 02-15: Fixed the ``example/test_one.sh`` script (it was using its old name ``one_test`` ). 02-06 ===== The :ref:`BenderQuad-name` documentation was improved by adding the fact that the *x* and *y* arguments to the ``f`` . *dy* member function are equal to the *x* and *y* arguments to ``BenderQuad`` . Hence values depending on them can be stored as private objects in *f* and need not be recalculated. 02-04 ===== The method for distributing the documentation needed to be changed in the top level ``makefile.am`` in order to be compatible with automake version 1.10. 02-03 ===== The change on :ref:`2007@mm-dd@02-01` had a new, saved as a static pointer, with no corresponding delete. This was not a bug, but it has been changed to avoid an error message when using CppAD with `valgrind `_. The change to the ``pow`` function on :ref:`06-12-10<2006@mm-dd@12-10>` did not include the necessary changes to the sparsity calculations. This has been fixed. 02-02 ===== Fix minor errors and improve profiling documentation. Also change the problem sizes used for the :ref:`speed-name` tests. 02-01 ===== There seems to be a bug in the cygwin version of g++ version 3.4.4 with the -O2 flag whereby some static variables in static member functions sometimes do not get constructed before being used. This has been avoided by using a static pointer and the new operator in cppad/local/ad.hpp. 01-29 ===== The copyright message was missing from some of the distribution files for some new files added on :ref:`06-12-15<2006@mm-dd@12-15>` . This resulted in the tarballs * . ``tgz`` and * . ``zip`` not existing for a period of time. The automated tests have been extended so that this should not happen again. {xrst_end 2007} ================================================ FILE: appendix/whats_new/2008.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2008 app} {xrst_spell fg retape speedtest subregions tarballs } Release Notes for 2008 ###################### mm-dd ***** 12-19 ===== In the documentation for :ref:`pow_int-name` change the integer exponent from ``int`` *y* to ``const int &`` *y* . In the implementation for :ref:`pow-name` make the integer base case agree with the documentation; i.e., change from ``int`` *x* to ``const int &`` *x* . 12-14 ===== Added another test of :ref:`mul_level-name` calculations (in the ``test_more`` directory). 12-04 ===== Extensive explanation for the ``ipopt_cppad/ipopt_cppad_ode`` example was provided in the section ipopt_cppad_ode. 11-22 ===== The CppAD interface to the Ipopt nonlinear programming solver :ref:`cppad_ipopt_nlp-name` has been moved from ``example/ipopt_cppad_nlp`` to ``ipopt_cppad/ipopt_cppad_nlp`` . 11-21 ===== The Microsoft's Visual C++ Version 9.0 generates a warning of the form ``warning C4396:`` %...% for every template function that is declared as a both a friend and inline (it thinks it is only doing this for specializations of template functions). The warnings are no longer generated because these ``inline`` directives are converted to empty code when a Microsoft Visual C++ is used. 11-20 ===== The function ``tanh`` ( *x* ) was added to the :ref:`unary_standard_math-name` functions. The ``abs`` and ``erf`` functions were removed from the :ref:`Base requirements` . The restrictions about the Base class were removed from :ref:`abs-name` , :ref:`atan2-name` , :ref:`LuRatio-name` , :ref:`erf-name` . Visual Studio Version 9.0 could not handle the large number of static constants in the CppAD :ref:`erf-name` function. This function was changed to a simpler representation that is much faster and that is differentiable at all points (not defined differently on subregions). The down side to this is that the new version is not as accurate. 10-27 ===== Change prototypes for ``ipopt_cppad/ipopt_cppad_ode`` helper routines to use ``const`` (where appropriate). 10-17 ===== Major improvements to the ``ipopt_cppad/ipopt_cppad_ode`` example. 10-16 ===== Minor improvement to description of optimization argument in ``ipopt_cppad/ipopt_cppad_ode`` . 09-30 ===== Add or modify some wish list entries; see ``cppad_ipopt_nlp`` (since removed), multiple argument forward (completed with :ref:`forward_dir-name` ), and sparsity patterns (:ref:`sparsity patterns` has been fulfilled). 09-26 ===== Use parenthesis and brackets to group terms of the form :math:`m \times I` to make the documentation of :ref:`cppad_ipopt_nlp-name` easier to read. Changed ``ipopt_cppad/ipopt_cppad_ode`` to use :math:`y(t)` for the solution of the ODE to distinguish it for :math:`x`, the vector we are optimizing with respect to. 09-18 ===== Changed ``ipopt_cppad/ipopt_cppad_ode`` to a case where :math:`x(t)` is a pair of exponential functions instead of a linear and quadratic. Fixed some of the comments in this example and included the source code in the documentation (which was missing by mistake). 09-17 ===== Changed ``ipopt_cppad/ipopt_cppad_ode`` to a case where there are two components in the ODE (instead of one). Also removed an initialization section that was only intended for tests with a specific initial value. 09-16 ===== Add ``ipopt_cppad/ipopt_cppad_ode`` , an example and test that optimizes the solution of an ODE. Change ``r_eval`` to ``eval_r`` in :ref:`cppad_ipopt_nlp-name` . Fix a dimension of ``u_ad`` error in ``ipopt_cppad_nlp`` . 09-12 ===== Converted from storing full Hessian and Jacobian to a sparse data structure in :ref:`cppad_ipopt_nlp-name` . This greatly reduced the memory requirements (and increased the speed) for sparse problems. 09-10 ===== Fixed more indexing bugs in :ref:`cppad_ipopt_nlp-name` that effected cases where the domain index vector :math:`J_{k, \ell}` was different for different values of :math:`k` and :math:`\ell`. In :ref:`cppad_ipopt_nlp-name` , combined *fg_info* ``->domain_index`` () and *fg_info* ``->range_index`` () into a single function called *fg_info* ``->index`` () . Also added more error checking (if ``NDEBUG`` is not defined). 09-09 ===== Fixed an indexing bug in :ref:`cppad_ipopt_nlp-name` . (This effected cases where the domain index vector :math:`J_{k, \ell}` was different for different values of :math:`k` and :math:`\ell`.) 09-07 ===== Change :ref:`cppad_ipopt_nlp-name` so that object and constraints are expressed as the double summation of simpler functions. This is more versatile that the single summation representation. 09-06 ===== Checked in a major change to :ref:`cppad_ipopt_nlp-name` whereby the object and constraints can be expressed as the sum of simpler functions. This is the first step in what will eventually be a more versatile representation. 09-05 ===== Fix bug in :ref:`cppad_ipopt_nlp-name` (not recording the function at the proper location. Here is the difference that occurred multiple places in the ``ipopt_cppad/ipopt_cppad_nlp.cpp`` source: :: for(j = 0; j < n_; j++) - x_ad_vec[0] = x[j]; + x_ad_vec[j] = x[j]; This did not show up in testing because there currently is no test of ``ipopt_cppad_nlp`` where the operation sequence depends on the value of :math:`x`. Changed ``eval_grad_f`` in ``ipopt_cppad_nlp.cpp`` to be more efficient. 09-04 ===== The :ref:`cppad_ipopt_nlp-name` interface has been changed to use a derived class object instead of a pointer to a function. 09-03 ===== The :ref:`cppad_ipopt_nlp-name` interface has been changed to use ``size_t`` instead of ``Ipopt::Index`` . 09-01 ===== Back out the changes made to :ref:`cppad_ipopt_nlp-name` on 08-29 (because testing proved the change to be less efficient in the case that motivated the change). 08-29 ===== The ``push_vector`` member function was missing from the :ref:`Cppad_vector@vectorBool` class. This has been fixed. In addition, it seems that for some cases (or compilers) the assignment *x* [ *i* ] = *y* [ *j* ] did not work properly when both *x* and *y* had type ``vectorBool`` . This has been fixed. The :ref:`cppad_ipopt_nlp-name` example has been extended so that it allows for both scalar and vector evaluation of the objective and constraints; see the argument *fg_vector* in :ref:`cppad_ipopt_nlp-name` . In the case where there is not a lot of common terms between the functions, the scalar evaluation may be more efficient. 08-19 ===== Add :ref:`push of a vector` to the ``CppAD::vector`` template class. This makes it easy to accumulate multiple scalars and :ref:`simple vectors` into one large ``CppAD::vector`` . 08-08 ===== There was an indexing bug in the :ref:`cppad_ipopt_nlp-name` example that affected the *retape* equal to ``false`` case. This has been fixed. In addition, the missing *retape* documentation was added. 07-02 ===== Extend :ref:`configure@Configure` command to check for extras libraries that are necessary for linking the ipopt example. 06-18 ===== Add specifications for the Ipopt class :ref:`cppad_ipopt_nlp-name` . This is only an example class it may change with future versions of CppAD. 06-15 ===== The nonlinear programming example ``ipopt_nlp_get_started.cpp`` was added. This is a preliminary version of this example. 06-11 ===== The sparsity pattern for the Hessian was being calculated each time by :ref:`SparseHessian` . This is not efficient when the pattern does not change between calls to ``SparseHessian`` . An optional sparsity pattern argument was added to ``SparseHessian`` so that it need not be recalculated each time. 06-10 ===== The sparsity pattern for the Jacobian was being calculated each time by :ref:`SparseJacobian` . This is not efficient when the pattern does not change between calls to ``SparseJacobian`` . An optional sparsity pattern argument was added to ``SparseJacobian`` so that it need not be recalculated each time. 05-08 ===== The :ref:`sparse_jacobian-name` routine has been added. The example in :ref:`sparse_hessian-name` pointed to :ref:`hessian.cpp-name` instead of :ref:`sparse_hessian.cpp-name` . This has been fixed. 05-03 ===== The ``retape`` flag has been added to :ref:`speed_main-name` . In addition the routines :ref:`link_det_minor-name` , :ref:`link_poly-name` , and :ref:`link_ode-name` pass this flag along to the speed test implementations (because the corresponding tests have a fixed operation sequence). If this flag is false, a test implementation is allowed to just tape the operation sequence once and reuse it. The following tests use this flag: :ref:`adolc_det_minor.cpp-name` , :ref:`cppad_det_minor.cpp-name` , :ref:`cppad_ode.cpp-name` , :ref:`adolc_poly.cpp-name` , :ref:`cppad_poly.cpp-name` . Create specialized zero order forward mode routine that should be faster, but does not test out as faster under cygwin g++ (GCC) 3.4.4. 04-20 ===== Added the :ref:`ode_evaluate-name` speed test utility in preparation for having ode speed tests. Created ode speed test for the ``cppad`` and ``double`` cases; see :ref:`speed_main-name` . In addition, added the examples :ref:`ode_evaluate.cpp-name` and :ref:`sparse_hessian.cpp-name` . Changed the :ref:`speed_main-name` routines defined for each package from ``compute_`` *name* to ``link_`` *name* . For example, in ``speed/cppad/det_minor.cpp`` , the function name ``compute_det_minor`` was changed to ``link_det_minor`` . 04-18 ===== Fix a problem in the :ref:`link_poly-name` correctness test. Also add :ref:`double_sparse_hessian.cpp-name` to the set speed and correctness tests (now available). 04-10 ===== Change all the :ref:`Adolc speed` examples to use :ref:`track_new_del-name` instead of using ``new`` and ``delete`` directly. This makes it easy to check for memory allocation errors and leaks (when ``NDEBUG`` is not defined). Also include in documentation sub functions that indicate the ``sparse_hessian`` speed test is not available for :ref:`double_sparse_hessian.cpp-name` , :ref:`fadbad_sparse_hessian.cpp-name` , and :ref:`sacado_sparse_hessian.cpp-name` . 04-06 ===== The following :ref:`wish list` entry has been completed and removed from the list: "Change private member variables names (not part of the user interface) so that they all end with an underscore." 04-04 ===== Fix a problem compiling the speed test :ref:`main` program with gcc 4.3. 03-27 ===== Corrected :ref:`cppad_sparse_hessian.cpp-name` so that it uses the sparse case when ``USE_CPPAD_SPARSE_HESSIAN`` is ``1`` . Also added a wish list sparsity pattern entry (the :ref:`glossary@Sparsity Pattern` entry has been fulfilled). Change the name of ``speedtest.cpp`` to :ref:`speed_program.cpp-name` . 02-05 ===== Change windows install instructions to use Unix formatted files (so only two instead of four tarballs are necessary for each version). The Microsoft project files for ``speed/cppad`` , ``speed/double`` , and ``speed/example`` were missing. This has also been fixed. 02-03 ===== There was an ambiguity problem (detected by g++ 4.3) with the following operations *x* *op* *y* where *x* and *y* were ``AD`` and *op* was a member operator of that class. This has been fixed by making all such member functions friends instead of members of ``AD`` . Remove compound assignment entry from wish list (it was fixed on :ref:`2007-05-26<2007@mm-dd@05-26>` ). Add an expression hashing entry to the :ref:`wish_list-name` (it has since been removed). Add Library and Scripting Languages to the wish list (this has since been fulfilled by the example :ref:`ad_in_c.cpp-name` ). 01-26 ===== The :ref:`LuFactor-name` routine gave a misleading error message when the input matrix had not a number or infinity in it. This has been fixed. 01-24 ===== The :ref:`configure@postfix_dir` has been added to the ``configure`` command line options. 01-21 ===== A sparse Hessian case was added to the :ref:`speed-name` tests; see :ref:`sparse_hessian` . 01-20 ===== CppAD can now be installed using ``yum`` on ``Fedora`` operating systems. 01-11 ===== The CppAD correctness tests assume that machine epsilon is less than ``1e-13`` . A test for this has been added to the ``test_more/test_more`` program. 01-08 ===== Added a :ref:`sparse_hessian-name` routine and extended :ref:`Hessian-name` to allow for a weight vector *w* instead of just one component *l* . {xrst_end 2008} ================================================ FILE: appendix/whats_new/2009.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2009 app} {xrst_spell adolc apx autoconf automake doxygen eps ip ipopt isnan mhelp nlp of of openmp prev pycppad retape svn tarball valgrind } Release Notes for 2009 ###################### mm-dd ***** 12-23 ===== The ``ADFun`` :ref:`fun_construct@Assignment Operator` was changed so that it now copies forward mode Taylor coefficients and sparsity pattern information. (This assignment operator was added on :ref:`2009@mm-dd@10-24` .) You can use :ref:`capacity_order-name` to delete the Taylor coefficients before copying them. Two new functions were added so that you can query and delete the forward mode sparsity information; see :ref:`ForSparseJac@f@size_forward_bool` and :ref:`ForSparseJac@f@size_forward_set` . 12-22 ===== Convert the optimization of a sequence of additions from multiple operators to one operator with a varying number of arguments. This improved the speed for forward and reverse mode computations of an optimized tape. 12-18 ===== It turns out that detection of a sequence of additions makes the optimization longer. This was simplified and makes slightly faster by converting two jointly recursive routines to one non-recursive routine that uses a stack for the necessary information. More work is planned to make this optimization faster. 12-12 ===== Detection of a sequence of additions that can be converted to one variable during the :ref:`optimize-name` process. This leads to a significant improvement in the tape size and speed. 12-04 ===== Change hash coding of parameter values as part of operators during the :ref:`optimize-name` process. This should leads to more detection and removal of duplicate operations. 12-02 ===== Fix minor grammatical error in the Purpose heading for :ref:`conditional expressions` . Add the following functions: :ref:`fun_property@size_op` , :ref:`fun_property@size_op_arg` , and :ref:`fun_property@size_op_seq` . In addition, improve and extend the :ref:`fun_property.cpp-name` example. 11-28 ===== Fix bug in tape optimization with :ref:`VecAD-name` objects. 11-27 ===== Remove duplicate expressions for the commutative binary operators; i.e., addition and multiplication. 11-26 ===== Improve :ref:`optimize-name` command so that it removes some duplicate expressions from the tape (more of this is planned). 10-30 ===== Make program that check Ipopt ODE example correctness a separate file ``ipopt_nlp_ode_check.cpp`` Split out Ipopt driver for ODE example ``ipopt_nlp_ode_run.hpp`` . Add the speed testing problem ``ipopt_cppad/ipopt_ode_speed.cpp`` . 10-29 ===== Split out the ode inverse problem, its simple representation, and its fast representation, as a separate files; to be specific, ``ipopt_nlp_ode_problem.hpp`` , ``ipopt_nlp_ode_simple.hpp`` , ``ipopt_nlp_ode_fast.hpp`` , and ``ipopt_nlp_ode_check.cpp`` . 10-28 ===== Improve the documentation for ``ipopt_nlp_ode_simple`` and ``ipopt_nlp_ode_fast`` . 10-27 ===== Moved old ``ipopt_cppad_simple.cpp`` to ``ipopt_nlp_get_started.cpp`` , created the example ``ipopt_nlp_ode_simple.hpp`` , and split and ``ipopt_cppad_ode.cpp`` into ``ipopt_nlp_ode_fast.hpp`` and ``ipopt_nlp_ode_check.cpp`` . 10-24 ===== Added the :ref:`fun_construct@Assignment Operator` to the ``ADFun`` object class. This makes a copy of the entire operation sequence in another function object. The intention is that the two functions objects can do calculations in parallel. In addition, CppAD now check for the ``ADFun`` :ref:`fun_construct@Copy Constructor` and generates an error message if it is used. 10-23 ===== The :ref:`sparse_hessian-name` routine was extended so the user can now choose between vectors of sets and boolean vectors for representing :ref:`sparsity patterns` . 10-21 ===== The :ref:`CheckSimpleVector-name` function was extended so that it can check simple vectors where the elements of the vector can not be assigned to integer values. This was done by adding the :ref:`CheckSimpleVector@x, y` arguments to ``CheckSimpleVector`` . 10-16 ===== The :ref:`sparse_jacobian-name` routine was extended so the user can now choose between vectors of sets and boolean vectors for representing :ref:`sparsity patterns` . 10-14 ===== The *packed* parameter for the sparsity routines :ref:`ForSparseJac-name` , :ref:`RevSparseJac-name` , and :ref:`RevSparseHes-name` (introduced on :ref:`2009@mm-dd@09-26` ) has been removed. It has been replaced by changing the argument and return values to be more versatile. To be specific, they can now represent sparsity using vectors of ``std::set`` instead of just as vectors of ``bool`` (see :ref:`sparsity patterns` ). 10-03 ===== The Microsoft Visual Studio project files for examples and testing and for more correctness testing were not including some new tests in their builds. This has been fixed. 09-30 ===== Added the :ref:`cppad_sparse_jacobian.cpp-name` speed test and increased the sizes used by :ref:`link_sparse_hessian-name` . Some mistakes were fixed in the documentation for speed tests :ref:`link_sparse_hessian-name` and :ref:`sparse_hes_fun-name` . 09-29 ===== The documentation definition of the function :math:`H(x)` in :ref:`RevSparseHes-name` was missing a factor of :math:`R`. This has been fixed. 09-28 ===== Changed :ref:`RevSparseHes-name` so that it uses a sparse representation when the corresponding call to :ref:`ForSparseJac-name` used a sparse representation. This should have been included with the change on 09-26 because Hessian sparsity patters after ``ForSparseJac`` with *packed* did not work. Thus, this could be considered a bug fix. 09-26 ===== Added the ``packed`` parameter to :ref:`ForSparseJac-name` and :ref:`RevSparseJac-name` . If *packed* is false, a sparse instead of packed representation is used during the calculations of sparsity patterns. The sparse representation should be faster, and use less memory, for very large sparse Jacobians. The functions ``ForSparseJac`` and ``RevSparseJac`` return packed representations. The plan is to eventually provide new member functions that return sparse representations. 09-20 ===== Fixed a bug in the :ref:`Hessian Sparsity` calculations that included use of :ref:`VecAD-name` objects. 09-19 ===== Some more memory allocation improvements (related to those on 09-18) were made. 09-18 ===== A bug was found in all the :ref:`sparsity_pattern-name` calculations. The result was that eight times the necessary memory was being used during these calculations. This has been fixed. 08-25 ===== Add :ref:`ad_fun.cpp-name` an example of how to create your own interface to an :ref:`ADFun-name` object. 08-14 ===== Add :ref:`ad_in_c.cpp-name` an example of how to link CppAD to other languages. 08_13 ===== Add an option to :ref:`optimize-name` an operation sequence. These changes come from the directory ``branches/optimize`` in the CppAD subversion repository. The dates below represent when the correspond change was made. #. 08-13: An automatic check of the :ref:`forward_zero-name` results was added after each call to :ref:`f.optimize()` (this :ref:`check` is skipped when ``NDEBUG`` is defined). In addition, all of the ``speed/cppad/`` * . ``cpp`` tests now check and use the speed test :ref:`speed_main@Global Options@optimize` flag. #. 08-11: Change the speed test :ref:`main program` so that it uses a list of options instead of a boolean flag for each option. This will make it possible to add options in the future with out having to change all the existing tests because the options are now global variables instead of arguments to the speed test routines; for example, see ``retape`` speed test option. #. 08-10: The routine for :ref:`optimizing` the operation sequence has been added has been further tested using ``test_more/optimize.cpp`` . Some bugs have been fix and the routine can now be trusted to work correctly. The function :ref:`fun_property@size_VecAD` function was added so that the user could see the ``VecAD`` vectors and elements corresponding to an operation sequence. #. 08-09: A routine for :ref:`optimizing` the operation sequence has been added. This is a preliminary version and needs more testing before it can be trusted to work correctly. 08-06 ===== Add hash table coding to reduce the number of copies of the same parameter value necessary in a tape recording. In addition, add the function :ref:`fun_property@size_par` was added so that the user could see the number of parameters corresponding to an operation sequence. 08-02 ===== Fix bug in new version of how :ref:`ForSparseJac-name` handles :ref:`VecAD-name` objects. Fix bug in overnight build where HTML version and entire documentation as one page versions of documentation were not being built. Fix missing new line under :ref:`SimpleVector@Element Access@Using Value` heading for simple vector documentation. 08-01 ===== Fix bug in reverse mode Jacobian :ref:`sparsity` for :ref:`VecAD-name` objects. 07-31 ===== The :ref:`forward` and :ref:`reverse` sparse Jacobian routines have been improved so the resulting sparsity patterns are valid for all values of the independent variables (even if you use :ref:`CondExp-name` or :ref:`VecAD-name` ). 07-26 ===== Convert developer documentation from forward and reverse mode sweep routines from OMhelp to doxygen. 07-25 ===== Add developer documentation for :ref:`PrintFor-name` operations. 07-24 ===== Add developer documentation for :ref:`Discrete-name` operations. 07-23 ===== Add developer documentation for tape evaluation of :ref:`VecAD-name` store operations. (a store operation changes the value of a VecAD element). Improve the :ref:`vec_ad.cpp-name` user example. 07-06 ===== Fixed a bug in second or higher order reverse mode calculations that used :ref:`VecAD-name` . This bug was demonstrated by the test case ``SecondOrderReverse`` in the file ``test_more/vec_ad.cpp`` . Add developer documentation for tape evaluation of the VecAD load operations (a load operation accesses an element of the vector but does not change it.) Fix ``isnan`` undefined in ``example/cond_exp.cpp`` error introduced on 07-04 change. 07-04 ===== Add developer documentation for the :ref:`CompareChange-name` operations during tape evaluation. These changes come from the directory ``branches/sweep`` in the CppAD subversion repository. The dates below represent when the correspond change was made. #. 07-04: Fixed a bug in second or higher order reverse mode calculations that included :ref:`conditional expressions` . This bug was demonstrated by the test case ``SecondOrderReverse`` in the file ``test_more/cond_exp.cpp`` . A simpler and useful example was provided for :ref:`conditional expressions` ; see :ref:`cond_exp.cpp-name` . #. 07-03: Some minor improvements were made to the documentation for :ref:`CondExp-name` . To be specific, a newer OMhelp option was used to change the formatting of the syntax, some of the argument names were changed to be more descriptive. #. 07-02: Add developer doxygen documentation of tape evaluation for power (exponentiation) operators. #. 07-01: Fix an example indexing error in ``introduction/exp_apx/exp_eps_for2.cpp`` (found by valgrind). Add developer doxygen documentation of tape evaluation for multiplication and division operators. #. 06-30: Add developer doxygen documentation of tape evaluation for addition and subtraction operators. #. 06-29: Add developer doxygen documentation of tape evaluation for sin, sinh, cos, and cosh. #. 06-28: Add developer doxygen documentation of tape evaluation for atan, asin, acos, sqrt, log. 06-25 ===== The tarball for most recent release (of the subversion trunk for CppAD) was not being placed in the `download `_ directory. This has been fixed. 06-22 ===== Fix compiler warnings during the ``openmp/run.sh`` test. Changed :ref:`speed_example.cpp-name` to omit the ``speed_test`` from the correctness result. In stead, a message is printed explaining that timing tests need to be run without a lot of other demands on the system. 06-21 ===== The configure instructions for :ref:`configure@ipopt_dir` had the wrong path for ``IpIpoptApplication.hpp`` . This has been fixed. 06-20 ===== Upgrade to from autoconf 2.61 to 2.63, and from automake 1.10.1 to 1.11. Fix conflict between CppAD's use of config.h preprocessor symbols and other packages use of the same symbol names. 06-06 ===== #. Using complex of an AD type (instead of AD of complex) was not working correctly in ``not_complex_ad.cpp`` because the :ref:`default constructor` for an AD object has an unspecified value. This has been fixed for the complex type by changing the default constructor to use value zero. (The ``not_complex_ad.cpp`` example has been removed; see :ref:`complex FAQ` .) #. Fixing the ``not_complex_ad.cpp`` problem above also fixed a warning generated by `valgrind `_. Now ``valgrind`` runs the CppAD ``example/example`` program with out any warning or error messages. In addition, a minor initialization error was fixed in the ``test_more/jacobian.cpp`` routine so now ``valgrind`` also runs the CppAD ``test_more/test_more`` program with out any warnings or error messages. 05-20 ===== A change was make to the trunk on 05-19 (svn revision 1361) that broke the :ref:`Unix install` procedure. This was has been fixed (revision 1362). 03-24 ===== Added cross references in the :ref:`examples` to occurrence of the following tokens: :ref:`AD-name` , :ref:`ADFun` , ``CPPAD_TEST_VECTOR`` , :ref:`Forward-name` , :ref:`Independent-name` , :ref:`Jacobian-name` :ref:`NearEqual-name` , :ref:`Reverse-name` . 02-20 ===== Demonstrate using AD to compute the derivative of the solution of an ODE with respect to a parameter (in the :ref:`runge_45.cpp-name` example). 02-15 ===== Change the distribution compressed tar file to only contain one copy of the documentation. Link to the current Internet documentation for the other three copies. 02-01 ===== Move the ``Prev`` and ``Next`` buttons at the top of the documentation to the beginning so that their position does not change between sections. This makes it easier to repeatedly select this links. 01-31 ===== Modify ``cppad/local/op_code.hpp`` to avoid incorrect warning by g++ version 4.3.2 when building ``pycppad`` (a python interface to CppAD). 01-18 ===== Sometimes an error occurs while taping AD operations. The :ref:`abort_recording-name` function has been added to make it easier to recover in such cases. Previously, CppAD speed and comparison tests used Adolc-1.10.2. The version used in the tests has been upgraded to `Adolc-2.0.0. `_ A discussion has been added to the documentation for :ref:`Jacobian-name` about its use of :ref:`Jacobian@Forward or Reverse` mode depending on which it estimates is more efficient. A minor typo has been fixed in the description of ``W(t, u)`` in :ref:`reverse_any-name` . To be specific, :math:`o ( t^{p-1} ) * t^{1-p} \rightarrow 0` has been replaced by :math:`o ( t^{p-1} ) / t^{1-p} \rightarrow 0`. 01-06 ===== Made some minor improvements to the documentation in :ref:`fun_construct-name` . {xrst_end 2009} ================================================ FILE: appendix/whats_new/2010.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2010 app} {xrst_spell blas ifdef lagragian libcppad libipopt linpack makefile retape src wshadow } Release Notes for 2010 ###################### mm-dd ***** 12-31 ===== #. Add specifications for the CppAD :ref:`pkgconfig-name` files. #. Update the CppAD README file. #. Move most all CppAD development shell scripts to the ``bin`` subdirectory of the distribution directory. #. Fix warnings generated by the ``g++`` compiler option ``-Wshadow`` ; for example, ``sparse_pack.hpp:101:2: warning: declaration of`` ``'end'`` ``shadows a member of`` ``'this'`` 11-27 ===== If ``NDEBUB`` was defined, the default CppAD :ref:`error handler` would return because its ``assert`` had no effect in this case. This has been fixed by placing a call to ``std::exit(1)`` after its assert. 09-26 ===== There was a bug (introduced on 09-22) in ``make test`` when the ``configure`` command was executed from a directory other than the distribution directory (the :ref:`cppad_ipopt_nlp-name` did not build). This has been fixed. 09-22 ===== Promote :ref:`cppad_ipopt_nlp-name` from an example to a library that gets installed (provided that the :ref:`configure@ipopt_dir` is specified on the ``configure`` command line). 08-21 ===== Fix problems with linking of :ref:`cppad_ipopt_nlp-name` test with both older and newer versions of ``ipopt`` . 07-14 ===== The new versions of ipopt use ``pkg-config`` to record the location where its necessary libraries are stored on the system. The cppad :ref:`configure@Configure` command has been improved so that it can work both with versions of ipopt that use ``pkg-config`` and ones that don't. 07-11 ===== Old versions of the ipopt library were located in *ipopt_dir* / ``lib/libipopt.`` * (see :ref:`configure@ipopt_dir` ), but newer versions will be located in *ipopt_dir* / ``lib/coin-or/libipopt.`` * . The directory *ipopt_dir* / ``lib/coin`` has been added to the library search path so that the cppad_ipopt_nlp examples work with both old and new versions of ipopt. 06-01 ===== In the case where the preprocessor symbol ``NDEBUG`` was defined (see :ref:`Faq@Speed` ), the function ``CheckSimpleVector`` ( ``const`` *Scalar* & *x* , ``const`` *Scalar* & *y* ) was not defined; see `bug report `_. This has been fixed. 04-28 ===== Change the multi-level taping examples :ref:`mul_level.cpp-name` and :ref:`mul_level_adolc.cpp-name` to be more efficient. 04-26 ===== Include Blas and Linpack libraries in makefile links for :ref:`cppad_ipopt_nlp-name` examples. This removes the need to use get.Blas when building Ipopt. The speed test in ``cppad_ipopt/speed`` was missing a link to the library ``../src/libcppad_ipopt.a`` . This has been fixed. 04-24 ===== There was a bug in the error checking done by ``cppad/local/sub_op.hpp`` that caused the following improper abort: | ``Error detected by false result for`` | |tab| ``arg`` [1] < ``i_z`` | ``at line 337 in the file`` | |tab| .../ ``include/cppad/local/sub_op.hpp`` This was fixed in the trunk. It was also fixed in the release with version number ``20100101.3`` which can be downloaded from the CppAD `download directory `_. 04-01 ===== Some of the :ref:`speed_utility-name` library (in ``speed/src`` ) was being compiled for debugging. This has been changed and they are now compiled with debugging off and optimization on. 03-11 ===== The old :ref:`reverse_any-name` example was moved to :ref:`reverse_three.cpp-name` , the old checkpoint example is now the general case reverse example, and a better ``checkpoint.cpp/`` example was created. 03-10 ===== The :ref:`optimize-name` routine would mistakenly remove some expressions that depended on the independent variables and that affected the result of certain :ref:`CondExp-name` operations. This has been fixed. 03-09 ===== Extend :ref:`reverse_any-name` so that it can be used for :ref:`checkpointing` ; i.e., splitting reverse mode calculations at function composition points. 03-03 ===== Fixed a bug in handling :ref:`vector of boolean` sparsity patterns. (when the number of elements per set was a multiple of ``sizeof(size_t))`` . 02-11 ===== The ``speed`` directory has been reorganized and the common part of the :ref:`speed_main@Link Routines` , as well as the ``microsoft_timer`` , have been moved to the subdirectory ``speed/src`` where a library is built. 02-08 ===== A bug was introduced in the :ref:`2010@mm-dd@02-05` change whereby the ``make`` command tried to build the ``libcppad_ipopt.a`` library even if ``IPOPT_DIR`` was not specified on the :ref:`configure@Configure` command line. This has been fixed. 02-06 ===== The Microsoft project files for the speed tests were extended so that the worked properly for the Release (as well as the Debug) configuration. (This required conversion from Visual Studio Version 7 to Visual Studio 2008 format.) Add an automated check for :ref:`optimize-name` bug fixed on ``02-05`` . (Automatic checking for :ref:`PrintFor-name` bug was added on ``02-05`` .) 02-05 ===== #. Simplify running all the tests by adding the ``make test`` command. #. Simplify the :ref:`configure@Configure` command by removing need for: ``--with-Speed`` , ``--with-Introduction`` , ``--with-Example`` , ``--with-TestMore`` , and ``--with-PrintFor`` . #. Add files that were missing in the Microsoft Visual Studio projects. #. Fix two significant bugs. One in the :ref:`optimize-name` command and the other in the :ref:`PrintFor-name` command. 02-03 ===== Fix a mistakes in the test :ref:`bender_quad.cpp-name` . In addition, the :ref:`optimize-name` routine was used to reduce the tape before doing the calculations. The routine :ref:`opt_val_hes-name` was added as an alternative to :ref:`BenderQuad-name` . 01-26 ===== Another speed improvement was made to :ref:`cppad_ipopt_nlp-name` . To be specific, the Lagragian multipliers where checked and components that were zero were excluded form the Hessian evaluation. 01-24 ===== It appears that in :ref:`cppad_ipopt_nlp-name` , when ``retape[k]`` was true, and ``L[k] > 1`` , it was possible to use the wrong operation sequence in the calculations (though a test case that demonstrated this could not be produced). This is because the argument value to :math:`r_k (u)` depends on the value of :math:`\ell` in the expression .. math:: r_k \circ [ J_{k, \ell} \otimes n ] (x) (even when the value of :math:`x` does not change). There was a bug in the ``ipopt_nlp_ode_check.cpp`` program, for a long time, that did not show up until now. Basically, the check had code of the was using an undefined value. This has been fixed. 01-23 ===== Improve the sparsity patterns and reduce the amount of memory required for large sparse problems using :ref:`cppad_ipopt_nlp-name` . The speed test ``cppad_ipopt/speed`` showed significant improvement. 01-20 ===== We plan to split up the ``ipopt_cppad/src/ipopt_cppad_nlp.hpp`` include file. In preparation, the example ``ipopt_cppad`` has been changed to ``cppad_ipopt`` . This will facilitate using ``CPPAD_IPOPT_*`` for the ``# ifdef`` commands in the new include files (note that they must begin with ``CPPAD`` ). 01-18 ===== The ``ipopt_cppad`` subdirectory of the distribution has been split into an ``src`` , ``example`` , and ``speed`` subdirectories. The ``example`` (``speed`` ) subdirectory is where one builds the :ref:`cppad_ipopt_nlp-name` examples (speed tests). 01-04 ===== The following items have been fulfilled and hence were removed for the :ref:`wish_list-name` : #. If an exception occurs before the call to the corresponding :ref:`ADFun-name` constructor or :ref:`Dependent-name` , the tape recording can be stopped using :ref:`abort_recording-name` . #. A speed test for :ref:`cppad_ipopt_nlp-name` was added; see ``ipopt_ode_speed.cpp`` . #. The :ref:`optimize-name` command uses hash coding to check when an expression is already in the tape and can be reused. #. The :ref:`optimize-name` command removes operations that are not used by any of the dependent variables. #. The :ref:`ad_in_c.cpp-name` example demonstrates how to connect CppAD to an arbitrary scripting language. #. The vector of sets option has been added to sparsity calculations; see :ref:`glossary@Sparsity Pattern` . {xrst_end 2010} ================================================ FILE: appendix/whats_new/2011.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2011 app} {xrst_spell automake boostvector builddir executables for for geq gpl hasnan initializer inv isnan libspeed makefile matlab omh prefixdir pthreads retape rosen runge svn tgz undefine vcproj wshadow } Release Notes for 2011 ###################### mm-dd ***** 12-30 ===== #. There was a bug when using :ref:`abs-name` with an ``AD< AD >`` argument, whereby the corresponding ``AD`` operation sequence depended on the value of the argument to the ``abs`` function. #. Change the specifications for the derivative of the :ref:`abs-name` function to be the :ref:`sign-name` function instead of a directional derivative. #. Add the :ref:`sign-name` function to the ``AD`` < *Base* > list of available functions. In addition, add the :ref:`base_std_math@sign` function to the list of :ref:`base type requirements` . 12-28 ===== The file :ref:`time_test.hpp` was not being included by ``cppad/cppad.hpp`` . This has been fixed. 12-21 ===== The types ``SizeVector`` , ``NumberVector`` , ``ADNumber`` , and ``ADVector`` , were in the global namespace and this was causing warnings about the shadowing of these declarations. The :ref:`cppad_ipopt_nlp@cppad_ipopt namespace` was created to avoid these problems. The simplest way to make old :ref:`cppad_ipopt_nlp-name` code work with this change is to use the command :: using namespace cppad_ipopt; 12-20 ===== #. Change ``team_start`` to :ref:`team_thread.hpp@team_create` and ``team_stop`` to :ref:`team_thread.hpp@team_destroy` . #. Change ``NDEBUG`` mentions to include link to :ref:`Faq@Speed@NDEBUG` . #. Improve :ref:`memory_leak-name` documentation. 11-29 ===== THe :ref:`time_test-name` routine was still executing the test at least twice, even if that was not necessary for the specified minimum time. This has been fixed. 11-27 ===== Move ``multi_thread.cpp`` to :ref:`thread_test.cpp-name` and fix its :ref:`running` instructions. 11-24 ===== Create :ref:`preprocessor-name` section with pointers to all the preprocessor symbols that are in the CppAD API. 11-21 ===== Separate :ref:`--with-boostvectorvector>` for *boost_dir* . This enables one to specify *boost_dir* for :ref:`team_bthread.cpp-name` with out using boost vectors. 11-20 ===== #. Move ``sum_i_inv.cpp`` to :ref:`harmonic.cpp-name` . #. Include the date, time, CppAD version, and :ref:`team_thread.hpp@team_name` in the :ref:`thread_test.cpp-name` output. 11-18 ===== #. The :ref:`thread_test.cpp-name` program was truncating *test_time* to the nearest integer. This has been fixed. #. The :ref:`time_test-name` routine has been made more efficient and now check for the case where only one execution of the test is necessary to achieve the desired *test_time* (it used to always run at least two). #. The ``sum_i_inv_time.cpp`` and :ref:`multi_newton.cpp-name` routines were calling the test an extra time at the end to check for correctness. The results of the last test are now cached and used for the correctness test so that an extra call is not necessary (to make the tests run faster when only a few repetitions are necessary). 11-17 ===== #. Create another speed testing routine :ref:`time_test-name` which is like :ref:`speed_test-name` but it returns the time instead of rate and as a ``double`` instead of a ``size_t`` . The use it for the timing tests in ``sum_i_inv_time.cpp`` and :ref:`multi_newton_time-name` . #. Add *test_time* as a command line argument to the multi-threading ``sum_i_inv`` and :ref:`thread_test.cpp@multi_newton` timing tests. 11-09 ===== Change ``thread_team.hpp`` to :ref:`team_thread.hpp-name` and do the same for all other names that ended in ``_team`` ; e.g., :ref:`team_openmp.cpp-name` . 11-07 ===== The users choice for ``test_vector`` was not actually changing the tests that the user ran. This have been fixed. 11-06 ===== Make all the output generated by :ref:`multi_newton.cpp-name` valid matlab and octave input so it is easy to plot the results. 11-04 ===== Use thread specific data to simplify :ref:`team_openmp.cpp-name` . 11-01 ===== Make :ref:`team_bthread.cpp-name` more similar to :ref:`team_pthread.cpp-name` . 10-30 ===== #. Reorganize and improve the :ref:`multi_thread-name` section and its subsections. #. There was a bug in :ref:`multi_newton.cpp-name` that only showed up when the number of threads was greater than or equal 4. This has been fixed. In addition, :ref:`multi_thread@CPPAD_MAX_NUM_THREADS` was increased from 2 to 4 (to enable testing for this bug). #. The accuracy expected in the ``sum_i_inv.cpp`` results were failing when ``mega_sum`` was large. This check has been modified to include a correction for *mega_sum* . 10-29 ===== The following changes were merged in from ``branches/thread`` : #. Move ``openmp`` to ``example/multi_thread/openmp`` . and create ``example/multi_thread/bthread`` , ``multi_thread/pthread`` with similar tests. #. Put multi-threading common code in ``multi_thread`` directory and threading system specific code in ``example/multi_thread/`` *threading* for *threading* equal to ``openmp`` , ``bthread`` , and ``pthread`` . #. Update the README file. #. Remove the ``bug/optimize.sh`` file (no longer a bug). #. Make ``arc_tan.cpp`` utility that can be used by multiple multi-threading tests. #. Create :ref:`team_thread.hpp-name` specifications, move OpenMP management to :ref:`team_openmp.cpp-name` , Boost thread management to :ref:`team_bthread.cpp-name` , and pthread management to :ref:`team_pthread.cpp-name` . #. All of the make files were modified so that the command :: make test would run the tests for the current directory. #. Extend the multi-threading speed tests ``sum_i_inv.cpp`` and :ref:`multi_newton.cpp-name` so they run using Boost threads and pthreads (as well as OpenMP threads). 10-14 ===== Fix some compiler warnings about shadowed variables that were detected by ``g++`` version ``4.6.1 20110908`` . 10-12 ===== #. The MAC version of the ``pthread`` library does not include the ``pthread_barrier_wait`` function; i.e., is not compliant with the IEEE Std 1003.1, 2004 Edition for ``pthread`` . This caused the ``pthread_simple_ad.cpp`` to fail to compile on the MAC. This has been fixed by not compiling the ``pthread`` examples unless ``pthread_barrier_wait`` is present. #. The :ref:`cppad_ipopt_nlp-name` routine has been changed to :ref:`optimize-name` the functions :math:`r_k (u)` such that ``retape`` ( *k* ) is false. 09-06 ===== #. Add the `boost multi-threading `_ examples :ref:`a11c_bthread.cpp-name` and ``bthread_simple_ad.cpp`` . #. Improve documentation for :ref:`ta_parallel_setup@thread_num` argument to ``parallel_setup`` . #. More simplification of ``bthread_simple_ad.cpp`` example. 09-05 ===== Simply and fix some problems with ``pthread_simple_ad.cpp`` , including avoiding a :ref:`team_pthread.cpp@Bug in Cygwin` . 09-02 ===== #. The OpenMP speed test program ``openmp/run.cpp`` was not setting the number of threads for the one thread case (so dynamic thread adjustment was used). This has been fixed. #. The :ref:`thread_alloc.cpp-name` example was missing from the Microsoft ``example/example.vcproj`` file and a attempt was made to link to missing OpenMP routines (this has been fixed). In addition, some Microsoft compiler warning have been fixed; see the examples and tests in the Windows install instructions. #. There was an oversight, and ``CPPAD_MAX_NUM_THREAD`` was being set to 2 when ``_OPENMP`` was not defined. This has been fixed and :ref:`multi_thread@CPPAD_MAX_NUM_THREADS` has been documented and is now part of the CppAD API. #. The ``pthread_simple_ad.cpp`` test failed under cygwin. This was because the previous test ``openmp_ad.cpp`` was set up calls to OpenMP routines that were still in effect when ``pthread/simple_ad`` ran. This has been fixed by making *num_threads* == 1 a special case in :ref:`parallel_setup` . 09-01 ===== Modify the CppAD trunk using the changes from svn revision 2060 to revision 2081 in the branch :: https://projects.coin-or.org/svn/CppAD/branches/pthread These changes are described below. #. 09-01: There was a bug in the :ref:`atomic_one-name` functions in the case where none of the elements of the argument to the function was a :ref:`glossary@Variable` . This has been fixed. In addition, old_tan.cpp generated an assert for this case and this has also been fixed (in addition to including an example for this case). #. 08-31: Move the ``sum_i_inv_time.cpp`` test from ``openmp/run.sh`` to ``openmp/run.cpp`` . #. 08-31: Change ``--with-openmp`` to OPENMP_FLAGS=openmp_flags configure command line argument. #. 08-30: Create the ``openmp/run.cpp`` program and move the ``openmp_multi_newton.cpp`` test from ``openmp/run.sh`` to ``openmp/run.cpp`` . This uses :ref:`configure@Configure` information for building the tests. #. 08-30: Document the ``--with-openmp`` configure command line argument. #. 08-30: Move ``openmp/multi_newton.hpp`` to ``openmp/newton_method.hpp`` and ``openmp/multi_newton.cpp`` to ``openmp/newton_example.cpp`` . #. 08-25: Replace :ref:`omp_alloc-name` by :ref:`thread_alloc-name` in :ref:`multi_thread-name` , the section on how to use CppAD in parallel. #. 08-25: Implement :ref:`omp_alloc-name` as links to corresponding :ref:`thread_alloc-name` sections. #. 08-25: Create the ``pthread_simple_ad.cpp`` example that does AD using the pthread library. In addition, fix some problems in ``openmp_simple_ad.cpp`` #. 08-25: Move ``openmp/example_a11c.cpp`` to :ref:`example/a11c_openmp.cpp` . #. 08-25: Move ``openmp/parallel_ad.cpp`` to ``openmp_simple_ad.cpp`` . #. 08-23: Beginning steps in replacing :ref:`omp_alloc-name` by :ref:`thread_alloc-name` : Replace :ref:`omp_alloc-name` by :ref:`thread_alloc-name` in the :ref:`utilities` . #. 08-23: move :ref:`omp_alloc-name` to the deprecated section of the documentation. #. 08-23: Change all :ref:`omp_alloc-name` section names to begin with ``omp_`` , and change all :ref:`thread_alloc-name` section names to begin with ``new_`` . #. 08-23: Convert :ref:`CppAD_vector-name` from using :ref:`omp_alloc-name` to using :ref:`thread_alloc-name` for memory allocation. #. 08-23: Extend the :ref:`memory_leak-name` routine to also check the :ref:`thread_alloc-name` allocator. #. 08-21: Create the OpenMP and pthread examples :ref:`a11c_openmp.cpp-name` , :ref:`a11c_pthread.cpp-name` , and ``openmp_simple_ad.cpp`` . These OpenMP examples were originally in the ``openmp`` directory, and have been moved, and modified to conform, to the normal example directory. 08-11 ===== Modify the CppAD trunk using the changes from svn revision 2044 to revision 2056 in the branch :: https://projects.coin-or.org/svn/CppAD/branches/base_require These changes are described below. #. 08-10: Add the output stream optional argument *s* in *f* . ``Forward`` (0, *x* , *s* ) See :ref:`zero order forward mode` and :ref:`PrintFor-name` . #. 08-10: Improve omp_alloc.cpp example. #. 08-09: :ref:`base_require-name` : Add :ref:`numeric_limits@epsilon` to the *Base* type requirements. #. 08-09: Extend ``epsilon`` to AD types. #. 08-08: Improve the :ref:`base_require-name` documentation for :ref:`standard math functions` . #. 08-08: :ref:`base_require-name` : Add ``abs_geq`` to the :ref:`requirements` for a user defined *Base* type. #. 08-08: Check that zero order forward mode results are approximately equal, instead of exactly equal, after an :ref:`optimize-name` operation. This fixes a bug in the optimize correctness check (The order of operations can be changed by ``optimize`` and hence the zero order forward mode results may not be exactly the same.) #. 08-07: Improve the :ref:`base_require-name` documentation for :ref:`base_identical@EqualOpSeq` , :ref:`base_identical@Identical` :ref:`base_require@Integer` , and :ref:`Ordered` operations. #. 08-06: Add the :ref:`base_cond_exp@CondExpRel` paragraph to the base requirements documentation. This was missing and are required for :ref:`CondExp-name` to work with ``AD`` < *Base* > arguments and a non-standard *Base* type. #. 08-04: :ref:`base_require-name` : Change the :ref:`include` file name to :ref:`base_require.hpp` . #. 08-04: Use :ref:`base_float.hpp-name` and :ref:`base_double.hpp-name` as additional examples for the :ref:`CondExp Base requirements` . 08-03 ===== Change :ref:`PrintFor-name` condition from less than or equal zero to not greater than zero;i.e., not positive. This makes ``nan`` print because it results in false for all comparisons. 08-02 ===== #. Change :ref:`PrintFor-name` so it no longer aborts execution when there is no operation sequence being recording; see :ref:`Independent@Start Recording` . #. Improve the :ref:`print_for_cout.cpp-name` example. 07-31 ===== Add a conditional version of the :ref:`PrintFor-name` command ``PrintFor`` ( *text* , *y* , *z* ) which only prints when *z* <= 0 . This is useful for error reporting during forward mode; i.e., reporting when the argument to the ``log`` function is not valid. 07-29 ===== #. The routines :ref:`set_max_num_threads` and ``get_max_num_threads`` were created. User's will need to replace calls to :ref:`max_num_threads` by calls to ``set_max_num_threads`` . #. The functions :ref:`omp_efficient-name` was deprecated because it has not been shown to be useful. 07-28 ===== #. Change :ref:`omp_return_memory-name` so that if :ref:`omp_max_num_threads-name` is one (the default), :ref:`omp_alloc-name` does not hold onto memory (keep it available for the corresponding thread). #. Add files that were missing from the Microsoft Visual Studio ``example`` and ``test_more`` subdirectory project files. #. Fix some warnings generated by Microsoft Visual Studio 2010 build. 07-27 ===== Make ``tan`` and ``tanh`` :ref:`atomic_base` operations; see :ref:`tan_forward-name` and :ref:`tan_reverse-name` . 07-25 ===== Finish the :ref:`atomic_one-name` example old_tan.cpp. This is also a design and implementation of the routines necessary to make ``tan`` and ``tanh`` CppAD atomic operations. 07-18 ===== The reverse mode formulas for :math:`Z(t)` need to involve the lower order Taylor coefficients for :math:`Y(t)`. This has been fixed in :ref:`tan_reverse-name` . 07-17 ===== #. Fix bug in :ref:`atomic_one-name` functions. To be specific, the Taylor coefficients for :math:`y`, of order less than *k* , were not passed into the ``atomic_one`` :ref:`atomic_one@forward` callback function. #. Derive the theory for including the tangent and hyperbolic tangent as CppAD atomic operations :ref:`tan_forward-name` and :ref:`tan_reverse-name` ; see the wish list item ``Tan and Tanh`` . #. Implement and test forward mode calculation of derivative for the tangent and hyperbolic tangent functions; see the new :ref:`atomic_one-name` example old_tan.cpp. 07-14 ===== #. The :ref:`configure-name` instructions for running the individual correctness and speed tests were out of date. This has been fixed; see :ref:`example and tests` . #. Move ``parallel_ad.cpp`` from ``example`` directory to ``openmp`` directory (and convert it from a function to a program). #. Simplify ``example_a11c.cpp`` by making it just a correctness test. #. Change ``openmp/run.sh`` so that it runs correctness tests with the compiler debugging flags. 07-13 ===== #. static hash code data that was begin used by multiple threads when recording ``AD`` < *Base* > operations :ref:`omp_in_parallel-name` execution mode. This has been fixed. #. Make the sparse calculations safe for use during :ref:`omp_in_parallel-name` execution mode. #. Add the ``parallel_ad.cpp`` example. #. Change ``example_a11c.cpp`` example so that is just a correctness (not speed) test. 07-11 ===== #. Change the upper limit for :ref:`omp_max_num_threads-name` from 32 to 48. #. Add :ref:`parallel` documentation for, ``nan`` , :ref:`Rosen34` , and :ref:`Runge45` . #. Fix :ref:`CheckNumericType-name` and :ref:`CheckSimpleVector-name` so they work properly when used in parallel mode. #. The changes below were made on the ``openmp/run.sh`` branch: #. Change to ``openmp/run.sh`` maximum number of threads instead of specifying the entire set of values to be tested. #. Change settings for ``newton_example`` so that ``n_gird`` is a multiple of the maximum number of threads. #. Report dynamic number of thread results as a separate result in the summary output line. #. Fix automatic removal of executables from ``openmp`` directory (was commented out). #. The documentation for ``openmp/run.sh`` was moved to the ``multi_thread`` section. 07-10 ===== #. Add link to :ref:`Discrete-title` in :ref:`multi_thread-name` . #. Make use of the :ref:`track_new_del-name` routines :ref:`omp_in_parallel-name` execution mode an error (it never worked properly); see :ref:`TrackNewDel multi-threading` . #. Change :ref:`memory_leak-name` so that it checks for a leak in all threads. This is what ``openmp_newton_example.cpp`` and ``sum_i_inv_time.cpp`` assumed was being done. 07-09 ===== All the OpenMP parallel execution requirements have been grouped in the section :ref:`multi_thread-name` . 07-07 ===== Add the routine :ref:`parallel_ad-name` to fix bug when using ``AD`` < *Base* > in :ref:`parallel` execution mode. 06-23 ===== #. Fix a bug whereby the assert | |tab| ``Error detected by false result for`` | |tab| |tab| ! ``omp_in_parallel`` () | |tab| ``at line`` *n* ``in the file`` | |tab| *prefix* / ``include/cppad/omp_alloc.hpp`` sometimes occurred. #. The routine :ref:`omp_max_thread-name` was deprecated, use the routine :ref:`omp_max_num_threads-name` instead. #. The deprecated routines have been grouped together in the :ref:`deprecated-name` section of the CppAD manual. 06-21 ===== #. The ``openmp/run.sh`` routine was changed to use zero, instead of ``automatic`` , for automatic choice of ``openmp/run.sh`` number of repeats and maximum number of threads. #. The output of each of the OpenMP examples / speed tests (run by ``openmp/run.sh`` ) was changed to be valid matlab / octave assignment statements. #. In the case where OpenMP is enabled during compilation, a summary for the different number of threads as added at the end of the ``openmp/run.sh`` output. 06-18 ===== #. The :ref:`configure@tape_addr_type` option was added to the :ref:`configure@Configure` command line. #. The function :ref:`fun_property@size_op_seq` results uses ``sizeof(CppAD_TAPE_ADDR_TYPE)`` where it used to use ``sizeof(size_t)`` . #. Remove ``cppad/config.h`` from CppAD distribution, (put the information in ``cppad/configure.hpp`` .) This removes the need to undefine symbols that were defined by ``cppad/config.h`` and that did not begin with ``CPPAD_`` . #. Change :ref:`adolc` library linkage so it works with version ``ADOL-C-2.2.0`` . 05-29 ===== Fix bug (introduced on :ref:`2011@mm-dd@05-22` ) whereby constructor might not be called (but required) when the :ref:`base type` is not plain old data. 05-28 ===== #. Add the :ref:`omp_efficient-name` routine to the :ref:`omp_alloc-name` system. #. Improve the ``omp_alloc`` tracing so it prints the same pointer as returned to the user (not an offset version of that pointer). 05-26 ===== Fix Visual Studio project files that were broken during the change on 05-22. In addition, in the file ``cppad/omp_alloc.hpp`` , suppress the following Microsoft Visual Studio warning :: warning C4345: behavior change: an object of POD type constructed with an initializer of the form () will be default-initialized 05-22 ===== #. The old memory tracking routines :ref:`track_new_del-name` have been deprecated. Their use should be replaced using the :ref:`omp_alloc-name` a memory allocator which is designed to work well in a multi-threading OpenMP environment; see :ref:`omp_alloc@Purpose` . #. The replacement of ``TrackNewDel`` by ``omp_alloc`` has been throughout the CppAD source code, including the examples that used ``TrackNewDel`` ; namely, :ref:`mul_level_adolc.cpp-name` , :ref:`mul_level_adolc_ode.cpp-name` . #. The CppAD vector template class and the :ref:`CppAD_vector@vectorBool` class were modified to use the ``omp_alloc`` :ref:`memory` manager. This should improves its speed of memory allocation :ref:`omp_in_parallel-name` sections of a program. #. The :ref:`speed_test-name` argument :ref:`speed_test@size_vec` call was by value, instead of by reference (as documented). This has been fixed and the call is now by reference. #. The :ref:`CppAD_vector@capacity` function has been added to the CppAD vector class. #. The simple vector :ref:`SimpleVector@Element Constructor and Destructor` description has been changed to explicitly specify that the default constructor is used to initialize elements of the array. #. The :ref:`fun_property@size_op_seq` documentation has been improved to mention that the allocated memory may be larger. 05-11 ===== #. Avoid ambiguity in the definition of the :ref:`complex isnan` function. #. Errors during ``make test`` were not being detected. This has been fixed. 05-03 ===== #. If ``NDEBUG`` is not defined, the :ref:`hasnan` function is used to make sure that the results of any :ref:`Forward-name` operation does not contain a nan (not a number). If so, an error message is generated and the program terminates. This error message and termination can be caught; see :ref:`ErrorHandler-name` . #. In the event that the :ref:`cppad_ipopt_nlp-name` objective function, the constraints, or their derivatives are infinite, an error message is generated and the program terminates (proved that ``NDEBUG`` is not defined and the default error handler has not been replaced). 04-29 ===== #. The Microsoft Visual Studio 2003 project files for the Windows examples and tests no longer worked because the current version of CppAD uses local types in template instantiation. These project files were converted to Visual Studio 2008 where they do work (if you use a later version, Visual Studio should automatically convert them for you). #. The old speed test directory was moved to ``speed_cppad`` before the new :ref:`speed-name` test organization was created on 2006-12-11 (revision 715 of the repository). The old speed tests have not been used for years and so have been deleted. 04-20 ===== The ``openmp/run.sh`` script what changed to take an argument that specifies which tests is run (it no longer runs all the tests). Also improve the openmp test program output formatting. 04-19 ===== The *use_ad* option was added to the ``openmp_newton_example.cpp`` test case. 03-19 ===== The subversion write protected directory ``bin/.svn`` was mistakenly part of the compressed tar file It has been removed. 03-11 ===== The vector of sets argument :ref:`atomic_one@rev_hes_sparse@r` to the ``atomic_one`` function *rev_hes_sparse* must have size greater than or equal to *n* . There was a check that its size was greater than or equal *q* . This was incorrect and has been fixed. 03-05 ===== Add the :ref:`conjugate gradient` example. 02-22 ===== Add the :ref:`speed_main@Global Options@atomic` option to the speed test program and use old_mat_mul.hpp during the :ref:`cppad_mat_mul.cpp-name` speed test when the atomic option is specified. 02-19 ===== There was a bug when :ref:`omp_max_thread-name` was set to one, and ``NDEBUG`` was not defined, the thread corresponding to parameters was one, but the only valid thread number was zero (only one thread) and an CPPAD stopped with an assertion error. This has been fixed. 02-17 ===== There was a mistake in ``openmp/run.sh`` where it attempted to remove a non-existent file in the case where ``openmp/run.sh`` openmp_flag was not ``""`` . This has been fixed. 02-15 ===== A matrix multiply speed test has been added. So far, this has only implemented for the :ref:`cppad` and :ref:`double` cases. (For the time being this test is not available for the other speed comparison cases.) 02-09 ===== A variable in ``atomic_one.hpp`` was declare of type *Base* when it should have been declared of type ``size_t`` . It caused the :ref:`atomic_one-name` feature to fail with some base types. This has been fixed. The old_mat_mul.hpp example has been improved by caching the :math:`x` variable information and using it during :ref:`reverse Hessian sparsity` calculations. Some of the :ref:`atomic_one-name` documentation was extended to include more explanation. 02-06 ===== The use can now define complex :ref:`atomic_base` operations and store them in a CppAD :ref:`ADFun-name` object. This item has been remove from the :ref:`wish list` . The documentation for :ref:`RevSparseHes-name` had a dimension error. This has been fixed. A faster set operations item was added to the wish list. This has since been satisfied by ``cppad_sparse_list`` choice during the install process (since removed). 02-02 ===== The documentation for :ref:`ForSparseJac-name` had some formatting errors. The errors have been fix and the documentation has been improved. 02-01 ===== The subversion install instructions were brought up to date. They have since been replaced by just separate ``subversion`` instructions. 01-19 ===== The directory where the :ref:`pkgconfig-name` file ``cppad.pc`` is stored has been moved from *prefixdir* / ``lib/pkgconfig/cppad.pc`` to *prefixdir* / ``share/pkgconfig/cppad.pc`` . 01-16 ===== The following have been fixed: #. The install of the documentation failed when it was done from a directory other than the top source directory. #. The GPL distribution had the output of the :ref:`configure@Configure` command in it. #. Since the change on 01-09, the file ``omh/appendix/11.omh`` has been required to build the documentation (and it has been missing from the distribution). #. Fadbad was generating warnings due to the ``-Wshadow`` flag with the ``g++`` compiler. The Fadbad :ref:`speed` tests have a special flag with this warning removed from the :ref:`configure@cxx_flags` . 01-09 ===== There were some problems running ``make test`` in the releases ``https://www.coin-or.org/download/source/CppAD/cppad-20110101.0.`` *license* . ``tgz`` where *license* is ``gpl`` or ``cpl`` . #. The version of automake used to build the corresponding ``makefile.in`` files did not define ``abs_top_builddir`` . #. The include file ``cppad_ipopt_nlp.hpp`` was always installed, even if :ref:`configure@ipopt_dir` was not defined on the ``configure`` command line. #. The speed test library ``libspeed.a`` was being installed (it is only intended for testing). These problems are fixed in the trunk and these fixes will be copied to the corresponding stable and release versions; i.e., ``https://www.coin-or.org/download/source/CppAD/cppad-20110101.1.`` *license* . ``tgz`` will not have this problem. {xrst_end 2011} ================================================ FILE: appendix/whats_new/2012.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2012 app} {xrst_spell datadir doxygen dw eigenvector fcur gitlab helgrind includedir inorder libdir libeigen lpthread lteuchos makefile posix src stdbool sys taddr trilinos txt uninitialised valgrind yyyymmdd } Release Notes for 2012 ###################### mm-dd ***** 12-30 ===== #. Merge changes in ``branches/ipopt_solve`` to ``trunk`` , delete that branch, and advance version number to ``cppad-20121230`` . #. Remove ``cppad/configure.hpp`` from repository because it is built by the configuration process (even for MS Visual Studio, now that we are using :ref:`cmake-name` ). #. Add the ``AD`` < *Base* > input stream operator :ref:`>>` . 12-29 ===== In ``branches/ipopt_solve`` : #. Complete implementation of sparse Jacobian and Hessian calculations and add options that allow to user to choose between forward and reverse sparse Jacobians. #. The :ref:`ipopt_solve-name` routine seems to be faster and simpler than :ref:`cppad_ipopt_nlp-name` . More speed comparisons would be good to have. #. All of the :ref:`ADFun Drivers` have added specifications for the zero order Taylor coefficients after the routine is called. For example, see :ref:`Hessian@Hessian Uses Forward` . 12-28 ===== In ``branches/ipopt_solve`` : #. Add the :ref:`ipopt_solve_retape.cpp-name` and :ref:`ipopt_solve_ode_inverse.cpp-name` examples. #. Use ``ipopt::solve`` :ref:`ipopt_solve@options` argument (and not a file) for all the Ipopt options. As well as allowing for adding ``ipopt::solve`` special options; e.g., :ref:`ipopt_solve@options@Retape` . 12-27 ===== In ``branches/ipopt_solve`` : Change documentation section names that begin with ``cppad_ipopt`` to begin with :ref:`ipopt_nlp` to distinguish them from :ref:`CppAD::ipopt::solve` . 12-26 ===== In ``branches/ipopt_solve`` : #. Convert documentation most all documentation references from the deprecated :ref:`configure-name` instructions to the new :ref:`cmake-name` instructions. #. Include the :ref:`Introduction-name` programs in the :ref:`cmake_check-name` built using :ref:`cmake-name` . #. Deprecate :ref:`cppad_ipopt_nlp-name` and replace it by :ref:`ipopt_solve-name` which is easier to use. This is a first version of ``ipopt_solve`` and its speed and memory use needs to be improved. 12-23 ===== Copy development ``trunk`` to ``branches/ipopt_solve`` . 12-22 ===== Define a doxygen module (group) for each file that has doxygen documentation. 12-20 ===== #. The :ref:`install instructions` were installing ``cppad/CMakeLists.txt`` and ``cppad/configure.hpp.in`` in the ``cppad`` include directory. This has been fixed so that only * . ``h`` and * . ``hpp`` files get installed in the ``cppad`` include directory. #. Advance the version number to ``cppad-20121220`` . 12-19 ===== The files ```` and ```` do not exist for all C compilers, and this caused a problem when using the Windows compiler. This has been fixed by defining the type bool inside the ``compare_c/det_by_minor.c`` source code. 12-17 ===== There was a mistake in a check for a valid op code in the file ``hash_code.hpp`` . This mistake could generate a C++ assertion with an unknown error source. It has been fixed. 12-15 ===== #. Advance version number from ``20121120`` to ``20121215`` . Note that the CppAD version number no longer automatically advances with the date and is rather chosen to advance to the current date. #. The :ref:`cmake-name` installation was putting the ``cppad.pc`` :ref:`pkgconfig-name` file in ``cppad_prefix`` / *cmake_install_datadir* / ``cppad.pc`` This has been fixed and is now ``cppad_prefix`` / *cmake_install_datadir* / ``pkgconfig/cppad.pc`` #. The :ref:`pkgconfig-name` documentation has been improved. #. The command for running the :ref:`adolc examples` and :ref:`eigen examples` was fixed (changed from ``make check`` to ``make check_example`` ). 12-14 ===== #. Fix the old :ref:`configure-name` so that it works with the new ``cppad.pc`` . #. Fix the old installation --with_Documentation option (it was attempting to copy from the wrong directory). 12-13 ===== #. Include documentation for :ref:`ipopt-name` #. Fix the ``cppad.pc`` :ref:`pkgconfig-name` file so that it includes the necessary libraries and include files when :ref:`cmake@include_ipopt` is specified; see :ref:`pkgconfig-name` . 11-28 ===== Update the :ref:`wish_list-name` : #. Remove Microsoft compiler warning item that has been fixed. #. Remove faster sparse set operations item that was completed using ``cppad_sparse_list`` (not part of user API). #. Remove :ref:`cmake-name` items that have been completed. #. Remove :ref:`CondExp-name` items related to using ``AD< std::complex >`` types because it is better to use ``std::complex< AD >`` . #. Remove :ref:`thread_alloc-name` memory chunk item that has been completed. #. Remove :ref:`VecAD-name` item about slicing from floating point type to ``int`` (not important). #. Change an Ipopt item to a :ref:`cppad_ipopt_nlp-name` (which was removed because ``cppad_ipopt_nlp`` is now deprecated). Add new ``cppad_ipopt_sum`` item to the wish list. (This has been removed because :ref:`checkpointing` can now be used for this purpose.) #. Add new ``atomic_one`` :ref:`wish_list-name` item (since removed). 11-21 ===== #. Fix the :ref:`download@Version` number in link to the current download files. #. Change the ``subversion`` download instructions to use the ``export`` instead of ``checkout`` command. This avoids downloading the source code control files. 11-20 ===== #. The ``cmake`` variables ``cmake_install_includedir`` and ``cmake_install_libdir`` were changed to :ref:`cmake@cmake_install_includedirs` and :ref:`cmake@cmake_install_libdirs` to signify the fact that they can now be a list of directories. #. Advance version number to ``cppad-20121120`` . 11-17 ===== #. Finish documenting the new :ref:`cmake-name` configuration instructions and deprecate the old :ref:`unix` instructions. #. Change the specifications for :ref:`multi_thread@CPPAD_MAX_NUM_THREADS` to allow for a value of one. This enables one to have more tapes during a program execution. #. Include the :ref:`C versus C++` speed comparison in the :ref:`cmake-name` build. 11-16 ===== Fix a warning that occurred in :ref:`Rosen34-name` when it was compiled with the preprocessor symbol ``NDEBUG`` defined. 11-14 ===== Advanced the CppAD version to ``cppad-20121114`` . #. Started documenting the :ref:`cmake-name` configuration procedure during installation. This included factoring out the :ref:`download-name` procedure as a separate section so that the same download instruction also apply to the :ref:`unix` install procedure. #. Changed :ref:`example/compare_change.cpp` to just return true when ``NDEBUG`` is defined. This enabled all the tests in the ``example`` directory to be compiled with ``NDEBUG`` is defined and to pass. #. In the case where ``NDEBUG`` is defined, removed detection of ``nan`` during forward mode from ``test_more/forward.cpp%`` . This enables all the tests in the ``test_more`` directory to be compiled with ``NDEBUG`` is defined and to pass. #. Started a wish list for CppAD's use of :ref:`cmake-name` . The wish list items were completed and removed. 11-09 ===== The :ref:`team_pthread.cpp-name` was failing to link on Ubuntu 12.04 because the libraries were not at the end of the link line. This has been fixed. 11-06 ===== #. Remove some remaining references to the old licenses CPL-1.0 and GPL-2.0; see :ref:`2012@mm-dd@10-24` . #. Remove out of date Microsoft project files from the distribution. The build system is being converted to use `cmake `_ which builds these files automatically and thereby keeps them up to date. This feature is not yet documented, but one can inspect the file ``bin/run_cmake.sh`` to see how to use ``cmake`` with CppAD. 11-04 ===== Add missing return value to the example ``base_alloc`` :ref:`base_alloc.hpp@CondExpOp` function. This has been fixed and the comments for this example have been improved. 10-31 ===== The CppAD profiling was not compiling the ``speed/src/\*.cpp`` files with the profiling flag. This has been changes (only for the profiling speed test). 10-30 ===== The :ref:`configure@fadbad_dir` directory install instructions were changed. To be specific, ``FADBAD++`` was changed to ``include/FADBAD++`` . This makes it more like the other optional packages. 10-25 ===== The test :ref:`runge45_1.cpp-name` was failing when using gcc-4.5.2. This has been fixed by properly defining ``fabs`` ( *x* ) where *x* is a double (without the ``std`` in front). 10-24 ===== Change the CppAD licenses from CPL-1.0 and GPL-2.0 to EPL-1.0 and GPL-3.0. 10-12 ===== #. Change all the multiple levels of AD examples to start with :ref:`mul_level-name` . To be specific, move ``taylor_ode.cpp`` to :ref:`mul_level_ode.cpp-name` and ``taylor_ode_adolc.cpp`` to :ref:`mul_level_adolc_ode.cpp-name` . #. Add :ref:`taylor_ode.cpp-name` as a example of Taylor's method for solving ODEs, (:ref:`mul_level_ode.cpp-name` is an application of this method to multi-level AD.) 10-04 ===== #. Change :ref:`speed_main-name` so that it outputs small rates (less than 1000) with two decimal points of precision (instead of as integers). In addition, flush result for each size when it finishes to give user more feedback about how things are progressing. #. Add the optional :ref:`time_test@test_size` argument to the ``time_test`` routine. 10-03 ===== Change the ``hold_memory`` speed to option to just :ref:`speed_main@Global Options@memory` . In addition, in the speed test output, include all of the options that are present in the output variable name; see :ref:`speed_main@Speed Results` . 10-02 ===== Fix another problem with Debian's ``/bin/sh`` shell executing ``example/multi_thread/test.sh`` ; see :ref:`2012@mm-dd@03-17` 09-24 ===== Improve documentation for the :ref:`atomic_one-name` :ref:`atomic_one@rev_hes_sparse` argument :ref:`atomic_one@rev_hes_sparse@v` . In addition, add sparsity calculations to the old_reciprocal.cpp example. 09-11 ===== Add ``user_simple.cpp`` , a simpler :ref:`atomic_one-name` example. 08-05 ===== #. A new type was added for the internal representation of :ref:`glossary@Sparsity Pattern@Vector of Sets` sparsity patterns; see the configure ``--with-sparse_option`` (since removed). #. A new speed test, :ref:`compare_c-name` , compares the speed of the same source code compiled with C and C++. 07-30 ===== #. The :ref:`CppAD_vector@clear` function was added to ``CppAD::vector`` . #. Warning !!: The ``CppAD::vector`` :ref:`CppAD_vector@resize` specifications were changed so that *x* . ``resize`` (0) no longer frees the corresponding memory (use *x* . ``clear`` () instead). #. Fix a bug in error checking during :ref:`optimize-name` procedure had the following ``valgrind`` symptom during the ``optimize.cpp`` example: :: ==6344== Conditional jump or move depends on uninitialised value(s) #. Fix mistake in old_tan.cpp where ``w[2] = 0`` was missing before the call :: dw = F.Reverse(1, w); 07-08 ===== #. Improve the documentation for :ref:`pow-name` and :ref:`pow_int-name` . #. Change all the example section names to be same as corresponding file names; e.g. change ``vectorBool.cpp`` to :ref:`vector_bool.cpp-name` for the example ``example/utility/vector_bool.cpp`` . 07-07 ===== Add the ``CPPAD_TAPE_ID_TYPE`` argument to the :ref:`configure@Configure` command line. 07-05 ===== Deprecate ``CPPAD_TEST_VECTOR`` and use :ref:`CPPAD_TESTVECTOR` in its place. This fixes a problem introduced by changes on 07-03 whereby code that used ``CPPAD_TEST_VECTOR`` would no longer work. 07-04 ===== #. Replace the requirement that the :ref:`SimpleVector-name` :ref:`SimpleVector@size` function return a ``size_t`` value to the requirement that it can be converted to a ``size_t`` value. #. The :ref:`--with-eigenvector` option was added to the ``configure`` command line. 07-03 ===== Fix bug in :ref:`atomic_one-name` functions identification of variables that caused old_tan.cpp to fail with error message :: Error detected by false result for y_taddr > 0 at line 262 in the file cppad/local/dependent.hpp 07-02 ===== Add ``eigen_plugin.hpp`` so that an Eigen vector can be used as a :ref:`SimpleVector-name` . This has since been removed; see :ref:`2017-05-11<2017@mm-dd@05-12>` . 07-01 ===== .. _Using custom scalar types: https:// libeigen.gitlab.io/eigen/docs-nightly/TopicCustomizing_CustomScalar.html #. Change :ref:`cppad_eigen.hpp-name` to match new specifications and example in eigen help files on `Using custom scalar types`_ . #. Fix bug whereby a newly constructed :ref:`VecAD-name` object was a :ref:`con_dyn_var@Variable` (instead of a parameter) directly after construction (when no previous :ref:`ADFun` object had been created). #. Change a ``ok != a == 0.`` to ``ok &= a == 0.`` in the example :ref:`ad_ctor.cpp-name` . #. Add the :ref:`eigen_array.cpp-name` example. 06-17 ===== #. Move :ref:`epsilon-name` to :ref:`numeric_limits-name` and add the functions ``min`` and ``max`` in *CppAD::numeric_limits<* ``Type`` > . #. Convert use of the deprecated :ref:`epsilon-name` in examples to use of ``numeric_limits`` :ref:`numeric_limits@epsilon` . #. Complete :ref:`cppad_eigen.hpp-name` interface to ``lowest`` and ``highest`` functions for all non-complex AD types. 06-16 ===== Add the example :ref:`eigen_det.cpp-name` that uses the `Eigen `_ linear algebra package. 06-15 ===== Include the :ref:`base_adolc.hpp-name` as ```` under the :ref:`configure@prefix_dir` directory. 06-12 ===== Increase the size and of the :ref:`sparse Jacobian speed tests` . 06-10 ===== #. Add the :ref:`hold_memory` option to the speed test main program. This was changed to just ``memory`` ; see :ref:`2012@mm-dd@10-03` . #. In :ref:`cppad_sparse_jacobian.cpp-name` , change ``USE_BOOL_SPARSITY`` from true to false. In addition, change the number of non-zeros per row from about approximately three to approximately ten. 06-09 ===== Change :ref:`adolc_sparse_jacobian.cpp-name` to use the sparse adolc Jacobian (instead of the full Jacobian) driver. This was also done for :ref:`adolc_sparse_hessian.cpp-name` , but there is a problem with the test that is being investigated. 06-08 ===== Implement the matrix multiply speed test :ref:`link_mat_mul-name` for all packages (there is a problem with the :ref:`fadbad_mat_mul.cpp-name` implementation and it is being looked into). 06-07 ===== Make all the speed tests implementations (for the specific packages) uniform by having a Specification and Implementation heading and similar indexing. For example, see :ref:`adolc_det_minor.cpp-name` , :ref:`cppad_det_minor.cpp-name` , :ref:`double_det_minor.cpp-name` , :ref:`fadbad_det_minor.cpp-name` , and :ref:`sacado_det_minor.cpp-name` . 06-05 ===== Add the :ref:`sacado_ode.cpp-name` speed test. 06-04 ===== #. The specifications for :ref:`Runge45-name` where changes so that it uses the ``fabs`` function instead of the ``<`` operation. This enabled the a more precise statement about its :ref:`Runge45@Operation Sequence` . #. The ``fabs`` function as added to the CppAD standard math library (see :ref:`abs-name` ) and the :ref:`base type requirements` . This enables one to write code that works with ``AD`` as well as ``double`` without having to define ``abs`` for ``double`` arguments (and similarly for ``float`` ). #. Add the :ref:`adolc_ode.cpp-name` and :ref:`fadbad_ode.cpp-name` speed tests (and edit the :ref:`cppad_ode.cpp-name` test). 06-03 ===== #. The ``CppAD::vector`` class was extended to allow assignment with the target of size zero and the source of non-zero size; see :ref:`CppAD_vector@Assignment@Check Size` . #. A memory leak and a bug in ``cppad_mat_mul.cpp`` were fixed (the bug was related to the change to ``CppAD::vector`` above). 06-02 ===== #. Remove the deprecated symbol ``CppADvector`` from the :ref:`det_by_lu-name` speed test source code :ref:`det_by_lu.hpp-name` . #. Include :ref:`memory_leak-name` in the list of :ref:`deprecated-name` features. #. Change the :ref:`ode_evaluate-name` speed test utility so that its :ref:`operation sequence` does not depend on the repetition; see :ref:`ode_evaluate@p@p == 0` in its documentation. #. Use same argument for taping and derivative evaluation when ``retape`` speed test option is true. #. Implement the *retape* == ``false`` option in :ref:`cppad_ode.cpp-name` . #. Have :ref:`cppad_det_lu.cpp-name` , :ref:`cppad_det_minor.cpp-name` , and :ref:`cppad_poly.cpp-name` , return false when one of the specified options is not supported. Do the same for *package* _ *test* . ``cpp`` for *package* equal to ``adolc`` , ``fadbad`` , and ``sacado`` and for *test* equal to ``det_lu`` , ``det_minor`` , ``poly`` . 06-01 ===== Change :ref:`cppad_sparse_hessian.cpp-name` and :ref:`cppad_sparse_jacobian.cpp-name` to use the *row* , *col* interface to :ref:`sparse_hessian-name` . In addition, implement the speed test ``retape`` speed test option for these tests. 05-31 ===== Add the ``cppad_print_optimize`` routine to so that the corresponding code does not need to be reproduced for all the :ref:`speed_cppad-name` tests. In addition, during CppAD speed tests, print out the optimization results for each test size. 05-30 ===== Change specifications for :ref:`link_sparse_hessian-name` so that the row and column indices are inputs (instead of being chosen randomly by the test for each repetition). This enables use of the ``retape`` speed test option during sparse Hessian speed tests. 05-29 ===== Add :ref:`index_sort-name` to the general purpose template :ref:`utilities` so that it can be used by the implementations of :ref:`link_sparse_jacobian-name` and :ref:`link_sparse_hessian-name` . 05-27 ===== Split the sparse Jacobian and Hessian test function the separate function :ref:`sparse_jac_fun-name` and :ref:`sparse_hes_fun-name` (do not use sparse Hessian for both). In addition, change row and column indices from *i* and *j* to *row* and *col* . 05-24 ===== Merged in changes from ``branches/sparse`` : #. A new interface was added to :ref:`sparse_jacobian-name` and :ref:`sparse_hessian-name` . This interface returns a sparse representation of the corresponding matrices using row and column index vectors. #. The examples :ref:`sparse_jacobian.cpp-name` and :ref:`sparse_hessian.cpp-name` were improved and extended to include the new interface. #. The definition of an :ref:`glossary@AD Function` was improved to include definition of the corresponding *n* and *m* . 04-19 ===== The *boost_dir* configure command line value has been changed to be the corresponding prefix during the installation of boost. To be specific, it used to be that *boost_dir* / *boost* was the boost include directory, now *boost_dir* / *include* is the boost include directory. This make it the same as the other directory arguments on the configure command line. In addition, it fixes some bugs in the detection of the boost multi-threading library. 04-18 ===== Add documentation and testing for not using :ref:`free_all` and :ref:`atomic_one clear` while in :ref:`parallel` mode. 04-17 ===== Fix bug when using :ref:`atomic_one-name` functions with :ref:`multi_threading` . 04-10 ===== Add control of the :ref:`configure@max_num_threads` argument to the unix :ref:`configure@Configure` command. 04-06 ===== #. A change was made to the way that the tapes were managed to reduce false sharing during :ref:`multi-threading` . Because of this change, it is now suggest that the user call :ref:`parallel_ad-name` after the multi-threading section of the program. #. The routine :ref:`ta_free_all-name` was created to make it easier to manage memory and the routine :ref:`memory_leak-name` was deprecated. #. Add the ``-lteuchos`` flag to the link line for the :ref:`speed_sacado-name` tests. (This was not necessary for ``trilinos-10.8.3`` but is necessary for ``trilinos-10.10.1`` ) 04-05 ===== The restriction was added that :ref:`parallel_ad-name` cannot be called while a tape is being recorded. This was necessary inorder to initialize some new statics in the tape. 04-01 ===== Fixed a race condition when using CppAD with :ref:`multi-threading` . This has been fixed and the error message below no longer occurs. Suppose that you ran the CppAD :ref:`configure@Configure` command in the ``work`` directory. If you then edited the file ``work/multi_thread/makefile`` and changed :: # AM_CXXFLAGS = -g $(CXX_FLAGS) AM_CXXFLAGS = -DNDEBUG -O2 $(CXX_FLAGS) to:: AM_CXXFLAGS = -g $(CXX_FLAGS) # AM_CXXFLAGS = -DNDEBUG -O2 $(CXX_FLAGS) and then executed the commands :: make clean make pthread_test valgrind --tool=helgrind ./pthread_test get_started The following error message would result: | |tab| ... ``snip ...`` | ==7041== ``Possible data race during write of size 4 at 0x8077460 by thread`` #1 | ==7041== ``at 0x804FE23: CppAD::AD::tape_new`` () ( ``tape_link.hpp:221`` ) | ... ``snip ...`` 03-27 ===== Reduce the amount of memory allocation and copying of information during a :ref:`Dependent-name` operation or an ``ADFun`` :ref:`fun_construct@Sequence Constructor` . 03-26 ===== Calling ``taylor_capacity`` , with to with capacity equal to zero, was not :ref:`capacity_order@c@Freeing Memory` . This has been fixed. 03-23 ===== #. Improve, the multi-threading examples :ref:`openmp_get_started.cpp-name` , :ref:`bthread_get_started.cpp-name` , and :ref:`pthread_get_started.cpp-name` . This includes separating generic code that can be used for all applications from problem specific code. #. Add initialization of statics in :ref:`parallel_ad@CheckSimpleVector` during ``parallel_ad`` call. These statics are required to use :ref:`CppAD::vector` . #. Add a debugging check to make sure :ref:`CheckSimpleVector-name` is initialized in sequential mode. 03-21 ===== Fix an incorrect error check in ``thread_alloc`` that did not allow :ref:`ta_return_memory-name` to return memory in sequential execution mode that was allocated by a different thread during parallel execution. 03-17 ===== Debian recently converted the default shell corresponding to ``/bin/sh`` to ``dash`` (which caused ``example/multi_thread/test.sh`` to fail). This has been fixed. In general, Debian's policy is that ``bin/sh`` will be a `Posix Shell `_. 03-11 ===== There was a bug in :ref:`thread_alloc-name` where extra memory was held onto even if :ref:`hold_memory` was never called and only one thread was used by the program. This caused ``valgrind --leak-check`` = ``full --show-reachable`` = ``yes`` to generate an error message. If :ref:`multiple threads` are used, one should free this :ref:`ta_free_available@Purpose@Extra Memory` for threads other than thread zero. If ``hold_memory`` is used, one should call :ref:`free_available` for all threads. 03-03 ===== #. Add the examples :ref:`openmp_get_started.cpp-name` , :ref:`bthread_get_started.cpp-name` and :ref:`pthread_get_started.cpp-name` . #. Fix bug in finding boost multi-threading library (due to fact that *boost_dir* is not the prefix during the boost installation). 03-02 ===== #. Change the name ``get_started.cpp`` to :ref:`team_example.cpp-name` #. The multi-threading ``team_example.cpp`` example was changed to use :math:`f(x) = \sqrt{ x^2 }` instead of the function :math:`{\rm atan2} [ \sin(x) , \cos (x) ]` (both functions should behave like the identity function :math:`f(x) = x`). This enabled the removal of ``example/multi_thread/arc_tan.cpp`` . #. In :ref:`team_example.cpp-name` check that all of the threads pass their individual test; i.e. ``work_all_`` [ *thread_num* ]. *ok* is true for all *thread_num* . 02-11 ===== #. The requirements in :ref:`base_member-name` were missing from the :ref:`base_require-name` documentation. In addition, the :ref:`base_require.cpp-name` example has been added. The specifications for :ref:`memory_leak-name` where changes so that calling routine specifies the amount of static memory to add. In addition, it is now possible to call ``memory_leak`` when :ref:`num_threads` is greater than one (still can't be in parallel mode). 02-10 ===== #. Add the missing Base class requirements in the entire :ref:`base_member-name` section and under the :ref:`base_require@Output Operator` in the :ref:`base_require-name` section. #. Add the :ref:`base_alloc.hpp-name` example. 02-09 ===== #. Add the *set_static* to :ref:`memory_leak-name` . This is necessary for testing base types that allocate memory for each element. #. Fix memory allocation bug in ``cppad/local/pod_vector.hpp`` when each element of the :ref:`Base` type allocated memory. 01-30 ===== Make another attempt to fix linking with boost threads where the wrong version of the library is in the system include directory; i.e., to have *boost_dir* override the default library. 01-27 ===== There were some problems with :ref:`configure's` automatic detection of the boost multi-threading library. These have been fixed. 01-24 ===== It used to be that :ref:`thread_alloc-name` did not hold onto memory when *num_threads* was one in the previous call to :ref:`parallel_setup` . Holding onto memory is now controlled by the separate routine :ref:`hold_memory` . This give the user more control over the memory allocator and the ability to obtain a speed up even when there is only one thread. To convert old code to the new interface, after each call to ``thread_alloc::parallel_setup`` ( *num_threads* , *in_parallel* , *thread_num* ); put the following call ``thread_alloc::hold_memory`` ( *num_threads* > 1); 01-23 ===== Change variable notation and use :ref:`optimize-name` in :ref:`mul_level.cpp-name` . 01-20 ===== #. Add the example :ref:`change_param.cpp-name` which shows how to compute derivatives of functions that have parameters that change, but derivatives are not computed with respect to these parameters. #. The documentation for machine :ref:`epsilon-name` has been improved. (The fact that it can be used for *Base* types was missing.) 01-19 ===== #. In cases where ``test.sh`` is trivial, put its operations in corresponding makefile. #. Fix problem compiling ``cppad/speed/sparse_evaluate.hpp`` under gcc on Fedora 17. #. Run ``example/multi_thread/test.sh`` from source directory (no need to copy to build directory). 01-16 ===== The test program ``example/multi_thread/test.sh`` failed if the openmp_flags not present in the ``configure`` command. This has been fixed. In addition, this ``test.sh`` has been made faster by cycling through the available threading systems instead of doing every system for every test. 01-15 ===== Fix ``make test`` so it works when :ref:`configure@Configure` is run in the distribution directory ``cppad-`` *yyyymmdd* (not just when it is run in a different directory). 01-12 ===== The ``-lpthread`` library was missing from the :ref:`multi_thread-name` test program linker command. This has been fixed. 01-07 ===== #. A duplicated code block beginning with :: if( fabs( fcur ) <= epsilon_ ) was removed from the routine ``multi_newton_worker`` . #. The distance between solutions that are joined to one solution has been corrected from :math:`(b - a) / (2 n )` to :math:`(b - a) / n`; see :ref:`multi_newton_run@xout` . The correction was in the file :ref:`multi_newton.cpp-name` where ``sub_length_ / 2`` was change to ``sub_length_`` . 01-02 ===== #. The :ref:`thread_alloc-name` memory allocator was changed to avoid certain false sharing situations (cases where two different thread were changing and using memory that is on the same page of cache). On one tests machine, the execution time for the 32 thread case for the test :: ./openmp_test multi_newton 1 32 1000 4800 10 true improved from 0.0302 seconds to 0.0135 seconds. #. There was a problem with the correctness test section of the :ref:`multi_newton_time-name` test. The convergence criteria, and correctness criteria, needed to be scaled by the largest argument values. This was a problem with over a hundred zeros were included in the test (and the largest argument value was :math:`100 \pi` or more). #. There was a problem with the way that :ref:`multi_newton_takedown-name` joined two solutions into one. It is possible that one of the solutions that needs to be joined is on the boundary and very close to a solution in the next (or previous interval) that is not on the boundary. In this case, the one with the smaller function value is chosen. for the previous {xrst_end 2012} ================================================ FILE: appendix/whats_new/2013.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2013 app} {xrst_spell algo autotools cout ctor impl ip src struct usead } Release Notes for 2013 ###################### mm-dd ***** 12-29 ===== #. The include file :ref:`cppad_eigen.hpp-name` now automatically includes ``cppad.hpp`` . #. There was a problem with this automation when ``eigen`` was used for the cppad :ref:`testvector-name` . This has been fixed. #. There was a problem with deprecated :ref:`configure-name` (created when optional implicit constructor from any type was added). This has been fixed by adding the ``--with-implicit_ctor`` option (later removed on :ref:`2017-02-10<2017@mm-dd@02-10>` .) 12-27 ===== The constructor from an arbitrary type to ``AD`` < *Base* > was implicit, but there was no specification to this effect. The caused problems when using CppAD with :ref:`eigen 3.2` (scheduled to be fixed in 3.3). The default for this constructor has been changed to be :ref:`ad_ctor@x@explicit` . In addition, other :ref:`ad_ctor@x@implicit` constructors are now listed in the documentation. If you get a compiler error on an constructor / assignment of the form ``AD`` < *Base* > ``x`` = *y* (that used to work) try changing the constructor call to ``AD`` < *Base* >( *y* ) A deprecated alternative is to make this constructor implicit using the cmake ``cppad_deprecated`` option during the install procedure. 12-26 ===== Document fact that monthly versions of the CppAD compressed tar file last till the end of the year. This is no longer the case; see :ref:`2018@mm-dd@05-20` in whats new for 2018. 12-24 ===== The interface to :ref:`eigen` defined a function ``NumTraits< CppAD::AD<`` *Base* > >:: ``dummy_epsilon`` () that should have been named ``dummy_precision()`` . This has been fixed. 11-27 ===== #. Fix bug when using :ref:`optimize-name` with an :ref:`ADFun-name` object containing the :ref:`sign-name` function. #. Add ``atomic_two_norm_sq.cpp`` , an atomic function example with domain dimension two and range dimension one. 11-13 ===== It used to be that one had to define the ``std::set`` version of :ref:`atomic_two_rev_sparse_jac-name` for each atomic function that was part of an :ref:`ADFun-name` object that was :ref:`optimized` . Now the current :ref:`atomic_two_option@atomic_sparsity` setting is used to determine if the ``bool`` or ``std::set`` version of ``rev_sparse_jac`` is used by the optimization process. 11-12 ===== Error detection and reporting (when ``NDEBUG`` is not defined) has been added for the following case: Using :ref:`optimize-name` with :ref:`atomic_two-name` functions that have not defined :ref:`optimize@Atomic Functions@rev_sparse_jac` . 10-29 ===== The :ref:`optimization` now handles nested conditional expressions. For example, give the code :: x = CondExpLt(left_x, right_x, true_x, false_x) y = CondExpGt(left_y, right_y, true_y, false_y) z = CondExpEq(left_z, right_z, x, y) only two of the conditional expressions will be evaluated (one will be skipped depending on the result of ``left_z == right_z`` ). For more details, see :ref:`optimize_nest_conditional.cpp-name` . 10-23 ===== #. Fix a bug in the optimization of calls to :ref:`atomic-name` functions. This bug existed before recent change to optimizing conditional expressions. This required adding the :ref:`RevSparseJac@dependency` argument to the reverse Jacobian sparsity pattern calculation. #. Fix the deprecated autotools install (see :ref:`configure-name` ) which was broken by the changes on 10-22. To be specific, the example for :ref:`number_skip-name` was not being included. 10-22 ===== #. Add :ref:`optimization` of conditional expressions; see :ref:`CondExp` . #. Add a phantom argument at the beginning of the operations sequence; :ref:`fun_property@size_op_arg` and :ref:`fun_property.cpp-name` . (This helps with the optimization mentioned above.) #. Add the function :ref:`number_skip-name` to measure how much optimization of the conditional expressions there is. 10-16 ===== Fix bug in :ref:`wish_list@Tracing` :ref:`atomic-name` functions. 10-15 ===== The documentation for the class :ref:`CppAD_vector@vectorBool` was improved. 10-14 ===== The script :ref:`get_adolc.sh-name` was added (for downloading and installing `ADOL-C `_) in the ``build`` directory. Note that this local install of Adolc requires ColPack; see :ref:`get_colpack.sh-name` . In addition, the requirement that ColPack and Adolc are installed with the same prefix was added. 10-13 ===== Make sure that all of the :ref:`preprocessor-name` symbols that are not part of the CppAD API, are undefined when the ```` file concludes. 10-12 ===== #. Change ``get_eigen.sh`` so that it will reuse install information when it is present. In addition document reuse for ``get_eigen.sh`` , :ref:`get_ipopt.sh-name` , and :ref:`get_sacado.sh-name` . #. Fix following ``g++`` error on OSX system: :: error: no match for 'operator|=' (operand types are 'std::vector::reference {aka std::_Bit_reference}' and 'bool') Check[i * n + j] |= F2[i * n + k] & r[ k * n + j]; ^ 09-20 ===== #. Add lines for :ref:`atomic_two-name` function documentation to both the definition and use of each operation. This required adding sub-headings in the example usages corresponding to the function documentation sections. For example; see atomic forward examples. #. Improve the documentation for :ref:`atomic_two_clear-name` and remove its use from the :ref:`atomic_base examples` (because it is not needed). 09-19 ===== Add links from the :ref:`atomic_two-name` functions documentation to the corresponding examples. This required adding headings in the examples that correspond to the function documentation sections. For example; see atomic forward examples. 09-18 ===== #. A segmentation fault would occur if an :ref:`ADFun-name` object used an :ref:`atomic-name` function that had been deleted. This has been fixed so that when ``NDEBUG`` is not defined, an error message is generated. #. A mistake in the documentation for :ref:`CppAD_vector@Memory and Parallel Mode` has been fixed. This corresponds to the change in the specifications for :ref:`CppAD::vector::resize` made on :ref:`2012-07-30<2012@mm-dd@07-30>` #. There was a bug during the :ref:`checking for nan` during :ref:`reverse-name` mode. This has been fixed. #. It appears, from inspecting the Ipopt source file ``Ipopt/src/Algorithm/IpIpoptAlg.cpp`` that the option ``sb`` to ``yes`` suppress the printing of the Ipopt banner. The Ipopt examples and tests have been changed to use this option (although it is not in the ipopt documentation). #. Fix the a typo in the documentation for ``ipopt_solve`` :ref:`ipopt_solve@options@Integer` options (``Numeric`` was changed to ``Integer`` ). 09-07 ===== There was a bug in the cumulative sum operator (which is used by :ref:`optimize-name` ) for :ref:`Forward-name` orders greater than zero. This was detected by the :ref:`checkpoint` tests when ``optimize`` was used to make the checkpoint functions faster. The bug has been fixed and the checkpoint functions now use optimize (and hence should be faster). 08-12 ===== #. The ability to turn on and off checking for :ref:`nan-name` in :ref:`Forward-name` mode results has been added; see :ref:`check_for_nan-name` . #. Use this option to remove the need to handle ``nan`` as a special case in :ref:`checkpoint` functions that :ref:`optimize@Atomic Functions` in within another function is optimized. #. Check :ref:`reverse` mode results when :ref:`check_for_nan-name` is true. (It used to be the case that only :ref:`forward` results were checked for ``nan`` .) 08-11 ===== If an :ref:`atomic-name` function had arguments that did not affect the final dependent variables in *f* , :ref:`f.optimize()` would fail. This has been fixed. In addition, documentation about using ``optimize`` with :ref:`optimize@Atomic Functions` has been added. 08-06 ===== Fix a case where the test ``test_more/num_limits.cpp`` failed because :: double inf = std::numeric_limits::infinity(); double check = std::complex(inf) / std::complex(1.) can result in the imaginary part of ``check`` being ``- nan`` . 07-26 ===== Allow for use of ``const::string&`` as a possible type for :ref:`atomic_two_ctor@atomic_base@name` in the ``atomic_base`` constructor. 05-28 ===== Remove *ok* return flag from :ref:`checkpoint algo` and :ref:`checkpoint atom_fun` . 05-21 ===== #. Deprecate the :ref:`atomic_one-name` interface and replace it by the :ref:`atomic_two-name` and :ref:`checkpoint` interfaces. #. There was a problem with the :ref:`cmake-name` command if the :ref:`cmake@cppad_cxx_flags` was not specified. This has been fixed. 05-17 ===== #. Add the :ref:`ForSparseJac@transpose` option to :ref:`ForSparseJac-name` . #. Add the :ref:`RevSparseHes@transpose` option to :ref:`RevSparseHes-name` . 05-15 ===== Change :ref:`RevSparseJac-name` parameter names to be closer to the :ref:`ForSparseJac-name` names so the difference is clearer. 05-14 ===== #. The :ref:`checkpoint` class has been added. This is a much easier way to do checkpointing than the old checkpoint example. The old checkpointing example is now the :ref:`rev_checkpoint.cpp-name` example. #. Fix bug in :ref:`RevSparseJac-name` for case when :ref:`RevSparseJac@q` was not equal to *m* (range dimension) and sparsity pattern was a vector of ``bool`` . #. Add the :ref:`RevSparseJac@transpose` option to :ref:`RevSparseJac-name` . 05-12 ===== The sparse hessian example in old_reciprocal.cpp was not being run. This has been fixed. 05-11 ===== The :ref:`atomic_one-name` examples names were all changed to begin with ``user`` . 05-04 ===== The option to compute :ref:`forward_order@xq@Multiple Orders` was added. The old_usead_2.cpp example shows the need for this. The problem is that a new atomic function interface needs to be designed with checkpointing as a particular application. Multiple order forward mode is the first step in this direction. 04-28 ===== #. The scripts ``get_eigen.sh`` and :ref:`get_sacado.sh-name` were added. If you are using Unix, and you do not have `Eigen `_ or `Sacado `_ installed on your system, you can use the corresponding script to download and install a local copy for use when testing CppAD. #. The code ``std::cout <<`` *X* , would generate a compile error when *X* was an Eigen matrix with ``CppAD::AD<`` *Base* > elements. This has been fixed. 04-27 ===== There was a problem using the output operator ``<<`` with and :ref:`eigen` matrix of ``AD`` elements. This has been fixed using a template partial specialization of :: template struct significant_decimals_default_impl because the original template requires definition of a implicit conversion from the scalar type to an ``int`` and this is dangerous for AD types (note that :ref:`Integer-name` is used for explicit conversions). 04-26 ===== #. The example old_usead_2.cpp was completed. This is a more realistic, but also more complicated, example of using AD to computed derivatives inside an atomic function. #. The script :ref:`get_fadbad.sh-name` has been added. If you are using Unix, and you do not have `FADBAD `_ installed on your system, you can use this script to download and install a local copy for use when testing CppAD. {xrst_end 2013} $subhead 04-20$$ The example old_usead_1.cpp was added. $subhead 04-16$$ The script $cref get_ipopt.sh$$ has been added. If you are using Unix, and you do not have $href%https://www.coin-or.org//projects/Ipopt.xml%Ipopt%$$ installed on your system, you can use this script to download and install a local copy for use when testing CppAD. $subhead 04-14$$ The following program, when executed, would cause a CppAD assertion with an unknown source: $codep # include int main(void) { size_t min_bytes = static_cast(-1); size_t cap_bytes; void *v_ptr = CppAD::thread_alloc::get_memory(min_bytes, cap_bytes); return 0; } $$ It now generates the following message, (when compiled without $cref/NDEBUG/faq/Speed/NDEBUG/$$)" $codep get_memory(min_bytes, cap_bytes): min_bytes is too large Error detected by false result for min_bytes < std::numeric_limits::max() / 2 $$ $subhead 03-02$$ The function $cref/isnan/nan/$$ no longer allows for systems that return false for $icode%x% != %x%$$ when $icode x$$ is $code NaN$$. This makes the $code isnan$$ function faster. In addition, it removes the need to store a static value which causes complications for parallel execution (as well as other problems). Thus, it is no longer necessary for the first call to $code isnan$$ to be during sequential execution and it has been removed from the multi-threading $cref/initialization/multi_thread/Initialization/$$ list. $subhead 03-01$$ Remove the $cref cmake$$ $code cppad_c11_flag$$ and instead automatically detect if the compiler supports specific c++11 features. $subhead 02-27$$ The test $cref num_limits.cpp$$ was failing during testing of Fedora-19; see $href%https://bugzilla.redhat.com/show_bug.cgi?id=913929% Bug 913929%$$. This has been fixed. $subhead 02-20$$ $list number$$ The documentation for the $cref cmake$$ install option $code cppad_test_vector_namespace$$ was fixed to be $cref/cppad_testvector/cmake/cppad_testvector/$$. This was also fixed for the $cref/eigen test vector/eigen/Test Vector/$$ documentation. $lnext An option to specify that the compiler supports c++11 constructs, $code cppad_c11_flag$$, was added to the $code cmake$$ command line. $lend $subhead 01-07$$ Fix undefined $code microsoft_timer$$ when building the $cref speed_example.cpp$$ program. $subhead 01-06$$ $list number$$ The $cref limits$$ documentation was corrected an improved. $lnext The $cref num_limits.cpp$$ example was simplified and restricted to just testing for $code AD$$. $lnext Testing for types other than $code AD$$ was moved to $code test_more/num_limits.cpp$$. In addition, $code test_more/num_limits.cpp$$ was modified to avoid the use of guard digits (and hence test failure) on more machines. $lend $subhead 01-05$$ $list number$$ The $cref num_limits.cpp$$ example was failing on some machines because they were using guard digits during calculations. This has been fixed by using vectors instead of scalars for storing values. $lnext The $cref speed_example.cpp$$ was not linking when using a shared object library for the code in $code speed/src$$. This has been fixed by not including the library when linking the speed examples. $lend $subhead 01-02$$ $list number$$ A new stable version of CppAD, for 2013, was created and its first release $code cppad-20130000.0$$ is available for download at $pre $$ $href%https://www.coin-or.org/download/source/CppAD/%$$ $lnext Advance version number for trunk to $code 20130102$$. $lend $subhead 01-01$$ When compiling with $cref/NDEBUG/Faq/Speed/NDEBUG/$$ defined, the following warning could be generated: $codei% %...%rev_sparse_jac.hpp:315:9: warning: unused variable 'm' [-Wunused-variable] %$$ This has been fixed. $end ================================================ FILE: appendix/whats_new/2014.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2014 app} {xrst_spell autoconf automake autotools bthread cmath cstdint ctor datadir docdir doxygen forwardany forwardone forwardzero ipopt omhelp openmp pthread retape uj } Release Notes for 2014 ###################### mm-dd ***** 12-30 ===== There was a bug in the :ref:`cmake-name` whereby it would sometimes mistakenly exit with the error message :: cppad_max_num_threads is not an integer greater than or equal 4 This has been fixed. 12-29 ===== The example ``not_complex_ad.cpp`` was using the type ``std::complex< CppAD::AD >`` and was failing to compile with the ``clang`` compiler. This example has been removed because it is not consistent with the C++ standard; see :ref:`complex FAQ` . 12-28 ===== #. Fix some warnings generated by clang 3.5 about local functions that were not being used; e.g., sub-tests that were not being executed. #. Fix ``cmake`` setting ``cppad_implicit_ctor_from_any_type`` Note that this cmake option has since been replaced by ``cppad_deprecated`` option. #. The ``clang++`` compiler was optimizing out the calculations in the :ref:`time_test.cpp-name` and :ref:`speed_test.cpp-name` examples. This caused these tests to hang while trying to determine how many times to repeat the test. This has been fixed. 12-27 ===== More work on the bug in :ref:`optimizing` conditional expressions. 12-26 ===== A minimal example for computing cross terms in atomic operation Hessian sparsity patterns ``atomic_two_rev_sparse_hes.cpp`` was added. 12-25 ===== More work on the bug in :ref:`optimizing` conditional expressions. 12-23 ===== The c++11 standard includes the error function :ref:`erf-name` in ``cmath`` . If the c++ compiler has the error function defined in ``cmath`` , the compiler version of the error function is used and it corresponds to an atomic operation. Fix typo in tangent reverse mode theory for :ref:`Positive Orders` . 12-22 ===== There was a bug related to :ref:`optimizing` conditional expressions. This has been fixed. 12-17 ===== Fix some compiler warnings and :ref:`speed-name` program names when using the deprecated :ref:`configure-name` install procedure. 12-16 ===== If the ``c++11`` include file ```` defines all the standard types, they can be used by to specify :ref:`cppad_tape_addr_type` and :ref:`cppad_tape_id_type` . 12-15 ===== Correct the title and index entries for :ref:`forward_two-name` from first to second order. 11-28 ===== Improve the index and ``search`` using a new version of the ``omhelp`` documentation tool. 11-27 ===== #. Add alignment to the :ref:`get_memory` and :ref:`create_array` specifications and :ref:`thread_alloc example` . #. Advance the deprecated :ref:`unix install` utilities to autoconf-2.69 and automake-1.13.4. 09-28 ===== Fix more bugs related to optimizing condition conditional expressions. #. Using old instead of new operator indices. #. Not properly following dependence back through atomic operations. #. Aborting during forward order zero, when skipping computation for a variable that was already completed (the skip is still useful for higher orders and for reverse mode). #. Reverse mode not properly handling the variable number of arguments in the conditional skip operation. #. Reverse mode tracing not properly handling the variable number of argument operations; i.e., conditional skip and cumulative summation. 09-27 ===== Fix a bug that occurred when :ref:`f.optimize` was used with a function *f* that contained calls to user defined :ref:`atomic-name` operations and :ref:`conditional expressions` . 09-25 ===== Fix a bug that occurred when :ref:`f.optimize` was used with a function *f* that contained :ref:`discrete-name` functions. 09-21 ===== Fix a typo in documentation for :ref:`any order reverse` . To be specific, :math:`x^{(k)}` was changed to be :math:`u^{(k)}`. 05-28 ===== #. Change the :ref:`speed_main@Sparsity Options@boolsparsity` so that it only affects the sparsity speed tests :ref:`sparse_jacobian` and :ref:`sparse_hessian` ; i.e., it is now ignored by the other tests. #. Improve the :ref:`speed-name` documentation page. 05-27 ===== #. The ``cppad_colpack.cpp`` file was not being copied to the specified directory. In addition, the specified directory was changed from an include directory to data directory because ``cppad_colpack.cpp`` is not an include file. #. If :ref:`colpack_prefix-name` was specified, the CppAD :ref:`pkgconfig-name` file was missing some information. This has been fixed. 05-23 ===== The :ref:`speed-name` test instructions were converted from using the old autotools :ref:`unix install` instructions to use the :ref:`cmake-name` install instructions. These instructions should work on any system, not just unix. 05-22 ===== #. Add multiple direction for mode :ref:`forward_dir-name` and use it to speed up the forward version of :ref:`sparse_jacobian-name` . Below is an example run of :ref:`cppad_sparse_jacobian.cpp-name` results before this change: :: cppad_sparse_jacobian_size = [ 100, 400, 900, 1600, 2500 ] cppad_sparse_jacobian_rate = [ 2973, 431.94, 142.25, 78.64, 26.87 ] and after this change: :: cppad_sparse_jacobian_size = [ 100, 400, 900, 1600, 2500 ] cppad_sparse_jacobian_rate = [ 6389, 954.26, 314.04, 180.06, 56.95 ] Due to the success of this change, ``multiple direction`` items were added to the wish list (they were later removed). #. Improve the forward mode tracing of arguments to, and results from, user defined :ref:`atomic-name` operations. 05-20 ===== #. Move ``speed/adolc/alloc_mat.hpp`` to ``speed/adolc/adolc_alloc_mat.hpp`` so it has the same name is its ``# ifndef`` command. #. Fix ``# ifndef`` command in ``cppad/ipopt/solve_callback.hpp`` . #. Add ``# ifndef`` command to ``test_more/extern_value.hpp`` . 05-19 ===== In the files ``cppad/local/asin_op.hpp`` and ``cppad/local/acos_op.hpp`` there were assignments of the form ``uj = 0.`` where ``u_j`` has type :ref:`Base` . These may have not be defined operations in certain cases and have been converted to ``uj = Base(0)`` . 05-16 ===== There was a mistake in printing the arguments for ``CSumOp`` and ``CSkipOp`` when using the undocumented ``TRACE`` option during forward mode (available in files of the form ``include/cppad/local/`` * ``sweep.hpp/`` ). This has been fixed. 05-14 ===== #. There were some global variables in the file ``cppad/local/op_code.hpp`` that might have caused multiple definitions during link time for CppAD applications. These variables have been changed to be local so that this cannot happen. #. There was a mistaken assert that the number of arguments for the ``BeginOp`` was zero that caused an abort when using the undocumented ``TRACE`` option available in files of the form ``include/cppad/local/`` * ``sweep.hpp/`` . This has been fixed. 03-18 ===== #. The :ref:`fun_deprecated@size_taylor` and :ref:`fun_deprecated@capacity_taylor` functions were deprecated; use :ref:`size_order-name` and :ref:`capacity_order-name` instead. #. The documentation for :ref:`forward-name` and the examples :ref:`forward.cpp-name` , :ref:`forward_order.cpp-name` , have been improved. To be more specific, :ref:`forward_order-name` now references the special cases :ref:`forward_zero-name` , :ref:`forward_one-name` and the new case :ref:`forward_two-name` . 03-17 ===== The :ref:`CppAD_vector@Assignment@Move Semantics` version of the ``CppAD::vector`` assignment statement was not checking vector sizes. This has been fixed so that things work the same with compilers that do not have move semantics. (Note that with move semantics, no extra memory allocation is done even if the target vector has a different size.) 03-09 ===== The documentation links ``forwardzero`` , ``forwardone`` , and ``forwardany`` have been changed to :ref:`forward_zero-name` , :ref:`forward_one-name` , and :ref:`forward_order-name` respectively. This may affect links from other web pages to the CppAD documentation. 03-05 ===== The names *p* and *q* in the :ref:`forward` , :ref:`reverse` , :ref:`atomic_two_forward-name` , and :ref:`atomic_two_reverse-name` functions were reverse so that *p* <= *q* . This is only a notational change to make the arguments easier to remember. 03-02 ===== #. In the output for the speed :ref:`speed_main@test@correct` test, mention which tests are not available. Note that the set of available tests can depend on the :ref:`list of options` . #. In the documentation for :ref:`sparse_jacobian@n_sweep` , mention that it is equal to the number of colors determined by the :ref:`sparse_jacobian@work@color_method` . #. The :ref:`speed_cppad-name` tests were simplified by removing the printing of auxiliary information related to the :ref:`speed_main@Global Options@optimize` option. Future auxiliary information will be passed back in a manner similar to :ref:`link_sparse_jacobian@n_color` for the sparse jacobian test. #. :ref:`CppAD_vector@Assignment@Move Semantics` were added to the ``CppAD::vector`` assignment operator. 03-01 ===== #. Change the prototype for *row* and *col* in the :ref:`link_sparse_jacobian-name` speed test to be ``const`` ; i.e., they are not changed. #. Move *x* near end of :ref:`link_sparse_hessian-name` speed test parameter list, (as is the case for :ref:`link_sparse_jacobian-name` ). 02-28 ===== The :ref:`CppAD_vector@data` function was added to the ``CppAD::vector`` template class. 02-27 ===== The CppAD developer documentation for the subdirectory ``cppad/ipopt`` was not being built by the command ``bin/run_doxygen.sh`` . This has been fixed. 02-26 ===== #. The :ref:`adolc` and :ref:`cppad` sparse jacobian speed tests now print out :ref:`sparse_jacobian@n_sweep` . #. The size of some of the :ref:`speed-name` test cases has been increased to test behavior for larger problems. #. A link to :ref:`ode_evaluate-name` was missing in the :ref:`speed_utility@Speed Utility Routines` table. This has been fixed. 02-23 ===== The :ref:`sparse_jacobian@work@color_method` option was added to the sparse Jacobian calculations. This enables one to use :ref:`ColPack` do color the rows or columns. The speed test :ref:`speed_main@Sparsity Options@colpack` option was also added (but not yet implemented for :ref:`sparse_hessian` speed tests). 02-22 ===== The program names in :ref:`thread_test.cpp-name` where changes from *threading* _ ``test`` to ``multi_thread_`` *threading* where *threading* is ``openmp`` , ``pthread`` or ``bthread`` . 02-17 ===== Fix ambiguous call to :ref:`nan@isnan` during MS Visual Studio 2012 compilation. 02-15 ===== #. The :ref:`speed_main@Sparsity Options@boolsparsity` option was added to the :ref:`speed_main-name` program. #. The ``retape`` option what changed to :ref:`speed_main@Global Options@onetape` so that the default is to retape (option not present). This was done because :ref:`fadbad` and :ref:`sacado` always retape. #. The documentation, and example source code, for all the speed :ref:`options` was improved (made clearer). #. Improve the success rate for :ref:`speed_test.cpp-name` and :ref:`time_test.cpp-name` . 01-26 ===== The destination directory for the :ref:`cppad documentation` is now set separately from the data directory using the ``cmake`` option ``-D cmake_install_docdir`` = *cmake_install_docdir* This has increased the flexibility of the documentation installation and removed the need for the option ``-D cppad_documentation`` = *yes_or_no* which has been removed. 01-21 ===== The destination directory for the cppad documentation used to be one of the following: | |tab| *prefix* / *datadir* / ``doc/cppad-`` *version* | |tab| *prefix* / *datadir* / ``doc/`` *postfix* ``cppad-`` *version* This has been changed by dropping the *version* number at the end of the directory name. 01-10 ===== The change on :ref:`2013-12-27<2013@mm-dd@12-27>` caused a conversion error in :ref:`atan2-name` when it was used with ``AD< AD >`` arguments (and other similar cases). This has been fixed. {xrst_end 2014} ================================================ FILE: appendix/whats_new/2015.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2015 app} {xrst_spell addon adouble aeps asctime autotools chrono cskip datadir docdir erfc gettimeofday gmtime hasnan ieee includedirs lcppad libdirs runge src unreferenced } Release Notes for 2015 ###################### mm-dd ***** 12-31 ===== The :ref:`download-name` instructions were modified to have more mention of git and less mention of subversion. 12-29 ===== Separate :ref:`to_string-name` from :ref:`ad_to_string-name` so that it can be used without the rest of CppAD; i.e., by including # ``include `` 12-28 ===== #. Add the :ref:`to_string-name` utility. #. Add :ref:`base_to_string-name` to the Base type requirements. #. A :ref:`wish_list@Base Requirements` item was added to the wish list. #. The :ref:`wish_list-name` item to reorganize the include directory has been removed. It was completed when the utilities was moved to ``cppad/utility`` ; see :ref:`2015@mm-dd@11-30` . 12-08 ===== #. A convention was included for addon :ref:`addon@Library Files` . #. Change new :ref:`utility-name` specifications to allow for individual file includes; e.g., ```` . 12-01 ===== Fix problem with :ref:`configure-name` install handling of the deprecated files. This included changing the autotools ``--with-implicit_ctor`` option to :ref:`configure-name` . This was removed on :ref:`2017-02-10<2017@mm-dd@02-10>` . 11-30 ===== #. The ``library`` section has been moved to the :ref:`utilities` section. In addition, the corresponding source code files in ``cppad`` have been moved to ``cppad/utility`` . #. The individual :ref:`utility-name` include files have been deprecated; see :ref:`include_deprecated-name` . For example, :: # include You should us the utility include instead; i.e., :: # include #. The :ref:`numeric_ad-name` routines where moved from the ``library`` the a separate documentation section. #. Change ``cmake_install_prefix`` to :ref:`cmake@cppad_prefix` and Change ``cmake_install_postfix`` to :ref:`cmake@cppad_postfix` . #. Change ``cppad_implicit_ctor_from_any_type`` to the cmake ``cppad_deprecated`` option and change its specifications to refer to all deprecated features. 11-25 ===== #. CppAD now installs the object library :: -lcppad_lib to be included when linking. Currently, it is only required when :ref:`colpack_prefix-name` is specified on the :ref:`cmake@CMake Command` . #. It is no longer necessary to compile and link the file :: cppad_colpack.cpp when :ref:`colpack_prefix-name` is specified during the install process; see :ref:`cmake@CMake Command` . (It is included in ``cppad_lib`` ). 11-24 ===== #. The ``check_for_nan`` output now includes the first dependent variable :ref:`check_for_nan@Error Message@index` that is ``nan`` in its error message. #. Change the :ref:`deprecated include` reference ``pow_int.h`` to ``pow_int.hpp`` in :ref:`pow_int-name` . 11-14 ===== There was a bug in the new :ref:`check_for_nan@get_check_for_nan` feature that writes independent variable values to a temporary file; see :ref:`2015@mm-dd@11-06` below. This has been fixed. 11-08 ===== #. Fixed a bug in the :ref:`RevSparseJac-name` routine. To be specific, the argument :ref:`RevSparseJac@r` was transposed from what the documentation said. (This has no effect in the usual case where *r* is the identity.) #. Added the ``bool_sparsity.cpp`` examples which show how to conserve memory when computing sparsity patterns. (This has since been replaced by :ref:`rc_sparsity.cpp-name` .) #. Modified the :ref:`ipopt_solve-name` procedure to take advantage of the memory conserving sparsity pattern calculations when :ref:`ipopt_solve@options@Retape` is false. #. Added the :ref:`CppAD_vector@vectorBool@bit_per_unit` function to the ``vectorBool`` class. (This aids the memory conservation mentioned above.) 11-06 ===== It is often difficult to determine what cause a ``nan`` result during an operation with an :ref:`ADFun-name` object. The new feature :ref:`check_for_nan@get_check_for_nan` was added to make this easier. 10-21 ===== There was a mistake in the documentation for :ref:`index_sort-name` , the argument :ref:`index_sort@ind` is not ``const`` . 10-16 ===== Add a :ref:`PrintFor-name` optimization wish list item. This has been done, see :ref:`optimize@options@no_print_for_op` . 10-06 ===== #. Add the ``CPPAD_USE_CPLUSPLUS_2011`` , ``CPPAD_NUMERIC_LIMITS`` , and ``CPPAD_STANDARD_MATH_UNARY`` , to the :ref:`preprocessor-name` section. In addition, remove checking that all user API preprocessor symbols are in this section form the :ref:`wish_list-name` . #. Alphabetize and make some corrections to :ref:`list of examples` . #. The documentation for some of the :ref:`deprecated-name` features was missing the date when they were deprecated. This has been fixed; e.g., see :ref:`configure@Deprecated 2012-12-26` . 10-04 ===== #. :ref:`base_require-name` : Add the macro :ref:`base_limits@CPPAD_NUMERIC_LIMITS` to aid in setting the numeric limits for a user defined *Base* class. #. :ref:`base_require-name` : The :ref:`numeric_limits@quiet_NaN` function has been added to the CppAD ``numeric_limits`` . Note the reason for not using :ref:`numeric_limits@std::numeric_limits` . #. The :ref:`nan@nan(zero)` function computes a ``nan`` by dividing zero by zero which results in a warning when using some compilers. This function has been deprecated and the corresponding :ref:`wish_list-name` item has been removed. #. Move documentation for :ref:`zdouble-name` to :ref:`deprecated-name` section and documentation for :ref:`numeric_limits-name` to :ref:`ADValued-name` . #. Remove all uses of, and references to, :ref:`zdouble-name` from the :ref:`examples` . 10-03 ===== :ref:`base_require-name` : It is no longer necessary to define the specialization for ``CppAD::epsilon<`` *Base>* () for each *Base* type. 10-02 ===== There was a bug in ``test_more/azmul.cpp`` whereby the vector ``z`` had the wrong dimension (in two places). This has been fixed. 09-28 ===== #. Use the current :ref:`atomic_two_option-name` setting to determine which type of sparsity patterns to use for :ref:`dependency` calculations during :ref:`optimize-name` procedure. It used to be that the :ref:`atomic_two_option@atomic_sparsity@bool_sparsity_enum` was used when :ref:`atomic_two_option@atomic_sparsity@pack_sparsity_enum` was specified. #. It is not longer an error to take the derivative of the square root function, because the result may be the part of a :ref:`conditional expression` that is not used. #. Update the :ref:`wish_list-name` section. 09-27 ===== #. It is no longer necessary to use the :ref:`zdouble-name` class when computing with :ref:`multiple levels of AD` :ref:`conditional expressions` and :ref:`reverse mode` . #. The ``zdouble`` class has been deprecated. Use the :ref:`azmul-name` function for absolute zero (when it is needed). 09-25 ===== :ref:`base_require-name` : :ref:`absolute zero multiplication` is now required for user defined base types. This makes it possible to combine :ref:`conditional expression` , :ref:`multiple levels` , :ref:`Reverse-name` , and a base type that has standard ieee multiplication; e.g., ``double`` . In other words, not all multiplications will need to have an absolute zero (as is the case with the :ref:`zdouble-name` base class. 09-24 ===== Fix some Visual Studio 2013 C++ level four ``/W4`` warnings (previous warnings were are level 3). In addition, disable warning ``4100`` unreferenced formal parameter, and warning ``4127`` conditional expression is constant. 09-23 ===== CppAD can optionally test its use with the external packages eigen , :ref:`ipopt` , and :ref:`colpack` . In addition, it can compare its :ref:`speed-name` with the external AD packages :ref:`adolc` , :ref:`fadbad` , and :ref:`sacado` . The scripts that download and install a local copy of these external packages have been modified to automatically skip installation when it has already been done. 09-21 ===== Improve discussion of windows download and testing. This discussion has since been removed; see +:ref:`2018@mm-dd@05-20` in whats new for 2018. 09-20 ===== #. Add the :ref:`cmake@cppad_profile_flag` to the list of possible ``cmake`` command arguments. #. More of the warnings generated by Visual Studio 2013 have been fixed. One remaining warning is about ``asctime`` and ``gmtime`` not being thread safe. 09-19 ===== #. There was a bug in the :ref:`base_alloc.hpp@numeric_limits` section of the example user defined base type. This has been fixed. #. There were some compile and link errors when running the tests using Visual Studio 2013. These have been fixed. #. Many of the warnings generated by Visual Studio 2013 have been fixed. 09-16 ===== The conditional expressions, :ref:`CondExp-name` , were not working for the type ``< CppAD::AD >`` where ``adouble`` is the ADOL-C AD type. This has been fixed by adding a call to :ref:`CPPAD_COND_EXP_REL` in ``base_adolc.hpp`` . 09-03 ===== #. There was a bug in the :ref:`CppAD_vector@vectorBool` :ref:`CppAD_vector@Assignment` . To be specific, it not allow a size zero vector to be assigned using a vector any other size. This has been fixed. #. The addition of the :ref:`pack` option on 08-31 introduced a bug in the calculation of :ref:`RevSparseHes-name` . The ``chkpoint_one_get_started.cpp`` example was changed to demonstrate this problem and the bug was fixed. 09-02 ===== The :ref:`dependency.cpp@Dependency Pattern` was not being computed correctly for the :ref:`sign-name` , :ref:`Discrete-name` , and :ref:`VecAD-name` operations. This has been fixed. This could have caused problems using :ref:`checkpoint` functions that used any of these operations. 08-31 ===== #. Mention the fact that using checkpoint functions can make :ref:`recordings faster` . #. Add the :ref:`pack` sparsity option for :ref:`atomic_two-name` operations. #. Add the pack sparsity option to :ref:`chkpoint_one` functions. #. Added the ``example/atomic/sparsity.cpp`` example. #. Remove mention of OpenMP from :ref:`thread_alloc::thread_num` (:ref:`thread_alloc-name` never was OpenMP specific). 08-30 ===== #. The :ref:`atomic_two_ctor@atomic_base@sparsity` argument was added to the ``atomic_base`` constructor and the :ref:`chkpoint_one` constructor. #. Make ``atomic_two_norm_sq.cpp`` an example with no set sparsity and ``atomic_two_reciprocal.cpp`` an example with no bool sparsity. #. Improve discussion of ``Independent`` and :ref:`Independent@Parallel Mode` . 08-29 ===== Some asserts in the :ref:`checkpoint` implementation were not using the CppAD :ref:`ErrorHandler-name` . This has been fixed. 08-28 ===== Free :ref:`checkpoint` function sparsity patters during :ref:`forward-name` operations that use its atomic operation. (They kept between sparsity calculations because they do not change.) 08-26 ===== Fix a bug in :ref:`RevSparseJac-name` when used to compute sparsity pattern for a subset of the rows in a :ref:`checkpoint` function. 08-25 ===== Reduce the amount of memory required for :ref:`checkpoint` functions (since sparsity patterns are now being held so they do not need to be recalculated). 08-20 ===== Added an example that computes the sparsity pattern for a subset of the :ref:`Jacobian` and a subset of the :ref:`Hessian` . 08-17 ===== #. Do some optimization of the :ref:`checkpoint` feature so that sparsity patterns are stored and not recalculated. #. Fix a warning (introduced on 08-11) where the ``CppAD::vector`` :ref:`CppAD_vector@data` function was being shadowed by a local variable. #. The source code control for CppAD has a link to ``compile`` , instead of real file. This sometimes caused problems with the deprecated :ref:`configure-name` install procedure and has been fixed. 08-16 ===== #. Improve the documentation for checkpoint functions. To be specific, change the :ref:`chkpoint_one@Syntax` to use the name *atom_fun* . In addition, include the fact that *atom_fun* must not be destructed for as along as the corresponding atomic operations are used. #. Add the :ref:`chkpoint_one@size_var` function to the checkpoint objects. 08-09 ===== Add the preservation of data to the specifications of a ``CppAD::vector`` during a :ref:`CppAD_vector@resize` when the capacity of the vector does not change. In addition, added and example of this to :ref:`cppad_vector.cpp-name` . 08-06 ===== The :ref:`zdouble-name` :ref:`numeric_limits` were not being computed properly. This has been fixed. 07-31 ===== Added the :ref:`sparse_sub_hes.cpp-name` example, a way to compute the sparsity for a subset of variables without using :ref:`multiple levels of AD` . 06-16 ===== #. There were some :ref:`cppad_assert@Unknown` asserts when the sparsity pattern *p* in :ref:`sparse_jacobian` and :ref:`sparse_hessian` was not properly dimensioned. These have been changed to :ref:`cppad_assert@Known` asserts to give better error reporting. #. In the special case where sparse Hessian :ref:`sparse_hessian@work` or sparse Jacobian :ref:`sparse_hessian@work` was specified and the set of elements to be computed was empty, the work vector is empty after the call (and it appears to need to be calculated on subsequent calls). This resulted in a bug when the sparsity pattern was not provided on subsequent calls (and has been fixed). 06-11 ===== #. Some C++11 features were not being taken advantage of after the change on :ref:`2015@mm-dd@05-10` . To be specific, move semantics, the high resolution clock, and null pointers. This has been fixed. #. In the example zdouble.cpp, the vector ``a1z`` was not properly dimensioned. This has been fixed and the dimensions of all the variables have been clarified. 06-09 ===== Add an :ref:`Independent@abort_op_index` item to the wish list. It has since been removed (domain errors may not affect the results due to :ref:`conditional expressions` ). 06-07 ===== Add a :ref:`absolute zero` item and a :ref:`numeric_limits-name` item to the wish list. The absolute zero item has been completed and the numeric limit item was modified on implementation. Remove the multiple directions with list item. 05-26: cond_exp_1 ================= There was a problem using :ref:`conditional expressions` with :ref:`multiple levels of AD` where the result of the conditional expression might not be determined during forward mode. This would generate an assert of the form: | |tab| ``Error detected by false result for`` | |tab| |tab| ``IdenticalCon`` ( *side* ) | |tab| ``at line`` *number* ``in the file`` | |tab| |tab| .../ ``include/cppad/local/cskip_op.hpp`` where *side* was ``left`` or ``right`` and *number* was the line number of an assert in ``cskip_op.hpp`` . This has been fixed. 05-26: cond_exp_2 ================= There was a problem with using :ref:`conditional expressions` and :ref:`reverse mode` with :ref:`multiple levels of AD` . This was problem was represented by the file ``bug/cond_exp_2.sh`` . #. The problem above has been fixed by adding the base type ``zdouble`` , see :ref:`CppAD motivation` for this new type. (It is no longer necessary to use ``zdouble`` to get an absolute zero because CppAD now uses :ref:`azmul-name` where an absolute zero is required.) #. The sections :ref:`mul_level-name` , :ref:`change_param.cpp-name` , :ref:`mul_level.cpp-name` , and :ref:`mul_level_ode.cpp-name` were changed to use :ref:`zdouble-name` . #. The :ref:`adolc-name` multi-level examples :ref:`mul_level_adolc.cpp-name` and :ref:`mul_level_adolc_ode.cpp-name` were changed to mention the limitations because Adolc does not have an :ref:`zdouble@Absolute Zero` . #. The example above were also changed so that AD variable names that indicated the level of AD for the variable. #. :ref:`base_require-name` : The base type requirements were modified to include mention of :ref:`absolute zero` . In addition, the base type requirements :ref:`base_require@API Warning` is now more informative. 05-11 ===== Reorganize the :ref:`unary_standard_math-name` documentation. 05-10 ===== #. Add the exponential minus one function :ref:`log1p-name` . #. :ref:`base_require-name` : If you are defining your own base type, note that ``log1p`` function was added to the base type requirements. #. Use the single preprocessor flag ``CPPAD_USE_CPLUSPLUS_2011`` to signal that the functions asinh, acosh, atanh, erf, erfc, expm1, log1p are part of the base type requirements. 05-09 ===== #. Add the exponential minus one function :ref:`expm1-name` . If you are defining your own base type, note that expm1 was added to the base type requirements. #. Fix some warnings about comparing signed and unsigned integers when using :ref:`cppad_testvector@eigen` for the CppAD test vector. (The eigen vector ``size()`` function returns an ``int`` instead of a ``size_t`` .) 05-08 ===== #. Add the inverse hyperbolic sine function :ref:`atanh-name` . If you are defining your own base type, note that atanh was added to the base type requirements. #. Fix a bug in the implementation of the ``acosh`` multiple direction forward mode :ref:`forward_dir-name` . 05-07 ===== Add the inverse hyperbolic sine function :ref:`acosh-name` . If you are defining your own base type, note that acosh was added to the base type requirements. 05-05 ===== Add the inverse hyperbolic sine function :ref:`asinh-name` . If you are defining your own base type, note that asinh was added to the base type requirements. 04-18 ===== In the sparse jacobian and sparse hessian calculations, If *work* is present, and has already been computed, the sparsity pattern *p* is not used. This has been added to the documentation; see :ref:`sparse jacobian` and :ref:`sparse hessian` documentation for *work* and *p* . 03-13 ===== Remove the syntax ``AD`` < *Base* > *y* = *x* for the :ref:`AD constructor` documentation because it does not work when the constructor is :ref:`ad_ctor@x@explicit` . Also document the restriction that the constructor in the :ref:`assignment` must be implicit. 03-06 ===== The developers of the `TMB `_ package reported that for large :ref:`ADFun-name` tapes, the :ref:`optimize-name` routine uses a large amount of memory because it allocates a standard set for each variable on the tape. These sets are only necessary for variables in :ref:`conditional expressions` that can be skipped once the independent variables have a set value. The problem has been reduced by using a NULL pointer for the empty set and similar changes. It still needs more work. 02-28 ===== It used to be the case that the :ref:`Reverse mode` would propagate :ref:`nan-name` through the :ref:`conditional expression` case that is not used. For example, if :: Independent(ax); AD aeps = 1e-10; ay[0] = CondExpGt( ax[0], aeps, 1.0/ax[0], 1.0/aeps ); ADFun f(ax, ay); The corresponding reverse mode calculation, at ``x[0] = 0.0`` , would result in :: Error detected by false result for ! ( hasnan(value) && check_for_nan_ ) This has been fixed so that only the conditional expression case that is used affects the reverse mode results. The example :ref:`cond_exp.cpp-name` was changed to reflect this (a check for ``nan`` was changed to a check for zero). Note that this fix only works when :ref:`base_identical@Identical@IdenticalCon` is true for the base type of the result in the conditional expression; e.g., one can still get a ``nan`` effect from the case that is not selected when using ``AD< AD >`` conditional expressions. 02-18 ===== If the compiler supports the c++11 feature ``std::chrono:high_resolution_clock`` then use it for the :ref:`elapsed_seconds-name` function. 02-16 ===== The new example :ref:`sub_sparse_hes.cpp-name` shows one way to compute a Hessian for a subset of variables without having to compute the sparsity pattern for the entire functions. 02-14 ===== Fix another bug in the derivative calculations for the c++11 version of the error function. 02-11 ===== Fix a bug in the optimization of conditional expressions. To be specific, if :ref:`faq@Speed@NDEBUG` is not defined, one could get an assert with the message: :: Error detected by false result for var_index_ >= NumRes(op_) 02-10 ===== The change on :ref:`2014-12-23<2014@mm-dd@12-23>` introduced a bug when the c++11 version of the error function was used with an :ref:`optimized` function. There was also a bug in the sparsity calculations for when this erf function was included. These bugs have been fixed. 02-09 ===== The test ``test_more/optimize.cpp`` was failing on some systems because an exactly equality check should have been a near equal check. This has been fixed. 02-07 ===== On some systems, the library corresponding to ``speed/src`` could not be found. This library is only used for testing and so has been changed to always be static (hence does not need to be found at run time). 02-06 ===== There was a bug in the coloring method change on :ref:`2015-01-07<2015@mm-dd@01-07>` . To be specific, *work* . ``color_method`` was not being set to ``"cppad.symmetric"`` after *work* . ``color_method.clear`` () . This has been fixed. 02-04 ===== #. Enable the same install of CppAD to be used both with and without C++11 features; e.g., with both ``g++ --std=c++11`` and with ``g++ --std=c++98`` . Previously if the :ref:`cmake@cppad_cxx_flags` specified C++11, then it could only be used in that way. #. The :ref:`cmake@CMake Command` now requires the version of ``cmake`` to be greater than or equal 2.8 (due a bug in ``cmake`` version 2.6). 02-03 ===== Improved the searching for the boost multi-threading library which is used for by the :ref:`team_bthread.cpp-name` case of the :ref:`thread_test.cpp-name` example and test. 02-02 ===== Improve the documentation for the :ref:`cmake@CMake Command` line options ``cmake_install_`` *dir* for *dir* equal to ``prefix`` , ``postfix`` , ``includedirs`` , ``libdirs`` , ``datadir`` , and ``docdir`` . 01-30 ===== Fix bug in :ref:`link_sparse_hessian-name` speed test introduced on :ref:`2015@mm-dd@01-09` below. 01-29 ===== Fix some warnings generated by ``g++ 4.9.2`` . 01-26 ===== The change of global variables to local in ``cppad/local/op_code.hpp`` on :ref:`2014-50-14<2014@mm-dd@05-14>` created a bug in :ref:`parallel_ad-name` (some local statics needed to be initialized). This has been fixed. 01-23 ===== There was a bug in the :ref:`cmake-name` install detection of compiler features. One symptom of this bug was that on systems that had the ``gettimeofday`` function, the cmake install would sometimes report ``cppad_has_gettimeofday`` = 0 This has been fixed. 01-21 ===== The deprecated :ref:`configure-name` procedure had a bug in the detection of when the size of an ``unsigned int`` was the same as the size of a ``size_t`` . This has been fixed. 01-20 ===== #. The new :ref:`compare_change-name` interface has been created and the old :ref:`CompareChange-name` function has been deprecated; see the :ref:`compare_change.cpp-name` example. This enables one to determine the source code during taping that corresponds to changes in the comparisons during :ref:`zero order forward` operations; see :ref:`Independent@abort_op_index` . #. This new :ref:`compare_change-name` interface can detect comparison changes even if :ref:`Faq@Speed@NDEBUG` is defined and even if :ref:`f.optimize()` has been called. The deprecated function ``CompareChange`` used to always return zero after *f* . ``optimize`` () and was not even defined when ``NDEBUG`` was defined. There was a resulting speed effect for this; see :ref:`optimize@options@no_compare_op` . #. The date when some features where deprecated has been added to the documentation. For example, see :ref:`include_deprecated@Deprecated 2006-12-17` . 01-09 ===== #. The change 01-07 below included (but did not mention) using a sparse, instead of full, structure for the Hessian in the test. This has also been done for the :ref:`sparse Jacobian` test. #. For both the :ref:`sparse_jacobian` and :ref:`sparse_hessian` tests, the sparse function is only chosen once (it used to be different for every repeat). This reduced the amount of computation not connected what is being tested. It also make the :ref:`speed_main@Global Options@onetape` a valid option for these tests. #. There was a bug in the :ref:`multiple direction forward` routine. Results for function values that are :ref:`con_dyn_var@Parameter` were not being computed properly (all the derivatives are zero in this case). This has been fixed. 01-07 ===== The following changes were merged in from the ``color_hes`` branch: #. Specify the type of :ref:`coloring` for the sparse hessian calculations. To be specific, instead of ``"cppad"`` and ``"colpack"`` , the choices are ``"cppad.symmetric"`` , ``"cppad.general"`` , and ``"colpack.star"`` . This is not compatible with the change on :ref:`2015@mm-dd@01-02` , which was so recent that this should not be a problem. #. The :ref:`link_sparse_hessian@n_color` values were not being returned properly by :ref:`cppad_sparse_hessian.cpp-name` and :ref:`adolc_sparse_hessian.cpp-name` . The CppAD version has been fixed and the ADOL-C version has been set to zero. #. The :ref:`link_sparse_hessian-name` example case was to sparse for good testing (by mistake). This has been fixed. #. Add *n_sweep* (now *n_color* ) to :ref:`link_sparse_hessian` and :ref:`speed_main` . #. Change the ``cppad`` sparse Hessian :ref:`sparse_hessian@work@color_method` to take advantage of the symmetry of the Hessian (in a similar fashion to the ``colpack`` coloring method). 01-02 ===== Added to option to uses :ref:`colpack` for the sparse Hessian :ref:`coloring method` ; see the example :ref:`colpack_hes.cpp-name` . {xrst_end 2015} ================================================ FILE: appendix/whats_new/2016.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2016 app} {xrst_spell adouble autotools chkpoint chrono colpack makefile mingw valgrind wmisleading xam } Release Notes for 2016 ###################### mm-dd ***** 12-23 ===== Added a way for the user to determine what tests options are available; see :ref:`cmake@make check` . 12-20 ===== Change the optimize :ref:`optimize@Examples` to use :ref:`NearEqual-name` for floating point tests (instead of exactly equal). There were some other exactly equal floating point tests that were failing on a ``mingw`` system. Theses have also been fixed. 12-18 ===== Add the :ref:`optimize@options@no_print_for_op` to the optimize routine. 12-13 ===== #. Fix a bug in :ref:`ForSparseHes-name` . To be more specific, there was a bug in handling the cumulative summations operator in this routine. This could only come up when used an :ref:`optimized` :ref:`ForSparseHes@f` , #. Add the :ref:`nest_conditional.cpp` example. 12-11 ===== Improve the :ref:`optimize-name` documentation. This includes making examples that demonstrate specific aspects of the optimization; see :ref:`forward_active.cpp` , :ref:`reverse_active.cpp` , :ref:`compare_op.cpp` , :ref:`conditional_skip.cpp` , :ref:`cumulative_sum.cpp` . 12-09 ===== The :ref:`optimize@options` argument was added to the optimize routine. 11-18 ===== Move classes and functions that are part of the user API from the ``cppad/local`` directory to the ``cppad/core`` directory. The remaining symbols, in the ``cppad/local`` directory, are now in the ``CppAD::local`` namespace. Note that a class in the ``CppAD`` name space, may have a member function that is not part of the user API. 11-14 ===== Increase the speed of the ``sparse_pack`` class. This improves the speed for :ref:`vector of boolean` sparsity pattern calculations. 11-13 ===== Merged in the ``sparse`` branch which has ``const_iterator`` , instead of ``next_element`` for the ``sparse_list`` and ``sparse_pack`` classes. These classes are not part of the CppAD API and hence their specifications can change (as in this case). They can be used to get more efficient representations of :ref:`sparsity patterns` . 10-27 ===== The optional :ref:`chkpoint_one@optimize` option was added to the checkpoint functions. 10-12 ===== #. Change :ref:`elapsed_seconds-name` to use ``std::chrono::steady_clock`` instead of ``std::chrono::high_resolution_clock`` . #. The test for C++11 features was failing on a Mac system because the elapsed time was returning as zero (between two events). This test has been made more robust by add a one millisecond sleep between the two clock accesses. 09-29 ===== The multiple directions version of :ref:`forward` was missing :ref:`erf-name` function in the case where C++ 2011 was supported; see `issue 16 `_. This has been fixed. 09-27 ===== Change the implementation of :ref:`atomic_two_eigen_cholesky.hpp-name` so that the computation of :math:`M_k` exactly agrees with the corresponding :ref:`theory` . 09-26 ===== #. A possible bug in the :ref:`optimize-name` command was fixed. To be specific, a warning of the form indentations; ``warning: this`` ``'if'`` ``clause does not guard...`` [ ``-Wmisleading-indentation`` ] using the gcc-6.2.1 compiler, was fixed and it may have fixed a bug. #. There was a problem with the :ref:`sacado` where the symbol ``HAS_C99_TR1_CMATH`` was being defined twice. This has been fixed by leaving it up to the sacado install to determine if this symbol should be defined. 09-16 ===== Fix a problem using the :ref:`speed_main@Sparsity Options@colpack` option to the ``speed_cppad`` program. (There was a problem whereby the ``speed_cppad`` program did not properly detect when ``colpack`` was available.) 09-13 ===== Test third order and fix bug in :ref:`atomic_two_eigen_cholesky.hpp-name` for orders greater than or equal to three. 08-30 ===== Add the :ref:`atomic_two_eigen_cholesky.cpp-name` example. 08-25 ===== #. Fix some missing include files in ``optimize.hpp`` and ``set_union.hpp`` (when compiling with MS Visual Studio 2015). #. Fix a warning in ``atanh.hpp`` (when compiling with MS Visual Studio 14). #. Fix a typo in the :ref:`atomic_two_eigen_mat_inv.hpp@Theory@Reverse` section of the ``eigen_mat_inv.hpp`` example. 07-17 ===== Add documentation for only needing to compute a :ref:`sparse_hessian@p@Column Subset` of the sparsity pattern when computing a subset of a sparse Hessians. In addition, improve the corresponding example :ref:`sparse_sub_hes.cpp-name` . 07-14 ===== Correct title in :ref:`ForSparseHes-name` (change Reverse to Forward). 06-30 ===== Change the ``atomic_two_mat_mul.cpp`` example so that on atomic object works for matrices of any size. (This has since been changed to the :ref:`atomic_three_mat_mul.cpp-name` example.) 06-29 ===== Change the :ref:`atomic_two-name` examples so they do no longer use the deprecated interfaces to ``for_sparse_jac`` , ``rev_sparse_jac`` , ``for_sparse_hes`` , and ``rev_sparse_hes`` . 06-27 ===== #. Improve the :ref:`atomic_two_eigen_mat_mul.hpp-name` and :ref:`atomic_two_eigen_mat_inv.hpp-name` examples. Most importantly, one atomic object now works for matrices of any size. #. Add the vector *x* , that contains the parameters in an atomic function call to the user following atomic functions: :ref:`for_sparse_jac` , :ref:`rev_sparse_jac` , :ref:`for_sparse_hes` , :ref:`rev_sparse_hes` . This enables one to pass parameter information to these functions; e.g., the dimensions of matrices that the function operates on. 06-25 ===== Add more entries to the optimization :ref:`wish_list` . 06-10 ===== Add a :ref:`wish_list@check_finite` wish list item. 05-05 ===== #. Add documentation for redirecting output using:ref:`PrintFor@s` in the ``PrintFor`` function. #. Change distributed version to build examples as debug instead of release version. (Was changed to release version while checking for compiler warnings; see ``04-17`` below). 04-17 ===== Fix all some compiler warnings that occurred when compiling the :ref:`examples` with :ref:`faq@Speed@NDEBUG` defined. 03-27 ===== #. Fix a bug in the calculation of the :ref:`atomic_two_eigen_mat_inv.hpp-name` :ref:`atomic_two_eigen_mat_inv.hpp@Private@reverse` example. #. Use a very simple method (that over estimates variables) for calculating :ref:`atomic_two_forward@vy` in the :ref:`atomic_two_eigen_mat_inv.hpp-name` :ref:`atomic_two_eigen_mat_inv.hpp@Private@forward` example. 03-26 ===== #. Implement and test the :ref:`atomic_two_eigen_mat_inv.cpp-name` :ref:`atomic_two_eigen_mat_inv.hpp@Private@reverse` is implemented. #. Fix a bug in the calculation of :ref:`atomic_two_forward@vy` in the :ref:`atomic_two_eigen_mat_inv.hpp-name` :ref:`atomic_two_eigen_mat_inv.hpp@Private@forward` example. 03-25 ===== #. Start construction of the :ref:`atomic_two_eigen_mat_inv.cpp-name` example, currently only :ref:`atomic_two_eigen_mat_inv.hpp@Private@forward` is implemented and tested. #. More improvements to :ref:`atomic_two_eigen_mat_mul.cpp-name` example. 03-24 ===== #. Fix build of ``example/atomic.cpp`` when :ref:`eigen-name` is not available (bug introduced when :ref:`atomic_two_eigen_mat_mul.cpp-name` was added). #. Extend :ref:`atomic_two_eigen_mat_mul.cpp-name` example to include :ref:`atomic_two_eigen_mat_mul.hpp@Private@for_sparse_jac` , :ref:`atomic_two_eigen_mat_mul.hpp@Private@rev_sparse_jac` , :ref:`atomic_two_eigen_mat_mul.hpp@Private@for_sparse_hes` , :ref:`atomic_two_eigen_mat_mul.hpp@Private@rev_sparse_hes` . #. Fix a bug in the :ref:`ForSparseHes-name` routine. #. Edit :ref:`atomic_two_rev_sparse_hes-name` documentation. 03-23 ===== #. Fix bug in autotools file ``example/atomic/makefile.am`` (introduced on 03-22). #. Improve the :ref:`atomic_two_eigen_mat_mul.cpp-name` example and extend it to include reverse mode. 03-22 ===== #. Start construction of the :ref:`atomic_two_eigen_mat_mul.cpp-name` example. #. change ``atomic_ode.cpp`` to ``chkpoint_one_ode.cpp`` and ``atomic_extended_ode.cpp`` to ``chkpoint_one_extended_ode.cpp`` . 03-21 ===== Change the :ref:`atomic_three_mat_mul.hpp-name` class name from ``mat_mul`` to ``atomic_mat_mul`` . This example use of the name ``mat_mul`` in the ``atomic_two_mat_mul.cpp`` example / test. (This has since been changed to the :ref:`atomic_three_mat_mul.cpp-name` example.) 03-20 ===== #. Include the sub-directory name to the include guards in * . ``hpp`` files. For example, :: # ifndef CPPAD_UTILITY_VECTOR_HPP # define CPPAD_UTILITY_VECTOR_HPP appears in the file ``cppad/utility/vector.hpp`` . This makes it easier to avoid conflicts when choosing :ref:`addon-name` names. #. Add the :ref:`set_union-name` utility and use it to simplify the :ref:`atomic-name` examples that use :ref:`glossary@Sparsity Pattern@Vector of Sets` sparsity patterns. 03-19 ===== #. Move ``atomic_two_mat_mul.cpp`` to ``atomic_mat_mul_xam.cpp`` (moved back on :ref:`2016@mm-dd@03-21` . #. Move ``atomic_matrix_mul.hpp`` to :ref:`atomic_three_mat_mul.hpp-name` . 03-17 ===== Add the ``atomic_ode.cpp`` and ``atomic_extended_ode.cpp`` examples. 03-12 ===== #. Move the example ``reverse_any.cpp`` to :ref:`rev_checkpoint.cpp-name` . #. Add the ``chkpoint_one_mul_level.cpp`` example. 03-05 ===== The following atomic function examples were added These examples are for a specific atomic operation. In addition, the domain and range dimensions for these examples are not one and not equal to each other: ``atomic_two_forward.cpp`` , ``atomic_two_reverse.cpp`` , ``atomic_two_for_sparse_jac.cpp`` , ``atomic_two_rev_sparse_jac.cpp`` , ``atomic_two_for_sparse_hes.cpp`` , ``atomic_two_rev_sparse_hes.cpp`` . 03-01 ===== #. Improve documentation of implementation requirements for the atomic :ref:`rev_sparse_jac` . #. Make some corrections to the :ref:`atomic_two_for_sparse_hes-name` documentation. and fix a bug in how CppAD used these functions. 02-29 ===== #. Merged sparse into master branch. This makes the :ref:`ForSparseHes-name` routine available for use. #. Changed the :ref:`speed_main@Global Options` in the speed test main program to use one global variable with prototype {xrst_spell_off} {xrst_code cpp} extern std::map global_option; {xrst_code} {xrst_spell_on} 02-28 ===== Fix a mistake in the old atomic ``example/sparsity/sparsity.cpp`` example. This example has since been changed to ``atomic_two_set_sparsity.cpp`` . 02-27 ===== The ``--with-sparse_set`` and ``--with-sparse_set`` options were removed from the :ref:`configure-name` install procedure. 02-26 ===== The condition that the operation sequence in *f* is :ref:`glossary@Operation@Independent` of the independent variables was added to the statement about the validity of the sparsity patterns; see *x* in :ref:`ForSparseJac` , :ref:`RevSparseJac` , and :ref:`RevSparseHes` . 02-25 ===== The :ref:`cmake-name` command line argument ``cppad_sparse_list`` has been removed (because it is so much better than the other option). 02-23 ===== A new version of the ``cppad_sparse_list`` class (not part of user API) uses reference counters to reduce the number of copies of sets that are equal. This improved the speed of sparsity pattern computations that use the :ref:`glossary@Sparsity Pattern@Vector of Sets` representation. For example, the results for the :ref:`cppad_sparse_hessian.cpp-name` test compare as follows: :: sparse_hessian_size = [ 100, 400, 900, 1600, 2500 ] sparse_hessian_rate_old = [ 1480, 265.21, 93.33, 41.93, 0.86 ] sparse_hessian_rate_new = [ 1328, 241.61, 92.99, 40.51, 3.80 ] Note that the improvement is only for large problems. In fact, for large problems, preliminary testing indicates that the new vector of sets representation preforms better than the :ref:`vector of boolean` representation. 01-21 ===== Fix a ``valgrind`` warning about use of uninitialized memory in the test ``test_more/chkpoint_one.cpp`` (the problem was in the test). 01-20 ===== #. Fix a ``valgrind`` warning about use of uninitialized memory when using the :ref:`adouble` base type. This required an optional :ref:`base_hash-name` function and the special :ref:`adouble hash_code` implementation. #. The ``adouble`` :ref:`to_string-name` functions required a special implementation; see :ref:`adouble to_string` . #. Add the :ref:`base_alloc.hpp@to_string` and :ref:`base_alloc.hpp@hash_code` examples to the ``base_alloc.hpp`` example. 01-18 ===== #. Fix ambiguity between ``CppAD::sin`` and ``std::sin`` , and other standard math functions, when using :: using namespace std; using namespace CppAD; This is OK for simple programs, but not generally recommended. See ``double`` version of base class definitions for :ref:`base_double.hpp@Unary Standard Math` for more details. #. Change Eigen array example :ref:`eigen_array.cpp-name` to use member function version of ``sin`` function (as per Eigen's array class documentation). {xrst_end 2016} ================================================ FILE: appendix/whats_new/2017.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2017 app} {xrst_spell autotools deallocations doxydoc doxygen readme subgraphs typedef uninitialised valgrind wconversion xq } Release Notes for 2017 ###################### mm-dd ***** API Changes =========== Speed tests no longer automatically compile in release mode; see :ref:`speed@debug_which` 12-14 ===== Add the :ref:`subgraph_hes2jac.cpp-name` example which computes sparse Hessians using subgraphs and Jacobians. 12-08 ===== A multi-threading wish list item was added. 12-06 ===== A :ref:`wish_list-name` item to enable one to iterate through a ``const`` :ref:`ADFun-name` operation sequence was completed. In addition, the :ref:`abs_normal_fun@f` argument to the ``abs_normal`` operation was converted to be ``const`` . 12-05 ===== The internal data object used to represent sparsity patterns as vectors of integers was improved; see :ref:`for_jac_sparsity@internal_bool` in ``for_jac_sparsity`` and other :ref:`sparsity_pattern@Preferred Sparsity Pattern Calculations` . 12-04 ===== Back out the ``hold_reverse_memory`` option. 12-01 ===== The ``hold_reverse_memory`` option was added. 11-30 ===== Edit the :ref:`download-name` instructions. 11-23 ===== The ``ADFun`` function :ref:`optimizer` was not handling hash code collisions properly. To be specific, only the arguments that were variables where checked for a complete match. The arguments that are constants need to also be checked. This has been fixed. 11-20 ===== #. Add the :ref:`subgraph_jac_rev-name` method for computing sparse Jacobians. #. Add the :ref:`speed_main@Global Options@subgraph` option to the CppAD speed tests. 11-19 ===== Add the :ref:`subgraph_reverse-name` method for computing sparse derivatives. This was inspired by the TMB package. 11-15 ===== #. Add wish list item for :ref:`subgraph sparsity` . #. Fix :ref:`cmake_check-name` when :ref:`cmake@include_ipopt` is not present on the cmake command line (make was trying to build some of the ipopt tests). 11-13 ===== #. Add the :ref:`speed_main@Global Options@hes2jac` option to the CppAD speed tests. #. Implement the :ref:`speed_main@Sparsity Options@subsparsity` option for the CppAD :ref:`sparse_hessian` test. #. Fix detection of invalid options in the speed test programs; see the :ref:`global` and :ref:`sparsity` options. 11-12 ===== Add the :ref:`speed_main@Sparsity Options@subsparsity` option to the CppAD speed tests. 11-08 ===== Add the :ref:`subgraph_sparsity-name` method for computing dependency and sparsity. This was inspired by the `TMB `_ package. 11-06 ===== More information has been added to the operation sequence. To be specific, the extra amount of ``f`` . *size_op* () * *sizeof* ( ``tape_addr_type`` ) was added to the value returned by :ref:`fun_property@size_op_seq` . 11-04 ===== The method for iterating through the tape has been changed. It now includes an extra data structure that makes it faster, but also requires slightly more memory. To be specific, the term ``f`` . *size_op* () * *sizeof* ( ``tape_addr_type`` ) * 2 was added to the value returned by :ref:`fun_property@size_op_seq` . In addition, some minor corrections were made to the :ref:`tape_addr_type` requirements. 10-23 ===== #. Require ``cmake.3.1`` or greater and fix a cmake warning by always using the new ``CMP0054`` policy. #. Fix a ``g++ 7.2.1`` warning about a possibly uninitialized value in the file ``cppad/local/optimize/hash_code.hpp`` . 09-16 ===== An optimization memory entry was added to the wish list and the :ref:`wish_list@check_finite` entry was modified. 08-30 ===== #. If :ref:`colpack_prefix-name` was not specified, one would get the following warning during the :ref:`cmake-name` command: ``Policy CMP0046 is not set: Error on non-existent dependency in`` This has been fixed by not adding the dependency when it is not needed. #. There was a problem running :ref:`make check` when :ref:`cmake@cppad_cxx_flags` was not specified. This has been fixed. This was probably introduced on :ref:`2017@mm-dd@05-29` . 08-29 ===== There was a problem on some systems that created an error when specializing the ``is_pod`` template function in the ``CppAD::local`` namespace. This has been fixed by testing for compatibility at during the :ref:`cmake-name` command and creating the file ``cppad/local/is_pod.hpp`` . 08-09 ===== Add the :ref:`wish_list@test_boolofvoid` wish list item. 08-08 ===== #. The :ref:`eigen_plugin.hpp-name` was put back in the :ref:`cppad_eigen.hpp-name` definitions. This makes CppAD incompatible with older versions of eigen; e.g., eigen-3.2.9. The plugin was removed on :ref:`2017@mm-dd@05-12` . #. Fix some minor typos in the documentation. To be specific: The font, in the :ref:`sparse_rc-name` and :ref:`sparse_rcv-name` syntax, for the text *target* = *pattern* The font, in :ref:`capacity_order-name` , for the text *xq* . ``size`` () == *f* . ``Domain`` () Remove a percent sign ``%`` , in :ref:`CppAD_vector-name` , in the text # ``include `` 07-25 ===== #. Fix warnings related to type conversions that occurred when one used ``-Wconversion`` with ``g++`` version 6.3.1. #. The warning were not fixed for complex AD types; e.g., :ref:`complex_poly.cpp-name` . The :ref:`wno_conversion-name` include file was added to deal with cases like this. 07-03 ===== #. The :ref:`min_nso_linear-name` abs-normal example was added. #. Fix bug in :ref:`abs_normal_fun-name` , to be specific, the multiplication of a variable on the left by a parameter was not handled. 07-01 ===== the :ref:`abs_normal` examples were converted from using quadratic programming problems to using linear programming problems. 06-28 ===== The :ref:`abs-normal` representation of non-smooth functions has been added. Examples and utilities that use this representation have also been included, see :ref:`abs_normal` . 06-11 ===== The atomic functions base class :ref:`atomic_two-name` makes more of an effort to avoid false sharing cache misses. This may the speed of multi-threaded applications with atomic functions; e.g., see :ref:`multi_atomic_two.cpp-name` . 06-10 ===== #. Add the multi-threading atomic function example :ref:`multi_atomic_two.cpp-name` . #. The ``example/multi_thread/test_multi`` directory used to have an example using the deprecated :ref:`atomic_one-name` functions in a multi-threading setting (that only built with the deprecated :ref:`configure-name` ). This have been removed. 06-07 ===== The multi-threading examples :ref:`harmonic.cpp-name` and :ref:`multi_newton.cpp-name` were re-organized. To be specific, the source code for each example was moved to one file. In addition, for each example, the documentation for each of the routines has been separated and placed next to its source code. 06-04 ===== Most all the :ref:`deprecated-name` features have been removed from the examples with the exception of those in the ``example/deprecated`` directory. 06-03 ===== Add the fact that the pair ( *row* , :ref:`link_sparse_hessian@col` ) is lower triangular to the speed test ``link_sparse_hessian`` routine. 06-01 ===== #. There was a bug in the :ref:`sparse_hes-name` routine and it was using the general coloring algorithm when :ref:`sparse_hes@coloring@cppad.symmetric` was specified. This has been fixed and improves the efficiency in this case. #. Some bugs were fixed in the use of :ref:`colpack` as the coloring algorithm for sparse Jacobian and Hessian calculations. This has improved the efficiency of Colpack colorings for computing Hessians (when ``colpack.symmetric`` is used). #. The ``colpack.star`` coloring method for sparse Hessians has been deprecated; see :ref:`sparse_hes` and :ref:`sparse_hessian` . Use the ``colpack.symmetric`` method instead; see :ref:`sparse_hes` and :ref:`sparse_hes` . 05-29 ===== #. Add the capability to compile so that CppAD debug and release mode can be mixed using ``CPPAD_DEBUG_AND_RELEASE`` . #. Add the :ref:`cmake@cppad_debug_which` flags that determines which files are compiled for debugging versus release during the CppAD testing; see :ref:`cmake_check-name` . #. There was a problem linking the proper libraries for using newer versions of :ref:`sacado` . This has been fixed. 05-19 ===== Most all the examples have been moved to ``example`` directory and grouped as sub-directories; e.g., the :ref:`ipopt_solve-name` examples are in the ``example/ipopt_solve`` directory. 05-14 ===== #. The file ``build.sh`` was moved to ``bin/autotools.sh`` , and `auto tools' has been changed to :ref:`configure-name` . #. The README file was replace by readme.md and AUTHORS was moved to authors. #. The NEWS, INSTALL, and ChangeLog files are no longer necessary for autotools build and have been removed. #. The file test_more/sparse_jacobian.cpp generated a warning under some gcc compiler options. This has been fixed. #. Specifications were added so that :ref:`to_string-name` yields exact results for integer types and machine precision for floating point types. #. Some editing was done to the :ref:`configure-name` instructions. 05-12 ===== #. The :ref:`Faq-name` has been updated. #. Remove includes of ``cppad/cppad.hpp`` from the ``include/cppad/speed/`` * . ``hpp`` files. This avoids an incompatibility between sacado and newer versions of eigen, when eigen is used for the :ref:`testvector-name` . #. The :ref:`eigen-name` package changed its requirements for defining Scalar types (some where between eigen-3.2.9 and eigen-3.3.3). The member variable :ref:`digit10` was added to the ``numeric_limits`` to accommodate this change. #. Note that this fix required adding ``digits10`` to the user defined *Base* type :ref:`requirements` ; see :ref:`base_limits-name` . #. In addition, it is no longer necessary to add the typedef ``typedef Scalar value_type`` ; so the file ``cppad/example/eigen_plugin.hpp`` has been removed. (This type definition was previously necessary for eigen vectors to be :ref:`simple vectors` .) 04-08 ===== The :ref:`optimization` , with a large number of :ref:`conditional expressions` , was performing many memory allocations and deallocations. This has been reduced. 04-02 ===== Fix a bug in the optimization of conditional expressions; see, :ref:`optimize@options@no_conditional_skip` . 03-31 ===== Fix some ``valgrind`` errors that occurred while running the CppAD test set. 03-29 ===== The following valgrind error might occur when the optimize skipped setting values that did not affect the dependent variables: ``Conditional jump or move depends on uninitialised value`` ( ``s`` ) This was not a bug, the code has been changed to avoid this error in order to make it easier to use valgrind with CppAD. 03-25 ===== #. The :ref:`sparse_hes-name` function was more efficient if there were more entries in each row of the requested :ref:`sparse_hes@subset` . This has been changed to more entries in each column, and documentation to this effect was included. #. The :ref:`optimize-name` routine was using to much memory when it was optimizing conditional skip operations; see :ref:`optimize@options@no_conditional_skip` . This has been fixed. 03-20 ===== There was a mistake in :ref:`sparse_jac-name` that caused the following assert to mistakenly occur: :: sparse_jac_rev: work is non-empty and conditions have changed Error detected by false result for color.size() == 0 || color.size() == n A test that using a previously stores work vector has been added to :ref:`sparse_jac_rev.cpp-name` and this bug has been fixed. 03-13 ===== The documentation for the Hessian in :ref:`rev_hes_sparsity-name` was transposed; i.e., the sense of :ref:`rev_hes_sparsity@transpose` was reversed. 03-11 ===== Add sparse assignment statements; see *other* for :ref:`sparse_rc` and :ref:`sparse_rcv` . 03-10 ===== Add the a sizing constructor to the :ref:`sparse_rc syntax` ; i.e., a constructor that sets the number of row, number of columns, and number of possibly non-zero values in the sparsity pattern. 03-06 ===== Fix a bug in the sparsity computation using the internal representation for :ref:`vectors of sets` ; i.e., when *internal_bool* was false in any of the :ref:`sparsity_pattern-name` calculations; e.g., :ref:`for_jac_sparsity` . 03-04 ===== Fix a bug in the optimization of conditional expressions; see :ref:`optimize@options@no_conditional_skip` . 02-26 ===== #. Fix warning during :ref:`cmake-name` command, on `cygwin `_ systems, about ``WIN32`` not being defined. #. Add element-wise atomic operations to the wish list. This was completed by the :ref:`atomic_four_vector-name` example. 02-21 ===== #. Minor improvements to syntax and documentation for :ref:`sparse_rc-name` and :ref:`sparse_rcv-name` . #. Separate preferred sparsity versions in :ref:`sparsity_pattern-name` and :ref:`sparse_derivative-name` . 02-19 ===== #. Remove the ``bool_sparsity.cpp`` example and add the :ref:`rc_sparsity.cpp-name` example. #. Check for duplicate entries during :ref:`sparse_rc@row_major` and ``col_major`` in ``sparse_rc`` . 02-15 ===== Fix bug when using :ref:`ForSparseHes-name` with atomic functions; i.e., :ref:`atomic_two_for_sparse_hes-name` . 02-13 ===== Improve :ref:`atomic_two_eigen_mat_mul.hpp@Private@for_sparse_jac` calculation in ``eigen_mat_mul.hpp`` example. It now checks for the parameter zero and does not propagate any sparsity in this case (because the result is always zero). 02-11 ===== #. Remove the 'Under Construction' heading from the :ref:`sparse_rc-name` and :ref:`sparse_rcv-name` documentation; i.e., they are ready for public use (part of the CppAD API). #. Fix some warning that occur when using :ref:`cppad_testvector@eigen` for the CppAD test vector. (The Eigen vector ``size()`` function returns an ``int`` instead of ``size_t`` .) #. Fix a bug in :ref:`sparse_jac_rev` . 02-10 ===== #. The subset of deprecated features corresponding to the cmake command option ``cppad_deprecated=YES`` have been completely removed. #. Fix problems with :ref:`configure-name` build (started near 02-01 while working on sparsity branch). #. Reorder (better organize) the :ref:`ADFun-name` documentation section. 02-09 ===== #. Remove the sparsity pattern wish list item. For sparsity patterns, this was completed by :ref:`sparse_rc-name` and the sparsity pattern routines that used it; e.g., :ref:`for_jac_sparsity-name` . For sparse matrices, it was completed by :ref:`sparse_rcv-name` and the sparse matrix routines that use it; e.g., :ref:`sparse_jac-name` . #. Add the Deprecated and :ref:`wish_list@Example` items to the wish list. (The Deprecated item was partially completed and partially removed.) 02-08 ===== #. Make coloring a separate argument to :ref:`sparse_jac` and :ref:`sparse_hes` . #. Add the :ref:`sparse_jac@group_max` argument to the ``sparse_jac_for`` function. 02-05 ===== #. Add the :ref:`sparse_jac_for` routine which uses :ref:`sparse_rc-name` sparsity patterns and :ref:`sparse_rcv-name` matrix subsets. #. Order for :ref:`sparse_rc-name` row-major and column-major was switched. This has been fixed. 02-03 ===== Add the :ref:`rev_jac_sparsity-name` :ref:`rev_hes_sparsity-name` , and :ref:`for_hes_sparsity-name` interfaces to sparsity calculations. These use :ref:`sparse_rc-name` sparsity patterns. 02-02 ===== Change :ref:`for_jac_sparsity@f@size_forward_bool` and Change :ref:`for_jac_sparsity@f@size_forward_set` so that they are a better approximation of the number of bytes (unsigned characters) being used. The exact same sparsity pattern might use different memory in two different function objects (because memory is allocated in chunks). The :ref:`fun_assign.cpp-name` example has been changed to reflect this fact. 02-01 ===== Add the :ref:`for_jac_sparsity-name` interface for the sparse Jacobian calculations. This is the first use of :ref:`sparse_rc-name` , a sparsity pattern class that uses row and column :ref:`index vectors` . 01-30 ===== Move the :ref:`sparsity_pattern-name` examples from ``example`` to ``example/sparse`` subdirectory. This included the sparse :ref:`driver` examples. 01-29 ===== Move the :ref:`utility-name` examples from ``example`` to ``example/utility`` subdirectory. 01-27 ===== Add a :ref:`addon-name` link to `cppad_swig `_ , a C++ AD object library and swig interface to Perl, Octave, and Python. 01-19 ===== Convert more examples / tests to use a multiple of machine epsilon instead of ``1e-10`` . 01-18 ===== #. Fix developer `doxydoc `_ documentation so that it works with newer version of doxygen. #. Fix a Visual C++ 2015 compilation problem in friend declarations in the file ``cppad/local/ad_tape.hpp`` . 01-17 ===== Change computed assignment to :ref:`compound assignment` . {xrst_end 2017} ================================================ FILE: appendix/whats_new/2018.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2018 app} {xrst_spell addons enums eps in in kasper kristensen mingw rcv tmb valgrind wconversion wfloat } Release Notes for 2018 ###################### mm-dd ***** 12-30 ===== On :ref:`2018@mm-dd@12-12` the directory ``cppad`` was moved to ``include/cppad`` . The ``make install`` step of the :ref:`install-name` instructions did not work after that. This has been fixed. 12-23 ===== The documentation for how to run ``multi_atomic_two`` was missing. This has been fixed. 12-17 ===== Keep the documentation for :ref:`deprecated-name` features, but remove their examples. The documentation is intended to help conversion to using features that have not been deprecated. 12-16 ===== Add ``bin/check_tab.sh`` and remove some remaining tab characters that it found in the CppAD source code. 12-15 ===== Fix name for special version of ``cmake`` required by :ref:`cmake@CMake Command@msys2` , ``mingw-w64-x86_64-cmake`` . 12-12 ===== #. Change tabs to spaces in both the source code and the examples. #. The ``inline`` specifiers was removed from template functions and class member functions in both the source code and examples; see the ``inline`` entry in the :ref:`wish_list` . #. In the documentation and examples change the ``Vector`` *Type* to *Type* ``Vector`` for *Type* denoting various types; e.g., ``Bool`` , ``Size`` , ``Double`` , ... #. Change the :ref:`license-name` to be ``EPL-2.0`` with a ``GPL-2.0`` or later alternative (so one version of the source code can be used for both EPL and GPL licenses). #. Change :math:`B^n` to :math:`\B{R}^n` when referring to the vector space that :ref:`ADFun-name` object refer to. (Note that this may not be real vectors; e.g., :ref:`base_complex.hpp-name` .) #. Move the ``cppad`` sub-directory to ``include/cppad`` . #. Add a checkpoint constructor item to the wish list. This has been completed; see the :ref:`chkpoint_two_ctor@fun` argument to the second generation checkpoint constructor. 11-13 ===== #. There was a problem using :ref:`elapsed_seconds-name` with the Microsoft Visual Studio compiler. To be specific, ``microsoft_timer`` could be an undefined external. This has been fixed. #. The ``# define NOMINMAX`` command was placed before ``# include `` so that ``std::min`` and ``std::max`` work properly. (This only matters when using the Visual Studio compiler.) #. For some unknown reason, the Microsoft Visual Studio compiler was giving a senseless error at the variable name ``small`` . This has been changed to ``eps`` in :ref:`interface2c.cpp-name` . #. The deprecated :ref:`cmake@CMake Command@autotools` was fixed to work with the current version of CppAD. #. Fix a warning in ``test_more/cppad_for_tmb/multi_atomic_two.cpp`` when using boost vectors for testing. 11-11 ===== Fix a warning in ``test_more/cppad_for_tmb/multi_chkpoint_one.cpp`` when using boost vectors for testing. 11-06 ===== The initial :ref:`size_order-name` is now zero for an :ref:`base2ad@af` created using the ``base2ad`` feature. 11-04 ===== #. The :ref:`cmake@CMake Command@Simple` version of the cmake command is now suggested as a starting point installing CppAD. #. The windows install procedure has been fixed; see :ref:`cmake@CMake Command@msys2` and :ref:`cmake@CMake Command@Visual Studio` . #. The Visual Studio compiler gave a warning because the ``get_record_compare`` (not in use API) was returning ``size_t`` when it should have returned a ``bool`` . It also gave a warning in ``test_more/general/erf.cpp`` about conversion from ``double`` to ``size_t`` . These have been fixed. #. The Visual Studio compiler also gave an error because it requires the :ref:`ADFun-name` copy constructor for assignment to a :ref:`base2ad-name` result. This copy constructor has been allowed (but only for this case). #. The Visual Studio compiler does not seem to be able to mix debug and release code. One of the tests had to be modified enable the user to avoid this; see :ref:`cmake@CMake Command@Visual Studio` . 11-01 ===== There was a bug in the :ref:`checkpoint` functions with multiple threads (see :ref:`2018@mm-dd@10-29` below). This has been fixed. 10-31 ===== #. The items in the :ref:`wish_list-name` were re-organized so that the New Atomic API entries were all under one heading. #. A credit to Kasper Kristensen was added to the :ref:`subgraph` research item. 10-29 ===== The following is a list of changes to the documentation: #. The :ref:`theory-name` section was moved to a higher level in the documentation and the :ref:`introduction-name` we put in that section. #. The :ref:`wish_list-name` section was brought up to date and the :ref:`research-name` section was added. #. Some of the links to the CppAD :ref:`addons` section were broken. This has been fixed. #. The wish list entries for the following items were removed because they were completed: :ref:`base2ad-name` , :ref:`checkpoint` functions now work with :ref:`multiple threads` , the :ref:`optimize-name` memory usage has been reduced. #. The operations sequence wish list entry was changed to the Constant entry which has since been completed; see :ref:`con_dyn_var@Constant` . The software guidelines entry was changed to just Convert Tabs to Spaces. 10-25 ===== #. There was bug in :ref:`f.optimize` when *f* has :ref:`dynamic parameters` . This has been fixed. #. Fix warning, from old ``gcc`` compilers, that ``-Wfloat-conversion`` and ``-Wfloat-conversion`` are not valid options. #. Instructions for obtaining GPL distribution were added near the top of the :ref:`home page` . 10-24 ===== The new :ref:`user_guide-name` home pages has been edited to be a better introduction to CppAD. In addition, more discussion was added to the :ref:`get_started.cpp-name` example. 10-19 ===== The speed tests for the deprecated :ref:`cppad_ipopt_nlp-name` now use the :ref:`cmake@cppad_debug_which` flag in the same was as other test (not always true). 10-18 ===== CppAD is now including the ``-Wconversion`` in its compilation. This results in warnings in more of the optional packages and so their include files are compiled like system files (without warnings); see :ref:`cmake@package_prefix` . 09-25 ===== Functions *af* , created by :ref:`af = f.base2ad` , could not use :ref:`af.Jacobian` . This has been fixed. 09-23 ===== The :ref:`checkpoint` functions did not work with functions that were created using :ref:`base2ad-name` . This has been fixed. 09-22 ===== There was a bug in the :ref:`checkpoint` function when they were used in :ref:`in parallel mode` . The :ref:`multi_chkpoint_one.cpp-name` example was added to test this and demonstrate use of checkpoints in parallel mode. 09-19 ===== Add the :ref:`base2ad-name` capability. This creates a function that computes using ``AD`` < *Base* > objects from one that computes using *Base* objects. This enables one to record any derivatives computations as part of another function and thereby removes the need for :ref:`multiple levels of AD` . For example, compare :ref:`base2ad.cpp-name` with :ref:`mul_level_ode.cpp-name` . 09-17 ===== Create a section with just the :ref:`atomic_two-name` specifications and another with the :ref:`atomic examples` . 09-16 ===== The ``taylor_ode.cpp`` example was moved to :ref:`taylor_ode.cpp-name` and changed to use the same notation as :ref:`taylor_ode-name` . 09-15 ===== #. Add a :ref:`fun_construct@Assignment Operator@Move Semantics` version of the function assignment operator. #. Move AD theory for using Taylor's method to solve an ode to the separate section :ref:`taylor_ode-name` . 08-27 ===== #. Add a discussion about :ref:`Independent@dynamic@Efficiency` for dynamic parameters. #. In a comment in the :ref:`new_dynamic.cpp-name` example, change 'independent dynamic parameters' to 'dynamic parameter'. #. The :ref:`cppad_eigen.hpp@eigen_vector` class was moved out of the global namespace to the CppAD namespace. This class was recently introduced (:ref:`2018@mm-dd@08-12` ) and so this change to the API should be easy to fix in user code. 08-19 ===== #. Fix dynamic parameter arguments to :ref:`discrete-name` functions. #. Fix some detection of identically zero and one for dynamic parameters. #. Fix an access of invalid memory during :ref:`new_dynamic-name` . #. Add optimization of dynamic parameters during :ref:`f.optimize` . 08-18 ===== #. Fix some remaining clang warnings. #. Fix :ref:`Integer-name` and :ref:`unary minus` for :ref:`glossary@Parameter@Dynamic` parameters. 08-17 ===== Fix :ref:`compound assignment` operators where left operand is a :ref:`glossary@Parameter@Constant` parameter and right operand is a :ref:`glossary@Parameter@Dynamic` parameter. 08-16 ===== Fix :ref:`compound assignment` operators where left operand is a :ref:`glossary@Parameter@Dynamic` parameter. and right operand is a :ref:`glossary@Variable` . 08-14 ===== Fix :ref:`compound assignment` operators where both operands are parameters and one is a :ref:`glossary@Parameter@Dynamic` parameter. 08-13 ===== Fix conversion warnings generated by the version 6.0 of the ``clang++`` compiler. 08-12 ===== Add the :ref:`cppad_eigen.hpp@eigen_vector` wrapper class so that an ``eigen_vector`` is a true :ref:`SimpleVector-name` ; i.e., it using ``size_t`` instead of ``Eigen::Index`` for its sizes and indices. 08-10 ===== Extend the CppAD vector class to enable :ref:`CppAD_vector@Element Access` using any index type that has a conversion to the type ``size_t`` . 08-08 ===== #. Fix a bug in the ``CppAD::local::pod_vector`` template class (not part of the user API) that wasted memory. This bug was part of the :ref:`2018@mm-dd@06-04` change this year. #. Fix very recent bug in the optimization of dynamic parameters in :ref:`PrintFor-name` statements. #. Fix some ``valgrind`` warnings about use of values that have not be initialized or set. #. Suppress printing of :ref:`PrintFor-name` operations during the :ref:`optimize-name` process. 08-05 ===== The amount of memory in an operation sequence has changed; see :ref:`f.size_op_seq` . 08-04 ===== Remove the restrictions on dynamic parameters. They can now be used anywhere except as indices and values in VecAD vectors. (This was fixed on :ref:`2019-01-06<2020@mm-dd@01-06>` .) 07-30 ===== #. The :ref:`con_dyn_var@Constant` function was added. #. :ref:`base_require@API Warning` for uses who define there own *Base* type. The function ``IdenticalPar`` was changed to :ref:`base_identical@Identical@IdenticalCon` and ``IdenticalEqualPar`` was change to ``IdenticalEqualCon`` . This was done because the value of :ref:`glossary@Parameter@Dynamic` parameters can change. #. The some errors in the :ref:`pkgconfig-name` files ``cppad.pc`` and ``cppad-uninstalled.pc`` were corrected. 07-27 ===== We are adding the dependent dynamic parameters; i.e., parameters that depend on the independent :ref:`Independent@dynamic` parameters in a call to Independent. For this reason the function *f* . ``size_dynamic`` () has been replaced by :ref:`fun_property@size_dyn_ind` , :ref:`fun_property@size_dyn_par` , and :ref:`fun_property@size_dyn_arg` . 07-23 ===== Change the required version of :ref:`cmake-name` from greater than or equal 3.1 to greater than or equal 2.8.4. 07-18 ===== Fix a bug (introduced on :ref:`2018@mm-dd@07-02` ) in :ref:`optimize-name` routine when dynamic parameters are present. 07-02 ===== Add a special type of parameter that can change value, see :ref:`new_dynamic@Purpose` for dynamic parameters. 06-27 ===== Simplify the process in the :ref:`optimize-name` procedure where it recognizes identical expressions (and do more error checking of this process). This may speed up the optimization for large cases where multiple expressions have the same hash code. 06-22 ===== Add an :ref:`wish_list@Abs-normal` wish list item. 06-20 ===== Worked more on reducing memory and increasing the speed of the :ref:`optimization` of ``ADFun`` < *Base* > objects. 06-19 ===== Another (smaller) reduction in the amount of extra memory used during the :ref:`optimize-name` process. This time a vector of length :ref:`fun_property@size_op` was converted from the type used for C++ enums to a type that only used one byte. 06-13 ===== Reduce the amount of extra memory used during the :ref:`optimize-name` process. To be more specific, two vectors that were separate now share the same memory. These vectors have size equal to :ref:`fun_property@size_op` for the old operation sequence, and element type :ref:`cmake@cppad_tape_addr_type` . 06-12 ===== There was false sharing in the hash table used to detect identical parameter values during the recording of ``AD`` < *Base* > operations. This has been fixed and should result in a speed up when multiple threads are recording at the same time. 06-04 ===== The memory used during :ref:`subgraph_reverse-name` , :ref:`subgraph_sparsity-name` , and :ref:`optimize-name` was changed to use the smallest addressing type possible for the tape in question. To be specific the smallest type from the set ``unsigned short`` , ``unsigned int`` , ``size_t`` , in that order. 05-29 ===== The :ref:`configure-name` install procedure was not working since 05-26. This has been fixed. 05-28 ===== #. The Coin download page is not longer supporting monthly version. These versions are now supported by the :ref:`download@Git` download procedure. #. The windows install procedures have not been kept up to date and so have been removed. It has since been fixed; see :ref:`cmake@CMake Command@msys2` and :ref:`cmake@CMake Command@Visual Studio` . 05-26 ===== Fix some warnings when testing with ``g++ -std=c++98`` . 05-20 ===== #. The amount of memory used to store each operator was changed from the size of an enum type to the size of an ``unsigned char`` ; i.e., one byte. This changed ``CppAD::local::OpCode`` to ``CPPAD_VEC_ENUM_TYPE`` in the expression used to compute :ref:`size_op_arg` . Note ``CPPAD_VEC_ENUM_TYPE`` is not in CppAD API and may change. #. There was a bug in the call to ``optimize`` for the CppAD :ref:`speed_cppad-name` tests. To be specific ``no_print_for`` was corrected to :ref:`no_prior_for_op` . 05-17 ===== The memory report for the CppAD matrix multiply speed test was changed to report the memory before clearing the atomic functions; see the end of :ref:`cppad_mat_mul.cpp-name` . 05-04 ===== The comparison operations were changed on :ref:`2015-01-20<2015@mm-dd@01-20>` when the :ref:`Independent@abort_op_index` option was added. This change caused a slow down CppAD because the :ref:`compare-name` operators used to not be recorded when ``NDEBUG`` was defined. The :ref:`Independent@record_compare` option has been added so that one can obtain this better performance (when one does not need the :ref:`compare_change-name` function or the :ref:`Independent@abort_op_index` feature). 05-03 ===== The following was merged in from the ``reduce_tape`` branch: A change was made in the way CppAD iterated over the operation sequence when subgraph operations were first added; see the heading :ref:`2017@mm-dd@11-20` in whats new for 2017. This caused CppAD to always use more memory for storing tapes. This has been fixed so this extra memory is only allocated when it is needed. In addition it can be freed; see :ref:`fun_property@size_random` and :ref:`subgraph_reverse@clear_subgraph` . In addition, this changed the amount of memory returned by :ref:`fun_property@size_op_seq` so that it no longer includes the part returned by ``size_random`` . 04-29 ===== Change the :ref:`cppad_sparse_hessian.cpp-name` speed test to support the :ref:`speed_main@Sparsity Options@symmetric` option for sparsity calculations. This was added because, even though the symmetric sparsity patterns are more efficient, they seem to take longer to calculation and hence general sparsity patterns may be faster; see the sparse hessian :ref:`sparse_hessian@work@color_method` . 04-13 ===== The :ref:`speed_cppad-name` tests were modified to report the amount of memory allocated at the end of each tests. This will aid in the reduction of CppAD memory usage. 03-20 ===== Fix bug in :ref:`optimize-name` and :ref:`subgraph_sparsity-name` when using C++11 :ref:`erf-name` function. 03-19 ===== Add more detail to the dynamic parameter wish list item. This item has been implemented and removed from the wish list; see :ref:`new_dynamic-name` . 03-04 ===== The ``example/abs_normal`` directory was missing from the :ref:`example` sub-directory list. This has been fixed. 03-02 ===== Add the :ref:`sparse_rcv@pat` member function to the ``sparse_rcv`` template class. 02-27 ===== Documentation was added to state that the *work* structure for :ref:`sparse_jac` ( :ref:`sparse_hes` ) can be used a different *f* and a different *subset* provided that Jacobian (Hessian) sparsity pattern for *f* and the sparsity pattern in *subset* are the same. 02-26 ===== The routine :ref:`sparse2eigen-name` , added on 02-20, only worked when the values in the matrix were integers. This bug has been fixed. 02-23 ===== The syntax for some of the :ref:`utility-name` files were missing the ``# include`` command (or just the ``#`` in the include command); e.g., see the :ref:`set_union@Syntax` for the ``set_union`` utility. 02-20 ===== Add the utility function :ref:`sparse2eigen-name` . {xrst_end 2018} ================================================ FILE: appendix/whats_new/2019.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2019 app} {xrst_spell autotools bthread cholesky colpack goto inv lepp mmdd omhelp pos subgraphs to to usr versa yy } Release Notes for 2019 ###################### mm-dd ***** 12-27 ===== #. Add the :ref:`graph_op_enum@Print` operator to the :ref:`cpp_ad_graph-name` and :ref:`json_ad_graph-name` ; see :ref:`graph_print_op.cpp-name` , :ref:`json_print_op.cpp-name` . #. Improve the error messaging when a Json :ref:`json_ad_graph@op_define_vec@op_code` in the operator definition section does not start with one and increment by one between definitions. 12-25 ===== #. Change *pos* to :ref:`PrintFor@notpos` in the ``PrintFor`` documentation because the text is printed when it is not positive. #. Add C++ graph member functions that :ref:`cpp_graph_vector@Syntax@Find` the index for an atomic or discrete function. 12-24 ===== #. Add the :ref:`discrete function` operator to the :ref:`json_ad_graph-name` . #. Fix bug when using a dynamic parameter for :ref:`PrintFor@notpos` or :ref:`PrintFor@value` in the ``PrintFor`` routine. 12-23 ===== Add the :ref:`graph_op_enum@Discrete Function` operator to the :ref:`cpp_ad_graph-name` ; see :ref:`graph_discrete_op.cpp-name` . 12-21 ===== Add an :ref:`wish_list@Abs-normal@Atomic Functions` item to the wish list for abs-normal functions. 12-20 ===== Add a :ref:`wish_list@Dynamic Parameters` item and a :ref:`wish_list@Graph Operators` item to the wish list. 12-18 ===== #. The :ref:`graph_atom_op.cpp-name` *Base* type was changed from ``double`` to ``float`` . #. Fix a bug in :ref:`from_graph-name` , :ref:`from_json-name` , :ref:`to_graph-name` , and :ref:`to_json-name` when the :ref:`from_json@Base` type is not ``double`` . 12-16 ===== Add more description in the comments for the :ref:`graph_op_enum values` . 12-15 ===== #. Added a list of the possible :ref:`ADFun-name` operators that are :ref:`missing` (not yet implemented) by :ref:`to_graph-name` and :ref:`to_json-name` . #. Absolute zero multiplication :ref:`json_graph_op@Binary Operators@azmul` was as added to the :ref:`json_ad_graph-name` and :ref:`cpp_ad_graph-name` as ``pow_graph_op`` . Functions that contain the this operator can be converted to and from ``cpp_graph`` objects; e.g., see :ref:`graph_azmul_op.cpp-name` . They can also be converted to and from strings containing :ref:`json_ad_graph-name` representations; e.g., see :ref:`json_azmul_op.cpp-name` . #. Improve the :ref:`graph_op_enum examples` by removing some type definitions and using statements. In addition, use :ref:`cppad_testvector-name` to demonstrate that any :ref:`simplevector-name` type can be used. 12-14 ===== The power operators was added to the :ref:`json_ad_graph-name` as :ref:`json_graph_op@Binary Operators@pow` and to the :ref:`cpp_ad_graph-name` as ``pow_graph_op`` ; see :ref:`graph_op_enum@Enum Values` . Functions that contain the :ref:`pow-name` operators can be converted to and from ``cpp_graph`` objects; e.g., see :ref:`graph_pow_op.cpp-name` . They can also be converted to and from strings containing :ref:`json_ad_graph-name` representations; e.g., see :ref:`json_pow_op.cpp-name` . 12-13 ===== Add :ref:`switch_var_dyn.cpp-name` , an example that switches between variables and dynamic parameters. 12-12 ===== Add a version of the ``from_graph`` routine that can convert dynamic parameters to variables and vice versa; see :ref:`from_graph@dyn2var` and *var2dyn* in the ``from_graph`` documentation. 12-09 ===== Add the ``Independent`` ( *x* , *dynamic* ) syntax to the :ref:`Independent-name` function and changes some of the :ref:`new_dynamic-name` example to use this syntax. 12-08 ===== Add wish list item for converting dynamic parameters to variables and vice versa. This has since been completed; see :ref:`2019@mm-dd@12-12` above. 11-15 ===== Fix clang 9.0.0 mistaken uninitialized warning in :ref:`a11c_openmp.cpp-name` . 11-10 ===== Change *n_independent* to *n_variable_ind* in :ref:`json_ad_graph` and :ref:`cpp_ad_graph` . In addition, the under construction warning was removed from :ref:`json_ad_graph-name` and :ref:`cpp_ad_graph-name` . 11-09 ===== The C++ data structure for an AD graph :ref:`cpp_ad_graph@cpp_graph` was add the user API for CppAD. 11-05 ===== More work on reducing the amount of memory used by the :ref:`from_json-name` and :ref:`to_json-name` operations. 10-28 ===== More work on reducing the amount of memory used by the :ref:`from_json-name` and :ref:`to_json-name` operations. 10-27 ===== #. Working on reducing the amount of memory used by the :ref:`from_json-name` and :ref:`to_json-name` operations. #. Improve sparse jacobian error messaging when coloring is :ref:`sparse_jac@coloring@cppad` and :ref:`sparse_jac@subset` has elements that are not in the sparsity :ref:`sparse_jac@pattern` . 10-22 ===== A left (right) bracket was added to the beginning of (end of) the :ref:`json_ad_graph@constant_vec` , :ref:`json_ad_graph@op_usage_vec` , :ref:`json_ad_graph@dependent_vec` values in a Json AD graph (to properly conform to Json format). 10-21 ===== Move the Json AD graph discussion of :ref:`json_ad_graph@Node Indices` under one heading and include a diagram of all the nodes. 10-17 ===== Include conversion from function to graph (:ref:`to_json-name` ) and back to function (:ref:`from_json-name` ) in the :ref:`json operator` examples. 10-02 ===== The default value for :ref:`cmake@cppad_prefix` was changed from ``/usr`` to the value of the cmake variable `CMAKE_INSTALL_PREFIX `_. 09-30 ===== Use a newer version of the program that generates the documentation ``omhelp`` . The search utility now shows the title and section tag in separate areas and hides the other keywords. Hitting return or double clicking in the title area, in addition to choosing the ``Goto`` button, will go to the selected section. 09-24 ===== Discussion of a :ref:`cmake@CMake Command@Warning` about boost as added to cmake command documentation. 09-23 ===== The ``cppad_deprecated`` option has been completely removed from the cmake command :ref:`cmake@CMake Command@Options` documentation. 09-18 ===== Merge in changes from json branch which implement the :ref:`json_graph_op@Compare Operators` . 09-17 ===== The autotools install was installing * . ``hpp.in`` instead of the corresponding * . ``hpp`` file. This has been fixed. 09-14 ===== Fix autotools install so files get installed to *prefix* / ``include/cppad`` instead of *prefix* / ``include/include/cppad`` . Also fix the autotools ``make test`` . 09-12 ===== Bring :ref:`configure-name` build and test up to date (this was deprecated at end of 2012 and support for this will eventually stop). 09-10 ===== The old Json unary operator examples were converted to be just tests and a single :ref:`json_unary_op.cpp-name` example was left in its place. 09-09 ===== The Json graph :ref:`json_graph_op@Conditional Expressions` were added. 08-29 ===== #. Extend :ref:`to_json-name` to handle :ref:`json_graph_op@Atomic Functions` . This should have been included with the atomic function change on :ref:`2019@mm-dd@08-17` . #. Simplify the :ref:`json_atom_op.cpp-name` and add a call to :ref:`to_json-name` to this example. #. The ``string_vec`` field was removed from the definition of an Json AD graph :ref:`json_ad_graph@AD Graph@function` . The corresponding operators will have strings in their :ref:`op_usage` . #. The vector branch was merged into the master. This brought in the changes below #. 08-25: Change :ref:`cppad_vector.cpp-name` example to use same names as in :ref:`CppAD_vector-name` documentation. #. 08-23: The :ref:`CppAD_vector-name` documentation page was edited (improved). #. 08-22: :ref:`CppAD_vector@Iterators` were added to the CppAD vector class. 08-26 ===== The Json graph :ref:`json_graph_op@Unary Operators` were added. 08-19 ===== The CppAD vector :ref:`CppAD_vector@Include` discussion was edited. In addition, the class :ref:`CppAD_vector@vectorBool` is now available as a separate include file. 08-17 ===== #. Add a the Json addition example :ref:`json_add_op.cpp-name` . #. Add the :ref:`atomic function` operator which enables one to call atomic functions as part of a Json AD graph; see the :ref:`json_atom_op.cpp-name` example. 08-16 ===== Add the case where :ref:`atomic_three_forward@need_y` is equal to ``constant_enum`` during ``atomic_three`` forward mode. This only needs to be implemented when by :ref:`from_json-name` is used with the atomic function. 08-13 ===== Fix a bug in the calculation of Hessian :ref:`sparsity patterns` when using :ref:`chkpoint_two-name` function. 08-12 ===== #. Fix an assert error for the :ref:`json_graph_op@sum` operator in :ref:`from_json-name` . #. Improve the documentation for mixing debug and release versions of CppAD in the same program using ``CPPAD_DEBUG_AND_RELEASE`` . 08-10 ===== #. Remove under construction heading from the examples :ref:`json_mul_op.cpp-name` , :ref:`json_sub_op.cpp-name` , :ref:`json_sum_op.cpp-name` (they have been working for a while now). #. Add the Json :ref:`json_graph_op@Binary Operators@div` operator. 08-09 ===== #. Add :ref:`json_ad_graph@AD Graph@function_name` to the Json graph representation of a function. #. Use the function name for reporting errors in a Json graph. 08-08 ===== #. Move the example include files ``eigen_cholesky.hpp`` , ``eigen_mat_inv.hpp`` , ``eigen_mat_mul.hpp`` from the ``include/cppad/example`` directory to the ``include/cppad/example/atomic_two`` directory. #. Move the example include file ``mat_mul.hpp`` from the ``include/cppad/example`` directory to the ``include/cppad/example/atomic_three`` directory. #. Improve the :ref:`atomic_three_define@Syntax` , and :ref:`atomic_three_define@parameter_x` discussion, in the documentation for the ``atomic_three`` functions. Furthermore, add an extra discussion of the parameters to the documentation for *taylor_x* in the :ref:`forward` and :ref:`reverse` callback functions. #. Change Json *string_vec* from first to second line below: | |tab| [ *n_string* , [ *first_string* , ..., *last_string* ] ] | |tab| *n_string* , [ *first_string* , ..., *last_string* ] ( *string_vec* was removed on :ref:`2019@mm-dd@08-29` ). #. Change Json :ref:`json_ad_graph@constant_vec` from first to second line below: | |tab| [ *n_constant* , [ *first_constant* , ..., *last_constant* ] ] | |tab| *n_constant* , [ *first_constant* , ..., *last_constant* ] #. Change Json :ref:`json_ad_graph@op_usage_vec` from first to second line below: | |tab| [ *n_op_usage* , [ *first_op_usage* , ..., *last_op_usage* ] ] | |tab| *n_op_usage* , [ *first_op_usage* , ..., *last_op_usage* ] #. Change Json :ref:`json_ad_graph@dependent_vec` from first to second line below: | |tab| [ *n_dependent* , [ *first_dependent* , ..., *last_dependent* ] ] | |tab| *n_dependent* , [ *first_dependent* , ..., *last_dependent* ] 08-06 ===== #. Fix a bug in ``chkpoint_two`` when :ref:`chkpoint_two_ctor@use_in_parallel` is true. #. Add the :ref:`multi_atomic_three.cpp-name` and :ref:`multi_chkpoint_two.cpp-name` examples. #. Deprecate the :ref:`multi_atomic_two.cpp-name` and :ref:`multi_chkpoint_one.cpp-name` examples 07-31 ===== Add an example representation of a sparse matrix using the an :ref:`json_ad_graph-name` ; see :ref:`json_sparse.cpp-name` . 07-30 ===== #. Add the json subtraction operator :ref:`json_graph_op@Binary Operators@sub` . #. Fix :ref:`from_json-name` addition and multiply operations where the first argument is a variable and second argument is a parameter. #. Add simple examples for the json operators that have been implemented; e.g., :ref:`json_sum_op.cpp-name` . #. Fix :ref:`to_json-name` when a cumulative summation has subtraction terms in its summation. 07-29 ===== Add the :ref:`json_sum_op.cpp-name` example and fix a bug in :ref:`from_json-name` that was demonstrated by this example. To be specific, the json :ref:`json_graph_op@sum` operator did not work when all its result was a :ref:`dynamic parameter` . 07-25 ===== Fix the warning (on some systems) that :ref:`rev_jac_sparsity-name` shadows a member of ``CppAD::atomic_three`` . 07-19 ===== The ``cppad_lib`` dynamic link library version number used to be *yy* . *mmdd* where the major version number *yy* was the year minus 2000 and the minor version number *mmdd* was the month and day. The value *mmdd* was to large for a Mac system minor version number. See :ref:`cmake@cmake_install_libdirs@cppad_lib` for the new library version number system. 07-18 ===== Change some assignment operators to equality operators in a few asserts (``=`` to ``==`` in the files ``optimize_run.hpp`` and ``player.hpp`` ). These were not bugs because the corresponding variable was being set to the value it should have been. 07-05 ===== #. The :ref:`json_ad_graph@op_usage_vec@n_usage` value generated by :ref:`to_json-name` was not correct. This has been fixed. #. All the operators that were implemented so far were included in the :ref:`json_ad_graph@op_define_vec` section generated by ``to_json`` . Now only operators that are used are included. This makes it easier for humans to understand simple example graphs (especially as the number of implemented operators increases). #. There was a bug in :ref:`from_json-name` when one of the operations was a dynamic parameter and the other was a constant. This has been fixed. #. All of the addition and multiplication operators (including cumulative sum operators) have been implemented and tested.. 07-04 ===== #. Improve the :ref:`from_json-name` error messages by including the previous two lines of Json in error message. #. Working on a :ref:`to_json-name` example that outputs graph for the derivative of a function. 07-03 ===== The format of a Json computational graph was changed to include a section at the top that defines the mapping between integer codes in the graph and corresponding operators, see the heading :ref:`json_ad_graph@op_define_vec` . This is now separate from :ref:`json_ad_graph@op_usage_vec` which is the part of the graph that is expect to be very large and is now less verbose. 06-29 ===== It used to be the case that optimized functions with cumulative summation operators could not be optimized. This has been fixed and it is now acceptable to :ref:`optimize@Re-Optimize` an ``ADFun`` object. 06-19 ===== #. Some work was done to improve the speed of sparsity calculations when using the internal representation for a :ref:`glossary@Sparsity Pattern@Boolean Vector` . #. An extra pair of braces was expected in each :ref:`json_ad_graph@op_usage` of an :ref:`json_ad_graph-name` . This has been fixed. 06-18 ===== There are some problems using as Windows DLL for the ``cppad_lib`` library. These problems have been circumvented (for the time being) by using as static library for Windows systems. 06-17 ===== A new feature that constructs a ``ADFun`` object from a Json representation of a computational graph was added; see :ref:`json_ad_graph-name` and :ref:`from_json-name` . This requires linking the ``cppad_lib`` library. It is a Minimal Viable Product and is intended for testing. As of yet, only has the addition and multiplication operators are implemented. 06-10 ===== Some work was done to improve the speed of sparsity calculations when using the internal representation for a :ref:`glossary@Sparsity Pattern@Vector of Sets` . This also reduced the time required by the :ref:`optimize-name` operation for some cases. 06-02 ===== The version number in the title of the top level for the :ref:`user_guide-name` documentation was not being updated. This started on ``20190521`` and has been fixed. 05-31 ===== Add the :ref:`swap` operation to the ``CppAD::vector`` template class and :ref:`CppAD_vector@vectorBool` class. In addition, change :ref:`cppad_vector.cpp-name` example so it focuses on operations that are not in :ref:`SimpleVector-name` . 05-28 ===== There was a bug in optimizing conditional expressions; i.e., optimizing without the option :ref:`optimize@options@no_conditional_skip` . This was a rare case that involving sets of conditional expressions and conditionally skipping a specific operator. The bug was reproduced in a regression test and is fixed. 05-22 ===== #. Some speed improvements were made to the :ref:`for_hes_sparsity-name` algorithm. To be specific the forward Jacobian and Hessian patterns are computed during the same pass and using the same structure. This avoids an extra pass on the operation sequence. In addition, when :ref:`for_hes_sparsity@internal_bool` is false, it enables smart points to the forward Jacobian and Hessian to share the same underlying sets of integers (when they are equal). #. Some minor improvements and updating was done for the :ref:`directory-name` and :ref:`glossary-name` sections. 05-21 ===== Simplify the example computation of Hessian using subgraphs :ref:`subgraph_hes2jac.cpp-name` . This simplification was a change from using :ref:`multiple levels of AD` to using :ref:`base2ad-name` . 05-07 ===== There was a bug in the checking for correctness of the :ref:`adolc_sparse_hessian.cpp-name` results. This was due to the fact that the sparsity patterns for ADOL-C may have more entries that CppAD knows are zero. 04-01 ===== Add the optional :ref:`time_test@repeat_out` argument to the ``time_test`` function. 03-27 ===== Improve the speed of sparse Hessian calculations that :ref:`sparse_hes@coloring@colpack.symmetric` coloring method by directly accessing the coloring determined by Colpack. 03-19 ===== There was a bug in :ref:`f.Reverse(q, w)` when *f* contained the :ref:`pow(x,y)` function and *x* == 0 . To be specific, it was not reporting zero instead of ``nan`` for the derivative. This has been partially, but not completely fixed. See the :ref:`wish_list@Reverse Mode` in the wish list. 03-13 ===== Fix a bug in :ref:`f.subgraph_jac_rev(x, subset)` . This bug appeared in a test that only returned the upper triangle of a symmetric matrix (the lower triangle worked). 02-26 ===== There was a problem linking the boost multi-threading library on some systems that caused the corresponding check to be skipped. This has been fixed. To be specific, :: make check_example_multi_thread_bthread: available was not in the :ref:`cmake@CMake Command` output. This has been fixed. 02-23 ===== The return value *n_sweep* was changed to :ref:`sparse_jac@n_color` because it is not the number of sweeps when :ref:`sparse_jac@group_max` is not equal to one. This change was also made for the :ref:`speed tests` . 02-22 ===== Add row major order specifications to the speed test routines :ref:`link_sparse_jacobian` and :ref:`link_sparse_hessian` . This reduced the overhead (increased the speed) for the :ref:`adolc_sparse_jacobian.cpp-name` and :ref:`adolc_sparse_hessian.cpp-name` test cases. 02-20 ===== #. Fix bug below in the new release `20190200.1 `_. #. There was a problem locating the boost multi-threading library. To be specific, the :ref:`cmake@CMake Command` would report ``Could not find boost multi-threading library`` but it would still try to use the library. This was because a file name with a ``-`` in it was also being used for a cmake variable name. This has been fixed. 02-06 ===== Improve the discussion of the purpose of for the different :ref:`subgraph` options which computing sparse Jacobians. 02-01 ===== #. A stable release for 2019 is now available on the `release `_ page. #. The :ref:`download-name` page was edited to include better discussion of the license and documentation. 01-18 ===== The documentation for :ref:`atomic_three-name` , and its subsections, was improved. 01-17 ===== The arguments :ref:`atomic_three_define@parameter_x` and :ref:`atomic_three_define@type_x` were included for all the ``atomic_three`` virtual functions. 01-16 ===== More improvements, and bug fixes, in the optimization of :ref:`atomic-name` operations. This results in a reduction in the number of parameters :ref:`fun_property@size_par` and the number of variables :ref:`fun_property@size_var` . 01-15 ===== Fix a bug in the optimization of :ref:`atomic_three-name` operations with :ref:`Independent@dynamic` parameters. 01-14 ===== #. The :ref:`rev_depend` call back function was added to ``atomic_three`` class. This enables one to :ref:`optimize-name` functions that use ``atomic_three`` operations; see the :ref:`rev_depend.cpp` example. #. The ability to :ref:`optimize-name` functions was extended to :ref:`chkpoint_two-name` operations; e.g., a call to optimize was added at the end of the :ref:`get_started.cpp` example. #. The :ref:`chkpoint_one-name` class was deprecated, use :ref:`chkpoint_two-name` instead. 01-06 ===== One of the two atomic matrix multiply examples was converted from using :ref:`atomic_two-name` to :ref:`atomic_three_mat_mul.cpp-name` . This conversion made it clear that the *type_x* argument needed to be included in the :ref:`jac_sparsity` and :ref:`hes_sparsity` functions. 01-05 ===== Merge in the ``atomic branch`` . This completed :ref:`wish_list-name` item for the new atomic function API; see :ref:`atomic_three-name` . This is an improvement over the :ref:`atomic_two-name` interface, which used the ``atomic_base`` class and has been deprecated. In addition the following changes were included: #. Move the checkpoint examples below the :ref:`checkpoint` documentation. #. A phantom parameter, at index zero, was added; see :ref:`fun_property@size_par` . #. There appears to have been a bug in ``put_con_par`` (not tested for) whereby a constant parameter might match a dynamic parameter (and get changed). This has been fixed. #. There was a mistake in the ``check_variable_dag`` routine whereby the operator ``LeppOp`` checked a parameter index as if it were a variable index. This has been fixed. 01-11 ===== Merge in the ``chkpoint`` branch. #. This created the :ref:`chkpoint_two-name` class as a replacement for the :ref:`chkpoint_one-name` class. This new checkpoint class takes advantage of :ref:`dynamic parameters` and can be used with :ref:`base2ad-name` . #. While creating the ``chkpoint_two`` class, a problem was discovered in the :ref:`atomic_three-name` class. To be specific, it does not yet work with :ref:`optimize-name` . A reverse dependency analysis is the proper way to fix this. The ``atomic_three`` ``type`` member function does a forward :ref:`atomic_three_for_type@Dependency Analysis` and so its name was changed from ``type`` to ``for_type`` . {xrst_end 2019} ================================================ FILE: appendix/whats_new/2020.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2020 app} {xrst_spell autotools callgrind cg github gitlab libs msys omh optionlist un } Release Notes for 2020 ###################### mm-dd ***** 12-28 ===== #. Add the :ref:`cppadcg_sparse_jacobian.cpp@PASS_SPARSE_JACOBIAN_TO_CODE_GEN` option to the cppadcg sparse jacobian speed test so one can compare passing a function, or its sparse Jacobian, to CppADCodeGen (for the same eventual result). #. Move ``compiled_fun`` to :ref:`code_gen_fun-name` , to emphasize the fact that it used the CppADCodeGen package. 12-27 ===== #. Include the example implementations ``compiled_fun.hpp`` and ``compiled_fun.cpp`` in the documentation. #. Add the :ref:`cppadcg_det_minor.cpp@PASS_JACOBIAN_TO_CODE_GEN` option to the cppadcg det_minor speed test so one can compare passing the determinant or its Jacobian to CppADCodeGen (for the same eventual result). 12-21 ===== #. Add a move semantics version of the copy constructor to :ref:`ADFun-name` , :ref:`sparse_rc-name` , :ref:`sparse_rcv-name` , and :ref:`CppAD_vector@vectorBool` template classes. This should improve the speed of handling function return values that have these types. #. The default ADFun copy constructor was deleted so that its use is now a compile error (it used to generate a run time error message). #. Move :ref:`poly.cpp-name` , :ref:`pow_int-name` , :ref:`runge_45.cpp-name` , and :ref:`rosen_34.cpp-name` from ``example/general`` to ``example/utility/`` . 12-20 ===== #. The :ref:`rosen_34.cpp-name` example was changed to use :ref:`independent@dynamic` parameters so it does not need to record a new function for every argument value. The new version of this example also uses the ADFun :ref:`fun_construct@swap` operation to avoid making a separate copy of an ADFun object. #. Fix a bug in the ADFun :ref:`fun_construct@swap` , which was added on 12-19. #. Add a move semantic version of the copy constructor to the :ref:`cppad_vector-name` template class. (This should improve the speed of handling function return values that are vectors.) 12-19 ===== Merge in ``cg`` branch which had the following changes: #. **API Change:** The *SizeVector* class in :ref:`sparse_rc` and :ref:`sparse_rcv` must support the ``swap`` operation. #. The ``swap`` operator was added to the :ref:`sparse_rc` and :ref:`sparse_rcv` template classes. #. A move semantics version of the assignment operator was added to the :ref:`sparse_rc` :ref:`sparse_rcv` template classes. #. The *job* option was added to the :ref:`det_minor` and :ref:`sparse_jacobian` speed tests. #. The ``compiled_fun`` example for using CppADCodeGen was added (since moved to :ref:`code_gen_fun-name` ). In addition, :ref:`cppadcg_sparse_jacobian.cpp-name` and :ref:`cppadcg_det_minor.cpp-name` examples were modified to use this utility. #. The ADFun :ref:`fun_construct@swap` operation, and move semantics copy constructor, were added. This ensures the move semantics assignment operator does not make any copies. 12-12 ===== Change the location where the source code for the :ref:`optional` packages is stored from ``build/external`` to ``external`` . This enables one to remove the old build directory without having to rebuild the optional packages. 12-11 ===== Change the autotools :ref:`configure@Configure` script so that it does better detection of the microsoft cl compiler. In addition, add documentation for the :ref:`configure@--enable-msvc` option. 12-06 ===== Fix ``bin/get_cppadcg.sh`` install of newer version of cppadcg. 12-05 ===== Advance to Oct 9, 2020 version of cppadcg. 12-04 ===== #. The cppad vector :ref:`cppad_vector@resize` operation now preserves the data even if the new size does not fit in the old capacity of the array. This makes ``cppad_vector`` more like ``std::vector`` . #. Fix a problem with the autotools build and the ``is_pod`` function. This function, which is not in the CppAD user API, was simplified because it is no longer necessary to handle the c++98 case. #. Fix the ``example/utility/to_string.cpp`` test; see `issue 44 `_. 12-03 ===== **API Change:** support of c++11 is required for this, and future versions of CppAD. 12-02 ===== The :ref:`pkgconfig-name` files were modified to make user of the fields ``Requires.private`` and ``Libs.private`` . In addition, the :ref:`pkgconfig@cppad.pc@Extra Definitions` were added. 12-01 ===== There was a mistake in the 20201130 version that caused ``CPPAD_HAS_IPOPT`` and ``CPPAD_HAS_ADOLC`` to be undefined, instead of 0 or 1, this has been fixed. 11-29 ===== #. Use pkg-config to determine install prefix for adolc, eigen, ipopt, and cppadcg; see :ref:`cmake@include_adolc` , :ref:`cmake@Eigen` , :ref:`cmake@include_ipopt` , and :ref:`cmake@include_cppadcg` . #. Fix warning that the variable ``not_used_rec_base`` was not initialized. 10-26 ===== Improve the documentation for building the CppAD tests using :ref:`cmake@CMake Command@Visual Studio` . 10-23 ===== Version ``cmake-3.19`` of cmake had an change that was not backward compatible; see `CHECK_CXX_SOURCE_RUNS `_. This broke the CppAD install and was fixed. Thus versions of CppAD before 20201023 do not work with version of CMake at or after 3.18. 10-22 ===== Add ``ar-lib`` to github repository distribution. This fixes a problem with autotools build when using the Microsoft C++ compiler and ``msys2`` . 10-21 ===== Modify the source file ``match_op.hpp`` to avoid a possibly un-initialized warning on some compilers. 10-20 ===== The *have_ar* variable has been removed from the :ref:`configure-name` configuration and the configuration now exits if ``AM_PROG_AR`` does not find an archiving tool. This was done to try and solve some configuration problems with a combination of ``msys2`` and the Visual Studio compiler. 10-17 ===== #. The :ref:`configure-name` configuration would fail when it tried to compile a :ref:`cppadcg-name` include file (the autotools configuration does not support ``cppadcg`` ). This has been fixed. #. The autotools configuration for he profiling version of the speed tests was out of date. It is sufficient to do speed profiling using the cmake configuration, so it has been removed from the autotools configuration. #. Add more documentation for the :ref:`cmake@cppad_profile_flag` and fix some problems building the ``speed_profile`` program. (We also use ``callgrind`` to do CppAD speed profiling.) 09-05 ===== Fix some errors and warnings when building with Visual Studio 2019; e.g. see `issue 60 `_. 06-08 ===== Fix compiler warning in ``match_op.hpp`` about possibly uninitialized variable. 06-04 ===== #. Advance :ref:`get_ipopt.sh-name` to ipopt-3.13.2. #. Fix some compiler warnings generated by g++-10.1.1. 05-27 ===== If :ref:`cmake@include_ipopt` is on the cmake command line, :ref:`ipopt_solve-name` is automatically included by ``cppad/cppad.hpp`` . The :ref:`sparse2eigen-name` utility can still be used even if ``include_eigen`` is not specified on the cmake command line. 05-26 ===== #. There was a bug in conditional expressions when the :ref:`condexp@left` or :ref:`condexp@right` argument is a :ref:`con_dyn_var@Dynamic` parameter and the other is a parameter and ``NDEBUG`` is not defined. There was a mistaken assertion that has been fixed. #. Advance most recent release from 20200000.2 to 20200000.3. 05-23 ===== #. The ``optimize`` hash coding used to remove identical expressions was improved. The can make a big difference for very large programs. #. The :ref:`collision_limit` option was added to the ``optimize`` command. #. The :ref:`optimize@exceed_collision_limit` function was added so one can check if a larger collision limit might make a difference. 05-01 ===== #. A problem was fixed in the :ref:`get_adolc.sh-name` installation of the optional package Adolc . To be specific, to be specific the Adolc source moved from Gitlab to Github. In addition, advance to Adolc master on 2020-04-01. #. The :ref:`optimize@options@no_cumulative_sum_op` option was added to the ``optimize`` routine because, in some cases, gathering cumulative summations interferes with recognizes pieces of summations that are the same in different cumulative sums. 03-11 ===== The ``CppAD::vector`` constructor was extended to allow its :ref:`size` to be a ``int`` (this used to generate a warning when using the ``clang`` compiler). 02-02 ===== The ``get_optional.sh`` prefix was extended so that it could be a :ref:`get_optional.sh@prefix@Relative Path` . In addition :ref:`get_optional.sh@prefix@Configuration` instructions were added to cover the case where the prefix is changed. 02-01 ===== The :ref:`get_optional.sh@get_optional.log` and :ref:`get_optional.sh@get_optional.err` files were added so one can see the progress of the ``get_optional.sh`` script. 01-29 ===== #. Add the :ref:`cppadcg_sparse_jacobian.cpp-name` speed test. #. The speed main program :ref:`speed_main@test@correct` test results were printed as *package* _ *test* _ *optionlist* _ ``ok`` = *result* This has been changed to *package* _ *test* _ *optionlist* _ ``correct`` = *result* 01-24 ===== #. Add the :ref:`cppadcg_det_minor.cpp-name` speed test. This is the first test using :ref:`cppadcg-name` (this package generates C source code for the derivatives). #. The package name was removed from the speed test size output (because the sizes do not depend on the package); see :ref:`speed_main@Speed Results` . 01-22 ===== #. Add a :ref:`json_get_started.cpp-name` example. #. Move the section :ref:`example_abs_normal-name` below the section :ref:`abs_normal_fun-name` . 01-18 ===== Modify the option package install scripts so they all use the same install :ref:`get_optional.sh@prefix` that the user can change in the file ``bin/get_optional.sh`` . In addition, these scripts were updated to use more recent versions of the optional packages. 01-17 ===== Create an example / template for adding a new package to the speed tests; see :ref:`speed_xpackage-name` . 01-12 ===== Documentation was added for the :ref:`base_ordered@Special Requirements` when the *Base* type does not support ordered comparison. As a consequence, the reverse mode the derivative of ``pow`` ( *x* , *y* ) may result in zero (not nan) when *x* is zero; see the :ref:`wish_list@Reverse Mode` wish list item. 01-06 ===== The ``VecAD`` objects have been extended to work with dynamic parameters. However, this is not as efficient as it should be; see :ref:`VecAD@Efficiency` and :ref:`wish_list@Dynamic Parameters@VecAD Vectors` under dynamic parameters in the wish list. 01-04 ===== #. The :ref:`VecAD-name` documentation was re-written. #. An empty directory with the name ``omh`` was being install below the include directory. This has been fixed. {xrst_end 2020} ================================================ FILE: appendix/whats_new/2021.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2021 app} {xrst_spell cplusplus cppadcg cxx isdigit json lcppad lexer libs ltmain msys rcv txt } Release Notes for 2021 ###################### mm-dd ***** 12-17 ===== Add forward mode calculations using ``AD`` to the :ref:`atomic_four_vector-name` example. 12-16 ===== Change ``virtual`` to ``override`` in all the :ref:`atomic_three_example-name` files and :ref:`multi_atomic_three_user-name` . 12-10 ===== #. Added the example :ref:`atomic_four_vector-name` . #. Fix some compiler warnings when building with ``NDEBUG`` defined. 12-08 ===== Update to a newer version of the :ref:`configure-name` . 09-04 ===== There was a bug in the :ref:`atan2-name` function. For example, the derivative of ``atan2(0,1)`` was incorrect; see `issue 120 `_. This has been fixed and the :ref:`atan2.cpp-name` example has been extended to do more testing. 08-31 ===== There was a bug in the CMake configuration files such that ``make check_example_multi_thread`` was not being constructed. Hence ``make check`` did not include the multi_thread tests. This has been fixed. 08-27 ===== The following error occurred ``compile_source_test: cplusplus_201100_ok is defined before expected`` if the :ref:`cmake-name` command was run twice, without first removing the ``CMakeCache.txt`` file. This has been fixed. 08-26 ===== The cmake command was extended to allow for one to specify :ref:`cmake@cmake_build_type` . This forced changing the default for :ref:`cmake@cppad_debug_which` from ``debug_all`` to the empty string. 07-31 ===== Do more work on pkgconfig files: Remove duplicates and put ``-L`` entries before ``-l`` entries in ``Libs`` section. Furthermore, move all information from private to normal section. 07-30 ===== Fix a bug in the :ref:`pkgconfig-name` files. To be specific, there was an extra ``-l`` at the front of the list of library flags; e.g., ``-l-lcppad_lib`` was changed to ``-lcppad_lib`` . 07-12 ===== #. Change all the scripts called by :ref:`get_optional.sh-name` to use all the processing units available on the system when compiling and linking the optional packages. #. Fix :ref:`get_colpack.sh-name` so that it does not leave behind ``ltmain.sh`` and ``test-driver`` in the top source directory. #. Change the :ref:`cmake-name` script use of ``CHECK_CXX_SOURCE_RUNS`` to ``CHECK_CXX_SOURCE_COMPILES`` . This makes it easier to use CppAD in a cross compiling context. 06-22 ===== Improve :ref:`Var2Par-name` documentation and :ref:`var2par.cpp-name` example by separating dynamic and constant parameters. 06-06 ===== The ``CppAD::vector`` assignment statement no longer requires the size of the vectors to be the same; see :ref:`CppAD_vector@Assignment@Check Size` . 06-02 ===== #. Remove ``microsoft_timer`` which is no longer needed by :ref:`elapsed_seconds-name` because c++11 required (starting this year). #. Remove any use of ``CMAKE_SOURCE_DIR`` . This allows CppAD's top source directory to be used as a subdirectory of a larger CMake package. 05-12 ===== #. The cmake :ref:`cmake@include_cppadcg` option was not working when cppadcg was the only optional package. This has been fixed. #. The smallest size for the :ref:`det_minor` speed test was changed from one to two (a 2 by 2 matrix). 04-30 ===== #. The conditional expression example :ref:`cond_exp.cpp-name` was improved. #. Restore the :ref:`sparse_rcv copy constructor` which was removed by mistake when the ``sparse_rcv`` move semantics constructor was added. 04-28 ===== #. The change to the :ref:`pow(x,y)` function on :ref:`2021@mm-dd@02-06` handled the case where *x* is zero. The :ref:`pow_nan.cpp-name` example was modified to show when the new version of this function generates nans. #. Remove some uses of the deprecated :ref:`nan(zero)` function. 04-27 ===== Fix the msys2 and cygwin system builds so that they use static (instead of dynamic) libraries. This is necessary so that static variables (in include files) can not have multiple instances in Windows. 04-26 ===== There was a problem with the :ref:`cmake-name` command when :ref:`ipopt-name` was the only optional package installed. This was related to the ipopt :ref:`ipopt@Include Directories` problem. 04-16 ===== Improve the instruction for adding a new speed test package; see :ref:`speed_xpackage-name` . 04-09 ===== The :ref:`cpp_graph_print-name` operation was failing when there were atomic or discrete functions. This has been fixed. 04-07 ===== Documentation was added for the :ref:`cpp_ad_graph@operator_arg@print_graph_op` operator. This is different from the function :ref:`cpp_graph_print-name` which prints an entire graph. 04-05 ===== The :ref:`cpp_graph_print-name` operation was modified so that it printed the text corresponding to atomic function names, discrete function names, and print operators. 04-02 ===== The enum value ``dis_graph_op`` was corrected to ``discrete_graph_op`` in the documentation for :ref:`cpp_ad_graph@operator_arg@discrete_graph_op` . 03-29 ===== Include the :ref:`cpp_ad_graph@dependent_vec` in the output of :ref:`cpp_graph_print-name` ; see ``y nodes`` in :ref:`print_graph.cpp-name` example output. 03-24 ===== The C++ AD graph constructor, :ref:`cpp_graph_ctor-name` , cannot be called the first tile in parallel mode. This condition has been added to the documentation and assets were added to make sure it is true. 03-11 ===== Improve the discussion of identically equal; see :ref:`base_identical@EqualOpSeq@More Complicated Case` . 03-09 ===== Simplify the :ref:`fabs.cpp-name` example. 03-08 ===== For years the following comment in reverse.hpp was forgotten: // ? ``should use`` += ``here`` , ``first make test to demonstrate bug`` A test case was created to demonstrate this bug and it was fixed (see ``duplicate_dependent_var`` in ``test_more/general/reverse.cpp`` ). This only affects reverse mode when :ref:`reverse_any@w` has size *q* * *m* and two of the dependent variables in the range of *f* are identically equal (actually the same variable). 03-07 ===== #. Fix bug in :ref:`f.to_graph` that occurred when *f* had ``fabs`` dynamic parameter operators. To be more specific, if ``NDEBUG`` was not defined, an error from an unknown source would be detected in the file ``to_graph.hpp`` . #. Make :ref:`unary_minus-name` an atomic operation, instead of using binary subtraction of zero minus the value being negated. In addition, add it to the :ref:`json_graph unary operators` and the :ref:`graph_op_enum values` . 02-21 ===== Add specifications for what is conditionally installed by the :ref:`cmake@Eigen` and :ref:`cmake@include_ipopt` options. In addition, make it clearer that :ref:`cmake@include_cppadcg` should only be used for testing. 02-16 ===== There was a problem with atomic functions, :ref:`optimize-name` , and reverse mode that would lead to unexpected nans. This has been fixed by changing values, that the optimizer determines are not used, from nan to zero. A discussion of this was added below :ref:`atomic_three_reverse@partial_x@azmul` in the atomic reverse documentation. 02-14 ===== #. Add the :ref:`print` command to the ``cpp_graph`` class. #. Change the name of a documentation section form ``seq_property`` to :ref:`fun_property-name` . #. Add setting and getting a ``ADFun`` function's :ref:`name` . 02-12 ===== #. In the case where :ref:`cmake@cppad_debug_which` is ``debug_all`` (``debug_none`` ) the corresponding *CMAKE_BUILD_TYPE* is now specified as ``Debug`` (``Release`` ). #. Fix the ``Policy CMP0054`` warning during the :ref:`cmake@CMake Command` . #. Fix the :ref:`cmake@CMake Command@Visual Studio` build. This included commenting out part of the CppAD vector test because the MSC compiler is confused between the vector's const_iterator and iterator; see ``# ifndef _MSC_VER`` in :ref:`cppad_vector.cpp-name` . 02-11 ===== Fix some problems with the linking of the ``cppad_lib`` library when using the Microsoft compiler. 02-08 ===== Fix some problems in the :ref:`pkgconfig@cppad.pc` file; see pull request `95 `_. 02-06 ===== A special version of the :ref:`pow(x, y)` function was added for the special case where *y* is a parameter. This properly handles the special case where *x* is zero and *y* is greater than the order of the derivative. In addition, it only requires one tape variable (instead of three) for each ``pow`` operation. 01-27 ===== There was a bug in the converting to abs_normal form when the function :ref:`abs_normal_fun@f` made use of the :ref:`pow-name` operator. To be specific, when compiling without ``NDEBUG`` defined, an assert was generated even though the code was correct. This has been fixed. 01-26 ===== Change the prototype for the cmake command :ref:`cmake@CMake Command@Options` to use *true_or_false* , instead of ``true`` to highlight the fact that one can choose either true or false. 01-08 ===== On some systems, the file ``cppad_lib/json_lexer.cpp`` would not compile because the ``std::isdigit`` function was not defined. This has been fixed. 01-07 ===== The example :ref:`pow_nan.cpp-name` was added. 01-05 ===== #. Improve discussion of :ref:`cppad_cxx` and make sure all uses are exactly as described. In addition, change mention of optional features from C++11 to C++17. #. The required version of :ref:`cmake-name` was advanced from 2.8.4 to 3.0. This fixes a policy CMP0042 warning on Mac systems. #. If the compiler, plus the flags in :ref:`cmake@cppad_cxx_flags` , does not by default support C++11, cmake is used to find and add the flags necessary for this support. 01-03 ===== #. Fix a bug in :ref:`reverse-name` mode for an ``ADFun< AD<`` *Base* > > function that has :ref:`Independent@dynamic` parameters and value of one of these parameters was zero or one when the function was recorded. #. Fix a bug in the :ref:`configure-name` building of ``cppad_lib`` . {xrst_end 2021} ================================================ FILE: appendix/whats_new/2022.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2022 app} {xrst_spell an an csrc div dll github isinf isnan libs neg nullptr nx ny omhelp posix rc tmpnam txt zdouble } Release Notes for 2022 ###################### mm-dd ***** 12-25 ===== #. The column widths of some of the documentation tables were set to be equal instead of adjusting to best fit the columns. This has been fixed. #. Fix two tables that were not converted to sphinx correctly. To be specific, the table below Matrix Dimension in :ref:`atomic_three_mat_mul.hpp-name` and :ref:`atomic_two_eigen_mat_mul.hpp-name` . 12-23 ===== Add documentation for the binary operation :ref:`ad_binary@Zero Special Cases` . In addition fix a bug in these special cases when zero was multiplied by or divided by nan. 12-16 ===== Finish conversion of CppAD documentation from omhelp to the sphinx `xrst `_ wrapper. The following is a link to the `old documentation `_ . 11-05 ===== Fix a conversion error during the tests by extending the zdouble :ref:`constructor` to ``zdouble`` ( *i* ) where *i* is ``long`` . 08-30 ===== The :ref:`optimize-name` operations was not handling comparison operators that only had parameters, with at least one dynamic parameter. This has been fixed. 07-16 ===== Add the option to configure eigen using ``eigen_prefix`` , as well as with ``include_eigen`` . The ``eigen_prefix`` option was removed on :ref:`2024-08-05 <2024@mm-dd@08-05>` . The ``include_eigen`` options was removed on :ref:`2024-10-02 <2024@mm-dd@10-02>` . 07-14 ===== #. There was a mistake in the implementation of the :ref:`ad_ctor@x@explicit` ``AD`` < *Base* > constructor. To be specific, the value was converted to ``double`` before being converted to ``AD`` < *Base* > . This has been fixed and the :ref:`ad_ctor-name` documentation has been improved to note which result are always constant parameters. #. **API Change** : It was necessary (due to the fix above) to add the :ref:`int constructor` and :ref:`double constructor` to the :ref:`base_require-name` . (Defining a base class is an advanced topic and only for experts). 07-12 ===== The :ref:`cmake@CMake Command` was not properly determining when posix threads could be used (when using a C compiler other than gcc). This suppressed the running of the corresponding examples; e.g., :ref:`a11c_pthread.cpp-name` . 07-10 ===== #. Add the cmake_needs_dot_slash symbol to the cmake command line. This has since been removed; see 2023 for :ref:`2023@mm-dd@06-01`. #. Change the test case in ``speed/example/speed_program.cpp`` because some compilers have gotten smarter and do not calculate values that are not used. This lead to an the repeat count overflowing and then an infinite loop. The :ref:`speed_test-name` and :ref:`speedtest-name` routines now check for an overflow of the repeat counter and abort. #. Suppress the Microsoft compiler divide by zero warning, C4723. 07-08 ===== Change `github actions `_: run on pushes to the master and stable branches, and run on all pull requests. 07-05 ===== Merge in pull_155 branch. This enabled the use of binary operators with Eigen matrices of type ``AD`` < *Base* > and type *Base* ; see :ref:`ScalarBinaryOpTraits` in ``cppad_eigen.hpp`` . 06-23 ===== #. Change *x* to :ref:`to_csrc@u` in the ``to_csrc`` operation and add specifications as the position of the independent dynamic parameters and variables in *u* . #. Add the :ref:`jit_dynamic.cpp-name` and :ref:`jit_compile.cpp-name` examples. #. Remove some remaining uses of ``CPPAD_TEST_VECTOR`` . #. Deprecate use of the following symbols: ``CPPAD_`` *package* ``VECTOR`` for *package* equal to ``CPPAD`` , ``STD`` , ``BOOST`` , ``EIGEN`` . #. Add the :ref:`cmake@cmake_defined_ok` symbol to the cmake command line. This enables one to run cmake with a ``CMakeCache.txt`` file. 06-22 ===== #. Modify the :ref:`cmake@CMake Command@msys2` and :ref:`cmake@CMake Command@Visual Studio` install suggestions. #. Split the JIT function type *type* _ ``forward_zero`` into ``jit_`` *type* for :ref:`to_csrc@JIT Functions` , and ``atomic_`` *type* for :ref:`to_csrc@Atomic Callbacks` . #. Add the :ref:`jit_atomic.cpp-name` example. #. The ``CPPAD_TEST_VECTOR`` macro was removed (it was deprecated on 2012-07-03). #. Remove the following symbols from the :ref:`testvector-name` documentation: ``CPPAD_`` *package* ``VECTOR`` for *package* equal to ``CPPAD`` , ``STD`` , ``BOOST`` , ``EIGEN`` . 06-21 ===== Change the visual C++ JIT compile flag ``/Tc`` to the flag ``/TC`` , so that the C source file need not follow directly after in the compile command. This fixed a test failure when using that compiler. 06-20 ===== JIT C source code generation, compilation, and linking were added using the following new features, examples, and speed tests: .. csv-table:: :widths: auto to_csrc,:ref:`to_csrc-title` create_dll_lib,:ref:`create_dll_lib-title` link_dll_lib,:ref:`link_dll_lib-title` example_jit,:ref:`example_jit-title` cppad_jit_det_minor.cpp,:ref:`cppad_jit_det_minor.cpp-title` The following improvements were made to existing features: #. Fix a clang++ compiler warning in the file check_for_nan.hpp. 05-27 ===== Add the theory, but not implementation, for the atomic linear ODE second order reverse mode; see :ref:`atomic_four_lin_ode_reverse_2-name` . 05-26 ===== #. The atomic linear ODE maximum :ref:`atomic_four_lin_ode@step` size was not being enforced during the reverse mode :ref:`atomic_four_lin_ode_reverse.hpp@Simpson's Rule` integral approximation. This has been fixed. #. Make :ref:`atomic_four_lin_ode_forward.hpp@extend_ode` a separate function in the atomic linear ODE example. 05-20 ===== Merge in the pull_144 branch. This fixed some problems with the windows visual studio build; see the corresponding pull request. 05-19 ===== #. The atomic four ``base_lin_ode`` function was changed to :ref:`base_solver` . In addition, a maximum :ref:`atomic_four_lin_ode@step` size was added to this atomic linear ODE solver. #. Fix setting of debug and release flags in ``test_more/debug_rel/CMakeLists.txt`` . 05-17 ===== The :ref:`atomic_four_lin_ode_rev_depend.hpp-name` example was added. In addition, remove the Under Construction headings from the :ref:`atomic_four_lin_ode-name` sections (it is ready for use). 05-16 ===== #. The :ref:`atomic_four_hes_sparsity@ident_zero_x` argument was added to the atomic four Hessian sparsity callback. The previous version of this callback still works but is deprecated. #. Add :ref:`Var2Par-name` set in the ``cppad_eigen.hpp`` :ref:`cppad_eigen.hpp@std Definitions` of the AD versions of ``isinf`` and ``isnan`` . #. Change the atomic four example Hessian sparsity callbacks to use the :ref:`atomic_four_hes_sparsity@ident_zero_x` . In the special case of :ref:`atomic_four_norm_sq.cpp-name` , do not include *ident_zero_x* and mention that this version of the callback is deprecated. 05-15 ===== #. Merge in the pull_140 branch. This extended CppAD :ref:`base_limits-name` and :ref:`numeric_limits-name` to include ``infinity()`` . In addition, ``cppad_eigen.hpp`` added ``isinf`` and ``isnan`` for AD types to the std namespace; see :ref:`cppad_eigen.hpp@std Definitions` . #. Fix a long standing bug in the :ref:`for_hes_sparsity-name` routine. For example, the function called ``case_seven`` in ``test_more/general/hes_sparsity.cpp/`` tests a case where this bug occurred. #. Add the :ref:`atomic_four_lin_ode_hes_sparsity.hpp-name` example. 05-13 ===== The :ref:`code_gen_fun examples` were not being built even when :ref:`cmake@include_cppadcg` was specified on the cmake command line. This has been fixed. 05-12 ===== The :ref:`cmake-name` script ``cmake/compile_source_test.cmake`` was not working properly on Darwin systems which resulted in the following warning: | |tab| ``warning:`` ``'tmpnam'`` ``is deprecated:`` ... | |tab| |tab| ``file_name`` = ``tmpnam`` ( ``nullptr`` ); This has been fixed. 05-11 ===== A mathematical :ref:`atomic_four_lin_ode_jac_sparsity.hpp@Theory` was added to the linear ODE Jacobian sparsity pattern example. In addition, the corresponding code was changed to match the theory. This may, or may not, have fixed some bugs in the previous version of this routine. 05-10 ===== The :ref:`atomic_four_jac_sparsity@ident_zero_x` argument was added to the atomic four Jacobian sparsity callback. The previous version of this callback still works but is deprecated. 05-09 ===== #. Add a theory section to the linear ODE :ref:`for_type` section. In addition more the linear ODE theory for forward and reverse mode to the corresponding :ref:`forward` and :ref:`reverse` sections. #. Back out an incorrect change on 04-15 to the meaning of :ref:`atomic_four_jac_sparsity@select_x` and :ref:`atomic_four_jac_sparsity@select_y` in the atomic four Jacobian sparsity documentation. #. Add the specification that the atomic four :ref:`atomic_four_call@for_type` callback will be called once for each atomic function call and before any other of the callbacks for the atomic function call. 05-07 ===== Add the :ref:`base2vec_ad.cpp-name` example demonstrating that :ref:`base2ad-name` does not convert :ref:`VecAD-name` operations as might be expected. Add a link to this example in both the base2ad and VecAD documentation and a :ref:`base2ad wish_list` item about this. 05-06 ===== There was a bug in combining the :ref:`discrete-name` functions with the :ref:`base2ad-name` operation. To be specific, if a discrete function was used during the recording of *f* and *af* = *f* . ``base2ad`` () , the following assert would happen during forward mode calculations using the *af* function: | |tab| ``error from unknown source`` | |tab| ``Error detected by false result for`` | |tab| |tab| ``index < List`` (). ``size`` () | |tab| ``at line 325 in the file`` | |tab| ... ``discrete.hpp`` This bug has been fixed. 04-21 ===== #. Modify :ref:`atomic_four_lin_ode_set.hpp-name` and :ref:`atomic_four_lin_ode_get.hpp-name` to re-use sparsity patterns instead of creating a new one for every call. This makes use of :ref:`sparse_rc@set_row_major` to speed up checking for equality of sparsity patterns. #. In the :ref:`atomic_four_lin_ode_implement-name` files, change ``assert`` to :ref:`CPPAD_ASSERT_UNKNOWN` . 04-20 ===== Add the :ref:`sparse_rc@set_row_major` , :ref:`sparse_rc@get_row_major` , :ref:`sparse_rc@set_col_major` , :ref:`sparse_rc@get_col_major` , functions to the ``sparse_rc`` template class. 04-19 ===== Change the ODE solver in :ref:`atomic_four_lin_ode-name` from :ref:`runge45-name` to :ref:`rosen34-name` . 04-15 ===== #. Add :ref:`atomic_four_lin_ode-name` an example of making the solution of an ODE an atomic operation. As with the other atomic four examples, the ``AD`` < *Base* > version of the derivative calculations a done using atomic operations. #. Add the :ref:`sparse_rc@other@Equality` operator to the sparsity pattern template class. 03-29 ===== Add the :ref:`atomic_four_lin_ode-name` example which is not yet complete. 03-26 ===== Change ``y(nx)`` to ``y(ny)`` in :ref:`atomic_four_mat_mul_forward.cpp-name` . 03-25 ===== #. Fix :ref:`atomic_four_mat_mul-name` by changing it's base class from ``atomic_four`` to ``atomic_four`` < *Base* > ; see :ref:`atomic_four_mat_mul.hpp-name` . #. Remove under construction warning from :ref:`atomic_four_mat_mul-name` . #. Change ``atomic_vector`` to :ref:`atomic_four_vector-name` . 03-24 ===== Fix :ref:`atomic_four_vector-name` by changing it's base class from ``atomic_four`` to ``atomic_four`` < *Base* > . Make some other similar corrections in :ref:`atomic_four_vector.hpp-name` . 03-23 ===== Improve error detection and message when both :ref:`cmake@cmake_build_type` and :ref:`cmake@cppad_debug_which` are chosen during the install procedure. 02-21 ===== Extend the json graph representation to include :ref:`json_graph_op@Atomic Functions@Atomic Four` functions. For example, see :ref:`json_atom4_op.cpp-name` . 02-19 ===== Extend the C++ graph representation to include atomic four :ref:`atomic functions` . For example, see :ref:`graph_atom4_op.cpp-name` . 02-18 ===== Fixed the value of *after* [ *i* ] in the :ref:`cpp_ad_graph@operator_arg@print_graph_op` documentation. To be specific, ``- 2`` was changed to ``- 1`` . 02-17 ===== Add ``identical_zero`` to the possible :ref:`atomic_four_for_type@ad_type` values in the atomic four ``for_type`` function. See :ref:`atomic_four_vector_for_type.hpp-name` , :ref:`atomic_four_mat_mul_for_type.hpp-name` , and :ref:`atomic_four_mat_mul_identical_zero.cpp-name` for an examples using this feature. 02-16 ===== Use the :ref:`atomic_four_norm_sq.cpp-name` overrides as examples for the virtual functions in the :ref:`atomic_four-name` base class. 02-13 ===== #. The :ref:`atomic_four_mat_mul-name` example class was added. #. The :ref:`atomic_four_vector-name` include path has changed to # ``include `` 02-12 ===== Add the :ref:`sparse_rc@push_back` and output stream (see :ref:`sparse_rc@os` ) operations to the ``sparse_rc`` template class. 02-11 ===== The value of ``Libs:`` in the ``cppad.pc`` :ref:`pkgconfig-name` file was not being set correctly for some cases. This has been fixed. 02-01 ===== Fix a possible integer overflow in the :ref:`to_string.cpp-name` example (might cause the test to fail). 01-27 ===== Convert :ref:`atomic_four_forward.cpp-name` from an atomic_three to an atomic_four example. 01-25 ===== Convert :ref:`atomic_four_dynamic.cpp-name` from an atomic_three to an atomic_four example. 01-23 ===== #. Add :ref:`atomic_four-name` , a simpler interface to atomic functions. This has the optional :ref:`atomic_four_call@call_id` argument which can be used to attach extra information to an atomic function call. #. The :ref:`atomic_four_vector-name` is a good example that uses *call_id* to specify which element by element operator is being called; see :ref:`call_id` . #. Remove element-by-element atomic operations from :ref:`wish_list-name` (completed by the atomic_vector class). 01-19 ===== Edit wish_list cppad_lib :ref:`wish_list@cppad_lib@Requirement` item. Add an :ref:`wish_list@Optimization@Atomic Functions` item to the optimization wish list. 01-15 ===== #. Fix some conversion warnings generated by the clang compiler for the :ref:`atomic_four_vector-name` example. #. Correct the order of p, q in function calls in :ref:`atomic_four_vector_forward_op.hpp-name` . 01-06 ===== #. Fix the ``[]`` operator using CppAD vector :ref:`iterators` . In addition it was extended to include ``size_t`` indices. #. Add reverse mode to the :ref:`atomic_four_vector-name` , mul, div, operators and fix some warning used ``size_t`` iterator indices. 01-04 ===== #. Add the neg operator to :ref:`atomic_four_vector-name` . #. Add reverse mode to the atomic_vector, add, sub, and neg operators. 01-01 ===== The ``example/atomic_three/vector_op.cpp`` example (see :ref:`atomic_four_vector-name` ) was split into multiple files and moved into the ``example/atomic_vector`` directory. {xrst_end 2022} ================================================ FILE: appendix/whats_new/2023.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2023 app} {xrst_spell autotools cstdint kludge kokkos mingw msys pacman trilinos uintptr usr } Release Notes for 2023 ###################### mm-dd ***** 12-27 ===== Fix link in :ref:`user_guide@Software Engineering` paragraph. 12-24 ===== Change the ``CPPAD_DEBUG_AND_RELEASE`` compiler flag to the cmake :ref:`cmake@cppad_debug_and_release` configuration flag. This changed the default behavior for compiling the CppAD library so that it allows for mixing debug and release code. 09-08 ===== #. Add the :ref:`cmake@cppad_static_lib` option to the install. #. Fix warning (on some newer compilers) when a :ref:`CppAD_vector@vectorBool` constructor specified the number of elements using a signed integer. 08-19 ===== Add :ref:`numeric_limits@max_digits10` to the AD numeric limits. If you define your own Base type, and you do not use the :ref:`base_limits@CPPAD_NUMERIC_LIMITS` macro, you will have to add ``max_digits10`` to your Base type's numeric limits. 08-12 ===== #. There was a bug in the to_csrc :ref:`to_csrc@compare_change` argument. To be specific, it was not adding to the input value but rather reporting the number of changes during a call. This has been fixed and demonstrated in the :ref:`jit_compare_change.cpp-name` example. #. A :ref:`link_dll_lib@fun_ptr@Warning !!` was added to the documentation for function pointers created by the dll linker. (This fact did not stand out as much in the previous documentation.) 08-10 ===== #. The :ref:`parallel_ad-name` routine was missing some initializations. This has been fixed. In addition, a list of :ref:`parallel_ad@Other Initialization` , that must be initialized before using in parallel mode, was included in the documentation. #. There was a bug introduce on :ref:`2023@mm-dd@06-20` when the val_graph branch was merged into the master. To be more specific, if :ref:`cmake@cppad_tape_addr_type` was ``size_t`` , a compile error occurred one of the ``is_pod`` definitions. This has been fixed. 08-09 ===== The :ref:`cmake@cmake_build_type` option was changed to not be case sensitive; e.g., you can now use ``debug`` or ``Debug`` for this option. 08-06 ===== Add the cumulative summation operator to the :ref:`to_csrc-name` conversion. The cumulative summation operator can occur during optimization. Not having this operator seemed like a bug when NDEBUG was true because one got no warning that the operator did not get converted. 07-29 ===== Add the CppAD vector :ref:`CppAD_vector@Comparison` operators and size with :ref:`CppAD_vector@Size Constructor@Value` constructor. 06-20 ===== #. Add ``unsigned int`` as a valid type for the CppAD vector :ref:`CppAD_vector@Size Constructor` and :ref:`CppAD_vector@resize` argument. #. Merge in the val_graph Branch: #. Add :ref:`optimize@options@val_graph` to the list of possible optimizer options. #. Add the :ref:`speed_main@Global Options@val_graph` option to the speed tests. This option has been implemented with all of the :ref:`speed_cppad-name` tests. #. Simplify :ref:`atomic_four_lin_ode_rev_depend.cpp-name` and add m to the `atomic_four_lin_ode_rev_depend.cpp@Problem Parameters` that can be changed. #. Fix the title of the :ref:`atomic_four_lin_ode_base_solver.hpp-title` section. #. Change all the atomic_four :ref:`examples ` that use :ref:`optimize-name` to include the ``val_graph`` option. 06-13 ===== Change the :ref:`cppad_det_minor.cpp-name` speed test to use the :ref:`optimize@options@no_cumulative_sum_op` optimization option (because it is faster for this case). 06-10 ===== #. The autotools configure script has been replaced by :ref:`configure-name` which just maps the configure script interface to the :ref:`cmake-name` interface. The corresponding autotools files were removed. #. In some cases there was a problem linking the adolc library to the :ref:`speed_adolc-name` tests. This has been fixed. 06-07 ===== Add links to :ref:`Install@Generator`, :ref:`Install@cppad.spec`, and :ref:`Install@xrst` in the CppAD install instructions. 06-04 ===== Fix a warning form the Trilinos Kokkos package when including :ref:`sacado_prefix-name` on the cmake command line. 06-01 ===== The ``cmake_needs_dot_slash`` symbol was removed from the cmake command line. It was a kludge for msys2 systems. The proper fix is to use {xrst_code sh} pacman -S mingw-w64-x86_64-cmake {xrst_code} ( not use ``pacman -S cmake`` ) to install cmake on an msys2 system. 05-31 ===== There was an error building the old version of the sacado library using :ref:`get_sacado.sh-name` with g++-13.1.1. This has been fixed by advancing to trilinos-14-0-0 and patching one of its include files; see trilinos issue `11923 `_ . 05-19 ===== Fix ``uintptr_t`` not defined (on some systems) by adding the following before using it: ``# include `` . 05-18 ===== Remove the unnecessary include directory ``/usr/include`` from the :ref:`speed_cppad_jit-name` speed tests. This was including multiple versions of some system include files on the msys system. 04-17 ===== Extend the CppAD::vector CppAD::vectorBool template classes so that the :ref:`CppAD_vector@resize` argument can be an ``int`` (this was causing a warning with the clang++ compiler). Also extend :ref:`CppAD_vector@vectorBool` so that any index type, that can be converted to size_t, can be used to access elements (without a clang++ warning). 04-01 ===== Extend index_sort so that the :ref:`index_sort@ind` vector can have elements of any integral type. 03-04 ===== There were some problems running :ref:`cmake_check-name` when :ref:`cmake@include_ipopt` was true (and certain combination of other cmake options). This has been fixed. 02-08 ===== A :ref:`Discrete-name` function is now initialized by the ``CPPAD_DISCRETE_FUNCTION`` macro instead of later the first time it is used. 02-07 ===== The atomic four rev_depend argument :ref:`atomic_four_rev_depend@ident_zero_x` was not catching all the identically zero values. This has been fixed. It was not a bug but was not as efficient when an atomic four function used this information to reduce the work. 02-05 ===== Merge in pull request 168. This fixes an issue with :ref:`link_dll_lib-name` on windows when building with unicode. 01-15 ===== The following atomic four ``rev_depend`` problems have been fixed. #. The atomic four :ref:`atomic_four_rev_depend@ident_zero_x` argument can be used to reduce the number of depend_x (not depend_y) values. #. The *ident_zero_x* argument should have been ``const`` in the prototype. #. The :ref:`atomic_four_vector-name` and :ref:`atomic_four_mat_mul-name` examples used the :ref:`atomic_four_rev_depend@Syntax@Deprecated 2022-05-10` version of the ``rev_depend`` callback; i.e., the one without the *ident_zero_x* argument. 01-12 ===== Fix a problem with the documentation conversion on :ref:`2022@mm-dd@12-16` of 2022. To be specific: ``name_1<`` *name_2* > was converted to ``name_1`` < *name_2* > 01-11 ===== The :ref:`CppAD_vector@Initializer Constructor` was added to the ``CppAD::vector`` template class. 01-07 ===== Print an error message and exit from the cmake command when a :ref:`cmake@package_prefix` does not contain any include directories or libraries. {xrst_end 2023} ================================================ FILE: appendix/whats_new/2024.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2024 app} {xrst_spell bitwise boostvector bostvector cassert cmd dismod dll env fadbad homebrew iostream ipopt isnan nul nx omhelp py sacado setvec svec usr xcode yy yyyy } Release Notes for 2024 ###################### mm-dd ***** 12-14 ===== Add a separate :ref:`cmake@CMake Command@true_or_false` paragraph to describe *true_or_false* in the cmake command. 12-09 ===== An option to build a local copy of the user or developer documentation was added to the install procedure; see :ref:`cmake@include_doc` . This also affects the :ref:`cmake@cmake_install_docdir` option. 11-23 ===== In the :ref:`atomic_four_norm_sq.cpp-name` example, remove dead code in its forward routine. To be specific, remove:: // Assume we are not using forward mode with order > 1 assert( ! ok ); return ok; Also in its reverse routine, improve the comment about the relation between G and H; to be specific:: // H( {x_j^k} ) = G[ F( {x_j^k} ), {x_j^k} ] 11-19 ===== Fix the :ref:`configure@--with-clang` and :ref:`configure@--with-verbose-make` options did not have any effect in the configure script. This has been fixed. 11-16 ===== The :ref:`for_hes_sparsity-name` and :ref:`ForSparseHes-name` routines did not handle the cumulative summation operators properly. These operators sum more than two variables or dynamic parameters at a time and may be created during ADFun :ref:`optimization ` . 11-13 ===== The :ref:`for_hes_sparsity-name` and :ref:`ForSparseHes-name` routines did not handle the :ref:`VecAD-name` load and store operations properly. This has been fixed. 11-12 ===== Remove extra unintended white space below Syntax headings that was introduced when the CppAD documentation was converted from omhelp to sphinx; see :ref:`2022-12-16<2022@mm-dd@12-16>` . For example: Old Syntax ---------- ``atomic_user`` *a_square_root* *a_square_root* ( *au* , *ay* ) New Syntax ---------- | ``atomic_user`` *a_square_root* | *a_square_root* ( *au* , *ay* ) 10-04 ===== The :ref:`jit_compile.cpp-name` example line was missing CPPAD_C_COMPILER_CMD in the line (this has been fixed):: const char* cmd = CPPAD_C_COMPILER_CMD " --version > temp"; 10-02 ===== #. The *include_eigen* argument was removed from the :ref:`cmake-name` command because Eigen is available using most package managers; see :ref:`cmake@Eigen` . #. Remove *boost_dir* and *eigen_dir* from :ref:`configure-name` (they no longer have any effect). 09-28 ===== Fix some conversion warnings between ``size_t`` , ``int`` , and ``short`` in the :ref:`speed_adolc-name` examples / tests. 09-24 ===== Convert the bitwise operators & and | , when used with bool operands, to the logical operations && and ||; see ``bin/no_bitwise.sh`` (this fixes many warnings). In addition, fix some other warnings and put some exceptions on ``bin/check_all.sh`` for some warnings that seem to be incorrect. 09-13 ===== #. Make all of the get_optional.sh routines now works on MacOS with homebrew, see :ref:`get_optional.sh@MacOS with Homebrew` . Do not test :ref:`colpack_prefix-name` on MacOS (there seems to be a problem with the ColPack install on that system). #. The :ref:`configure@--with-verbose-make` option was added to the configure script. In addition, :ref:`configure-name` was mistakenly requiring that ColPack was installed; see :ref:`colpack_prefix-name`. This has been fixed. 09-11 ===== #. The :ref:`get_ipopt.sh@Source Directory` was changed from ``external/Ipopt-``\ *version* to ``external/Ipopt,git`` . #. Advance get_ipopt :ref:`get_ipopt.sh@Version` from 3.13.2 to 3.14.16. 09-09 ===== #. The :ref:`cmake@CMake Command@Visual Studio` instructions were improved. #. ``CppAD`` was added before ``isnan`` , see the discussion of :ref:`nan@std::isnan` . 08-10 ===== There was a problem with :ref:`CppAD_vector@iterators` that caused the sort test in :ref:`cppad_vector.cpp-name` to fail when using Visual C++ on Windows or Xcode C++ on MacOS. To be specific, this was the case where the iterator needs to be ``const`` , even though the value it points to is not ``const`` . This has been fixed. 08-09 ===== #. Suppress the testing of sort using CppAD vector iterators on MacOs with the Xcode C++ compiler; search for CPPAD_CXX_IS_XCODE in :ref:`cppad_vector.cpp-name` . #. The :ref:`jit_compile.cpp-name` example was hanging on some Windows systems because they do not support writing to the``nul`` device. This has been fixed by writing to temporary files. 08-08 ===== Add the :ref:`dos_build.bat-name` install example. 08-05 ===== The cmake ``-D eigen_prefix`` option was removed and ``PKG_CONFIG_PATH`` requirements were added for ``include_eigen`` (and the other packages). 08-04 ===== Standard threading was added to the multi-threading examples; see :ref:`a11c_sthread.cpp-name` , :ref:`sthread_get_started.cpp-name` , :ref:`sthread_get_started.cpp-name` , :ref:`team_sthread.cpp-name` . 05-12 ===== A bug in the use of base types that are not ordered (e.g., :ref:`complex ` ) was introduced on :ref:`2023@mm-dd@08-10` of 2023, when the val_graph branch was merged in the master branch. This has been fixed by requiring such base types to define the ordering compare operations as resulting in an error; see :ref:`base_cond_exp@CondExpTemplate@Not Ordered` . 04-25 ===== The :ref:`configure-name` script was changed to detect when the eigen package is requested and the compiler is not c++14 or greater. The :ref:`configure-name` and :ref:`cmake-name` scripts were changed to detect when the sacado package is requested and the compiler is not c++17 or greater. 04-20 ===== The ``# include`` commands were missing at the top of some of the valvector examples. This has been fixed; e.g., see ref:`valvector_get_started-name` . 04-04 ===== The file example/multi_thread/bthread/get_started.cpp had an error when ``CPPAD_TESTVECTOR`` was :ref:`testvector@std::vector` (The elements of a standard vectors of bool may a single bits instead of a boolean value). The has been fixed. 04-03 ===== #. Add the :ref:`valvector_llsq_obj.cpp-name` example. #. Add the :ref:`valvector_sum.cpp-name` member function. #. Fix the titles :ref:`valvector_ad_sum.cpp-title` and :ref:`valvector_ad_join.cpp-name` . #. Add the following to :ref:`valvector_base_require.cpp-name` : {xrst_code cpp} x = valvector( {0.0, 0.0} ); ok &= CppAD::IdenticalZero(x); {xrst_code} 03-28 ===== Change the get_eigen.sh version from 3.3.7 to the master branch on 2024-03-24; see `issue 200 `_ . Note that the new version of eigen requires c++14 or higher. 03-23 ===== #. Change the upper case one letter variable names in the :ref:`interp_onetape.cpp-name` example; to be specific, *X* -> *ax* and *Y* -> *ay* , *A* -> *ax_grid* , *F* -> *af_grid* , *I* -> *ay_linear* . #. Add the :ref:`atomic_four_bilinear.cpp-name` example. 03-19 ===== Fix broken links found using ``--external_links`` option to the ``xrst`` documentation generator. 03-16 ===== The :ref:`ADFun constructor` was changed to use *ax* instead of *x* , and *ay* instead of *y* , for AD values. In addition, the :ref:`fun_construct@Sequence Constructor` discussion was improved. 03-15 ===== #. The user's choice of :ref:`ta_parallel_setup@in_parallel` routine was not being called properly in some cases. This has been fixed. This would cause CppAD to not properly report some usage errors during parallel execution. #. Fixing the problem above exposed the fact that, when mixing debug and release builds, :ref:`CheckSimpleVector-name` must be defined. This has also been fixed. #. The discussion, in the multi-threading get_started examples, about the :ref:`openmp_get_started.cpp@ADFun Constructor` was improved. #. The parallel_ad routine has been improved so that it initializes CheckSimpleVector for more cases; see :ref:`parallel_ad@CheckSimpleVector` . 03-13 ===== The :ref:`bthread_get_started.cpp-name` and :ref:`pthread_get_started.cpp-name` examples were made simpler by directly using the threading system instead of going through the :ref:`team_thread.hpp-name` interface. 03-12 ===== The :ref:`openmp_get_started.cpp-name` example was made simpler by directly using openmp instead of going through the :ref:`team_thread.hpp-name` interface. 03-11 ===== The multi-threading ``simple_ad`` examples where changed to ``get_started`` ; see :ref:`openmp_get_started.cpp-name` , :ref:`pthread_get_started.cpp-name` , :ref:`bthread_get_started.cpp-name` . 03-09 ===== The number of :ref:`parallel_ad@CheckSimpleVector` cases called by parallel_ad() was increased to include the following:: CheckSimpleVector< bool, CppAD::vectorBool >() CheckSimpleVector< size_t, CppAD::vector >() CheckSimpleVector< Base, std::vector >() CheckSimpleVector< AD, std::vector< AD > >() In addition, the CheckSimpleVector :ref:`CheckSimpleVector@Parallel Mode` documentation and error message has been improved. 03-06 ===== #. The ``to_csrc`` parameters :ref:`to_csrc@nu` and :ref:`to_csrc@u` were denoted by ``nx`` and ``x`` in the generated source code. This has been changed an now the generated source uses ``nu`` and ``u`` for these parameters. #. The :ref:`to_csrc-name` documentation was changed to use *atomic_name* for the name of the atomic (instead of reusing *function_name* which has another purpose). In addition, :ref:`jit_atomic.cpp-name` was also changed to use *atomic_name* . 03-02 ===== Add the :ref:`valvector_ad_split-name` and :ref:`valvector_ad_join-name` examples. 03-01 ===== Fix bug in the optimizer :ref:`optimize@options@val_graph` option. To be more specific, the following assert could occur during the optimization:: vector: index greater than or equal vector size 02-29 ===== Add the :ref:`valvector_compare_op@op@Ordered Operators` to the valvector class. This is needed to optimize tapes where the base type is valvector. 02-28 ===== Add the :ref:`valvector-name` example base class. 02-27 ===== The omhelp package is not longer used by CppAD ( see :ref:`2022@mm-dd@12-16` ) so the links to its documentation were removed. In addition, the instructions for :ref:`download@Git@Building Documentation` were out of date and have been fixed. 02-26 ===== #. Correct some errors in the numeric type :ref:`NumericType@Operators` table. Also the table under :ref:`CondExp@Purpose` on the conditional expression documentation. (These were errors in the automatic conversion from omhelp to sphinx.) #. The ``<=`` operator was removed from the base type requirements :ref:`base_member@Bool Operators` . 02-23 ===== Include more of the example drivers in the documentation; see :ref:`example_driver-name` . In addition, remove unnecessary includes for ``iostream`` and ``cassert`` from these drivers. 02-22 ===== #. The shebang in the :ref:`configure-name` script was changed from ``#! /bin/bash`` to ``#! /usr/bin/env bash`` . This should make it work on more systems. #. The configure script *eigen_dir* , *fadbad_dir* , and *sacado_dir* directories were not being passed to the ``cmake`` . This has been fixed. #. The configure ``--help`` option for using boost vectors was corrected from ``--with-bostvector`` to ``--with-boostvector`` . 02-21 ===== For each year, change the web page name ``whats_new_``\ *yy* to *yyyy* where *yy* and *yyyy* identify the year. In addition, change the title for these pages to Release Notes for the corresponding year. This breaks old links to these web pages. For example, the old link to the heading directly below was:: https://cppad.readthedocs.io/latest/whats_new_24.html#whats-new-24-mm-dd-02-14 Its new link, which is much shorter, is:: https://cppad.readthedocs.io/latest/2024.html#mm-dd-02-14 02-14 ===== The :ref:`optimize-name` routine could crash when checking if an operator with the same hash code was a match with the argument order switched (for add and multiply). This problem occurred during speed.py_ , which is an example and test of dismod_at, which uses CppAD. This crash required a very special (and improbable) case that we were unable to reproduce in an automated regression test; see comment that starts with 2024-02-14 in `include/cppad/local/optimize/match_op.hpp` . .. _speed.py: https://dismod-at.readthedocs.io/latest/user_speed.py.html 01-16 ===== fix dll linking of jit code by changing *cppad_c_compiler_path* to *cppad_c_compiler_cmd* (spaces in the path were making commands fail). 01-12 ===== #. There were some problems using the cmake C compiler for the examples and tests of jit compilation, so that has been backed out for now. #. There was a mistake in detecting if size_t is the same as :ref:`cmake@cppad_tape_addr_type`. This could cause a compile error in `op_hash_table.hpp` and has been fixed. 01-11 ===== Use the *cppad_c_compiler_path* detected by :ref:`cmake-name` to run the C compiler for the Clang and GNU cases; see the `create_dll_lib`` defaults for :ref:`create_dll_lib@options@compile` and :ref:`create_dll_lib@options@link` . 01-10 ===== #. Fix some problems with the install on 32 bit systems. To be specific, the :ref:`thread_alloc-name` memory allocator was not properly aligning doubles. #. Suppress the test of the ``svec_setvec`` class. (The class has a bug and is not currently being used.) 01-08 ===== On some systems ``unsigned int`` and ``size_t`` are the same type and this would cause a compile error in ``vector.hpp`` and ``vector_bool.hpp`` . This has been fixed. 01-07 ===== The forward_two :ref:`forward_two@Syntax` was corrected. To be specific, ``Forward`` (1, *x2* ) was changed to ``Forward`` (2, *x2* ) . In addition, some other minor improvements were made to the forward_two documentation page. 01-03 ===== #. The change on :ref:`2023@mm-dd@12-24` 2023, when :ref:`cmake@cppad_debug_and_release` was implemented, was not done properly and might cause a mistaken assertion. This has been fixed. #. Each *file_name* in the latest documentation was moved (this is important if you have links to previous web pages): .. list-table:: * - Old Location - ``https://cppad.readthedocs.io/``\ *file_name* * - New Location - ``https://cppad.readthedocs.io/latest/``\ *file_name* 01-02 ===== #. The :ref:`compare_change.cpp-name` example/test would sometimes fail when :ref:`cmake@cppad_debug_which` was ``debug_even`` or ``debug_odd`` (because it was compiled for debugging and including a release version of a utility). This has been fixed by making this example/test a separate program. #. The :ref:`CppAD_vector@vectorBool` constructor would generate a warning when called with an ``int`` for the size of the vector (on some compilers). This has been fixed. {xrst_end 2024} ================================================ FILE: appendix/whats_new/2025.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2025 app} {xrst_spell dll jit rpath msys dev Woverloaded azmul toml } Release Notes for 2025 ###################### mm-dd ***** 12-30 ===== Previously, CppAD only checked for spelling errors in the documentation. The `typos`_ package was used to fix many spelling errors in the code (including example code). This was done in two commits. #. The `first commit`_ only included the errors where typos provided a single correction and the error had four or more letters. #. The second commit included changes that were individually reviewed. In addition the file ``typos.toml`` was added (this defines some CppAD typos spelling exceptions). .. _typos: https://github.com/crate-ci/typos {xrst_spell_off} .. _first commit: https://github.com/coin-or/CppAD/commit/ 95146e99838fd908833846a85c72dde882b46f6a {xrst_spell_on} 12-14 ===== #. Let p denote a dynamic parameter, z denote the constant zero, and e denote the constant one. The number of dynamic parameter operations was reduced by recognizing the following special cases (where p is a dynamic parameter: {xrst_code text} p = p + z p = p - z p = p * e p = p / e e = pow(p, z) z = pow(z, p) z = azmul(z, p) z = azmul(p, z) {xrst_code} #. The :ref:`chkpoint_one-name` source code, when compiled with g++ version 15.2.1, was causing the following warnings: {xrst_code text} ... rev_sparse_hes ... was hidden [-Woverloaded-virtual=] ... rev_sparse_jac ... was hidden [-Woverloaded-virtual=] ... for_sparse_jac ... was hidden [-Woverloaded-virtual=] {xrst_code} This has been fixed. 12-07 ===== #. The :ref:`pow-name` function was identically zero (one), when the base (exponent) during recording was the dynamic parameter zero; see issue 242. This has been fixed. #. Update the development tools (see dev_tools.sh in xrst repository). 06-23 ===== The default value for :ref:`cmake@cmake_install_libdirs` was changed from ``lib`` to ``lib;lib64`` so that ``lib64`` is also searched for library files. 04-18 ===== Increase size of matrices in the :ref:`det_minor` speed tests. 04-01 ===== Fix a :ref:`conditional expressions` bug that affect :ref:`optimize-name` and :ref:`subgraph_sparsity-name` . optimizing functions with 03-21 ===== If :ref:`cmake@cppad_tape_addr_type` was chosen to be `size_t` , a multiple definition of ``setup_random`` compiler error would occur. This has been fixed. 02-09 ===== Systems that use windows dll files, (msys, cygwin, and windows) would always build a static version of the cppad library; i.e., the `cmake@cppad_static_lib` flag was ignored in this case. This has been changed so that the cppad_static_lib flag is always respected. The default value for *cppad_static_lib* on msys, cygwin, and windows is true and on other systems it is false; i.e., if you do not specify cppad_static_lib you will get the same result as before this change. 02-02 ===== The rpath in may not have been set properly in :ref:`cmake@cmake_install_libdirs@cppad_lib` because ``CMAKE_INSTALL_RPATH`` was not set. This has been fixed its value is printed during the cmake command. 01-24 ===== #. Fix linking the pthread examples ( e.g. :ref:`pthread_get_started.cpp-name` ) when the pthread library is in a non-standard location. #. Fix linking the standard thread examples ( e.g. :ref:`sthread_get_started.cpp-name` ) when the standard thread library required the pthread library. 01-17 ===== #. The :ref:`cmake@cppad_link_flags` option was added to the cmake command. #. The detection of when ``unsigned int`` and ``size_t`` were the same, by the cmake command, was incorrect in cases where the compiler flags affected this result. #. Fix some errors of the form *function_name*\ ``(unsigned int)`` cannot be overloaded with *function_name*\ ``(size_t)`` . #. The :ref:`dll_lib.cpp@Restrictions` , on when the dll_lib.cpp example is tested were documented. #. The :ref:`example_jit@Restrictions` was added to the jit example documentation. 01-10 ===== Split the external links at the top of the user guide into: :ref:`user_guide@Git Repository` , :ref:`user_guide@Versions` , :ref:`user_guide@Other Links` . {xrst_end 2025} ================================================ FILE: appendix/whats_new/2026.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-26 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin 2026 app} {xrst_spell rst } Release Notes for 2026 ###################### mm-dd ***** 04-08 ===== #. Change the cppad source code and documentation to use 4 spaces for one tab; i.e., one level of indentation. This used to be 3 spaces because that works well with rst lists (the cppad documentation uses rst), but 4 spaces is the most common choice used by programmers. #. Rename the default git branch from master to main. 01-03 ===== More spelling corrections in the source code comments found by a newer version of the typos package; see :ref:`2025@mm-dd@12-30` whats new for 2025. {xrst_end 2026} ================================================ FILE: appendix/whats_new/whats_new.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-23 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin whats_new app} CppAD Release Notes ################### Introduction ************ The sections listed below contain a list of the changes to CppAD in reverse order by date. The purpose of these sections is to assist you in learning about changes between various versions of CppAD. {xrst_toc_hidden appendix/whats_new/2026.xrst appendix/whats_new/2025.xrst appendix/whats_new/2024.xrst appendix/whats_new/2023.xrst appendix/whats_new/2022.xrst appendix/whats_new/2021.xrst appendix/whats_new/2020.xrst appendix/whats_new/2019.xrst appendix/whats_new/2018.xrst appendix/whats_new/2017.xrst appendix/whats_new/2016.xrst appendix/whats_new/2015.xrst appendix/whats_new/2014.xrst appendix/whats_new/2013.xrst appendix/whats_new/2012.xrst appendix/whats_new/2011.xrst appendix/whats_new/2010.xrst appendix/whats_new/2009.xrst appendix/whats_new/2008.xrst appendix/whats_new/2007.xrst appendix/whats_new/2006.xrst appendix/whats_new/2005.xrst appendix/whats_new/2004.xrst appendix/whats_new/2003.xrst } This Year ********* :ref:`2026-name` Previous Years ************** :ref:`2025-name` , :ref:`2024-name` , :ref:`2023-name` , :ref:`2022-name` , :ref:`2021-name` , :ref:`2020-name` , :ref:`2019-name` , :ref:`2018-name` , :ref:`2017-name` , :ref:`2016-name` , :ref:`2015-name` , :ref:`2014-name` , :ref:`2013-name` , :ref:`2012-name` , :ref:`2011-name` , :ref:`2010-name` , :ref:`2009-name` , :ref:`2008-name` , :ref:`2007-name` , :ref:`2006-name` , :ref:`2005-name` , :ref:`2004-name` , :ref:`2003-name` . {xrst_end whats_new} ================================================ FILE: appendix/wish_list.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin wish_list app} {xrst_spell adolc grep tapeless } The CppAD Wish List ################### See Also ******** :ref:`research-name` Purpose ******* The items on this list are improvements and extensions to CppAD that are currently being considered. base2ad ******* It would be nice if :ref:`base2ad-name` functioned as expected with :ref:`VecAD-name` operations; see :ref:`base2vec_ad.cpp-name` . Dynamic Parameters ****************** Comparison Operators ==================== The comparisons for dynamic parameters are not being included in the :ref:`Independent@record_compare` is true. This should be fixed and included in the :ref:`compare_change@number` returned by *f* . ``compare_change`` . In addition, we will need a way to know if :ref:`compare_change@op_index` corresponds to a variable or dynamic parameter operator. VecAD Vectors ============= Currently, when a :ref:`VecAD-name` vector only depends on dynamic parameters it becomes a variable; see :ref:`VecAD@Efficiency` . This is a simple solution to the problem of having to pass the state of a vector, when it becomes a variable, from a dynamic sweep to a zero order forward mode sweep. Graph Operators *************** The :ref:`graph_op_enum@Missing Operators` should be implemented so that they can be include in conversion between :ref:`ADFun-name` objects and the AD graphs; see :ref:`cpp_ad_graph-name` , :ref:`json_ad_graph-name` . Reverse Mode ************ Reverse mode calculation of the function :math:`f : \B{R} \rightarrow \B{R}` defined by :math:`y = f(x)` where *ay* [0] = ``pow`` ( *ax* [0], 0.5) * ``pow`` ( *ax* [0], 0.5) yields the result zero when :math:`x_0 = 0`; see the file ``bug/pow.sh`` . This is a feature of using :ref:`azmul-name` to select which components of a function are differentiated. This enables one component to yield :ref:`nan-name` for a partial derivative while another might not. If a separate flag was used for which variables are selected, reverse mode multiplications would not need to be converted to ``azmul`` and the function above would return ``nan`` for the derivative value. This may also be faster that using ``azmul`` . Atomic Examples *************** Convert the remaining :ref:`atomic_two_examples` , and :ref:`atomic_three_examples` to use the :ref:`atomic_four-name` interface. {xrst_comment -------------------------------------------------------------- } Abs-normal ********** Atomic Functions ================ The :ref:`abs_normal_fun-name` conversion does not handle atomic functions. This should be fixed. Return Functions ================ Change the :ref:`abs_normal_fun-name` to return the functions :ref:`abs_normal_fun@g@z(x, u)` and :ref:`abs_normal_fun@g@y(x, u)` instead of :math:`g(x, u)` and :math:`a(x)`. We can add a utility that computes :math:`a(x)` using :math:`z(x, u)`, :math:`a_i (x) = | z_i (x, a(x) ) |` and :math:`z_i` does not depends on :math:`u_j` for :math:`j \geq i`. Cancellation ============ Avoid cancellation when computing the difference in the absolute value function at the current point :math:`\hat{x}` the displaced point :math:`x = \hat{x} + \Delta x`; i.e., .. math:: |z_i (x, \tilde{a}(x) ) | - |z_i (\hat{x}, a(\hat{x}) ) | {xrst_comment -------------------------------------------------------------- } cppad_lib ********* Requirement =========== Currently ``cppad_lib`` library is only needed if one uses :ref:`colpack` , :ref:`json_ad_graph-name` , :ref:`cpp_ad_graph-name` , or :ref:`code_gen_fun-name` .. inline ====== The C++ ``inline`` specifier is used to avoid multiple copies of functions (that are not templates) during the link process. Perhaps some of these functions should be regular functions and part in the :ref:`colpack_prefix@cppad_lib` library. Compilation Speed ================= Perhaps complication speed when using ``AD`` could significantly be increased by including some of it's member functions in the cppad_lib library. {xrst_comment ------------------------------------------------------------- } checkpoint ********** Tapeless AD =========== Perhaps there should be a version of the :ref:`chkpoint_two-name` class that uses a tapeless AD package to compute the derivative values. This would allow for algorithms where the operations sequence depends on the independent variable values. There is a question as to how sparsity patterns would be determined in this case. Perhaps they would be passed into the constructor. If it was known to be constant, the user could compute the pattern using CppAD. Otherwise, the user could input a conservative estimate of the pattern that would be correct. Re-taping ========= Perhaps the ``checkpoint`` class should allow for re-taping derivative values. This would also allow for algorithms where the operations sequence depends on the independent variable values. Perhaps (as for tapeless entry above) the sparsity pattern should be passed into the constructor. Testing ======= There should be some examples and tests for both speed and memory use that demonstrate that checkpointing is useful. {xrst_comment -------------------------------------------------------------- } Subgraph ******** Forward Mode ============ The :ref:`subgraph_jac_rev-name` routine computes sparsity patterns of Jacobians using reverse mode. It is possible that a forward mode version of this method would be better for some cases. Sparsity ======== The :ref:`subgraph_sparsity-name` calculation treats each atomic function call as if all of its outputs depend on all of its inputs; see :ref:`subgraph_sparsity@Atomic Function` . These sparsity patterns could be made more efficient (could have fewer possibly non-zeros entries) by using the sparsity patterns for the atomic functions. {xrst_comment -------------------------------------------------------------- } check_finite ************ #. Sometimes one only gets infinite value during zero order forward and nan when computing corresponding derivatives. Change :ref:`check_for_nan-name` to ``check_finite`` (not infinite or nan) so that error detection happens during zero order forward instead of later. #. In addition, the current :ref:`check_for_nan-name` writes the corresponding zero order values to a temporary file. It would be nice if the ``check_finite`` routine made writing the zero order values optional. test_boolofvoid *************** For general purpose use, the :ref:`test_boolofvoid-name` should be usable without including a memory check at the end. Example ******* Split the :ref:`example list` into separate groups by the corresponding example subdirectory. {xrst_comment ------------------------------------------------------------ } Optimization ************ Atomic Functions ================ There is some confusion as to the value of the Taylor coefficients for atomic function arguments and results that have been optimized out. See :ref:`optimize@Atomic Functions` in optimize, :ref:`2021@mm-dd@02-16` in whats new for 2021, :ref:`atomic_three_rev_depend@depend_x@Optimize` in atomic_three, and :ref:`atomic_four_rev_depend@depend_x@Optimize` in atomic_four. Taping ====== Perhaps some of the optimization done while taping forward mode should be delayed to the ``optimization`` step. Special Operators ================= Add special operators that can be implemented more efficiently, e.g., ``square`` ( *x* ) = *x* * *x* and have the optimizer recognize when they should be used. (They could also be in the user API, but it would not be expected that the user would use them.) {xrst_comment --------------------------------------------------------------} Base Requirements ***************** Change the :ref:`Base requirements` to use template specialization instead of functions so that there is a default value for each function. The default would result in a :ref:`cppad_assert@Known` assert when the operation is used and not defined by the base class. An example of this type of template specialization can be found in the implementation of :ref:`to_string-name` . Adolc ***** Create a documentation page that shows how to convert Adolc commands to CppAD commands. Forward Mode Recomputation ************************** If the results of :ref:`forward_order-name` have already been computed and are still stored in the :ref:`ADFun-name` object (see :ref:`size_order-name` ), then they do not need to be recomputed and the results can just be returned. Iterator Interface ****************** All of the CppAD simple vector interfaces should also have an iterator version for the following reasons: #. It would not be necessary to copy information to simple vectors when it was originally stored in a different type of container. #. It would not be necessary to reallocate memory for a result that is repeatedly calculated (because an iterator for the result container would be passed in). Tracing ******* Add tracing the operation sequence to the user API and documentation. Tracing the operation sequence is currently done by changing the CppAD source code. Use the command :: grep '^# *define *CPPAD_.*_TRACE' cppad/local/sweep\*.hpp to find all the possible tracing flags. atan2 ***** The :ref:`Atan2-name` function could be made faster by adding a special operator for it. {xrst_end wish_list} ================================================ FILE: appveyor.yml ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # # build platform, i.e. x86, x64, Any CPU. This setting is optional. platform: - x64 # branches to build on push branches: only: - master # msys2 environment environment: matrix: - HOST_ARCH_ARG: --host=x86_64-w64-mingw32 ADD_PATH: /mingw64/bin APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 # scripts that run after cloning repository install: - C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-x86_64-cmake" # # run custom scripts instead of automatic MSBuild build_script: - C:\msys64\usr\bin\bash -lc " cd $APPVEYOR_BUILD_FOLDER; export PATH=$PATH:$ADD_PATH; bin/appveyor.sh make check install uninstall " # notifications: - provider: Email to: - bradbell@seanet.com on_build_status_changed: true on_build_failure: true on_build_success: false ================================================ FILE: authors ================================================ Statement of CppAD Authorship and Copyright =========================================== Bradley M. Bell is the sole author of CppAD. While Bradley M. Bell worked for the University of Washington during the development of CppAD, the following are also true: 1. The CppAD package was not written or maintained as part of any sponsored grant at the University of Washington. 2. Bradley M. Bell was not paid by the University of Washington for the time he worked on CppAD. 3. Working on CppAD was not part of Bradley M. Bell's normal duties at for the University of Washington. Thus, in accordance with Section 2 of the University of Washington's copyright policy (see the file uw_copy_040507.html in this directory) Bradley M. Bell is the copyright holder for CppAD. ================================================ FILE: batch_edit.sed ================================================ # ---------------------------------------------------------------------------- # None of the lists below can have white space or a dollar sign in an entry. # # list of directories that are added to the repository by batch_edit.sh # new_directories=' # ' # list of files that are deleted by batch_edit.sh # delete_files=' # ' # List of files that are not edited by the sed commands in this file # (with the possible exception of the extra_seds commands). # The files in bin/devel.sh ignore_files are automatically in this list. # ignore_files=' # val_graph/comp_xam.cpp # val_graph/test/fun2val.cpp # include/cppad/local/val_graph/tape.hpp # ' # list of files and or directories that are moved to new names # move_paths=' # ' # list of sed commands that map old file and directory names to new names. # The characters @s, @d, @n get converted to a space, dollar sign, new line. # move_seds=' # ' # list of files that get edited by the extra_seds command # extra_files=' # ' # list of sed commands that are applied to the extra files, # after the other sed commands in this file. # The characters @s, @d, @n get converted to a space, dollar sign, new line. # extra_seds=' # ' # ---------------------------------------------------------------------------- # Put other sed commands below here and without # at start of each line /const tape_t\* *tape/! b one N N N N N N s|const tape_t\*\( *\)tape\( *\),|const tape_t*\1 tape\2 ,| s|\(\n *\)\([^ ]*\)\( *\)\([^ ]*\)\( *\),|\1\2\3 \4\5 ,|g s|\n\( *\)\([^ ]*\)\( *\)\([^ ]*\)\( *\))|\n\1Vector< Vector >\& val_vec_vec ,\ \1\2\3 \4\5 )| : one ================================================ FILE: bin/appveyor.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- if [ $0 != "bin/appveyor.sh" ] then echo 'bin/appveyor.sh: must be executed from its parent directory' exit 1 fi if [ "$1" != 'make' ] && [ "$1" != 'test_one' ] then echo 'usage: bin/appveyor.sh (make|test_one) target1 target2 ...' echo 'target: if make specified, is one of the available make commands' echo if test_one, specified, is the path to a test file. exit 1 fi cmd="$1" # ----------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ----------------------------------------------------------------------------- if [ -e build ] then echo_eval rm -r build fi echo_eval mkdir build echo_eval cd build echo_eval cmake \ -G '"Unix Makefiles"' \ -D cppad_prefix=$(pwd)/prefix \ -D CMAKE_C_COMPILER=gcc \ -D CMAKE_CXX_COMPILER=g++ \ .. # ----------------------------------------------------------------------------- # Microsoft DLLs must be in current directory or execution path PATH+=":$(pwd)/cppad_lib" # ----------------------------------------------------------------------------- # build target1, target2, ... if [ "$cmd" == 'make' ] then shift while [ "$1" != '' ] do echo_eval make "$1" shift done else echo_eval cd .. shift while [ "$1" != '' ] do echo_eval bin/test_one.sh "$1" shift done fi # ----------------------------------------------------------------------------- echo 'bin/appveyor.sh: OK' exit 0 ================================================ FILE: bin/build.bat ================================================ if exist build ( rmdir /S build ) mkdir build cd build cmake ^ -G "NMake Makefiles"^ -D cppad_cxx_flags="/MP /EHs /EHc /std:c++17 /Zc:__cplusplus"^ -D cppad_static_lib=TRUE^ .. nmake check ================================================ FILE: bin/check_addon.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- if [ "$0" != 'bin/check_addon.sh' ] then echo "bin/check_addon.sh: must be executed from its parent directory" exit 1 fi # ----------------------------------------------------------------------------- addon_list=' CG PY TMB MIXED ' grep_cmd='' for addon in $addon_list do if [ "$grep_cmd" == '' ] then grep_cmd="CPPAD_${addon}_" else grep_cmd+="|CPPAD_${addon}_" fi done # echo "Checking source code for names reserved for addon packages" echo "-------------------------------------------------------" ok="yes" file_list=`git ls-files` for file in $file_list do if grep -E $grep_cmd $file > /dev/null then echo "$file contains $grep_cmd" ok="no" fi done echo "-------------------------------------------------------" if [ "$ok" = "no" ] then echo 'bin/check_if.sh: Error' exit 1 else echo 'bin/check_if.sh: OK' exit 0 fi ================================================ FILE: bin/check_all.sh ================================================ #! /usr/bin/env bash set -e -u # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- # if [ "$0" != 'bin/check_all.sh' ] then echo "bin/check_all.sh: must be executed from its parent directory" exit 1 fi if [ $# == 1 ] then if [ "$1" == --help ] then cat << EOF bin/check_all.sh flags possible flags --mixed mix debug and release compiles --debug only compile for debugging --release only compile for release --verbose_make generate verbose makefiles --skip_external_links do not check documentation external links --skip_check_copy do not check copyright messages --suppress_spell_warnings do not check for documentation spelling errors EOF exit 0 fi fi # # build_type, verbose_make, skip_external_links, suppress_spell_warnings build_type='mixed' verbose_make='no' skip_external_links='no' skip_check_copy='no' suppress_spell_warnings='no' while [ $# != 0 ] do case "$1" in --mixed) build_type=mixed ;; --debug) build_type=debug ;; --release) build_type=release ;; --verbose_make) verbose_make='yes' ;; --skip_external_links) skip_external_links='yes' ;; --skip_check_copy) skip_check_copy='yes' ;; --suppress_spell_warnings) suppress_spell_warnings='yes' ;; *) echo "bin/check_all.sh: command line argument "$1" is not valid" exit 1 ;; esac # shift done # # grep and sed source bin/grep_and_sed.sh # # LD_LIBRARY_PATH # all linking of dynamic libraries should use rpath export LD_LIBRARY_PATH='' # # top_srcdir top_srcdir=`pwd` echo "top_srcdir = $top_srcdir" # # echo_eval echo_eval() { echo $* eval $* } # # echo_log_eval echo_log_eval() { echo "$* >& check_all.tmp" echo "$*" > $top_srcdir/check_all.tmp if ! $* >& $top_srcdir/check_all.tmp then tail $top_srcdir/check_all.tmp echo 'Error: see check_all.tmp' exit 1 fi # 1. If we don't have c++17 and mkstemp, then temp_file is not thread safe. # # 2. If using g++ -O3 -DNDEBUG -Wall, # an improper compile time warning is generated at: # forward.hpp:187, reverse.hpp:151, independent.hpp:100-109, # base_alloc.hpp:143, abs_min_quad.hpp:424 . # # warning warning='no' if [ "$compiler" == '--clang' ] then if $sed $top_srcdir/check_all.tmp \ -e '/temp_file.cpp:.*warning.*tmpnam/d' \ | $grep ': *warning *:' then warning='yes' fi else if $sed $top_srcdir/check_all.tmp \ -e '/temp_file.cpp:.*warning.*tmpnam/d' \ -e '/forward.hpp:187:.*warning.*outside array bounds/d' \ -e '/reverse.hpp:151:.*warning.*outside array bounds/d' \ -e '/independent.hpp:10[0-9]:.*warning.*outside array bounds/d' \ -e '/base_alloc.hpp:143:.*warning.*may be used uninitialized/d' \ -e '/abs_min_quad.hpp:424:.*bound.*exceeds maximum/d' \ | $grep ': *warning *:' then warning='yes' fi fi if [ "$warning" == 'yes' ] then echo "The warnings above happened during the command: $*" echo "see the file $top_srcdir/check_all.tmp" exit 1 fi # # check_all.log echo ' cat check_all.tmp >> check_all.log' cat $top_srcdir/check_all.tmp >> $top_srcdir/check_all.log } # # random_01 random_01() { set +e eval random_01_$1="`expr $RANDOM % 2`" eval echo "random_01_$1=\$random_01_$1" set -e } # # check_all.log # start new check_all.log echo "date > check_all.log" date | $sed -e 's|^|date: |' > check_all.log # # $HOME/prefix/cppad if [ -e "$HOME/prefix/cppad" ] then echo_log_eval rm -r $HOME/prefix/cppad fi # # version version=$( $sed -n -e '/^SET( *cppad_version *"[0-9.]*")/p' CMakeLists.txt | \ $sed -e 's|.*"\([^"]*\)".*|\1|' ) # # compiler random_01 compiler if [ "$random_01_compiler" == '0' ] then compiler='default' else compiler='--clang' fi # # standard random_01 standard if [ "$random_01_standard" == '0' ] then random_01 standard if [ "$random_01_standard" == '0' ] then standard='--c++11' else standard='--c++17' fi else standard='--c++17' fi # # use_configure random_01 use_configure if [ "$random_01_use_configure" == '0' ] then random_01 use_configure if [ "$random_01_use_configure" == '0' ] then use_configure='yes' else use_configure='no' fi else use_configure='no' fi # # package_vector, debug_which if [ "$build_type" == 'debug' ] then package_vector='--cppad_vector' debug_which='--debug_all' elif [ "$build_type" == 'release' ] then package_vector='--cppad_vector' debug_which='--debug_none' else if [ "$build_type" != 'mixed' ] then msg="build_type = $build_type not debug release or mixed" echo "bin/check_all.sh $msg" exit 1 fi random_01 debug_which if [ "$random_01_debug_which" == '0' ] then debug_which='--debug_even' else debug_which='--debug_odd' fi # random_01 package_vector if [ "$random_01_package_vector" == '0' ] then package_vector='--boost_vector' else if [ "$standard" == '--c++17' ] then package_vector='--eigen_vector' else package_vector='--std_vector' fi fi fi # # debug_which if [ "$use_configure" == 'yes' ] then debug_which='--debug_none' fi cat << EOF tarball = cppad-$version.tgz compiler = $compiler standard = $standard debug_which = $debug_which package_vector = $package_vector use_configure = $use_configure verbose_make = $verbose_make EOF cat << EOF >> $top_srcdir/check_all.log tarball = cppad-$version.tgz compiler = $compiler standard = $standard debug_which = $debug_which package_vector = $package_vector use_configure = $use_configure verbose_make = $verbose_make EOF # # compiler if [ "$compiler" == 'default' ] then compiler='' fi # # standard, exclude_package if [ "$standard" == '--c++17' ] then standard='' # default for run_cmake.sh and configure exclude_package='' else exclude_package='--no_sacado' fi if [ "$(uname)" == 'Darwin' ] then exclude_package+=' --no_colpack' fi # # prefix # absolute prefix where optional packages are installed eval `$grep '^prefix=' bin/get_optional.sh` if [[ "$prefix" =~ ^[^/] ]] then prefix="$(pwd)/$prefix" fi if [ ! -d $prefix/include/cppad/cg ] then echo "Cannot find $prefix/include/cppad/cg" echo 'Probably need to run bin/get_optional.sh' exit 1 fi # # typos if which typos >& /dev/null then if ! typos then echo 'check_all: see typos errors above' exit 1 fi fi # # check_version if echo $version | $grep '[0-9]\{4\}0000[.]' > /dev/null then # special interactive case for stable versions. echo_eval bin/check_version.sh else echo_log_eval bin/check_version.sh fi # # bin/check_*.sh # Run automated checks for the form bin/check_*.sh with a few exceptions. list=$( ls bin/check_* | $sed \ -e '/check_all.sh/d' \ -e '/check_doxygen.sh/d' \ -e '/check_install.sh/d' \ -e '/check_copy.sh/d' \ -e '/check_invisible/d' ) # echo_eval bin/check_invisible.sh if [ "$skip_check_copy" == 'no' ] then echo_eval bin/check_copy.sh fi for check in $list do echo_log_eval $check done # # run_xrst.sh flags='' if [ "$skip_external_links" == 'no' ] then flags+=' --external_links' fi if [ "$suppress_spell_warnings" == 'yes' ] then flags+=' --suppress_spell_warnings' fi bin/run_xrst.sh $flags # # build/cppad-$version.tgz echo_log_eval bin/package.sh # # build/cppad-$version echo_log_eval cd build echo_log_eval tar -xzf cppad-$version.tgz echo_log_eval cd cppad-$version # # build/cppad-$version/bin/get_optional.sh $sed -i bin/get_optional.sh -e "s|^prefix=.*|prefix=$prefix|" # # builder if [ "$use_configure" == 'yes' ] then builder='make' elif [ "$verbose_make" == 'yes' ] then builder='make' else builder='ninja' fi # # verbose_flag if [ "$verbose_make" == 'yes' ] then verbose_flag='--verbose_make' else verbose_flag='' fi # # configure or cmake if [ "$use_configure" == 'yes' ] then echo_log_eval bin/run_configure.sh \ $verbose_flag \ $compiler \ $standard \ $package_vector else echo_log_eval bin/run_cmake.sh \ $verbose_flag \ $compiler \ $standard \ $debug_which \ $exclude_package \ $package_vector fi echo_log_eval cd build # # n_job if which nproc >& /dev/null then n_job=$(nproc) else n_job=$(sysctl -n hw.ncpu) fi # # build: check echo_log_eval $builder -j $n_job check # # speed/cppad/speed_cppad for option in onetape colpack optimize atomic memory boolsparsity do echo_eval speed/cppad/speed_cppad correct 432 $option done # # speed/adolc/speed_adolc echo_eval speed/adolc/speed_adolc correct 432 onetape echo_eval speed/adolc/speed_adolc sparse_jacobian 432 onetape colpack echo_eval speed/adolc/speed_adolc sparse_hessian 432 onetape colpack # # bin/test_multi_thread.sh cd .. bin/test_multi_thread.sh $builder cd build # # print_for test # redo this build in case it is commented out above program='example/print_for/example_print_for' echo_log_eval $builder -j $n_job example_print_for echo_log_eval $program $program | $sed -e '/^Test passes/,$d' > temp.1.$$ $program | $sed -e '1,/^Test passes/d' > temp.2.$$ if diff temp.1.$$ temp.2.$$ then rm temp.1.$$ temp.2.$$ echo_log_eval echo "print_for: OK" else echo_log_eval echo "print_for: Error" exit 1 fi # # bin/test_install.sh echo_log_eval cd .. if [ "$standard" == '' ] then echo_log_eval bin/test_install.sh $builder --c++17 else echo_log_eval bin/test_install.sh $builder $standard fi # # echo "date >> check_all.log" date | $sed -e 's|^|date: |' >> $top_srcdir/check_all.log # ---------------------------------------------------------------------------- echo "$0: OK" >> $top_srcdir/check_all.log echo "$0: OK" exit 0 ================================================ FILE: bin/check_copy.sh ================================================ #! /usr/bin/env bash set -e -u # !! EDITS TO THIS FILE ARE LOST DURING UPDATES BY xrst.git/bin/dev_tools.sh !! # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2023-25 Bradley M. Bell # ---------------------------------------------------------------------------- # bin/check_copy.sh # Checks that the copyright message, in all the source files, # is correct and up to date. If there were any errors, a message is printed, # it is automatically corrected, and this script exits with an error. # Files that are not checked can be specified in bin/dev_setting.sh # ---------------------------------------------------------------------------- if [ "$0" != "bin/check_copy.sh" ] then echo "bin/check_copy.sh: must be executed from its parent directory" exit 1 fi if [ "$#" != 0 ] then echo 'check_copy does not expect any arguments' exit 1 fi # # grep, sed source bin/grep_and_sed.sh # # spdx_license_id, no_copyright_list source bin/dev_settings.sh # # yy yy=$(date +%y) # # ---------------------------------------------------------------------------- if [ $# != 0 ] then echo 'bin/check_copy.sh does not expect any arguments' exit 1 fi if [ "$0" != 'bin/check_copy.sh' ] then echo 'bin/check_copy.sh: must be executed from its parent directory' exit 1 fi if [ ! -e './.git' ] then echo 'bin/check_copy.sh: cannot find ./.git' exit 1 fi # --------------------------------------------------------------------------- # fullname fullname='' if [ "${USER+x}" != '' ] then for contributor in $contributor_list do if [[ $contributor == ${USER}* ]] then fullname=$(echo $contributor | sed -e 's|^.*:||' -e 's|_| |g') fi done if [ "$fullname" == '' ] && [ "${USER+x}" != '' ] then echo "Cannot user name = $USER in bin/dev_settings.sh contributor_list" exit 1 fi fi # --------------------------------------------------------------------------- # copyright_all, copyright_changed echo "#" > temp.sed for name in $no_copyright_list do if [ -f $name ] then echo "^$name\$" | $sed -e 's|/|[/]|g' -e 's|.*|/&/d|' >> temp.sed elif [ -d $name ] then echo "^$name/" | $sed -e 's|/|[/]|g' -e 's|.*|/&/d|' >> temp.sed else echo "$name in no_copyright_list is not a file or directory" exit 1 fi done copyright_all=$(git ls-files | $sed -f temp.sed) copyright_changed=$( git status --porcelain | $sed -e 's|^...||' | $sed -f temp.sed ) # --------------------------------------------------------------------------- missing='no' changed='no' for file_name in $copyright_all do # if file has not been deleted if [ -e $file_name ] then # if file does not have expected license identifier if ! $grep "$spdx_license_id\$" $file_name > /dev/null then if [ "$missing" == 'no' ] then echo "Cannot find line that ends with:" echo " $spdx_license_id" echo "In the following files:" fi echo "$file_name" missing='yes' fi fi done # --------------------------------------------------------------------------- cat << EOF > temp.sed /SPDX-FileContributor:[ 0-9.-]*$fullname/! b end s|\\([0-9]\\{4\\}\\)[-0-9]* |\\1-$yy | s|20$yy-$yy |20$yy | # : end EOF list='' if [ "$fullname" != '' ] then list="$copyright_changed" fi for file_name in $list do if [ -e $file_name ] && [ -f $file_name ] then if ! $grep "SPDX-FileContributor:[ 0-9.-]*$fullname" $file_name \ > /dev/null then echo "username = $USER, fullname = $fullname" echo "The following pattern does not appear in $file_name" echo 'SPDX-FileContributor:[ 0-9.-]*'$fullname: exit 1 fi $sed -f temp.sed $file_name > temp.$$ if diff $file_name temp.$$ > /dev/null then rm temp.$$ else if [ "$changed" == 'no' ] then echo 'The following file contributor dates have been updated' fi echo $file_name if diff $file_name temp.$$ then echo 'check_version.sh: program error' exit 1 fi changed='yes' if [ -x $file_name ] then mv temp.$$ $file_name chmod +x $file_name else mv temp.$$ $file_name fi fi fi done # if [ "$missing" = 'yes' ] || [ "$changed" == 'yes' ] then echo 'check_copy.sh: The copyright messages above were updated.' echo 'Re-execute bin/check_copy.sh ?' exit 1 fi echo 'bin/check_copy.sh: OK' exit 0 ================================================ FILE: bin/check_define.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- if [ $0 != 'bin/check_define.sh' ] then echo 'bin/check_define.sh: must be executed from its parent directory' exit 1 fi # ----------------------------------------------------------------------------- echo 'Check '# define' versus '# undef' names and check for addon names' echo '-----------------------------------------------------------------------' file_list=`git ls-files '*.hpp' '*.in' | sed -n -e '/^include\/cppad\//p'` add_on_list='CG PY TMB MIXED' # # preprocessor symbols in user API sed -n -e "/^# *undef /p" xrst/preprocessor.xrst | sed \ -e "s/^# *undef *\([A-Za-z0-9_]*\).*/\1/" > check_define.2 # for file in $file_list do include_guard=`echo $file | sed \ -e 's|^include/||' \ -e 's|\.in||' \ -e 's|/|_|g' \ -e 's|\.hpp|_hpp|' \ | tr [a-z] [A-Z] ` # define if [ ! -e $file.in ] then sed -n -e "/^# *define /p" $file | sed \ -e "/^# *define *$include_guard/d" \ -e '/^# define NOMINMAX/d' \ -e "s/^# *define *\([A-Za-z0-9_]*\).*/\1/" >> check_define.1 fi # undef if [ ! -e $file.in ] then # note is special sed -n -e "/^# *undef /p" $file | sed \ -e '/CPPAD_LOCAL_UTILITY_CPPAD_VECTOR_ITR_HPP/d' \ -e "s/^# *undef *\([A-Za-z0-9_]*\).*/\1/" >> check_define.2 fi # add_on for add_on in $add_on_list do if grep "CPPAD_${add_on}_" $file then add_on_error='true' fi done done # sort lists for file in check_define.1 check_define.2 do sort -u $file > check_define.3 mv check_define.3 $file done if ! diff check_define.1 check_define.2 then echo 'check_define.sh: Error: defines and undefs do not match' rm check_define.1 check_define.2 exit 1 fi rm check_define.1 check_define.2 echo '-----------------------------------------------------------------------' if [ "$add_on_error" == 'true' ] then echo 'check_define.sh: Error: add_on preprocessor symbol found' exit 1 fi echo 'check_define.sh: OK' exit 0 ================================================ FILE: bin/check_deprecated.sh ================================================ #! /bin/bash -eu # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ----------------------------------------------------------------------------- if [ "$0" != "bin/check_deprecated.sh" ] then echo "bin/check_deprecated.sh: must be executed from its parent directory" exit 1 fi if [ "$#" != '0' ] then echo 'usage: bin/check_deprecated.sh.sh' exit 1 fi # ----------------------------------------------------------------------------- file_list=`git ls-files example speed | sed -e '/example\/deprecated\//d'` # ----------------------------------------------------------------------------- # deprecated functions with not arguments list_just_name=' CPPAD_TRACK_ CPPAD_TEST_VECTOR CppADCreateUnaryBool CppADCreateDiscrete zdouble colpack.star ' list_namespace=' omp_alloc cppad_ipopt ' template_name=' epsilon ' list_no_argument=' Order Memory Size taylor_size use_VecAD size_taylor capacity_taylor CompareChange memory_leak ' list_one_argument=' nan Dependent omp_max_thread memory_leak ' list_two_argument=' ' list_three_argument=' ' for file in $file_list do for name in $list_just_name do if grep "$name" $file > /dev/null then echo "$name is deprecated and appreas in $file" exit 1 fi done for name in $list_namespace do if grep "[^a-zA-Z_]$name::" $file > /dev/null then echo "$name:: is deprecated and appreas in $file" exit 1 fi done for name in $list_namespace do if grep "using *$name[^a-zA-Z_]" $file > /dev/null then echo "using $name is deprecated and appreas in $file" exit 1 fi done for name in $template_name do if grep "[^a-zA-Z_]$name *< *[a-zA-Z_][a-zA-Z_]* *>" $file > /dev/null then echo "$name is deprecated and appreas in $file" exit 1 fi done for fun in $list_no_argument do if grep "[^a-zA-Z_]$fun *( *)" $file > /dev/null then echo "$fun() is deprecated and appreas in $file" exit 1 fi done for fun in $list_one_argument do if sed -e "s|bool *$fun(void)||" $file | \ grep "[^a-zA-Z_]$fun *( *[a-zA-Z_0-9.][a-zA-Z_0-9.]* *)" > /dev/null then echo "$fun(arg1) is deprecated and appreas in $file" exit 1 fi done for fun in $list_two_argument do if grep "[^a-zA-Z_]$fun *([^,)]*,[^,)]*)" $file > /dev/null then echo "$fun(arg1,arg2) is deprecated and appreas in $file" exit 1 fi done for fun in $list_three_argument do if grep "[^a-zA-Z_]$fun *([^,)]*,[^,)]*,[^,)]*)" $file > /dev/null then echo "$fun(arg1,arg2,arg3) is deprecated and appreas in $file" exit 1 fi done done # ----------------------------------------------------------------------------- echo 'bin/check_deprecated.sh: OK' exit 0 ================================================ FILE: bin/check_doxygen.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- if [ ! -e "bin/check_doxygen.sh" ] then echo "bin/check_doxygen.sh: must be executed from its parent directory" exit 1 fi # ----------------------------------------------------------------------------- if ! bin/run_doxygen.sh then echo 'check_doxygen.sh: Error' exit 1 fi echo 'check_doxygen.sh: OK' exit 0 ================================================ FILE: bin/check_example.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- if [ ! -e "bin/check_example.sh" ] then echo "bin/check_example.sh: must be executed from its parent directory" exit 1 fi # # grep, sed source bin/grep_and_sed.sh # ----------------------------------------------------------------------------- echo "Checking that all examples are in xrst/example_list.xrst" echo "-------------------------------------------------------" file_list=`git ls-files | $sed -n \ -e '/^example\/deprecated\//d' \ -e '/^example\//p'` # $sed < xrst/example_list.xrst > check_example.$$ \ -n -e '/[$]begin list_all_examples\$\$/,/\$end/p' # # Make sure all example names are of the form *.cpp or *.hpp check=`$sed -n -e '/$rref [0-9a-zA-Z_]*\.[hc]pp/d' \ -e '/$rref/p' check_example.$$` if [ "$check" != '' ] then echo $check echo 'Not all examples are for *.hpp or *.cpp files' exit 1 fi ok="yes" for file in $file_list do # only files in example directory with [$]begin *.cpp or *.hpp # e.g., example/multi_thread/harmonic.xrst has [$]begin harmonic.cpp$$ in it name=`$grep '[$]begin *[0-9a-zA-Z_]*\.[hc]pp' $file | $sed -e 's/.*[$]begin *//' -e 's/ *$$.*//'` if [ "$name" != "" ] then if ! $grep "$name" check_example.$$ > /dev/null then echo "$name is missing from xrst/example_list.xrst" ok="no" fi fi done rm check_example.$$ echo "-------------------------------------------------------" if [ "$ok" != "yes" ] then echo "Error: nothing should be between the two dashed lines above" exit 1 fi # # fix sort order; see # unix.stackexchange.com/questions/87745/what-does-lc-all-c-do/87763#87763 export LC_ALL='C' # dir_list=' example/graph example/json example/general ' for dir in $dir_list do echo "Checking $dir file versus example names" name=`echo $dir | $sed -e 's|.*/||'` # ls $dir/*.cpp | $sed \ -e "s|$dir/||" -e "/^$name\\.cpp/d" -e 's|\.cpp||' > check_example.1.$$ if [ "$name" == 'general' ] then list=`ls $dir/*.hpp \ | $sed -e "s|$dir/||" -e 's|\.hpp$||' -e '/^general$/d'` for file in $list do $sed -i check_example.1.$$ -e "/^$file\$/d" done fi $sed -n -e '/^extern bool [a-z0-9A-Z_]*(void);/p' $dir/$name.cpp \ | $sed -e 's/extern bool \([a-z0-9A-Z_]*\)(void);/\1/' \ | $sed -e 's/\([a-z]\)\([A-Z]\)/\1_\2/g' \ | tr '[A-Z]' '[a-z]' \ | sort > check_example.2.$$ if ! diff check_example.1.$$ check_example.2.$$ then rm check_example.1.$$ check_example.2.$$ echo "$dir: file and function names do not agree" echo 'see output above.' exit 1 fi done rm check_example.1.$$ check_example.2.$$ # echo 'bin/check_example.sh: OK' exit 0 ================================================ FILE: bin/check_if.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- if [ "$0" != 'bin/check_if.sh' ] then echo "bin/check_if.sh: must be executed from its parent directory" exit 1 fi # ----------------------------------------------------------------------------- # CppAD uses preprocessor '# if 0' comment blocks for temporary changes # that will to be removed before testing for check in. echo "Checking for '# if 0' and '# if NDEBUG' commands in source code" echo "-------------------------------------------------------" ok="yes" list=`git ls-files | sed -n \ -e '/\.cpp$/p' \ -e '/\.hpp$/p' \ -e '/\.hpp.in$/p'` for file in $list do if grep '^# *if *0 *$' $file > /dev/null then # CppAD uses /* comment */ for all its block comments echo "$file has an '# if 0' preprocessor command" ok="no" fi if grep '^# *if *NDEBUG *$' $file > /dev/null then # This should probably be # ifndef NDEBUG ? echo "$file has an '# if NDEBUG' preprocessor command" ok="no" fi done echo "-------------------------------------------------------" if [ "$ok" = "no" ] then echo 'bin/check_if.sh: Error' exit 1 else echo 'bin/check_if.sh: OK' exit 0 fi ================================================ FILE: bin/check_include_def.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- if [ ! -e "bin/check_include_def.sh" ] then echo "bin/check_include_def.sh: must be executed from its parent directory" exit 1 fi # --------------------------------------------------------------------------- echo "Differences between include file names and ifndef at top directives." echo "Also make sure same ifndef not used by two different files." echo "-------------------------------------------------------------------" list=`git ls-files | sed -n -e '/\.hpp$/p'` different='no' for file_name in $list do dir=`echo $file_name | sed -e 's|/[^/]*$||'` name=`echo $file_name | sed -e 's|^.*/||'` first_dir=`echo $dir | sed -e 's|/.*||'` # macro_name=`sed -n -e '/^# *ifndef *CPPAD_[0-9A-Z_]*_HPP$/p' $file_name | \ sed -e 's|^# *ifndef *||'` check=`echo $file_name | tr [a-zA-Z/.] [A-Za-z__]` # if [ "$first_dir" == 'include' ] then check=`echo $check | sed -e 's|INCLUDE_||'` else check="CPPAD_$check" fi # if [ "$macro_name" == '' ] then echo "file_name=$file_name" echo 'Cannot find ^# *ifndef *CPPAD_[0-9A-Z_]*_HPP' different='yes' elif [ "$macro_name" != "$check" ] then echo " file_name=$file_name" echo "macro_name=$macro_name" different='yes' fi done # echo "-------------------------------------------------------------------" if [ $different = 'yes' ] then echo "Error: nothing should be between the two dashed lines above" exit 1 else echo "Ok: nothing is between the two dashed lines above" exit 0 fi ================================================ FILE: bin/check_include_file.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- if [ ! -e "bin/check_include_file.sh" ] then msg="must be executed from its parent directory" echo "bin/check_include_file.sh: $msg" exit 1 fi # # $grep, sed source bin/grep_and_sed.sh # ----------------------------------------------------------------------------- # echo "Checking difference between C++ include directives and file names." echo "-------------------------------------------------------------------" if [ -e check_include_file.1.$$ ] then echo "bin/check_include_file.sh: unexpected check_include_file.1.$$" exit 1 fi list=`git ls-files | $sed -n \ -e '/\.cpp$/p' \ -e '/\.hpp$/p'` for file in $list do $sed -n $file \ -e '/^# *include *> check_include_file.1.$$ done # cat check_include_file.1.$$ | \ $sed -e 's%[^<]*<%%' -e 's%>.*$%%' | \ sort -u > check_include_file.2.$$ # # The following files should never be included: # cppad/local/var_op/prototype_op.hpp # cppad/local/optimize/define_prototype.hpp # All other files should. # # The files cppad/configure.hpp and cppad/local/is_pod.hpp # are not in git repository (they are built during configuration) git ls-files | $sed -n -e '/include\/cppad\/.*\.hpp$/p' | \ $sed \ -e '1,1s|^|include/cppad/configure.hpp\n|' \ -e '1,1s|^|include/cppad/local/is_pod.hpp\n|' \ -e '/include\/cppad\/local\/var_op\/prototype_op.hpp/d' \ -e '/include\/cppad\/example\/eigen_plugin.hpp/d' | \ $sed -e 's|^include/||' | \ sort -u > check_include_file.3.$$ # different='no' if ! diff check_include_file.2.$$ check_include_file.3.$$ > /dev/null then found='no' different='yes' for file in `cat check_include_file.2.$$` do if ! $grep "$file" check_include_file.3.$$ > /dev/null then found='yes' echo "The file include/$file is unknown to git." echo 'Perhaps it needs to be added with the command' echo " git add include/$file" fi done for file in `cat check_include_file.3.$$` do if ! $grep "$file" check_include_file.2.$$ > /dev/null then found='yes' echo "The included $file is no longer included." echo 'Perhaps it needs to be git deleted ?' fi done if [ "$found" == 'no' ] then echo 'bin/check_include_file.sh: Cannot find reason for difference' echo 'Improve this script.' exit 1 fi fi for index in 1 2 3 do rm check_include_file.$index.$$ done # echo "-------------------------------------------------------------------" if [ $different = "yes" ] then echo "Error: nothing should be between the two dashed lines above" exit 1 else echo "Ok: nothing is between the two dashed lines above" exit 0 fi ================================================ FILE: bin/check_include_xrst.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- if [ ! -e "bin/check_include_xrst.sh" ] then echo "bin/check_include_xrst.sh: must be executed from its parent directory" exit 1 fi # ----------------------------------------------------------------------------- count=`git ls-files include/cppad | grep '/xrst/.*\.hpp' | wc -l` if [ $count != '0' ] then git ls-files include/cppad | grep '/xrst/.*\.hpp' echo 'Cannot put *.hpp files below xrst in include directory' echo 'because install of include directory will exclude them.' exit 1 fi echo 'check_include_xrst: OK' exit 0 ================================================ FILE: bin/check_invisible.sh ================================================ #! /usr/bin/env bash set -e -u # !! EDITS TO THIS FILE ARE LOST DURING UPDATES BY xrst.git/bin/dev_tools.sh !! # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2020-25 Bradley M. Bell # ----------------------------------------------------------------------------- # bin/check_invisible.sh # Checks that there is no invisible white space in any of the source files. # If there is, a message is printed about it, it is automatically removed, # and this script exits with an error. # Files that are not checked can be specified in bin/dev_setting.sh # ----------------------------------------------------------------------------- if [ "$0" != "bin/check_invisible.sh" ] then echo "bin/check_invisible.sh: must be executed from its parent directory" exit 1 fi if [ "$#" == 0 ] then all='false' elif [ "$#" == 1 ] && [ "$1" == 'all' ] then all='true' else echo 'usage: bin/check_invisible [all]' exit 1 fi # # sed source bin/grep_and_sed.sh # # invisible_and_tab_ok source bin/dev_settings.sh # ---------------------------------------------------------------------------- # # sed.$$ echo '#' > sed.$$ for name in $no_copyright_list do if [ -f $name ] then echo "^$name\$" | $sed -e 's|/|[/]|g' -e 's|.*|/&/d|' >> sed.$$ elif [ -d $name ] then echo "^$name/" | $sed -e 's|/|[/]|g' -e 's|.*|/&/d|' >> sed.$$ else echo "$name in no_copyright_list is not a file or directory" exit 1 fi done # # file_list if [ "$all" == 'true' ] then file_list=$(git ls-files | $sed -f sed.$$) else file_list=$(git status --porcelain | \ $sed -e '/^D/d' -e 's|^...||' | $sed -f sed.$$ ) fi # ---------------------------------------------------------------------------- # # sed.$$ cat << EOF > sed.$$ s|[ \\t][ \\t]*\$|| s| *\t|\t|g 1{/^[ \\t]*\$/d} \${/^[ \\t]*\$/d} EOF # # changed, file changed='no' for file in $file_list do if [ -f "$file" ] then $sed -f sed.$$ $file > copy.$$ if ! diff $file copy.$$ > diff.$$ then changed='yes' echo "$file: original (<) invisible space removed (>)" cat diff.$$ if [ -x $file ] then chmod +x copy.$$ fi mv copy.$$ $file else rm copy.$$ fi rm diff.$$ fi done rm sed.$$ if [ "$changed" == 'yes' ] then echo 'check_invisible.sh: The invisible white space above have been fixed' echo 'Re-execute bin/check_invisible.sh ?' exit 1 fi echo 'check_invisible.sh: OK' exit 0 ================================================ FILE: bin/check_nominmax.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- if [ "$0" != 'bin/check_nominmax.sh' ] then echo "bin/check_nominmax.sh: must be executed from its parent directory" exit 1 fi # ----------------------------------------------------------------------------- list=$(git grep -n '^# *include *' | sed -e 's|\(:[0-9]*:\).*|\1|') for match in $list do file=$(echo $match | sed -e 's|\([^:]*\):\([0-9]*\):|\1|') line=$(echo $match | sed -e 's|\([^:]*\):\([0-9]*\):|\2|') let "line = $line - 2" if ! sed -n "${line}p" $file | grep '^# *define *NOMINMAX' > /dev/null then cat << EOF In $file # ifndef NOMINMAX # define NOMINMAX # endif Must be the three lines before: # include EOF fi done # echo 'check_nominmax.sh: OK' exit 0 ================================================ FILE: bin/check_op_code.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- if [ ! -e "bin/check_op_code.sh" ] then echo "bin/check_op_code.sh: must be executed from its parent directory" exit 1 fi file='include/cppad/local/op_code_var.hpp' # --------------------------------------------------------------------------- # order in enum list sed -n -e '/^enum/,/NumberOp/p' $file | sed \ -e '/^enum/d' \ -e '/NumberOp/d' \ -e 's/^[ ]*//' \ -e 's/,.*//' > temp.1 # ----------------------------------------------------------------------------- # check NumArgTable sed -n -e '/NumArgTable\[\]/,/NumberOp/p' $file | \ sed \ -e '/NumArgTable\[\]/d' \ -e '/NumberOp/d' \ -e 's|^ */[*] ||' \ -e 's| *[*]/.*||' > temp.2 # if ! diff temp.1 temp.2 then echo 'check_op_code.sh: NumArgTable list is different from enum list' exit 1 fi # ----------------------------------------------------------------------------- # check NumResTable sed -n -e '/NumResTable\[\]/,/NumberOp/p' $file | \ sed \ -e '/NumResTable\[\]/d' \ -e '/NumberOp/d' \ -e 's|^ */[*] ||' \ -e 's| *[*]/.*||' > temp.2 # if ! diff temp.1 temp.2 then echo 'check_op_code.sh: NumResTable list is different from enum list' exit 1 fi # ----------------------------------------------------------------------------- # check OpNameTable sed -n -e '/const char \*OpNameTable\[\]/,/"Number"/p' $file | \ sed \ -e '/OpNameTable\[\]/d' \ -e '/"Number"/d' \ -e 's|^ *"||' \ -e 's|".*||' \ > temp.2 # if ! diff temp.1 temp.2 then echo 'check_op_code.sh: OpNameTable list is different from enum list' exit 1 fi # ----------------------------------------------------------------------------- # clean up rm temp.1 temp.2 # ---------------------------------------------------------------------------- echo "$0: OK" exit 0 ================================================ FILE: bin/check_sort.sh ================================================ #! /usr/bin/env bash set -e -u # !! EDITS TO THIS FILE ARE LOST DURING UPDATES BY xrst.git/bin/dev_tools.sh !! # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2020-25 Bradley M. Bell # ----------------------------------------------------------------------------- # bin/check_sort.sh # Checks that for all files, all the sections between # BEGIN_SORT_THIS_LINE_PLUS_# # END_SORT_THIS_LINE_MINUS_# # are sorted. If not, it is corrected and an error is returned. # ----------------------------------------------------------------------------- if [ "$0" != "bin/check_sort.sh" ] then echo "bin/check_sort.sh: must be executed from its parent directory" exit 1 fi if [ "$#" == 0 ] then all='false' elif [ "$#" == 1 ] && [ "$1" == 'all' ] then all='true' else echo 'usage: bin/check_sort [all]' echo 'see usage message forbin/sort.sh' exit 1 fi # # grep, sed source bin/grep_and_sed.sh # ---------------------------------------------------------------------------- # file_list if [ "$all" == 'true' ] then file_list=$(git grep -l 'BEGIN_SORT_THIS_LINE_PLUS_') else file_list=$(\ git status --porcelain | \ $sed -e '/^D/d' -e 's|^...||' -e 's|^.*-> *||' \ ) fi # # ok ok='yes' for file_name in $file_list do check='yes' if [ "$file_name" == 'bin/sort.sh' ] then check='no' fi if [ "$file_name" == 'bin/check_sort.sh' ] then check='no' fi if [ -d "$file_name" ] then check='no' else if ! $grep BEGIN_SORT_THIS_LINE $file_name > /dev/null then check='no' fi fi if [ "$check" == 'yes' ] then if ! bin/sort.sh $file_name >& temp.$$ then cat temp.$$ echo 'check_sort.sh: Error' rm temp.$$ exit 1 fi last_line=$(tail -1 temp.$$) if [ "$last_line" == 'sort.sh: Done' ] then cat temp.$$ ok='no' fi fi done # if [ -e "temp.$$" ] then rm temp.$$ fi if [ "$ok" == 'no' ] then echo 'check_sort.sh: Some files have been sorted (run again to get OK).' exit 1 fi echo 'check_sort.sh: OK' exit 0 ================================================ FILE: bin/check_tab.sh ================================================ #! /usr/bin/env bash set -e -u # !! EDITS TO THIS FILE ARE LOST DURING UPDATES BY xrst.git/bin/dev_tools.sh !! # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2020-25 Bradley M. Bell # ----------------------------------------------------------------------------- # bin/check_tabls.h # Checks for tabs in the source and returns an error if it finds any. # Files that are not checked can be specified in bin/dev_settings.sh. # ----------------------------------------------------------------------------- if [ "$0" != "bin/check_tab.sh" ] then echo "bin/check_tab.sh: must be executed from its parent directory" exit 1 fi if [ "$#" == 0 ] then all='false' elif [ "$#" == 1 ] && [ "$1" == 'all' ] then all='true' else echo 'usage: bin/check_tab [all]' exit 1 fi # # sed source bin/grep_and_sed.sh # # invisible_and_tab_ok source bin/dev_settings.sh # ---------------------------------------------------------------------------- # # sed.$$ cat << EOF > sed.$$ s|.*-> *|| EOF for name in $invisible_and_tab_ok do if [ -f $name ] then echo "^$name\$" | $sed -e 's|/|[/]|g' -e 's|.*|/&/d|' >> sed.$$ elif [ -d $name ] then echo "^$name/" | $sed -e 's|/|[/]|g' -e 's|.*|/&/d|' >> sed.$$ else echo "$name in no_copyright_list is not a file or directory" exit 1 fi done # # file_list if [ "$all" == 'true' ] then file_list=$(git ls-files | $sed -f sed.$$) else file_list=$(git status --porcelain | \ $sed -e '/^D/d' -e 's|^...||' | $sed -f sed.$$) fi # # ok ok='yes' for file in $file_list do if $grep -P '\t' $file > /dev/null then echo "$file has a tab" ok='no' fi done if [ "$ok" != 'yes' ] then echo 'check_tab: Error' rm sed.$$ exit 1 fi # ----------------------------------------------------------------------------- rm sed.$$ echo 'check_tab.sh: OK' exit 0 ================================================ FILE: bin/check_tempfile.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- if [ ! -e "bin/check_tempfile.sh" ] then echo "bin/check_tempfile.sh: must be executed from its parent directory" exit 1 fi # ----------------------------------------------------------------------------- list=`ls | sed -n \ -e '/^new.[0-9]*$/d' \ -e '/^temp.[0-9]*$/d' \ -e '/\/temp.[0-9]*$/d' \ -e '/\.[0-9]*$/p'` if [ "$list" != '' ] then echo 'Use following command to remove temporary files:' cmd='rm ' for file in $list do cmd+=" $file" done echo " $cmd" echo 'check_tempfile.sh: Error' exit 1 fi echo 'check_tempfile.sh: OK' exit 0 ================================================ FILE: bin/check_trace.sh ================================================ #! /usr/bin/env bash # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2024 Bradley M. Bell set -e -u # ---------------------------------------------------------------------------- if [ $0 != "bin/check_trace.sh" ] then echo "bin/check_trace.sh: must be executed from its parent directory" exit 1 fi change='no' file_list=$(git ls-files include/cppad/local/sweep/*.hpp) for file in $file_list do sed -e 's|\(# *define *[A-Z0-9_]*\)_TRACE 1|\1_TRACE 0|' $file \ > check_trace.$$ if diff $file check_trace.$$ > /dev/null then rm check_trace.$$ else change='yes' echo "$file changed as follows:" set +e diff $file check_trace.$$ set -e mv check_trace.$$ $file fi done if [ "$change" == 'yes' ] then echo "$0: Error" exit 1 fi # ---------------------------------------------------------------------------- echo "$0: OK" exit 0 ================================================ FILE: bin/check_user_def.py ================================================ #! /usr/bin/env python3 # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- import re import sys import subprocess # # p_symbol_heading p_symbol_heading = re.compile( r'\n(CPPAD[A-Z_]*)\n([^\n]*)\n' ) # # p_symbol_undef p_symbol_undef = re.compile( r'\n# undef (CPPAD[A-Z_]*)\n' ) # def main() : # if sys.argv[0] != 'bin/check_user_def.py' : sys.exit( 'bin/check_user_def.py must execute from its parent directory' ) # # dev_doc_symbol_list dev_doc_symbol_list=[ 'CPPAD_VEC_AD_COMP_ASSIGN', 'CPPAD_FOR_HES_TRACE', ] # # exclude_list exclude_list = [ 'include/cppad/configure.hpp.in', 'configure', ] # # file_list command = [ 'git', 'ls-files' ] result = subprocess.run( command , stdout = subprocess.PIPE , stderr = subprocess.PIPE , text = True , ) if result.returncode != 0 : msg = 'Error: ' + ' '.join(command) + '\n' msg += result.stderr sys.exit(msg) file_list = result.stdout.strip().split('\n') # # file_list file_tmp = list() for file in file_list : exclude = file in exclude_list if not exclude : file_tmp.append(file) file_list = file_tmp # # preprocessor_data file_obj = open('xrst/preprocessor.xrst', 'r') preprocessor_data = file_obj.read() file_obj.close() # # undef_list undef_list = list() # # m_symbol_undef m_symbol_undef = p_symbol_undef.search( preprocessor_data ) while m_symbol_undef != None : undef_list.append( m_symbol_undef.group(1) ) # # m_symbol_undef start = m_symbol_undef.end() - 1 m_symbol_undef = p_symbol_undef.search( preprocessor_data , start) # # file for file in file_tmp : # # file_data file_obj = open(file, 'r') try : file_data = file_obj.read() except : file_data = '' file_obj.close() # # m_symbol_heading m_symbol_heading = p_symbol_heading.search( file_data ) while m_symbol_heading != None : # # error symbol = m_symbol_heading.group(1) underline = m_symbol_heading.group(2) error = False if len(symbol) == len(underline) : if underline == len(symbol) * underline[0] : if symbol not in undef_list : if symbol not in dev_doc_symbol_list : error = True if error : msg = f'{file}:\n' msg += f'Documentation for {symbol} appears in this file ' msg += 'but it is supposed to be in the user API.' sys.exit(msg) # # m_symbol_heading start = m_symbol_heading.end() m_symbol_heading = p_symbol_heading.search( file_data , start) # main() print('check_user_def.py: OK') ================================================ FILE: bin/check_version.sh ================================================ #! /usr/bin/env bash set -e -u # !! EDITS TO THIS FILE ARE LOST DURING UPDATES BY xrst.git/bin/dev_tools.sh !! # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2020-26 Bradley M. Bell # ----------------------------------------------------------------------------- # bin/check_verison.sh # Checks that the version number in the version_file_list are correct; # see bin/dev_settings.sh for more discussion. # ----------------------------------------------------------------------------- # # echo_eval echo_eval() { echo $* eval $* } # # sed source bin/grep_and_sed.sh # # package_name, version_file_list source bin/dev_settings.sh # if [ "$0" != "bin/check_version.sh" ] then echo "bin/check_version.sh: must be executed from its parent directory" exit 1 fi if [ "$#" != 0 ] then echo 'check_version does not expect any arguments' exit 1 fi if [ ! -e './.git' ] then echo 'bin/check_version.sh: cannot find ./.git' exit 1 fi # ----------------------------------------------------------------------------- # # branch branch=$(git branch --show-current) # # first_version_file first_version_file=$(echo $version_file_list | $sed -e 's|^ *||' -e 's| .*||') # # version cat << EOF > temp.sed /["'][0-9]{8}["']/b one /["'][0-9]{8}[.][0-9]{1,2}["']/b one /["'][0-9]{4}[.][0-9]{1,2}[.][0-9]{1,2}["']/b one b end # : one s|.*["']([0-9]{8})["'].*|\\1| s|.*["']([0-9]{8}[.][0-9]{1,2})["'].*|\\1| s|.*["']([0-9]{4}[.][0-9]{1,2}[.][0-9]{1,2})["'].*|\\1| p # : end EOF version=$($sed -n -r -f temp.sed $first_version_file | head -1) # # version_type if [[ "$version" =~ ^[0-9]{8}$ ]] then version_type=1 elif [[ "$version" =~ ^[0-9]{8}[.][0-9]{1,2}$ ]] then version_type=2 elif [[ "$version" =~ ^[0-9]{4}[.][0-9]{1,2}[.][0-9]{1,2}$ ]] then version_type=3 else echo "check_version.sh: can't find version number in $first_version_file" exit 1 fi if [[ "$branch" =~ ^stable/.* ]] then if [ "$version_type" == 1 ] then echo "check_version.sh: version in $first_version_file" echo "is not for a release but this is the $branch branch" exit 1 elif [ "$version_type" == 3 ] then if [[ "$version" =~ ^[0-9]{4}[.][^0].*$ ]] then echo "check_version.sh: version in $first_version_file" echo "is not for a release but this is the $branch branch" exit 1 fi fi fi # # version if [ "$branch" == 'master' ] || [ "$branch" == 'main' ] then if [ "$version_type" == 1 ] then version=$(date +%Y%m%d) elif [ "$version_type" == 2 ] then echo "check_version.sh: version in $first_version_file" echo "is for a release but this is the $branch branch" exit 1 else if [[ "$version" =~ ^[0-9]{4}[.]0[.][0-9]{1,2}$ ]] then echo "check_version.sh: version in $first_version_file" echo "is for a release but this is the $branch branch" exit 1 fi version=$(date +%Y.%-m.%-d) fi fi # # temp.sed cat << EOF > temp.sed s|(["'])[0-9]{8}(["'])|\\1$version\\2| s|(["'])[0-9]{8}[.][0-9]{1,2}(["'])|\\1$version\\2| s|(["'])[0-9]{4}[.][0-9]{1,2}[.][0-9]{1,2}(["'])|\\1$version\\2| # s|$package_name-[0-9]{8}\$|$package_name-$version| s|$package_name-[0-9]{8}([^.])|$package_name-$version\\1| s|$package_name-[0-9]{8}[.][0-9]{1,2}|$package_name-$version| s|$package_name-[0-9]{4}[.][0-9]{1,2}[.][0-9]{1,2}|$package_name-$version| EOF # # check_version check_version() { $sed -r "$1" -f temp.sed > temp.out if ! diff "$1" temp.out > /dev/null then version_ok='no' echo "check_version.sh: changes to $1" set +e diff "$1" temp.out set -e # if [ -x "$1" ] then mv temp.out "$1" chmod +x "$1" else mv temp.out "$1" fi fi } # # version_ok version_ok='yes' # # check_version for file in $version_file_list do check_version $file done # # ---------------------------------------------------------------------------- if [ "$version_ok" == 'no' ] then echo 'check_version.sh: The version numbers were fixed (see above).' echo 'Re-execute bin/check_version.sh ?' exit 1 fi echo 'check_version.sh OK' exit 0 ================================================ FILE: bin/colpack.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- if [ "$1" != 'forward' ] && [ "$1" != 'reverse' ] then echo 'usage: ./colpack.sh option' echo 'where option is "forward" or "reverse"' exit 1 fi if [ "$1" == 'forward' ] then color_variant="COLUMN_PARTIAL_DISTANCE_TWO" else color_variant="ROW_PARTIAL_DISTANCE_TWO" fi # ---------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ----------------------------------------------- if [ ! -e 'build/colpack' ] then echo_eval mkdir -p build/colpack fi echo 'create: build/colpack/colpack.cpp' cat<< EOF > build/colpack/colpack.cpp // Example using BipartiteGraphPartialColoringInterface // to generate the seed matrix for Jacobian #include "ColPackHeaders.h" int main() { size_t i, j, k; using std::cout; using std::endl; //* 32x9 matrix size_t i_RowCount = 32; size_t i_ColumnCount = 9; size_t i_MaxNonZerosInRows = 3; // JP[32][9] std::vector JP(i_RowCount); unsigned int n_data = i_RowCount * (i_MaxNonZerosInRows + 1); std::vector JP_memory(n_data); for(i = 0; i < i_RowCount; i++) JP[i] = JP_memory.data() + i * (i_MaxNonZerosInRows + 1); // JP[0][0] = 0; JP[1][0] = 1; JP[1][1] = 0; JP[2][0] = 1; JP[2][1] = 1; JP[3][0] = 1; JP[3][1] = 2; JP[4][0] = 1; JP[4][1] = 0; JP[5][0] = 3; JP[5][1] = 0; JP[5][2] = 1; JP[5][3] = 3; JP[6][0] = 3; JP[6][1] = 1; JP[6][2] = 2; JP[6][3] = 4; JP[7][0] = 2; JP[7][1] = 2; JP[7][2] = 5; JP[8][0] = 1; JP[8][1] = 3; JP[9][0] = 3; JP[9][1] = 3; JP[9][2] = 4; JP[9][3] = 6; JP[10][0] = 3; JP[10][1] = 4; JP[10][2] = 5; JP[10][3] = 7; JP[11][0] = 2; JP[11][1] = 5; JP[11][2] = 8; JP[12][0] = 1; JP[12][1] = 6; JP[13][0] = 2; JP[13][1] = 6; JP[13][2] = 7; JP[14][0] = 2; JP[14][1] = 7; JP[14][2] = 8; JP[15][0] = 1; JP[15][1] = 8; JP[16][0] = 1; JP[16][1] = 0; JP[17][0] = 2; JP[17][1] = 0; JP[17][2] = 1; JP[18][0] = 2; JP[18][1] = 1; JP[18][2] = 2; JP[19][0] = 1; JP[19][1] = 2; JP[20][0] = 2; JP[20][1] = 0; JP[20][2] = 3; JP[21][0] = 3; JP[21][1] = 1; JP[21][2] = 3; JP[21][3] = 4; JP[22][0] = 3; JP[22][1] = 2; JP[22][2] = 4; JP[22][3] = 5; JP[23][0] = 1; JP[23][1] = 5; JP[24][0] = 2; JP[24][1] = 3; JP[24][2] = 6; JP[25][0] = 3; JP[25][1] = 4; JP[25][2] = 6; JP[25][3] = 7; JP[26][0] = 3; JP[26][1] = 5; JP[26][2] = 7; JP[26][3] = 8; JP[27][0] = 1; JP[27][1] = 8; JP[28][0] = 1; JP[28][1] = 6; JP[29][0] = 1; JP[29][1] = 7; JP[30][0] = 1; JP[30][1] = 8; JP[31][0] = 0; cout << endl << "Sparsity pattern of Jacobian:" << endl; cout << " "; for(k = 0; k < 9; k++) cout << setw(3) << k; cout << endl; for(i = 0; i < i_RowCount; i++) { cout << setw(3) << i << ":"; k = 0; for (j = 1; j <= (int) JP[i][0]; j++) { while(k < JP[i][j]) { cout << setw(3) << 0; k++; } cout << setw(3) << 1; k++; } while(k < 9) { cout << setw(3) << 0; k++; } cout << endl; } // Step 1: Read the sparsity pattern of the given Jacobian matrix // (adolc format) and create the corresponding bipartite graph ColPack::BipartiteGraphPartialColoringInterface g( SRC_MEM_ADOLC, JP.data(), i_RowCount, i_ColumnCount ); g.PrintBipartiteGraph(); // Step 2: Do Partial-Distance-Two-Coloring // of the bipartite graph with the specified ordering g.PartialDistanceTwoColoring( "SMALLEST_LAST", "$color_variant" ); g.PrintColumnPartialColors(); g.PrintColumnPartialColoringMetrics(); // Step 3: From the coloring information, create and return seed matrix int ip1_SeedRowCount; int ip1_SeedColumnCount; double** RSeed = g.GetSeedMatrix(&ip1_SeedRowCount, &ip1_SeedColumnCount); int rows = ip1_SeedRowCount; int cols = ip1_SeedColumnCount; cout << "Seed matrix: (" << rows << "," << cols << ")" << endl; cout << " "; for(j = 0; j < cols; j++) cout << setw(3) << j; cout << endl; for(i = 0; i < rows; i++) { cout << setw(3) << i << ":"; for(j = 0; j < cols; j++) cout << setw(3) << int(RSeed[i][j]); cout << endl; } return 0; } EOF # ---------------------------------------------------------------------------- echo_eval cd build/colpack echo_eval g++ colpack.cpp \ -I$HOME/prefix/colpack/include/ColPack \ -L$HOME/prefix/colpack/lib64 \ -l ColPack \ -o colpack # echo_eval valgrind --leak-check=yes ./colpack # ---------------------------------------------------------------------------- echo "$0: OK" exit 0 ================================================ FILE: bin/deprecate_xam.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- if [ "$0" != 'bin/deprecate_xam.sh' ] then echo 'bin/deprecate_xam.sh: must be executed from its parent directory' exit 1 fi if [ "$1" == '' ] then echo 'bin/deprecate_xam.sh file' echo 'where file is an example file that is to be deprecated' exit 1 fi old_file="$1" ext=`echo $old_file | sed -e 's|.*\.||'` if [ $ext != 'cpp' ] then echo 'bin/depreate_xam.sh: file name does not end in .cpp' exit 1 fi dir=`echo $old_file | sed -e 's|/.*||'` if [ $dir != 'example' ] then echo 'bin/depreate_xam.sh: file name does not start with example/' exit 1 fi # ----------------------------------------------------------------------------- # move file root_name=`echo $old_file | sed -e 's|.*/||' -e 's|\.cpp$||'` new_dir="test_more/deprecated" new_file="$new_dir/$root_name.cpp" echo "$omh_name" if [ -e $new_file ] then git reset -- $new_file $old_file rm $new_file git checkout $old_file fi echo "git mv $old_file $new_file" git mv $old_file $new_file # ----------------------------------------------------------------------------- # change old directory old_dir=`echo $old_file | sed -e 's|/[^/]*$||'` old_program=`echo $old_dir | sed -e 's|.*/||'` git checkout $old_dir/CMakeLists.txt sed -i $old_dir/CMakeLists.txt -e "/$root_name.cpp/d" git checkout $old_dir/makefile.am sed -i $old_dir/makefile.am -e "/$root_name.cpp/d" git checkout $old_dir/$old_program.cpp sed -i $old_dir/$old_program.cpp -e "/$root_name/d" # ----------------------------------------------------------------------------- # change new directory git checkout $new_dir/CMakeLists.txt sed -i $new_dir/CMakeLists.txt \ -e "s|deprecated.cpp|&\\n $root_name.cpp|" git checkout $new_dir/makefile.am sed -i $new_dir/makefile.am \ -e "s|deprecated.cpp.*|&\\n\\t$root_name.cpp \\\\|" git checkout $new_dir/deprecated.cpp sed -i $new_dir/deprecated.cpp \ -e "s|bool old_mat_mul.*|&\\nextern bool $root_name(void);|" \ -e "s|Run( old_mat_mul.*|&\\n Run( $root_name, \"$root_name\" );|" # ----------------------------------------------------------------------------- # file omhelp links omh_name=`grep '$begin' $new_file | sed -e 's|$begin ||' -e 's|\$\$.*||'` git checkout omh/example_list.omh sed -i omh/example_list.omh -e "/$omh_name/d" # ----------------------------------------------------------------------------- echo 'bin/deprecate_xam.sh: OK' exit 0 ================================================ FILE: bin/dev_settings.sh ================================================ # --------------------------------------------------------------------------- # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-26 Bradley M. Bell # SPDX-FileContributor: 2025 Perry de Valpine # --------------------------------------------------------------------------- # source bin/dev_settings.sh # Sets the value of the the development tool variables for this package. # # Unless this is xrst.git/bin/dev_settings.sh, # only edit the value for each of the variables, any other changes will # be lost the next time xrst.git/bin/dev_tools.sh updates this file. # --------------------------------------------------------------------------- # # Directories # If an file name below is a directory it specifies all the # files in the directory. # # spdx_license_id # Each file, except those specified by no_copyright_list, should have a line # that ends with the following text: spdx_license_id='EPL-2.0 OR GPL-2.0-or-later' # # package_name package_name='cppad' # # index_page_name # is the xrst index page_name for this projects documentation. index_page_name='user_guide' # # version_file_list # The possible patterns for a latest version number are: # yyyymmdd or yyyy.month.day # where yyyymmdd is an eight decimal digit representation of the date. # yyyy is the year (as four decimal digits yyy), month is a number # between 1 and 12, and the day is a number between 1 and 31 # The possible patterns for a release version number are: # yyyy0000.release or yyyy.0.release # where release is a number between 0 and 99. # # The patterns above without release are used for the master and main branches # and corresponds to the current year, month and day. # The patterns above with release are used for stable/* branches. # # The first version file of the list below must have one copy of its # version surrounded by single or double quotes. This determines the version # when the branch is not master or main. All occurrences of the version, in the # files listed below, with the following forms are updated by check_version.sh: # $package_name-$version or '$version' or "$version" # # We use tag for the version corresponding to the current stable release. # This is (is not) the same as the current version on a stable branch # (on the master or main branch). All occurrences of the tag, in the files # listed below, with the following forms are updated by new_release.sh: # archive/$tag.tar.gz # In addition, all occurrences of stable-yyyy and release-yyyy are updated. version_file_list=' CMakeLists.txt user_guide.xrst ' # All the occurrences of the version in the files above are checked to see # that they agree. # # contributor_list # This maps the system user name $USER to the contributor's full name. # A full name can have multiple user names, but each user name can have # only one full name. The colon separates the user name from the full name. # The entries are separate by white space, so the under bar character is used # for spaces in the full name. contributor_list=' bradbell:Bradley_M._Bell ' # # no_copyright_list # These files and directories do not have the spdx license id in them. # If an entry below is a directory it specifies all the files in the directory. # BEGIN_SORT_THIS_LINE_PLUS_2 no_copyright_list=' .circleci/config.yml .github/workflows .gitignore .readthedocs.yaml .travis.yml COPYING appveyor.yml authors batch_edit.sed bin/build.bat cmake/cppad_uninstall.cmake coin.png epl-2.0.txt typos.toml uw_copy_040507.html xrst.toml ' # END_SORT_THIS_LINE_MINUS_2 # # invisible_and_tab_ok # These files are not checked for invisible white space or tabs. # If an entry below is a directory it specifies all the files in the directory. invisible_and_tab_ok=' batch_edit.sed coin.png uw_copy_040507.html ' # # check_git_commit # Files that have automatic changes that should not be committed every time. # Including them in this list gives the user the option to abort their changes. check_git_commit=' ' ================================================ FILE: bin/dos_build.bat ================================================ echo off rem SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later rem SPDX-FileCopyrightText: Bradley M. Bell rem SPDX-FileContributor: 2024 Bradley M. Bell rem -------------------------------------------------------------------------- goto end_of_comment_block {xrst_begin dos_build.bat} {xrst_spell cmd } Compile and Test CppAD using Dos ################################ Syntax ****** {xrst_code bat} cmd /c bin\dos_build.bat {xrst_code} Eigen ***** This example includes the eigen optional package. Other optional packages could be included in a similar fashion. Source ****** {xrst_literal rem BEGIN SOURCE rem END SOURCE } {xrst_end dos_build.bat} :end_of_comment_block rem --------------------------------------------------------------------------- rem BEGIN SOURCE rem .git if not exist .git ( echo Expected .git to be a subdirectory of working directory exit 1 ) rem rem CONDA_PREFIX if defined CONDA_PREFIX ( echo CONDA_PREFIX = %CONDA_PREFIX% ) else ( echo CONDA_PREFIX is not defined exit ) rem PKG_CONFIG_PATH set PKG_CONFIG_PATH=%CONDA_PREFIX%\Library\share\pkgconfig echo PKG_CONFIG_PATH=%PKG_CONFIG_PATH% if exist %PKG_CONFIG_PATH%\eigen3.pc ( echo Found eigen3 in PKG_CONFIG_PATH ) else ( echo Did not find eigen3 in PKG_CONFIG_PATH: suggest echo conda install eigen exit ) rem INCLUDE_DIR set INCLUDE_DIR=%CONDA_PREFIX%\Library\include echo INCLUDE_DIR=%INCLUDE_DIR% if exist %INCLUDE_DIR%\Eigen\Core ( echo Found Eigen\Core in INCLUDE_DIR ) else ( echo Did not find Eigen\Core in INCLUDE_DIR: suggest echo mklink /d %INCLUDE_DIR%\Eigen %INCLUDE_DIR%\eigen3\Eigen exit ) rem MSVS_DIR set MSVS_DIR=C:\Program Files\Microsoft Visual Studio echo MSVS_DIR=%MSVS_DIR% if exist "%MSVS_DIR%" ( echo Found MSVS_DIR ) else ( echo Did not find MSVS_DIR echo Install Visual Studio ? exit 1 ) rem rem temp.out set SAVE_DIR=%cd% cd %MSVS_DIR% dir /s vcvarsall.bat > %SAVE_DIR%/temp.out cd %SAVE_DIR% ren rem temp.py echo import re > temp.py echo f_obj = open('temp.out', 'r') >> temp.py echo data = f_obj.read() >> temp.py echo pattern = '\n *Directory *of *(.*)' >> temp.py echo m_obj = re.search(pattern, data) >> temp.py echo if m_obj == None : >> temp.py echo print( 'not_found' ) >> temp.py echo else : >> temp.py echo print( m_obj.group(1) ) >> temp.py rem rem rem VCVARSALL_DIR python temp.py > temp set /p VCVARSALL_DIR= # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- if [ ! -e "bin/doxyfile.sh" ] then echo "bin/doxyfile.sh: must be executed from its parent directory" exit 1 fi # ----------------------------------------------------------------------------- if [ "$3" == "" ] then echo "usage: bin/doxyfile.sh version error_file output_directory" echo "creates the doxygen configuration file ./doxyfile" exit 1 fi version="$1" error_file="$2" output_directory="$3" # ----------------------------------------------------------------------------- # convert multi-line assignments to single line assignments. echo "doxygen -g doxyfile > /dev/null" doxygen -g doxyfile > /dev/null cat << EOF > doxyfile.$$ /^[A-Z_]* *=.*\\\\$/! b end : loop N /\\\\$/b loop s|\\\\\\n| |g s| *| |g # :end EOF sed -i doxyfile -f doxyfile.$$ # ----------------------------------------------------------------------------- include_directory_list=`find include/cppad -type d | tr '\n' ' ' ` # ----------------------------------------------------------------------------- # PREDEFINED:see http://www.stack.nl/~dimitri/doxygen/manual/preprocessing.html # 2DO: change EXTRACT_ALL to NO so get warnings for undocumented functions. echo "create doxyfile.$$" cat << EOF > doxyfile.$$ ALWAYS_DETAILED_SEC = YES BUILTIN_STL_SUPPORT = YES ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES EXTRACT_ALL = YES EXTRACT_LOCAL_CLASSES = YES EXTRACT_PRIVATE = YES EXTRACT_STATIC = YES EXTRACT_ANON_NSPACES = YES FILE_PATTERNS = *.hpp *.cpp FULL_PATH_NAMES = NO GENERATE_HTML = YES GENERATE_LATEX = NO GENERATE_TREEVIEW = YES INHERIT_DOCS = NO INLINE_INHERITED_MEMB = YES INPUT = $include_directory_list LATEX_BATCHMODE = YES MACRO_EXPANSION = YES MULTILINE_CPP_IS_BRIEF = YES OUTPUT_DIRECTORY = $output_directory PREDEFINED = "__cplusplus=201103" "CPPAD_USE_CPLUSPLUS_2011=1" PROJECT_NAME = "CppAD: A C++ Algorithmic Differentiation Package" PROJECT_NUMBER = $version QT_AUTOBRIEF = YES REFERENCED_BY_RELATION = YES REFERENCES_LINK_SOURCE = NO SEPARATE_MEMBER_PAGES = YES SHOW_DIRECTORIES = YES SHOW_INCLUDE_FILES = NO SORT_GROUP_NAMES = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = YES SOURCE_BROWSER = YES STRIP_CODE_COMMENTS = NO SUBGROUPING = NO TAB_SIZE = 5 VERBATIM_HEADERS = NO WARN_LOGFILE = $error_file WARN_NO_PARAMDOC = YES EOF sed \ -e 's/^/s|^\\(/' \ -e 's/ *=/ *=\\).*|\\1/' \ -e 's/$/|/' \ -i doxyfile.$$ # # echo "sed -f doxyfile.$$ -i doxyfile" sed -f doxyfile.$$ -i doxyfile # echo "rm doxyfile.$$" rm doxyfile.$$ # ---------------------------------------------------------------------------- echo "$0: OK" exit 0 ================================================ FILE: bin/get_adolc.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- # {xrst_begin get_adolc.sh} # {xrst_comment_ch #} # # Download and Install Adolc in Build Directory # ############################################# # # Syntax # ****** # ``bin/get_adolc.sh`` # # Purpose # ******* # If you are using Unix, this command will download and install # :ref:`Adolc` # in the CppAD ``build`` directory. # # Requirements # ************ # You must first use :ref:`get_colpack.sh-name` to download and install # the ColPack coloring algorithms (used for sparse matrix derivatives). # # Distribution Directory # ********************** # This command must be executed in the # :ref:`download@Distribution Directory` . # # Source Directory # **************** # The Adolc source code is downloaded into the sub-directory # ``external/adolc.git`` below the distribution directory. # # Prefix # ****** # The :ref:`get_optional.sh@prefix` # in the file ``bin/get_optional.sh`` is used for this install. # # Version # ******* # This will install the following version of Adolc # {xrst_spell_off} # {xrst_code sh} version='e1fe476' # {xrst_code} # {xrst_spell_on} # This corresponds to the git master on Nov 13, 2020. # # Configuration # ************* # If the file # # ``external/adolc-`` *version* . ``configured`` # # exists, the configuration will be skipped. # Delete this file if you want to re-run the configuration. # # {xrst_end get_adolc.sh} # ----------------------------------------------------------------------------- package='adolc' if [ $0 != "bin/get_$package.sh" ] then echo "bin/get_$package.sh: must be executed from its parent directory" exit 1 fi # ----------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ----------------------------------------------------------------------------- web_page='https://github.com/coin-or/ADOL-C.git' cppad_dir=`pwd` # ----------------------------------------------------------------------------- # n_job if which nproc > /dev/null then n_job=$(nproc) else n_job=$(sysctl -n hw.ncpu) fi # ---------------------------------------------------------------------------- # prefix eval `grep '^prefix=' bin/get_optional.sh` if [[ "$prefix" =~ ^[^/] ]] then prefix="$cppad_dir/$prefix" fi echo "prefix=$prefix" # ----------------------------------------------------------------------------- configured_flag="external/$package-${version}.configured" echo "Executing get_$package.sh" if [ -e "$configured_flag" ] then echo "Skipping configuration because $configured_flag exits" echo_eval cd external/$package.git/build echo_eval make -j $n_job install echo "get_$package.sh: OK" exit 0 fi # -------------------------------------------------------------------------- if [ -e /usr/lib64 ] then libdir='lib64' else libdir='lib' fi # ----------------------------------------------------------------------------- if [ ! -d external ] then echo_eval mkdir external fi echo_eval cd external # ----------------------------------------------------------------------------- if [ ! -e "$package.git" ] then echo_eval git clone $web_page $package.git fi echo_eval cd $package.git echo_eval git reset --hard echo_eval git checkout --quiet $version # ----------------------------------------------------------------------------- system=`uname | tr [A-Z] [a-z] | sed -e 's|\([a-z][a-z]*\).*|\1|'` # ----------------------------------------------------------------------------- if which autoconf >& /dev/null then echo_eval autoreconf --install --force fi # ----------------------------------------------------------------------------- if [ ! -e build ] then echo_eval mkdir build fi echo_eval cd build # ----------------------------------------------------------------------------- flags="--prefix=$prefix --with-colpack=$prefix --libdir=$prefix/$libdir" flags+=" --enable-static --enable-shared --enable-atrig-erf" # echo_eval ../configure $flags echo_eval make -j $n_job install # ----------------------------------------------------------------------------- echo_eval touch $cppad_dir/$configured_flag echo "get_$package: OK" ================================================ FILE: bin/get_colpack.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- # {xrst_begin get_colpack.sh} # {xrst_comment_ch #} # # Download and Install ColPack in Build Directory # ############################################### # # Syntax # ****** # ``bin/get_colpack.sh`` # # Purpose # ******* # If you are using Unix, this command will download and install # :ref:`Colpack` # in the CppAD ``build`` directory. # # Distribution Directory # ********************** # This command must be executed in the # :ref:`download@Distribution Directory` . # # Source Directory # **************** # The ColPack source code is downloaded into the sub-directory # ``external/colpack.git`` below the distribution directory. # # Prefix # ****** # The :ref:`get_optional.sh@prefix` # in the file ``bin/get_optional.sh`` is used for this install. # # Version # ******* # This will install the following version of ColPack # {xrst_spell_off} # {xrst_code sh} version='1.0.10' # {xrst_code} # {xrst_spell_on} # # Configuration # ************* # If the file # # ``external/colpack-`` *version* . ``configured`` # # exists, the configuration will be skipped. # Delete this file if you want to re-run the configuration. # # {xrst_end get_colpack.sh} # ----------------------------------------------------------------------------- package='colpack' if [ $0 != "bin/get_$package.sh" ] then echo "bin/get_$package.sh: must be executed from its parent directory" exit 1 fi # ----------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ----------------------------------------------------------------------------- web_page='https://github.com/CSCsw/ColPack.git' cppad_dir=`pwd` # ----------------------------------------------------------------------------- # n_job if which nproc > /dev/null then n_job=$(nproc) else n_job=$(sysctl -n hw.ncpu) fi # ---------------------------------------------------------------------------- # prefix eval `grep '^prefix=' bin/get_optional.sh` if [[ "$prefix" =~ ^[^/] ]] then prefix="$cppad_dir/$prefix" fi echo "prefix=$prefix" # ----------------------------------------------------------------------------- configured_flag="external/$package-${version}.configured" echo "Executing get_$package.sh" if [ -e "$configured_flag" ] then echo "Skipping configuration because $configured_flag exits" echo_eval cd external/$package.git echo_eval make -j $n_job install echo "get_$package.sh: OK" exit 0 fi # -------------------------------------------------------------------------- if [ -e /usr/lib64 ] then libdir='lib64' else libdir='lib' fi # ----------------------------------------------------------------------------- if [ ! -d external ] then echo_eval mkdir external fi echo_eval cd external # ----------------------------------------------------------------------------- if [ ! -e "$package.git" ] then echo_eval git clone $web_page $package.git fi # ----------------------------------------------------------------------------- echo_eval cd $package.git echo_eval git checkout --quiet v$version # ----------------------------------------------------------------------------- if which autoconf >& /dev/null then if which libtoolize >& /dev/null then echo_eval libtoolize else echo_eval glibtoolize fi echo_eval autoreconf --install --force else echo "Error: autoconf and libtool required for this script" exit 1 fi # ----------------------------------------------------------------------------- if [[ "$(uname)" == CYGWIN* ]] || [ "$(uname)" == Darwin ] then lib_type='--enable-static --disable-shared' echo_eval ls -l ./configure echo_eval chmod +x ./configure else lib_type='--disable-static --enable-shared' fi echo_eval ./configure \ --prefix=$prefix \ --libdir=$prefix/$libdir \ $lib_type # echo_eval touch $cppad_dir/$configured_flag echo_eval make -j $n_job install # ----------------------------------------------------------------------------- echo "get_$package: OK" ================================================ FILE: bin/get_cppadcg.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- # {xrst_begin get_cppadcg.sh} # {xrst_spell # doxygen # } # {xrst_comment_ch #} # # Download and Install CppADCodeGen in Build Directory # #################################################### # # Syntax # ****** # ``bin/get_cppadcg.sh`` # # Purpose # ******* # If you are using Unix, this command will download and install # `cppadcg `_ # in the CppAD ``build`` directory. # # Requirements # ************ # You must first use your package manager to install # :ref:`cmake@Eigen` . # # Distribution Directory # ********************** # This command must be executed in the # :ref:`download@Distribution Directory` . # # Source Directory # **************** # The Cppadcg source code is downloaded into the sub-directory # ``external/cppadcg.git`` below the distribution directory. # # Prefix # ****** # The :ref:`get_optional.sh@prefix` # in the file ``bin/get_optional.sh`` is used for this install. # # Git Hash # ******** # This will install the commit of Cppadcg with the following git hash # {xrst_spell_off} # {xrst_code sh} git_hash='e15c57207ea42a9572e9ed44df1894e09f7ce67e' # {xrst_code} # {xrst_spell_on} # The date corresponding to this commit was 2024-06-16. # # Configuration # ************* # If the file # # ``external/cppadcg-`` *git_hash* . ``configured`` # # exists, the configuration will be skipped. # Delete this file if you want to re-run the configuration. # # Documentation # ************* # If you change the setting for ``CREATE_DOXYGEN_DOC`` to ``ON`` , # the doxygen documentation for CppADCodeGen will be installed in the directory # # *prefix* / ``share/doc/cppadcg/html`` # # where *prefix* has the value specified in the # :ref:`get_optional.sh` file. # # {xrst_end get_cppadcg.sh} # ----------------------------------------------------------------------------- package='cppadcg' if [ $0 != "bin/get_$package.sh" ] then echo "bin/get_$package.sh: must be executed from its parent directory" exit 1 fi # ----------------------------------------------------------------------------- # echo_eval echo_eval() { echo $* eval $* } # # grep, sed source bin/grep_and_sed.sh # ----------------------------------------------------------------------------- web_page='https://github.com/joaoleal/CppADCodeGen' cppad_repo=$(pwd) # ----------------------------------------------------------------------------- # n_job if which nproc > /dev/null then n_job=$(nproc) else n_job=$(sysctl -n hw.ncpu) fi # ---------------------------------------------------------------------------- # prefix eval `$grep '^prefix=' bin/get_optional.sh` if [[ "$prefix" =~ ^[^/] ]] then prefix="$cppad_repo/$prefix" fi echo "prefix=$prefix" # ----------------------------------------------------------------------------- configured_flag="external/$package-${git_hash}.configured" echo "Executing get_$package.sh" if [ -e "$configured_flag" ] then echo "Skipping configuration because $configured_flag exits" echo_eval cd external/$package.git/build echo_eval make -j $n_job install echo "get_$package.sh: OK" exit 0 fi # ----------------------------------------------------------------------------- # Create build/cppad_lib/libcppad_lib.* to aid in cppadcg install if [ ! -d build ] then echo_eval mkdir build fi echo_eval cd build if [ -e CMakeCache.txt ] then echo_eval rm CMakeCache.txt fi echo_eval cmake .. echo_eval make echo_eval cd .. # ----------------------------------------------------------------------------- # Change into external if [ ! -d external ] then echo_eval mkdir external fi echo_eval cd external # ----------------------------------------------------------------------------- # cppadcg.git if [ ! -e $package.git ] then echo_eval git clone $web_page $package.git fi echo_eval cd $package.git echo_eval git reset --hard echo_eval git checkout --quiet $git_hash # # cmake/FindCppAD.cmake # Modify FindCppAD.cmake so can use git repository # version of CppAD (not yet installed). cat << EOF > temp.sed s|IF *( *DEFINED *CPPAD_HOME *)|IF (DEFINED CPPAD_GIT_REPO)\\ # This setting is used for testing before installing CppAD.\\ # CPPAD_GIT_REPO is the a CppAD git repository. It is assumed that\\ # cmake and make have been executed in CPPAD_GIT_REPO/build.\\ SET(CPPAD_INCLUDE_DIR "\${CPPAD_GIT_REPO}/include" )\\ SET(CPPAD_LIBRARIES\\ "\${CPPAD_GIT_REPO}/build/cppad_lib"\\ )\\ INCLUDE_DIRECTORIES(\\ "\${CPPAD_INCLUDE_DIR}"\\ )\\ #\\ IF( NOT EXISTS "\${CPPAD_INCLUDE_DIR}/cppad/cppad.hpp" )\\ MESSAGE(FATAL_ERROR\\ "Cannot find CPPAD_GIT_REPO/include/cppad/cppad.hpp"\\ )\\ ENDIF()\\ IF( NOT EXISTS "\${CPPAD_INCLUDE_DIR}/cppad/configure.hpp" )\\ MESSAGE(FATAL_ERROR\\ "Cannot find CPPAD_GIT_REPO/include/cppad/configure.hpp"\\ )\\ ENDIF()\\ #\\ FIND_LIBRARY( CPPAD_LIB_PATH\\ cppad_lib\\ PATHS \${CPPAD_LIBRARIES}\\ NO_DEFAULT_PATH\\ )\\ IF( NOT CPPAD_LIB_PATH )\\ MESSAGE(FATAL_ERROR\\ "Cannot find \${library} library below CPPAD_GIT_REPO="\\ "{CPPAD_GIT_REPO}"\\ )\\ ENDIF()\\ #\\ SET(CPPAD_FOUND TRUE)\\ \\ ELSEIF (DEFINED CPPAD_HOME)| EOF echo_eval git checkout cmake/FindCppAD.cmake echo_eval $sed -i -f temp.sed cmake/FindCppAD.cmake # ----------------------------------------------------------------------------- # make install if [ ! -e build ] then echo_eval mkdir build fi echo_eval cd build if [ -e CMakeCache.txt ] then rm CMakeCache.txt fi echo_eval cmake \ -D CPPAD_GIT_REPO="$cppad_repo" \ -D CMAKE_INSTALL_PREFIX=$prefix \ -D GOOGLETEST_GIT=ON \ -D CREATE_DOXYGEN_DOC=OFF \ .. echo_eval make -j $n_job install # ----------------------------------------------------------------------------- echo_eval touch $cppad_repo/$configured_flag echo "get_$package.sh: OK" ================================================ FILE: bin/get_fadbad.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # {xrst_begin get_fadbad.sh} # {xrst_comment_ch #} # # Download and Install Fadbad in Build Directory # ############################################## # # Syntax # ****** # ``bin/get_fadbad.sh`` # # Purpose # ******* # If you are using Unix, this command will download and install # :ref:`Fadbad` # in the CppAD ``build`` directory. # # Distribution Directory # ********************** # This command must be executed in the # :ref:`download@Distribution Directory` . # # Source Directory # **************** # The Fadbad source code is downloaded into the sub-directory # ``external/FADBAD++`` below the distribution directory. # # Prefix # ****** # The :ref:`get_optional.sh@prefix` # in the file ``bin/get_optional.sh`` is used for this install. # # Version # ******* # This will install the following version of Fadbad # {xrst_spell_off} # {xrst_code sh} version='2.1' # {xrst_code} # {xrst_spell_on} # # {xrst_end get_fadbad.sh} # ----------------------------------------------------------------------------- package='fadbad' if [ $0 != "bin/get_$package.sh" ] then echo "bin/get_$package.sh: must be executed from its parent directory" exit 1 fi # ----------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ----------------------------------------------------------------------------- web_page='http://uning.dk//download' cppad_dir=`pwd` # ----------------------------------------------------------------------------- # prefix eval `grep '^prefix=' bin/get_optional.sh` if [[ "$prefix" =~ ^[^/] ]] then prefix="$cppad_dir/$prefix" fi echo "prefix=$prefix" # ----------------------------------------------------------------------------- if [ ! -d external ] then echo_eval mkdir external fi echo_eval cd external # ----------------------------------------------------------------------------- if [ ! -e "FADBAD++-$version.tar.gz" ] then echo_eval wget --no-check-certificate $web_page/FADBAD++-$version.tar.gz fi if [ -e "FADBAD++" ] then echo_eval rm -r FADBAD++ fi echo_eval tar -xzf FADBAD++-$version.tar.gz if [ ! -e "$prefix/include" ] then echo_eval mkdir -p "$prefix/include" fi if [ -e "$prefix/include/FADBAD++" ] then echo_eval rm -r "$prefix/include/FADBAD++" fi echo_eval cp -r FADBAD++ "$prefix/include/FADBAD++" # ----------------------------------------------------------------------------- echo "get_$package.sh: OK" ================================================ FILE: bin/get_ipopt.sh ================================================ #! /usr/bin/env bash set -e -u # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- # {xrst_begin get_ipopt.sh} # {xrst_spell # blas # lapack # } # {xrst_comment_ch #} # # Download and Install Ipopt in Build Directory # ############################################# # # Syntax # ****** # ``bin/get_ipopt.sh`` # # Purpose # ******* # If you are using Unix, this command will download and install # `Ipopt `_ in the # CppAD ``build`` directory. # # Requirements # ************ # This It is assumed that a copy of the Blas and Lapack is installed on # the system. # # Distribution Directory # ********************** # This command must be executed in the # :ref:`download@Distribution Directory` . # # Source Directory # **************** # The Ipopt source code is downloaded and compiled in the sub-directory # ``external/Ipopt.git`` below the distribution directory. # # Prefix # ****** # The :ref:`get_optional.sh@prefix` # in the file ``bin/get_optional.sh`` is used for this install. # # Version # ******* # This will install the following version of Ipopt # {xrst_spell_off} # {xrst_code sh} ipopt_version='3.14.16' # {xrst_code} # {xrst_spell_on} # # Configuration # ************* # If the file # # ``external/ipopt-`` *version* . ``configured`` # # exists, the configuration will be skipped. # Delete this file if you want to re-run the configuration. # # {xrst_end get_ipopt.sh} # ---------------------------------------------------------------------------- ipopt_url='https://github.com/coin-or/Ipopt' # ---------------------------------------------------------------------------- # # echo_eval function echo_eval() { echo $* eval $* } # # clone_url_name_version # creates ./name.git from corresponding url and version # $1 = url # $2 = name # $3 = version function clone_url_name_version() { if [ ! -e $2.git ] then echo_eval git clone $1.git $2.git fi echo_eval cd $2.git echo_eval git reset --hard echo_eval git fetch origin echo_eval git checkout --quiet $3 if [ ! -e build ] then echo_eval mkdir build fi cd .. } # ---------------------------------------------------------------------------- if [ $0 != 'bin/get_ipopt.sh' ] then echo 'bin/get_ipopt.sh: must be executed from its parent directory' exit 1 fi # ---------------------------------------------------------------------------- # # n_job if which nproc >& /dev/null then n_job=$(nproc) else n_job=$(sysctl -n hw.ncpu) fi # # configured_flag configured_flag="external/Ipopt-${ipopt_version}.configured" echo "Executing get_ipopt.sh" if [ -e "$configured_flag" ] then echo "Skipping configuration because $configured_flag exits" echo_eval cd external/Ipopt.git/build make -j $n_job install echo "get_ipopt.sh: OK" exit 0 fi if ls external/Ipopt-*.configured > /dev/null then echo_eval rm -rf external/Mumps.git echo_eval rm -rf external/ASL.git echo_eval rm external/Ipopt-*.configured fi # # prefix eval `grep '^prefix=' bin/get_optional.sh` if [[ "$prefix" =~ ^[^/] ]] then prefix="$(pwd)/$prefix" fi echo "prefix=$prefix" # # external if [ ! -e external ] then mkdir external fi echo_eval cd external # # configure_all configure_all="--disable-dependency-tracking" configure_all+=" --prefix=$prefix" configure_all+=" --enable-shared" # # external/build_type/Ipopt.git name='Ipopt' clone_url_name_version $ipopt_url $name "releases/$ipopt_version" # # external/build_type/ASL.git # external/build_type/mumps.git for name in 'ASL' 'Mumps' do # clone_url_name_version line=$(grep "ThirdParty/$name" 'Ipopt.git/.coin-or/Dependencies') url=$(echo $line | awk '{print $2}' ) version=$(echo $line | awk '{print $3}' ) clone_url_name_version $url $name $version # # get.$name cd $name.git if [ -e "./get.$name" ] then if [ ! -e "./get.$name.done" ] then echo_eval ./get.$name touch ./get.$name.done fi fi cd .. done # # Install ASL cd ASL.git/build echo_eval ../configure $configure_all echo_eval make -j $n_job install cd ../.. # # Install Mumps configure_mumps="$configure_all" if [ "$(uname)" == 'Darwin' ] then if which brew then metis_libdir=$(brew --prefix)/lib metis_incdir=$(brew --prefix)/include if [ ! -e "$metis_libdir/libmetis.dylib" ] then echo 'MacOS+brew: Cannot find metis library directory' fi if [ ! -e "$metis_incdir/metis.h" ] then echo 'MacOS+brew: Cannot find metis include directory' fi configure_mumps+=" --with-metis-lflags='-L$metis_libdir -lmetis'" configure_mumps+=" --with-metis-cflags='-I$metis_incdir'" fi fi cd Mumps.git/build echo_eval ../configure $configure_mumps echo_eval make -j $n_job install cd ../.. # # Install Ipopt cd Ipopt.git/build echo_eval ../configure $configure_all --disable-java echo_eval make -j $n_job install cd ../.. # # configured_flag cd .. touch $configured_flag # ---------------------------------------------------------------------------- echo 'get_ipopt.sh: OK' exit 0 ================================================ FILE: bin/get_optional.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- # # {xrst_begin get_optional.sh} # {xrst_spell # ggrep # grep # gsed # homebrew # sed # } # {xrst_comment_ch #} # # Download and Install The CppAD Optional Packages # ################################################ # # Syntax # ****** # ``bin/get_optional.sh`` # # Purpose # ******* # If you are using Unix, this command will download and install # all of the optional packages that can be used with CppAD. # # Distribution Directory # ********************** # This command must be executed in the # :ref:`download@Distribution Directory` . # # prefix # ****** # This is the prefix for installing the optional packages. # It can be changed by editing its setting of *prefix* below # in the file ``bin/get_optional.sh`` . # Note that there can only be one setting that is not commented out with # a at the start of its line. # # Absolute Path # ============= # If the first character in the prefix is a ``/`` , # it is an absolute path; e.g., the following setting: # {xrst_spell_off} # {xrst_code sh} # prefix="$HOME/prefix/cppad" # {xrst_code} # {xrst_spell_on} # # Relative Path # ============= # If the first character in the prefix is **not** a ``/`` , # it is a path relative to the distribution directory; # e.g., the following setting: # {xrst_spell_off} # {xrst_code sh} prefix="build/prefix" # {xrst_code} # {xrst_spell_on} # # Configuration # ============= # If you do an install and then change the *prefix* , # you should delete all the files listed by the following command: # # ``ls external/`` * . ``configured`` # # MacOS with Homebrew # ******************* # If you are using MacOS with homebrew, get_optional requires # the gnu versions of grep and sed; i.e., ``ggrep`` and ``gsed`` . # # get_optional.log # **************** # This file contains the standard out output for each of the optional scripts # in the order that they are executed. # # get_optional.err # **************** # This file contains the standard error output for each of the optional scripts # in the order that they are executed. # # Contents # ******** # {xrst_toc_table # bin/get_adolc.sh # bin/get_cppadcg.sh # bin/get_colpack.sh # bin/get_fadbad.sh # bin/get_ipopt.sh # bin/get_sacado.sh # } # # {xrst_end get_optional.sh} # ----------------------------------------------------------------------------- if [ $0 != "bin/get_optional.sh" ] then echo "bin/get_optional.sh: must be executed from its parent directory" exit 1 fi # ----------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } if [ -e 'get_optional.log' ] then echo_eval rm get_optional.log fi if [ -e 'get_optional.err' ] then echo_eval rm get_optional.err fi # ----------------------------------------------------------------------------- list='colpack adolc fadbad ipopt sacado cppadcg' for package in $list do echo "bin/get_${package}.sh 1>> get_optional.log 2>> get_optional.err" if bin/get_${package}.sh 1>> get_optional.log 2>> get_optional.err then echo "bin/get_${package}.sh: OK" else echo "bin/get_${package}.sh: Error; try following:" echo ' tail ./get_optional.err' exit 1 fi done # ----------------------------------------------------------------------------- echo "get_optional: OK" ================================================ FILE: bin/get_sacado.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- # {xrst_begin get_sacado.sh} # {xrst_spell # trilinos # } # {xrst_comment_ch #} # # Download and Install Sacado in Build Directory # ############################################## # # Syntax # ****** # ``bin/get_sacado.sh`` # # Purpose # ******* # If you are using Unix, this command will download and install # :ref:`Sacado` # in the CppAD ``build`` directory. # # Distribution Directory # ********************** # This command must be executed in the # :ref:`download@Distribution Directory` . # # Source Directory # **************** # The Sacado source code is downloaded into the sub-directory # ``external/trilinos.git`` below the distribution directory. # # Prefix # ****** # The :ref:`get_optional.sh@prefix` # in the file ``bin/get_optional.sh`` is used for this install. # # Version # ******* # This will install the version of Sacado corresponding to the following # version of Trilinos: # {xrst_spell_off} # {xrst_code sh} version='14-0-0' # {xrst_code} # {xrst_spell_on} # # Configuration # ************* # If the file # # ``external/sacado-`` *version* . ``configured`` # # exists, the configuration will be skipped. # Delete this file if you want to re-run the configuration. # # {xrst_end get_sacado.sh} # ----------------------------------------------------------------------------- package='sacado' if [ $0 != "bin/get_$package.sh" ] then echo "bin/get_$package.sh: must be executed from its parent directory" exit 1 fi # # grep, sed source bin/grep_and_sed.sh # ----------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ----------------------------------------------------------------------------- web_page='https://github.com/trilinos/Trilinos/archive/refs/tags' release_name="trilinos-release-$version" cppad_dir=`pwd` # ----------------------------------------------------------------------------- # n_job if which nproc > /dev/null then n_job=$(nproc) else n_job=$(sysctl -n hw.ncpu) fi # ---------------------------------------------------------------------------- # prefix eval `$grep '^prefix=' bin/get_optional.sh` if [[ "$prefix" =~ ^[^/] ]] then prefix="$cppad_dir/$prefix" fi echo "prefix=$prefix" # ----------------------------------------------------------------------------- configured_flag="external/$package-${version}.configured" echo "Executing get_$package.sh" if [ -e "$configured_flag" ] then echo "Skipping configuration because $configured_flag exits" echo_eval cd external/$release_name/build echo_eval make -j $n_job install echo "get_$package.sh: OK" exit 0 fi # ----------------------------------------------------------------------------- # libdir if [ -e /usr/lib64 ] then libdir='lib64' else libdir='lib' fi # ----------------------------------------------------------------------------- # change into external directory if [ ! -d external ] then echo_eval mkdir external fi echo_eval cd external # ----------------------------------------------------------------------------- # create the trilions source directory and change into it if [ ! -e $release_name ] then wget "$web_page/$release_name.tar.gz" tar -xzf "$release_name.tar.gz" mv Trilinos-$release_name $release_name # # see https://github.com/trilinos/Trilinos/issues/11923 file="$release_name/packages/teuchos/core/src/Teuchos_BigUIntDecl.hpp" if ! $grep '^#include ' $file > /dev/null then $sed -i $file -e 's|#include |&\n#include |' fi fi echo_eval cd $release_name # ----------------------------------------------------------------------------- # change into build sub-directory if [ ! -e build ] then echo_eval mkdir build fi echo_eval cd build # ----------------------------------------------------------------------------- cmd="cmake \ -D CMAKE_BUILD_TYPE=RELEASE \ -D Trilinos_ENABLE_Sacado=ON \ -D Sacado_ENABLE_TESTS=OFF \ -D CMAKE_INSTALL_PREFIX=$prefix \ -D Trilinos_INSTALL_LIB_DIR=$prefix/$libdir \ .." echo $cmd $cmd echo_eval make -j $n_job install # ----------------------------------------------------------------------------- echo_eval touch $cppad_dir/$configured_flag echo "get_$package.sh: OK" ================================================ FILE: bin/git_commit.sh ================================================ #! /usr/bin/env bash set -e -u # !! EDITS TO THIS FILE ARE LOST DURING UPDATES BY xrst.git/bin/dev_tools.sh !! # --------------------------------------------------------------------------- # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # --------------------------------------------------------------------------- # bin/git_commit.sh # Opens your editor with comments about this git commit. # 1. The git commit log message will not include comment lines. # 2. The file git_commit.log contains the message for the previous commit # so that you can read it in and modify it for this commit. # 3. If typos is installed on your system, it will be used to check for # errors in git_commit.log. If so, you can read it and modity it # when you re-run git_commit.sh. # 4. The branch of the commit is automatically placed a the beginning # of the first line for the message. # 5. All the modified files are automatically included in the commit. # 6. The variable check_git_commit in bin/dev_settings.sh can be used # to selectively revert certain files before the commit. # --------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ----------------------------------------------------------------------------- if [ $# != 0 ] then echo 'usage: bin/git_commit.sh: does not expect arguments' exit 1 fi if [ "$0" != 'bin/git_commit.sh' ] then echo 'bin/git_commit.sh: must execute this script from its parent directory' exit 1 fi if [ ! -e './.git' ] then echo 'bin/git_commit.sh: cannot find ./.git' exit 1 fi # # grep, sed source bin/grep_and_sed.sh # # check_git_commit source bin/dev_settings.sh # ----------------------------------------------------------------------------- # EDITOR set +u if [ "$EDITOR" == '' ] then echo 'bin/git_commit.sh: EDITOR is not defined.' exit 1 fi set -u # ----------------------------------------------------------------------------- # check_git_commit echo 's|^...||' > temp.sed for name in $check_git_commit do if [ -f $name ] then echo "^$name\$" | $sed -e 's|/|[/]|g' -e 's|.*|/&/p|' >> temp.sed elif [ -d $name ] then echo "^$name/" | $sed -e 's|/|[/]|g' -e 's|.*|/&/p|' >> temp.sed else echo "$name in check_git_commit is not a file or directory" exit 1 fi done list=$( git status --porcelain | $sed -n -f temp.sed ) for file in $list do res='' while [ "$res" != 'revert' ] && [ "$res" != 'commit' ] do read -p "$file: Revert or commit changes [revert/commit] ?" res done if [ "$res" == 'revert' ] then git reset $file git checkout $file fi done # ----------------------------------------------------------------------------- # new files # convert spaces in file names to @@ list=$( git status --porcelain | $sed -n -e '/^?? /p' | \ $sed -e 's|^?? ||' -e 's|"||g' -e 's| |@@|g' ) for file in $list do # convert @@ in file names back to spaces file=$(echo $file | $sed -e 's|@@| |g') res='' while [ "$res" != 'delete' ] && [ "$res" != 'add' ] && [ "$res" != 'abort' ] do read -p "'$file' is unknown to git, [delete/add/abort] ?" res done if [ "$res" == 'delete' ] then # may be spaces in file name so do not use echo_eval echo "rm '$file'" rm "$file" elif [ "$res" == 'abort' ] then echo 'bin/git_commit.sh: aborting' exit 1 else git add "$file" fi done # ----------------------------------------------------------------------------- # temp.log branch=$(git branch --show-current) cat << EOF > temp.log $branch: # 1. Enter message for this commit above this line. # 2. The message for the previous commit is in git_commit.log (if it exists). # 3. This commit will abort if the first line does not begin with "$branch:" # because $branch is the branch for this commit. # 4. Lines starting with '#' are not included in the message. # 5. Below is a list of the files for this commit: EOF git status --porcelain | $sed -e 's|^|# |' >> temp.log $EDITOR temp.log $sed -i -e '/^#/d' temp.log if ! head -1 temp.log | $grep "^$branch:" > /dev/null then echo "Aborting because first line does not begin with $branch:" echo 'See ./temp.log' exit 1 fi if ! head -1 temp.log | $grep "^$branch:.*[^ \t]" > /dev/null then echo "Aborting because only white space follow $branch: in first line" echo 'See ./temp.log' exit 1 fi # # git_commit.log mv temp.log git_commit.log if which typos >& /dev/null then typos git_commit.log fi # ----------------------------------------------------------------------------- # git add echo_eval git add --all # # # git commit git commit --file=git_commit.log # echo 'bin/git_commit.sh: OK' exit 0 ================================================ FILE: bin/grep_and_sed.sh ================================================ # --------------------------------------------------------------------------- # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # --------------------------------------------------------------------------- # source bin/grep_and_sed # Sets the shell variables grep and sed to the gnu version for this system # --------------------------------------------------------------------------- # # sed, grep sed=sed grep=grep if [ "$(uname)" == 'Darwin' ] then if which gsed > /dev/null then sed=gsed else echo 'darwin_names.sh: cannot find gsed (gnu sed) on MacOS system' fi if which ggrep > /dev/null then grep=ggrep else echo 'darwin_names.sh: cannot find ggrep (gnu grep) on MacOS system' fi fi ================================================ FILE: bin/group_list.sh ================================================ #! /usr/bin/env bash set -e -u # !! EDITS TO THIS FILE ARE LOST DURING UPDATES BY xrst.git/bin/dev_tools.sh !! # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2020-25 Bradley M. Bell # ---------------------------------------------------------------------------- # bin/group_list.sh # Returns a space separated list of the group names for this project. # The list is extracted from the [root_file] section of the xrst.toml # file for this project. # If there is no xrst.toml file for this project, the group list is just # the default group. # # sed source bin/grep_and_sed.sh # if [ ! -e xrst.toml ] then echo 'default' exit 0 fi # # temp.sed cat << EOF > temp.sed /^\\[root_file\\]/ ! b end : loop N /\\n\\[/! b loop s|^\\[root_file\\]|| s|\\n *#[^\\n]*||g s|\\n\\([A-Za-z0-9_.]*\\) *=[^\\n]*|\\1 |g s|\\n[^\\n]*\$|| # p # : end EOF # # group_list group_list=$( $sed -n -f temp.sed xrst.toml) # echo "$group_list" ================================================ FILE: bin/master_revert.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- if [ "$0" != 'bin/master_revert.sh' ] then echo "bin/master_revert.sh: must be executed from its parent directory" exit 1 fi # ----------------------------------------------------------------------------- branch=$(git branch | sed -n '/^[*]/p' | sed -e 's/[*] *//') list=$( git diff master $branch | \ sed -n -e '/^diff --git/p' | \ sed -e 's|^diff --git a/||' -e 's| b/|@|' ) for pair in $list do master_file=$(echo $pair | sed -e 's|@.*||') branch_file=$(echo $pair | sed -e 's|[^@]*@||') if [ "$master_file" == "$branch_file" ] then echo "reverting $master_file" git show master:$master_file > $branch_file else echo 'skipping move of' echo "$master_file -> $branch_file" fi done echo 'master_revert.sh: OK' exit 0 ================================================ FILE: bin/new_release.sh ================================================ #! /usr/bin/env bash set -e -u # !! EDITS TO THIS FILE ARE LOST DURING UPDATES BY xrst.git/bin/dev_tools.sh !! # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2020-25 Bradley M. Bell # ----------------------------------------------------------------------------- # bin/new_release.sh [--skip_stable_check_all] # Creates and check a release for the year and release number specified below. # # bin/check_all.sh [--skip_external_links] # is used by new_release to skip checking external links. # new_release.sh skips this when testing before the new release (tag) exists. # ----------------------------------------------------------------------------- year='2026' # Year for this stable version release='0' # first release for each year starts with 0 # ----------------------------------------------------------------------------- if [ "$0" != 'bin/new_release.sh' ] then echo 'bin/new_release.sh: must be executed from its parent directory' exit 1 fi if [ ! -e './.git' ] then echo 'bin/new_release.sh: cannot find ./.git' exit 1 fi if [[ "$year" =~ ^[0-9]{4}$ ]] then echo "year = $year" else echo "new_release.sh: year = $year is not valid" exit 1 fi if [[ "$release" =~ ^[0-9]{1,2}$ ]] then echo "release = $release" else echo "new_release.sh: release = $release is not valid" exit 1 fi # # skip_stable_check_all skip_stable_check_all='no' while [ $# != 0 ] do if [ "$1" == '--skip_stable_check_all' ] then skip_stable_check_all='yes' else echo 'bin/new_release.sh [--skip_stable_check_all]' echo "$1 is not a valid argument" exit 1 fi shift done # ----------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ----------------------------------------------------------------------------- # # main_branch main_branch=$(git branch --show-current) if [ "$main_branch" != 'master' ] && [ "$main_branch" != 'main' ] then echo 'bin/new_release.sh: execute using master or main branch' exit 1 fi # # sed source bin/grep_and_sed.sh # # version_file_list source bin/dev_settings.sh # # first_version_file first_version_file=$(echo $version_file_list | $sed -e 's|^ *||' -e 's| .*||') # # version_type cat << EOF > temp.sed /["'][0-9]{8}["']/b one /["'][0-9]{8}[.][0-9]{1,2}["']/b one /["'][0-9]{4}[.][0-9]{1,2}[.][0-9]{1,2}["']/b one b end # : one s|.*["']([0-9]{8})["'].*|\\1| s|.*["']([0-9]{8}[.][0-9]{1,2})["'].*|\\1| s|.*["']([0-9]{4}[.][0-9]{1,2}[.][0-9]{1,2})["'].*|\\1| p # : end EOF version=$($sed -n -r -f temp.sed $first_version_file) if [[ "$version" =~ ^[0-9]{8}$ ]] then version_type=1 elif [[ "$version" =~ ^[0-9]{8}[.][0-9]{1,2}$ ]] then version_type=2 elif [[ "$version" =~ ^[0-9]{4}[.][0-9]{1,2}[.][0-9]{1,2}$ ]] then version_type=3 fi # # tag if [ "$version_type" == 1 ] || [ "$version_type" == 2 ] then tag="${year}0000.$release" else tag=$year.0.$release fi # # tag_committed tag_committed='no' if git tag --list | grep "$tag" > /dev/null then tag_committed='yes' fi # # stable_branch stable_branch=stable/$year # # stable_local_hash if ! git show-ref --hash "heads/$stable_branch" > /dev/null then echo "Cannot find local version of $stable_branch. Do the following ?" echo "git branch $stable_branch $main_branch" exit 1 fi stable_local_hash=$(git show-ref --hash "heads/$stable_branch" ) # # stable_remote_hash if ! git show-ref --hash "origin/$stable_branch" > /dev/null then echo "Cannot find remote version of $stable_branch. Do the following ?" echo "git checkout $stable_branch" echo "git push --set-upstream origin $stable_branch" exit 1 fi stable_remote_hash=$(git show-ref --hash "origin/$stable_branch" ) # # main_local_hash main_local_hash=$(git show-ref --hash "heads/$main_branch" ) # # main_remote_hash main_remote_hash=$(git show-ref --hash "origin/$main_branch" ) # # ---------------------------------------------------------------------------- # Changes to main_branch # ---------------------------------------------------------------------------- # # version_file_list cat << EOF > temp.sed s|stable-[0-9]{4}|stable-$year|g s|release-[0-9]{4}|release-$year|g # s|archive/[0-9]{4}[.][0-9]*[.][0-9]*[.]tar[.]gz|archive/$tag.tar.gz| s|archive/[0-9]{8}[.]tar[.]gz|archive/$tag.tar.gz| s|archive/[0-9]{8}[.][0-9]*[.]tar[.]gz|archive/$tag.tar.gz| # s|tags/[0-9]{4}[.][0-9]*[.][0-9]*>|tags/$tag>| s|tags/[0-9]{8}>|tags/$tag>| s|tags/[0-9]{8}[.][0-9]*>|tags/$tag>| # s|tags/[0-9]{4}[.][0-9]*[.][0-9]* *\$|tags/$tag| s|tags/[0-9]{8} *\$|tags/$tag| s|tags/[0-9]{8}[.][0-9]* *\$|tags/$tag| # EOF for file in $version_file_list do $sed -r -i $file -f temp.sed done # # run_xrst.sh if [ "$tag_committed" == 'yes' ] then echo_eval bin/run_xrst.sh --external_links else echo_eval bin/run_xrst.sh fi # # git_status git_status=$(git status --porcelain) if [ "$git_status" != '' ] then echo "bin/new_release: git status is not empty for $main_branch branch" echo 'use bin/git_commit.sh to commit its changes ?' exit 1 fi # ---------------------------------------------------------------------------- # Changes to stable branch # ---------------------------------------------------------------------------- if ! git show-ref $stable_branch > /dev/null then echo "bin/new_release: neither local or remove $stable_branch exists." echo 'Use the following to create it ?' echo " git branch $stable_branch" exit 1 fi if ! git checkout $stable_branch then echo "bin/new_release: should be able to checkout $stable_branch" exit 1 fi # # version_file_list cat << EOF > temp.sed s|stable-[0-9]{4}|stable-$year|g s|release-[0-9]{4}|release-$year|g # s|archive/[0-9]{4}[.][0-9]*[.][0-9]*[.]tar[.]gz|archive/$tag.tar.gz| s|archive/[0-9]{8}[.]tar[.]gz|archive/$tag.tar.gz| s|archive/[0-9]{8}[.][0-9]*[.]tar[.]gz|archive/$tag.tar.gz| # s|tags/[0-9]{4}[.][0-9]*[.][0-9]*>|tags/$tag>| s|tags/[0-9]{8}>|tags/$tag>| s|tags/[0-9]{8}[.][0-9]*>|tags/$tag>| # s|tags/[0-9]{4}[.][0-9]*[.][0-9]* *\$|tags/$tag| s|tags/[0-9]{8} *\$|tags/$tag| s|tags/[0-9]{8}[.][0-9]* *\$|tags/$tag| # EOF for file in $version_file_list do $sed -r -i $file -f temp.sed done # # first_version_file cat << EOF > temp.sed s|(["'])[0-9]{8}(["'])|\\1$tag\\2| s|(["'])[0-9]{8}[.][0-9]{1,2}(["'])|\\1$tag\\2| s|(["'])[0-9]{4}[.][0-9]{1,2}[.][0-9]{1,2}(["'])|\\1$tag\\2| EOF $sed -r -f temp.sed -i $first_version_file if ! grep "['\"]$tag['\"]" $first_version_file > /dev/null then echo "bin/rew_release: branch = $stable_branch" echo "Version number should be $tag in $first_version_file" exit 1 fi # # check_version # changes to version ? if ! bin/check_version.sh then echo 'Continuing even though bin/check_version made changes.' if ! bin/check_version.sh then echo 'Continuing even though bin/check_version made more changes.' fi fi # # check_all.sh if [ "$skip_stable_check_all" == 'no' ] then if [ "$tag_committed" == 'yes' ] then echo_eval bin/check_all.sh --suppress_spell_warnings else echo_eval bin/check_all.sh \ --suppress_spell_warnings --skip_external_links fi fi # # git_status git_status=$(git status --porcelain) if [ "$git_status" != '' ] then echo "bin/new_release: git status --porcelean not empty for $stable_branch" echo 'use bin/git_commit.sh to commit its changes ?' exit 1 fi # ----------------------------------------------------------------------------- # # stable_remote if [ "$stable_remote_hash" == '' ] then empty_hash='yes' echo "bin/new_release: remote $stable_branch does not exist." echo 'Use the following to create it ?' echo " git push origin $stable_branch" exit 1 fi if [ "$stable_local_hash" != "$stable_remote_hash" ] then empty_hash='yes' echo "bin/new_release: local and remote $stable_branch differ." echo "local $stable_local_hash" echo "remote $stable_remote_hash" echo 'Use git push to fix this ?' exit 1 fi # # push tag if [ "$tag_committed" == 'no' ] then read -p 'commit release or abort [c/a] ?' response if [ "$response" == 'a' ] then exit 1 fi echo "git tag -a -m 'created by new_release.sh' $tag $stable_remote_hash" git tag -a -m 'created by new_release.sh' $tag $stable_remote_hash # echo "git push origin $tag" git push origin $tag # echo 'bin/new_release.sh: must be re-run to check external links' exit 1 fi # # main_remote git checkout $main_branch if [ "$main_local_hash" != "$main_remote_hash" ] then empty_hash='yes' echo "bin/new_release: local and remote $main_branch differ." echo "local $main_local_hash" echo "remote $main_remote_hash" echo 'Use git push to fix this ?' exit 1 fi # ---------------------------------------------------------------------------- echo 'bin/new_release.sh: OK' exit 0 ================================================ FILE: bin/no_bitwise.sh ================================================ #! /usr/bin/env bash set -e -u # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2020-24 Bradley M. Bell # ----------------------------------------------------------------------------- if [ "$0" != "bin/no_bitwise.sh" ] then echo "bin/no_bitwise.sh: must be executed from its parent directory" exit 1 fi if [ "$#" != 0 ] then echo 'bin/no_bitwise.sh: does not expect any arguments' exit 1 fi # ---------------------------------------------------------------------------- # git reset --hard ? response='' while [ "$response" != 'yes' ] && [ "$response" != 'abort' ] do read -p 'git reset --hard [yes/abort] ?' response done if [ "$response" == 'abort' ] then echo 'bin/no_bitwise.sh: aborted' exit 1 fi git reset --hard # # sed.$$ cat << EOF > sed.$$ s/] *& *\\([a-z][a-z_]*\\)\\[/] \\&\\& \\1[/g s/] *| *\\([a-z][a-z_]*\\)\\[/] || \\1[/g # s/) *& *(/) \\&\\& (/g s/) *| *(/) || (/g # s/] *& *\\([!(]\\)/] \\&\\& \\1/g s/] *| *\\([!(]\\)/] || \\1/g # s/) *& *\\([A-Za-z][A-Za-z_]*[[(]\\)/) \\&\\& \\1/g s/) *| *\\([A-Za-z][A-Za-z_]*[[(]\\)/) || \\1/g # s/] *\\& *$/] \\&\\&/ s/] | *$/] ||/ EOF # # file_list file_list=$(git ls-files | sed -n \ -e '/\.hpp$/p' \ -e '/\.cpp/p'\ -e '/example[/]abs_normal[/]qp_interior.hpp/d' ) # # *.hpp, *.cpp # replace bitwise operands on logicals by logical operations. for file in $file_list do sed -i $file -f sed.$$ done # # sed.$$ rm sed.$$ echo 'no_bitwise.sh: OK' exit 0 # The script above was run on 2024-09021 following by the following commands: # # git_new.sh to # rm build/speed/cppad/*.out # bin/speed_new.sh all # # Here is the results: # # one=cur, two=new # det_lu_rate_one = [ 1355694, 7815, 1052, 305.70, 132.33 ] # det_lu_rate_two = [ 1326588, 7668, 1031, 305.01, 128.91 ] # det_minor_rate_one = [ 780970, 449962, 154473, 33566, 5070 ] # det_minor_rate_two = [ 775970, 444017, 146952, 32100, 4839 ] # mat_mul_rate_one = [ 1633224, 5487, 750.39, 195.23, 75.93 ] # mat_mul_rate_two = [ 1646152, 5404, 733.41, 199.21, 74.30 ] # ode_rate_one = [ 275842, 1531, 430.16, 191.22, 113.16 ] # ode_rate_two = [ 274601, 1508, 428.69, 192.79, 113.09 ] # poly_rate_one = [ 1882647, 776135, 538845, 417875, 333869 ] # poly_rate_two = [ 1840832, 748291, 539711, 414037, 340217 ] # sparse_hessian_rate_one = [ 475.58, 41.42, 7.5392, 2.1304, 0.8436 ] # sparse_hessian_rate_two = [ 478.50, 38.16, 7.6061, 2.1297, 0.9010 ] # sparse_jacobian_rate_one = [ 1434, 332.37, 124.84, 35.39, 21.27 ] # sparse_jacobian_rate_two = [ 1522, 338.94, 119.77, 34.85, 20.94 ] ================================================ FILE: bin/package.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- if [ "$0" != "bin/package.sh" ] then echo "bin/package.sh: must be executed from its parent directory" exit 1 fi echo_eval() { echo $* eval $* } # grep, sed source bin/grep_and_sed.sh # ----------------------------------------------------------------------------- # # version version=`$sed -n -e '/^SET( *cppad_version *"[0-9.]*"/p' CMakeLists.txt | \ $sed -e 's|.*"\([^"]*\)".*|\1|'` # # build if [ ! -e 'build' ] then echo_eval mkdir build fi # # build/cppad-version if ls build/cppad-* >& /dev/null then echo_eval rm -r build/cppad-* fi mkdir build/cppad-$version git ls-files -z | xargs -0 tar -czf build/cppad-$version/tar.tgz cd build/cppad-$version tar -xzf tar.tgz rm tar.tgz # # build/cppad-$version.tgz cd .. echo_eval tar -czf cppad-$version.tgz cppad-$version echo_eval rm -r cppad-$version # echo 'package.sh: OK' exit 0 ================================================ FILE: bin/run_cmake.sh ================================================ #! /usr/bin/env bash # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- set -e -u echo $0 $* # ---------------------------------------------------------------------------- if [ ! -e "bin/run_cmake.sh" ] then echo "bin/run_cmake.sh: must be executed from its parent directory" exit 1 fi # ----------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ----------------------------------------------------------------------------- verbose_make='no' standard='c++17' m32='no' profile_speed='no' callgrind='no' clang='no' static='no' yes_adolc='yes' yes_colpack='yes' yes_ipopt='yes' yes_fadbad='yes' yes_cppad_jit='yes' yes_cppadcg='yes' yes_sacado='yes' yes_documentation='yes' addr_t_type='int' testvector='boost' debug_which='debug_all' while [ "$#" != 0 ] do if [ "$1" == '--help' ] then cat << EOF usage: bin/run_cmake.sh: \\ [--help] \\ [--verbose_make] \\ [--c++11] \\ [--m32] \\ [--profile_speed] \\ [--callgrind] \\ [--clang ] \\ [--static] \\ [--no_adolc] \\ [--no_colpack] \\ [--no_ipopt] \\ [--no_fadbad] \\ [--no_cppad_jit] \\ [--no_cppadcg] \\ [--no_sacado] \\ [--no_optional] \\ [--no_documentation] \\ [--addr_t_] \\ [--_vector] \\ [--debug_] The --help option just prints this message and exits. The value above must be one of: cppad, boost, eigen, or std. The value must be one of: odd, even, all, none. The value must be one of: size_t, int, unsigned_int EOF exit 0 fi case "$1" in --verbose_make) verbose_make='yes' ;; --c++11) standard='c++11' ;; --m32) m32='yes' ;; --profile_speed) profile_speed='yes' ;; --callgrind) callgrind='yes' ;; --clang) clang='yes' ;; --static) static='yes' ;; --no_adolc) yes_adolc='no' ;; --no_colpack) yes_colpack='no' ;; --no_ipopt) yes_ipopt='no' ;; --no_cppadcg) yes_cppadcg='no' ;; --no_cppad_jit) yes_cppad_jit='no' ;; --no_fadbad) yes_fadbad='no' ;; --no_sacado) yes_sacado='no' ;; --no_optional) yes_adolc='no' yes_colpack='no' yes_ipopt='no' yes_cppadcg='no' yes_fadbad='no' yes_sacado='no' testvector='cppad' ;; --no_documentation) yes_documentation='no' ;; # ---------------------------------------------------------------------- --addr_t_size_t) addr_t_type='size_t' ;; --addr_t_int) addr_t_type='int' ;; --addr_t_unsigned_int) addr_t_type='unsigned int' ;; # ---------------------------------------------------------------------- --cppad_vector) testvector='cppad' ;; --boost_vector) testvector='boost' ;; --eigen_vector) testvector='eigen' ;; --std_vector) testvector='std' ;; # ---------------------------------------------------------------------- --debug_odd) debug_which='debug_odd' ;; --debug_even) debug_which='debug_even' ;; --debug_all) debug_which='debug_all' ;; --debug_none) debug_which='debug_none' ;; # ---------------------------------------------------------------------- *) echo "$1 is an invalid option, try bin/run_cmake.sh --help" exit 1 esac shift done if [ "$standard" == 'c++11' ] then # Scacdo cannot be used with c++11 if [ "$yes_sacado" == 'yes' ] then echo 'Cannot use sacado with c++11' exit 1 fi fi # ----------------------------------------------------------------------------- # prefix eval `grep '^prefix=' bin/get_optional.sh` if [[ "$prefix" =~ ^[^/] ]] then prefix="$(pwd)/$prefix" fi echo "prefix=$prefix" # ----------------------------------------------------------------------------- # PKG_CONFIG_PATH PKG_CONFIG_PATH="$prefix/share/pkgconfig" for libdir in lib lib64 do PKG_CONFIG_PATH+=":$prefix/$libdir/pkgconfig" done export PKG_CONFIG_PATH # --------------------------------------------------------------------------- if [ ! -e build ] then echo_eval mkdir build fi echo_eval cd build for name in CMakeCache.txt build.ninja Makefile do if [ -e $name ] then echo_eval rm -r $name fi done # ----------------------------------------------------------------------------- # clean all variables in cmake cache cmake_args='-U .+ -D cmake_defined_ok=FALSE' # ----------------------------------------------------------------------------- # Generator if [ "$verbose_make" == 'yes' ] then cmake_args+=" -G 'Unix Makefiles' -D CMAKE_VERBOSE_MAKEFILE=YES" else cmake_args+=" -G Ninja" fi # ----------------------------------------------------------------------------- # cppad_prefix cmake_args+=" -D cppad_prefix=$prefix" # # cmake_install_includedirs if [ -d '/usr/include' ] then cmake_args+=" -D cmake_install_includedirs=include" fi # # cmake_install_datadir if [ -d '/usr/share' ] then cmake_args+=" -D cmake_install_datadir=share" fi # # cmake_install_docdir if [ -d '/usr/share' ] && [ "$yes_documentation" == 'yes' ] then cmake_args+=" -D cmake_install_docdir=share/doc" cmake_args+=" -D include_doc=true" fi # # cmake_install_libdirs if [ -d '/usr/lib64' ] then cmake_args+=" -D cmake_install_libdirs='lib64;lib'" elif [ -d '/usr/lib' ] then cmake_args+=" -D cmake_install_libdirs='lib;lib64'" fi # # {package}_prefix prefix_list='' include_list='' if [ "$yes_cppadcg" == 'yes' ] then if [ ! -e "$prefix/include/cppad/cg/cg.hpp" ] then echo "Cannot find $prefix/include/cppad/cg/cg.hpp" exit 1 fi include_list+=" cppadcg" fi if [ "$yes_fadbad" == 'yes' ] then if [ ! -e "$prefix/include/FADBAD++/badiff.h" ] then echo "Cannot find $prefix/include/FADBAD++/badiff.h" exit 1 fi prefix_list+=" fadbad" fi if [ "$yes_adolc" == 'yes' ] then if [ ! -d "$prefix/include/adolc" ] then echo "Cannot file $prefix/include/adolc" exit 1 fi include_list+=" adolc" fi if [ "$yes_colpack" == 'yes' ] then if [ ! -e "$prefix/include/ColPack" ] then echo "Cannot find $prefix/include/ColPack" exit 1 fi prefix_list+=" colpack" fi if [ "$yes_ipopt" == 'yes' ] then if [ ! -e "$prefix/include/coin-or/IpNLP.hpp" ] then echo "Cannot find $prefix/include/coin-or/IpoptConfig.hpp" exit 1 fi include_list+=" ipopt" fi if [ "$yes_sacado" == 'yes' ] then if [ ! -e "$prefix/include/Sacado_config.h" ] then echo "Cannot find $prefix/include/Sacado_config.h" exit fi prefix_list+=" sacado" fi for package in $include_list do cmake_args+=" -D include_${package}=true" done for package in $prefix_list do cmake_args+=" -D ${package}_prefix=$prefix" done # # cppad_link_flags if [ "$m32" == 'yes' ] then cmake_args+=" -D cppad_link_flags=-m32" fi # # cppad_cxx_flags if [ "$m32" == 'yes' ] then cppad_cxx_flags="-std=$standard -m32" else cppad_cxx_flags="-std=$standard" fi cppad_cxx_flags+=' -Wall -pedantic-errors -Wshadow' cppad_cxx_flags+=' -Wfloat-conversion -Wconversion' if [ "$debug_which" == 'debug_all' ] then # CMAKE_CXX_FLAGS_DEBUG include -g so do not need it here cppad_cxx_flags+=" -O0" elif [ "$callgrind" == 'yes' ] then # This is a quote from the Callgrind manual: # 'As with Cachegrind, you probably want to compile with debugging info # (the -g option) and with optimization turned on.' cppad_cxx_flags+=" -g" fi if [ "$(uname)" == 'Darwin' ] then if which brew > /dev/null then cppad_cxx_flags+=" -I $(brew --prefix)/include" fi fi # # cmake_args cmake_args+=" -D cppad_cxx_flags='$cppad_cxx_flags'" # # clang if [ "$clang" == 'yes' ] then cmake_args+=" -D CMAKE_C_COMPILER=clang" cmake_args+=" -D CMAKE_CXX_COMPILER=clang++" fi # # static if [ "$static" == 'yes' ] then cmake_args+=" -D cppad_static_lib=true" fi # # profile if [ "$profile_speed" == 'yes' ] then cmake_args+=" -D cppad_profile_flag=-pg" fi # # debug_and_release if [ "$debug_which" == 'debug_none' ] || [ "$debug_which" == 'debug_all' ] then cmake_args+=" -D cppad_debug_and_release=false" fi # # simple options cmake_args+=" -D cppad_testvector=$testvector" cmake_args+=" -D cppad_debug_which=$debug_which" cmake_args+=" -D cppad_max_num_threads=48" cmake_args+=" -D cppad_tape_id_type='$addr_t_type'" cmake_args+=" -D cppad_tape_addr_type='$addr_t_type'" # echo_eval cmake $cmake_args .. # # ---------------------------------------------------------------------------- echo "$0: OK" exit 0 ================================================ FILE: bin/run_configure.sh ================================================ #! /usr/bin/env bash # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- set -e -u echo $0 $* # ---------------------------------------------------------------------------- if [ ! -e "bin/run_configure.sh" ] then echo "bin/run_configure.sh: must be executed from its parent directory" exit 1 fi # ----------------------------------------------------------------------------- with_clang='' with_verbose_make='' cpp_standard='c++17' with_vector='' while [ "$#" != '0' ] do if [ "$1" == '--help' ] then cat << EOF usage: bin/run_configure.sh \\ [--help] \\ [--clang] \\ [--verbose_make] \\ [--c++ ] \\ [--_vector] The value yy is two decimal digits specifying the C++ standard year. The value must be one of: cppad, boost, eigen, std. EOF exit 0 fi case "$1" in --clang) with_clang='--with-clang' ;; --verbose_make) with_verbose_make='--with-verbose-make' ;; --c++*) cpp_standard=$(echo "$1" | sed -e 's|^--||') ;; --cppad_vector) with_vector='' ;; --boost_vector) with_vector='--with-boostvector' ;; --eigen_vector) with_vector='--with-eigenvector' ;; --std_vector) with_vector='--with-stdvector' ;; *) echo "$1 is an invalid option, try bin/run_configure.sh --help" exit 1 esac shift done # ----------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ----------------------------------------------------------------------------- # prefix eval `grep '^prefix=' bin/get_optional.sh` if [[ "$prefix" =~ ^[^/] ]] then prefix="$(pwd)/$prefix" fi echo "prefix=$prefix" # ----------------------------------------------------------------------------- # # PKG_CONFIG_PATH PKG_CONFIG_PATH="$prefix/lib64/pkgconfig:$prefix/lib/pkgconfig" PKG_CONFIG_PATH="$prefix/share/pkgconfig:$PKG_CONFIG_PATH" export PKG_CONFIG_PATH # # cppad_cxx_flags cppad_cxx_flags="-std=$cpp_standard -Wall -pedantic-errors -Wshadow" cppad_cxx_flags+=" -Wfloat-conversion -Wconversion" if [ "$(uname)" == 'Darwin' ] then if which brew > /dev/null then cppad_cxx_flags+=" -I $(brew --prefix)/include" fi fi # 2DO: clang++ 14.05 is generating a lot of warnings (we should fix these) # # scaado_prefix cxx_standard_year=$(echo $cpp_standard | sed -e 's|c++||') if [ "$cxx_standard_year" -lt 17 ] then scaado_prefix='' else scaado_prefix="SACADO_DIR=$prefix" fi # # --------------------------------------------------------------------------- if [ ! -e build ] then echo_eval mkdir build fi echo_eval cd build # ----------------------------------------------------------------------------- ../configure \ --prefix=$prefix \ $with_clang \ $with_verbose_make \ $with_vector \ MAX_NUM_THREADS=32 \ CXX_FLAGS="'$cppad_cxx_flags'" \ ADOLC_DIR=$prefix \ FADBAD_DIR=$prefix \ IPOPT_DIR=$prefix \ $scaado_prefix \ TAPE_ADDR_TYPE=size_t \ TAPE_ID_TYPE=size_t # ---------------------------------------------------------------------------- echo "$0: OK" exit 0 ================================================ FILE: bin/run_doxygen.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- if [ $0 != 'bin/run_doxygen.sh' ] then echo 'bin/run_doxygen.sh: must be executed from its parent directory' exit 1 fi echo_eval() { echo $* eval $* } # ----------------------------------------------------------------------------- # run doxygen version=`version.sh get` error_file='doxygen.err' output_directory='doxydoc' for name in doxyfile $error_file $output_directory do if [ -e $name ] then echo_eval rm -r $name fi done echo_eval mkdir doxydoc echo_eval bin/doxyfile.sh $version $error_file $output_directory # echo 'doxygen doxyfile > doxygen.log' doxygen doxyfile > doxygen.log # ----------------------------------------------------------------------------- # check for warnings and errors # doxygen_version=`doxygen --version | sed -e 's|\.|*100+|' -e 's|\.|*10+|' -e 's|\..*||'` let doxygen_version=$doxygen_version if (( $doxygen_version <= 155 )) then doxygen_version=`doxygen --version` echo "doxygen version $doxygen_version is <= 1.5.6" echo "Hence it is to old to check for warnings or errors." exit 0 fi if (( $doxygen_version == 163 )) then doxygen_version=`doxygen --version` echo "doxygen version $doxygen_version is == 1.6.3" echo "Hence it has a problem with warnings about missing # defines;" echo "see http://comments.gmane.org/gmane.text.doxygen.general/8594" exit 0 fi list=`head doxygen.err` if [ "$list" == "" ] then echo 'run_doxygen.sh OK' exit 0 fi echo 'bin/run_doxygen.sh: Doxygen errors or warnings; see doxygen.err' echo 'run_doxygen.sh: Error' exit 1 ================================================ FILE: bin/run_xrst.sh ================================================ #! /usr/bin/env bash set -e -u # !! EDITS TO THIS FILE ARE LOST DURING UPDATES BY xrst.git/bin/dev_tools.sh !! # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2020-25 Bradley M. Bell # ---------------------------------------------------------------------------- # bin/run_xrst.sh flags # possible flags # --help print the run_xrst.sh help message # --target_tex create tex (instead of html) files # --exclude_dev exclude developer documentation # --suppress_spell_warnings do not check for documentation spelling errors # --rst_line_numbers sphinx errors and warnings use rst line numbers # --replace_spell_commands replace xrst_spell commands assuming no errors # --external_links check documentation external links # # xrst.toml # The group_list argument will be automatically extracted from xrst.toml # # .readthedocs # The index_page_name will be automatically extracted from .readthedocs.yaml # ---------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } # # grep, sed source bin/grep_and_sed.sh # # index_page_name source bin/dev_settings.sh # ----------------------------------------------------------------------------- if [ "$0" != 'bin/run_xrst.sh' ] then echo 'bin/run_xrst.sh must be run from its parent directory.' exit 1 fi if [ $# == 1 ] then if [ "$1" == --help ] then cat << EOF bin/run_xrst.sh flags possible flags --help print the run_xrst.sh help message --target_tex create tex (instead of html) files --exclude_dev exclude developer documentation (group dev) --suppress_spell_warnings do not check for documentation spelling errors --rst_line_numbers sphinx errors and warnings use rst line numbers --replace_spell_commands replace xrst_spell commands assuming no errors --external_links check documentation external links EOF exit 0 fi fi # # exclude_dev, extra_flags target_tex='no' exclude_dev='no' extra_flags='' while [ $# != 0 ] do case "$1" in --target_tex) target_tex='yes' ;; --exclude_dev) exclude_dev='yes' ;; --suppress_spell_warnings) extra_flags+=" $1" ;; --external_links) extra_flags+=" $1 --link_timeout 5" ;; *) echo "bin/run_xrst.sh: command line argument "$1" is not valid" exit 1 ;; esac # shift done # # extra_flags if [ "$target_tex" == 'yes' ] then extra_flags+=' --target tex' else extra_flags+=' --target html' fi # # build/html if [ -e build/html ] then rm -r build/html fi # # group_list group_list=$(bin/group_list.sh | $sed -e 's|^| |' -e 's|$| |' ) if [ "$exclude_dev" == 'yes' ] then group_list=$( echo "$group_list" | $sed -e 's| dev | |' ) fi group_list=$( echo $group_list | $sed -e 's|^ *||' -e s'| *$||' ) # # n_job if which nproc >& /dev/null then n_job=$(nproc) else n_job=$(sysctl -n hw.ncpu) fi # # xrst # python3 -m will search the current working directory first echo_eval python3 -m xrst \ --local_toc \ --html_theme sphinx_rtd_theme \ --index_page_name $index_page_name \ --group_list $group_list \ --number_jobs $n_job \ $extra_flags # ----------------------------------------------------------------------------- echo 'run_xrst.sh: OK' exit 0 ================================================ FILE: bin/sort.sh ================================================ #! /usr/bin/env bash set -e -u # !! EDITS TO THIS FILE ARE LOST DURING UPDATES BY xrst.git/bin/dev_tools.sh !! # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2023-25 Bradley M. Bell # ----------------------------------------------------------------------------- # bin/sort.sh file_name # Checks all the sections between # BEGIN_SORT_THIS_LINE_PLUS_# # END_SORT_THIS_LINE_MINUS_# # in file_name are sorted. If not, it is corrected and an error is returned. # ----------------------------------------------------------------------------- # fix sort order; see # unix.stackexchange.com/questions/87745/what-does-lc-all-c-do/87763#87763 export LC_ALL='C' # if [ "$#" == 0 ] then cat<< EOF usage: sort.sh file_name BEGIN_SORT_THIS_LINE_PLUS_nb: defines beginning line for sort. END_SORT_THIS_LINE_MINUS_ne: defines ending line for sort. The tokens nb and ne are both a single non-zero decimal digit. If the exit status is 0, the last line on standard out is one of the following: 'sort.sh: OK' The file was already sorted 'sort.sh: Done' The file was sorted EOF exit 1 fi file_name="$1" # # grep, sed source bin/grep_and_sed.sh # if $grep 'BEGIN_SORT_THIS_LINE_PLUS_[1-9][0-9]' "$file_name" then echo "in BEGIN_SORT_THIS_LINE_PLUS_nb in file $file_name" echo 'nb has more that one decial digit.' exit 1 fi if $grep 'END_SORT_THIS_LINE_MINUS_[1-9][0-9]' "$file_name" then echo "in END_SORT_THIS_LINE_PLUS_ne in file $file_name" echo 'ne has more that one decial digit.' exit 1 fi # -------------------------------------------------------------------------- # # is_file_executable if [ -x $file_name ] then is_file_executable='yes' else is_file_executable='no' fi # # begin_sum # is th sum for the beginning line number set +e begin_sum=`$grep --line-number 'BEGIN_SORT_THIS_LINE_PLUS_[1-9]' $file_name | \ $sed -e 's|\([0-9]*\):.*BEGIN_SORT_THIS_LINE_PLUS_\([1-9]\).*|\1+\2|'` set -e if [ "$begin_sum" == '' ] then echo "sort.sh $file_name" echo "Cannot find BEGIN_SORT_THIS_LINE_PLUS_nb in $file_name" exit 1 fi # # begin_line begin_count=0 for sum in $begin_sum do # This does the summation let begin_line[$begin_count]="$sum" let begin_count="$begin_count + 1" done # # end_diff # ios the difference for the ending line number set +e end_diff=`$grep --line-number 'END_SORT_THIS_LINE_MINUS_[1-9]' $file_name | \ $sed -e 's|\([0-9]*\):.*END_SORT_THIS_LINE_MINUS_\([1-9]\).*|\1-\2|'` set -e if [ "$end_diff" == '' ] then echo "sort.sh $file_name" echo "Cannot find END_SORT_THIS_LINE_MINUS_nb in $file_name" exit 1 fi # # end_line end_count=0 for diff in $end_diff do # This does the difference let end_line[$end_count]="$diff" let end_count="$end_count + 1" done if [ $begin_count != $end_count ] then echo "sort.sh $file_name" echo 'number of BEGIN_SORT_THIS_LINE_PLUS_nb is not equal to' echo 'number of END_SORT_THIS_LINE_MINUS_ne.' exit 1 fi # # first_line first_line='1' # # last_line last_line=`wc -l $file_name | $sed -e 's|^ *\([0-9]*\) .*|\1|'` # # count, stop_line_previous, sorted.$$ count=0 stop_line_previous=0 cp $file_name sorted.$$ while [ $count -lt $begin_count ] do # # start_line, stop_line, count start_line=${begin_line[$count]} stop_line=${end_line[$count]} let count="$count + 1" echo "sort.sh: sorting lines $start_line to $stop_line in $file_name" # if [ $start_line -gt $stop_line ] then echo "start_line = $start_line >= stop_line = $stop_line " rm sorted.$$ exit 1 fi # # stop_line_previous if [ $stop_line_previous -ge $start_line ] then echo "previous stop_line=$stop_line_previous >= start_line=$start_line" rm sorted.$$ exit 1 fi stop_line_previous="$stop_line" # # temp.$$ if [ "$start_line" != "$first_line" ] then let start_m1="$start_line - 1" $sed -n -e "1,${start_m1}p" sorted.$$ >> temp.$$ fi # $sed -n -e "${start_line},${stop_line}p" sorted.$$ | sort >> temp.$$ # if [ "$stop_line" != "$last_line" ] then stop_p1=`expr $stop_line + 1` $sed -n -e "${stop_p1},${last_line}p" sorted.$$ >> temp.$$ fi # # sorted.$$ mv temp.$$ sorted.$$ done if diff sorted.$$ $file_name > /dev/null then rm sorted.$$ echo 'sort.sh: OK' else mv sorted.$$ $file_name if [ "$is_file_executable" == 'yes' ] then chmod +x $file_name fi echo 'sort.sh: Done' fi exit 0 ================================================ FILE: bin/speed_branch.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-23 Bradley M. Bell # ---------------------------------------------------------------------------- possible_tests=' all det_lu det_minor mat_mul ode poly sparse_hessian sparse_jacobian ' possible_options=' atomic boolsparsity colpack memory onetape optimize revsparsity subgraph ' # ---------------------------------------------------------------------------- program="bin/speed_branch.sh" if [ "$0" != "$program" ] then echo "$program: must be executed from its parent directory" exit 1 fi branch_start=$(git branch --show-current) if [ "$branch_start" != 'master' ] && [ "$branch_start" != 'main' ] then echo 'bin/speed_branch.sh: must start from master or main branch' exit 1 fi if [ "$3" == '' ] then tests=`echo $possible_tests | sed -e 's|\n||' -e 's|\t| |'` options=`echo $possible_options | sed -e 's|\n||' -e 's|\t| |'` cat << EOF usage: $program branch_one branch_two test_name [option_1] [option_2] ... possible tests are: $tests possible options are: $options EOF exit 1 fi # branch_one="$1" shift branch_two="$1" shift test_name="$1" if ! echo "$possible_tests" | grep "$test_name" > /dev/null then echo "speed_branch.sh: $test_name is not a valid test name" exit 1 fi shift option_list="$test_name" for option in $* do if ! echo $possible_options | grep "$option" > /dev/null then echo "speed_branch.sh: $option is not a valid option" exit 1 fi option_list="${option_list}_$option" done if [ "$test_name" == 'all' ] then test_name='speed' fi # ---------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ----------------------------------------------------------------------------- if [ ! -d '.git' ] then echo "$program: only implemented for git repository" exit 1 fi # ---------------------------------------------------------------------------- target_dir='build/speed/cppad' if [ ! -e $target_dir ] then echo_eval mkdir -p $target_dir fi # ----------------------------------------------------------------------------- for branch in $branch_one $branch_two do # for stable branches, remove stable/ from output file name out_file=`echo $branch.$option_list.out | sed -e 's|stable/||'` log_file=`echo $branch.log | sed -e 's|stable/||'` # if [ -e "$target_dir/$out_file" ] then echo "Using existing $target_dir/$out_file" else # use --quiet to suppress detached HEAD message echo_eval git checkout --quiet $branch # # speed/main.cpp echo "git show $branch_start:speed/main.cpp > speed/main.cpp" git show $branch_start:speed/main.cpp > speed/main.cpp # day=`git log -1 --date=iso | grep '^Date:' | \ sed -e 's|Date: *||' -e 's|-||g' -e 's| .*||'` if [ "$test_name" == 'speed' ] || [ "$test_name" == 'sparse_hessian' ] then if [ "$day" -le '20150130' ] then echo "test_name is all or sparse_hessian" echo "and branch $branch came on or before 20150130" echo "when bug was fixed in the sparse_hessian speed test." exit 1 fi fi # echo "bin/run_cmake.sh --debug_none --no_optional >& $target_dir/$log_file" bin/run_cmake.sh --debug_none --no_optional >& $target_dir/$log_file # echo "ninja check_speed_cppad >>& $target_dir/$log_file" ninja -C build check_speed_cppad >& speed_branch.log.$$ # cat speed_branch.log.$$ >> $target_dir/$log_file rm speed_branch.log.$$ # echo "$target_dir/speed_cppad $test_name 123 $* > $target_dir/$out_file" $target_dir/speed_cppad $test_name 123 $* > $target_dir/$out_file # # speed/main.cpp echo_eval git checkout speed/main.cpp fi done # return to master (branch where we started) echo_eval git checkout --quiet master # # compare the results echo " one=$branch_one , two=$branch_two" out_file_one=`echo $branch_one.$option_list.out | sed -e 's|stable/||'` out_file_two=`echo $branch_two.$option_list.out | sed -e 's|stable/||'` bin/speed_diff.sh $target_dir/$out_file_one $target_dir/$out_file_two # ---------------------------------------------------------------------------- echo "$0: OK" exit 0 ================================================ FILE: bin/speed_diff.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-23 Bradley M. Bell # ---------------------------------------------------------------------------- if [ "$0" != 'bin/speed_diff.sh' ] then echo 'bin/speed_diff.sh: must be executed from its parent directory' exit 1 fi if [ "$2" == '' ] then cat << EOF usage: bin/speed_diff.sh speed_one.out speed_two.out where speed_one.out and speed_two.out are outputs from the speed_cppad program running its 'speed' test. EOF exit 1 fi speed_one="$1" speed_two="$2" if [ ! -f "$speed_one" ] then echo "speed_diff.sh: the file $speed_one does not exist." exit 1 fi if [ ! -f "$speed_two" ] then echo "speed_diff.sh: the file $speed_two does not exist." exit 1 fi # # --------------------------------------------------------------------------- sed -n -e'/_size *=/p' $speed_one | sort -u > speed_diff.1.$$ sed -n -e'/_size *=/p' $speed_two | sort -u > speed_diff.2.$$ if ! diff speed_diff.1.$$ speed_diff.2.$$ then echo 'The sizes above do not agree' echo "< for $speed_one" echo "> for $speed_two" rm speed_diff.1.$$ speed_diff.2.$$ exit 1 fi rm speed_diff.1.$$ speed_diff.2.$$ # --------------------------------------------------------------------------- # sed -n \ -e 's|^[a-z]*_||' \ -e 's|_rate|_rate_one|' -e '/_rate_one/p' \ -e 's|available|available_one|' -e '/available_one/p' \ $speed_one > speed_diff.$$ # sed -n \ -e 's|^[a-z]*_||' \ -e 's|_rate|_rate_two|' -e '/_rate_two/p' \ -e 's|available|available_two|' -e '/available_two/p' \ $speed_two >> speed_diff.$$ # cat speed_diff.$$ | sort -u rm speed_diff.$$ # ---------------------------------------------------------------------------- echo "$0: OK" exit 0 ================================================ FILE: bin/speed_new.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-23 Bradley M. Bell # ---------------------------------------------------------------------------- program="bin/speed_new.sh" if [ "$0" != "$program" ] then echo "$program: must be executed from its parent directory" exit 1 fi if [ "$1" == '' ] then cat << EOF usage: $program test_name [option_1] [option_2] ... possible test are: all, det_lu, det_minor, mat_mul ode, poly, sparse_hessian, sparse_jacobian possible options are: atomic, boolsparsity, colpack, memory, onetape, optimize, revsparsity EOF exit 1 fi if [ ! -d new ] then echo "$program: the directory ./new does not exist." echo 'It should contain the new source code.' exit 1 fi test_name="$1" shift option_list="$test_name" for option in $* do option_list="${option_list}_$option" done if [ "$test_name" == 'all' ] then test_name='speed' fi # ---------------------------------------------------------------------------- target_dir='build/speed/cppad' if [ ! -e $target_dir ] then echo_eval mkdir -p $target_dir fi # ---------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ---------------------------------------------------------------------------- # for name in cur new do if [ "$name" == 'cur' ] then # revert cppad source code to the current version echo_eval git reset --hard --quiet else echo_eval git_new.sh from fi echo "bin/run_cmake.sh --debug_none --no_optional > /dev/null" bin/run_cmake.sh --debug_none --no_optional > /dev/null # out_file="$name.$option_list.out" if [ -e "$target_dir/$out_file" ] then echo "Using existing $target_dir/$out_file" else # compile the speed test echo "ninja -C build check_speed_cppad > $target_dir/$name.log" ninja -C build check_speed_cppad > $target_dir/$name.log # # run speed test for the current version echo "$target_dir/speed_cppad $test_name 123 $* > $target_dir/$out_file" $target_dir/speed_cppad $test_name 123 $* > $target_dir/$out_file # fi done # compare the results echo " one=cur, two=new" bin/speed_diff.sh \ $target_dir/cur.$option_list.out $target_dir/new.$option_list.out # ---------------------------------------------------------------------------- echo "$0: OK" exit 0 ================================================ FILE: bin/speed_package.sh ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- if [ "$0" != 'bin/speed_package.sh' ] then echo "bin/run_cmake.sh: must be executed from its parent directory" exit 1 fi # ----------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ----------------------------------------------------------------------------- #! /bin/bash -e git reset --hard package='cppadcg' include_file='cppad/cg/cg.hpp' home_page='https://github.com/joaoleal/CppADCodeGen' # -------------------------------------------------------- Package=`echo $package | sed -e 's|^.|\U&|'` PACKAGE=`echo $package | sed -e 's|^.*|\U&|'` # -------------------------------------------------------- # speed/package directory cat << EOF > speed_package.$$ s|XPACKAGE|$PACKAGE|g s|Xpackage|$Package|g s|xpackage|$package|g EOF if [ -e speed/$package ] then echo_eval rm -r speed/$package fi cp -r speed/xpackage speed/$package git add speed/$package list=`ls speed/$package` for file in $list do echo_eval sed -i speed/$package/$file -f speed_package.$$ done # -------------------------------------------------------- # omh/speed_package.omh cat << EOF > speed_package.$$ /\$head fadbad_prefix/! b one : loop_1 N /\\n *\$/! b loop_1 s|.*|\\n| b end # : one s|Fadbad|$Package|g s|fadbad|$package|g : end EOF file="speed/$package/speed_$package.omh" cp speed/fadbad/speed_fadbad.omh $file git add $file echo_eval sed -i $file -f speed_package.$$ # -------------------------------------------------------- # speed/speed.xrst cat << EOF > speed_package.$$ s|\$cref/Fadbad/fadbad_prefix/Fadbad Home Page/\$\$,|&\\ \$cref/$Package/${package}_prefix/$Package Home Page/\$\$,| s|^\\( *\\)speed/fadbad/speed_fadbad.omh%|&\\ \\1speed/$package/speed_$package.omh%| s|\$spell|&\\ $Package| EOF file='speed/speed.omh' echo_eval sed -i $file -f speed_package.$$ # -------------------------------------------------------- # speed/main.cpp cat << EOF > speed_package.$$ s|# ifdef CPPAD_XPACKAGE_SPEED|# ifdef CPPAD_${PACKAGE}_SPEED\\ # define AD_PACKAGE "$package"\\ # endif\\ &| EOF file='speed/main.cpp' echo_eval sed -i $file -f speed_package.$$ # -------------------------------------------------------- # speed/CMakeLists.txt cat << EOF > speed_package.$$ /ENDIF( *cppad_has_fadbad *)/! b end s|\$|\\ IF( cppad_has_$package )\\ ADD_SUBDIRECTORY($package)\\ ENDIF( cppad_has_$package )| : end EOF file='speed/CMakeLists.txt' echo_eval sed -i $file -f speed_package.$$ # -------------------------------------------------------- # CMakeLists.txt cat << EOF > speed_package.$$ /prefix_info(fadbad/! b end s|\$|\\ #\\ # ${package}_prefix\\ prefix_info($package \${system_include} "$package install prefix")| : end EOF file='CMakeLists.txt' echo_eval sed -i $file -f speed_package.$$ # -------------------------------------------------------- # omh/install/package_prefix short_name=`echo $include_file | sed -e 's|.*/||'` cat << EOF > speed_package.$$ s|http://uning.dk/|$home_page| s|%fadbad_prefix%/%dir%/FADBAD++/badiff.h|%${package}_prefix%/%dir%/$include_file| s|badiff[.]h|$short_name| s|Fadbad|$Package|g s|fadbad|$package|g s|\$spell|&\\ $include_file| EOF file="omh/install/${package}_prefix.omh" cp omh/install/fadbad_prefix.omh $file git add $file echo_eval sed -i $file -f speed_package.$$ # -------------------------------------------------------- # omh/install/cmake.omh cat << EOF > speed_package.$$ /^ -D fadbad_prefix=%fadbad_prefix%/! b one s|\$|\\ -D ${package}_prefix=%${package}_prefix% \\\\| b end # :one s|^\$rref fadbad_prefix\$\\\$|&\\ \$rref ${package}_prefix\$\$| s|^ omh/install/fadbad_prefix.omh%|&\\ xrst/install/${package}_prefix.omh%| s|\$spell|&\\ $package| # : end EOF file='omh/install/cmake.omh' echo_eval sed -i $file -f speed_package.$$ # -------------------------------------------------------- # bin/run_cmake.sh cat << EOF > speed_package.$$ s|^yes_fadbad='yes'|&\\ yes_$package='yes'| s|^\\( *\\)\\[--no_fadbad\\].*|&\\ \\1[--no_$package] \\\\\\\\| s|^\\( *\\)--no_fadbad)|\\1--no_$package)\\ \\1yes_$package='no'\\ \\1;;\\ \\ &| s|^package_list=''|&\\ if [ "\$yes_$package" == 'yes' ]\\ then\\ if [ ! -e "\$prefix/include/$include_file" ]\\ then\\ echo "Cannot find \$prefix/include/$include_file"\\ exit 1\\ fi\\ package_list="\$package_list $package"\\ fi| EOF file='bin/run_cmake.sh' echo_eval sed -i $file -f speed_package.$$ # -------------------------------------------------------- echo 'speed_package.sh: OK' exit 0 ================================================ FILE: bin/tag_month.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- if [ $0 != "bin/tag_month.sh" ] then echo "bin/tag_month.sh: must be executed from its parent directory" exit 1 fi svn_repository="https://projects.coin-or.org/svn/CppAD" # -------------------------------------------------------------------------- # make sure that master is currently checked out git_branch=`git branch | sed -e '/^\*/! d' -e 's|^\* *||'` if [ "$git_branch" != 'master' ] then echo 'tag_month.sh: master is not currently checkout out' exit 1 fi # -------------------------------------------------------------------------- # make sure that version is consistent and all changes are checked in version.sh check # -------------------------------------------------------------------------- # make sure repo is up to date git_status=`git status -s` if [ "$git_status" != '' ] then echo 'tag_month.sh: master has changes that are not checked in' exit 1 fi # -------------------------------------------------------------------------- # check that version corresponds to first of a month dd=`version.sh get | sed -e 's|......\([0-9][0-9]\)|\1|' ` if [ "$dd" != '01' ] then echo 'tag_month.sh: version does not correspond to first day of a month' exit 1 fi # -------------------------------------------------------------------------- # date of last change to svn repository svn_date=`svn log $svn_repository/trunk --limit 1 | grep '^r[0-9]* *|' | \ sed -e 's/^[^|]*|[^|]*| *\([0-9-]*\).*/\1/' -e 's|-||g'` # -------------------------------------------------------------------------- # get and check hash codes # local_hash=`git show-ref master | sed -e '/\/origin\//d' -e 's| refs.*||'` remote_hash=`git show-ref master | sed -e '/\/origin\//! d' -e 's| refs.*||'` svn_hash=`svn log $svn_repository/trunk --limit 1 | \ grep 'end *hash *code:' | sed -e 's|end *hash *code: *||'` # if [ "$local_hash" != "$remote_hash" ] then echo "tag_month.sh: master changes haven't been pushed to git repository" echo "local_hash = $local_hash" echo "remote_hash = $remote_hash" exit 1 fi if [ "$remote_hash" != "$svn_hash" ] then echo "tag_month.sh: master changes haven't been pushed to svn repository" echo "remote_hash = $remote_hash" echo "svn_hash = $svn_hash" exit 1 fi # ----------------------------------------------------------------------------- # If this version has already been tagged, delete the tag version=`version.sh get` if git tag --list | grep "$version" then read -p "Delete preious tag for version $version [y/n] ?" response if [ "$response" != 'y' ] then echo 'tag_month.sh: aborting because tag already exists' exit 1 fi git tag -d $version git push --delete origin $version fi # echo "git tag -a \\" echo "-m \"Last changes copied to $svn_repository/trunk on $svn_date\" \\" echo "$version $svn_hash" git tag -a \ -m "Last changes copied to $svn_repository/trunk on $svn_date" \ $version $svn_hash # echo "git push origin $version" git push origin $version # ---------------------------------------------------------------------------- echo "$0: OK" exit 0 ================================================ FILE: bin/test_install.sh ================================================ #! /usr/bin/env bash set -e -u # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- # This script name is 'test_*' instead of 'check_*' because it requires # that cmake has already created build/bukld.ninja or Makefile. program='bin/test_install.sh' if [ "$0" != "$program" ] then echo "$program: must be executed from its parent directory" exit 1 fi if [ "$#" != 2 ] then echo "usage: $program builder standard" echo 'builder: is ninja or make' echo 'standard: is --c++yy where yy is the year for the C++ standard' exit 1 fi builder="$1" standard="$2" if [ "$1" != 'ninja' ] && [ "$1" != 'make' ] then echo "$program: builder is not ninja or make" exit 1 fi if [ "$builder" == 'ninja' ] && [ ! -e build/build.ninja ] then echo "$program: builder is ninja and cannot find build/build.ninja" exit 1 fi if [ "$builder" == 'make' ] && [ ! -e build/Makefile ] then echo "$program: builder is make and cannot file build/Makefile" exit 1 fi if ! echo "$standard" | grep '^--c++[0-9][0-9]' > /dev/null then echo "$program: standard is not --c++yy where ecxh y is a deciman digit" eiit 1 fi # ----------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ----------------------------------------------------------------------------- # prefix eval `grep '^prefix=' bin/get_optional.sh` if [[ "$prefix" =~ ^[^/] ]] then prefix="$(pwd)/$prefix" fi # ----------------------------------------------------------------------------- PKG_CONFIG_PATH="$prefix/share/pkgconfig" LD_LIBRARY_PATH="" for dir in lib lib64 do PKG_CONFIG_PATH+=":$prefix/$dir/pkgconfig" LD_LIBRARY_PATH+=":$prefix/$dir" done export PKG_CONFIG_PATH export LD_LIBRARY_PATH # ----------------------------------------------------------------------------- # make install cd build $builder install # ----------------------------------------------------------------------------- # # rpath rpath=$(find $prefix -name 'libcppad_lib.*' | head -1 ) rpath=$(echo $rpath | sed -e 's|/[^/]*$||') # # cflags cflags="$(echo $standard | sed -e 's|^--c++|-std=c++|')" if [ "$(uname)" == Darwin ] then if which brew > /dev/null then # 2DO: This flag should be added automatically by pkg-config cflags+=" -I $(brew --prefix)/include" fi fi cflags+=" $(pkg-config cppad --cflags)" # # libs libs=$(pkg-config cppad --libs) # # test_install if [ ! -e test_install ] then mkdir test_install fi cd test_install # # CppAD get_started cp ../../example/get_started/get_started.cpp get_started.cpp echo_eval g++ $cflags $libs -Wl,-rpath $rpath get_started.cpp -o get_started echo 'CppAD: ./get_started' if ! ./get_started then echo "$program: $(pwd)/get_started test failed." exit 1 fi # # ipopt_solve get_started cp ../../example/ipopt_solve/get_started.cpp get_started.cpp cat << EOF >> get_started.cpp int main(void) { if( ! get_started() ) return 1; return 0; } EOF echo_eval g++ $cflags $libs -Wl,-rpath $rpath get_started.cpp -o get_started echo 'ipopt_solve: ./get_started' if ! ./get_started then echo "$program: $(pwd)/get_started test failed." exit 1 fi # ----------------------------------------------------------------------------- echo "$program: OK" exit 0 ================================================ FILE: bin/test_multi_thread.sh ================================================ #! /usr/bin/env bash # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell set -e -u # ---------------------------------------------------------------------------- # This script is 'test_*' instead of 'check_*' because it requires # that cmake has already created build/bukld.ninja or Makefile. program='bin/test_multi_thread.sh' if [ "$0" != "$program" ] then echo "$program: must be executed from its parent directory" exit 1 fi if [ "$#" != 1 ] then echo "usage: $program builder" echo 'where builder is ninja or make' exit 1 fi builder="$1" if [ "$1" != 'ninja' ] && [ "$1" != 'make' ] then echo "usage:usage: $program builder" echo 'where builder is ninja or make' exit 1 fi if [ "$builder" == 'ninja' ] && [ ! -e build/build.ninja ] then echo "$program: builder is ninja and cannot find build/build.ninja" exit 1 fi if [ "$builder" == 'make' ] && [ ! -e build/Makefile ] then echo "$program: builder is make and cannot file build/Makefile" exit 1 fi # ----------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } # --------------------------------------------------------------------------- # circular shift program list and set program to first entry in list next_program() { program_list=`echo "$program_list" | sed -e 's| *\([^ ]*\) *\(.*\)|\2 \1|'` program=`echo "$program_list" | sed -e 's| *\([^ ]*\).*|\1|'` } # ---------------------------------------------------------------------------- # # build cd build $builder check_example_multi_thread # # program_list, skip program_list='' skip='' for threading in bthread openmp pthread sthread do dir="example/multi_thread/$threading" if [ ! -e "$dir" ] then skip+=" $threading" else program="$dir/example_multi_thread_${threading}" program_list+=" $program" # # all programs check the fast cases echo_eval $program a11c echo_eval $program get_started echo_eval $program team_example fi done if [ "$program_list" != '' ] then # test_time=1,max_thread=4,mega_sum=1 next_program echo_eval $program harmonic 1 4 1 # # test_time=1,max_thread=4,num_solve=100 next_program echo_eval $program atomic_two 1 4 100 next_program echo_eval $program atomic_three 1 4 100 next_program echo_eval $program chkpoint_one 1 4 100 next_program echo_eval $program chkpoint_two 1 4 100 # # test_time=2,max_thread=4,num_zero=20,num_sub=30,num_sum=50,use_ad=true next_program echo_eval $program multi_newton 2 4 20 30 50 true fi # if [ "$skip" != '' ] then echo "Skipped following multi_treading tests: $skip" fi echo 'test_multi_thread.sh: OK' exit 0 ================================================ FILE: bin/test_one.sh.in ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- set_libdir() { my_prefix="$1" my_lib="$2" libdir='' if [ -e $my_prefix/lib ] then if ls $my_prefix/lib | grep "lib$my_lib" > /dev/null then libdir='lib' fi fi if [ -e $my_prefix/lib64 ] then if ls $my_prefix/lib64 | grep "lib$my_lib" > /dev/null then libdir='lib64' fi fi if [ "$libdir" == '' ] then echo "bin/test_one.sh: cannot find lib$my_lib" echo " in $my_prefix/lib or $my_prefix/lib64" exit 1 fi my_path="$my_prefix/$libdir" library_flags="$library_flags -L$my_path -l$my_lib" if ! echo "$LD_LIBRARY_PATH:" | grep "$my_path:" > /dev/null then if [ "$LD_LIBRARY_PATH" == '' ] then LD_LIBRARY_PATH="$my_path" else LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$my_path" fi fi } # ----------------------------------------------------------------------------- # Command line arguments if [ "$0" != 'bin/test_one.sh' ] then cat << EOF usage: bin/test_one.sh dir/file [extra] dir: directory in front of file name file: name of *.cpp file, with extension, that contains the test extra: extra source files and/or options for the compile command EOF exit 1 fi if ! echo "$1" | grep '/' > /dev/null then echo 'test_one.sh: dir missing in' echo ' bin/test_one.sh dir/file' exit 1 fi dir=`echo $1 | sed -e 's|/[^/]*$||'` file=`echo $1 | sed -e 's|.*/||'` extra="$2" # # case where main program is in parent directory main=`echo *$dir | sed -e 's|.*/||' -e 's|$|.cpp|'` if [ ! -e "$dir/$main" ] then subdir=`echo $dir | sed -e 's|.*/||'` dir=`echo $dir | sed -e 's|/[^/]*$||'` file="$subdir/$file" fi # --------------------------------------------------------------------------- # Check command line arguments if [ "$dir" == '' ] then echo "test_one.sh: cannot find dir/file in \$1='$1'" exit 1 fi if [ ! -e "$dir/$file" ] then echo "test_one.sh: Cannot find the file $dir/$file" exit 1 fi if [ ! -e 'build/CMakeFiles' ] then echo 'test_one.sh: Must first execute bin/run_cmake.sh' exit 1 fi # --------------------------------------------------------------------------- # Clean out old output files if [ -e test_one.exe ] then rm test_one.exe fi if [ -e test_one.cpp ] then rm test_one.cpp fi top_srcdir=$(pwd) # --------------------------------------------------------------------------- PKG_CONFIG_PATH="@cppad_prefix@/share/pkgconfig" PKG_CONFIG_PATH="$PKG_CONFIG_PATH:@cppad_prefix@/lib/pkgconfig" PKG_CONFIG_PATH="$PKG_CONFIG_PATH:@cppad_prefix@/lib64/pkgconfig" export PKG_CONFIG_PATH # --------------------------------------------------------------------------- # initialize export LD_LIBRARY_PATH='' include_flags='' library_flags='' # # adolc if [ @cppad_has_adolc@ == 1 ] then include_flags="$include_flags $(pkg-config adolc --cflags)" library_flags="$library_flags $(pkg-config adolc --libs)" library_path=$(pkg-config adolc --libs | sed -e 's|.*-L\([^ ]*\).*|\1|') LD_LIBRARY_PATH="$library_path:$LD_LIBRARY_PATH" fi if [ @cppad_has_eigen@ == 1 ] then include_flags="$include_flags $(pkg-config eigen3 --cflags)" library_flags="$library_flags $(pkg-config eigen3 --libs)" fi # # ipopt if [ @cppad_has_ipopt@ == 1 ] then include_flags="$include_flags $(pkg-config ipopt --cflags)" library_flags="$library_flags $(pkg-config ipopt --libs)" library_path=$(pkg-config ipopt --libs | sed -e 's|.*-L\([^ ]*\).*|\1|') LD_LIBRARY_PATH="$library_path:$LD_LIBRARY_PATH" fi # if [ @cppad_has_boost@ == 1 ] then library_flags="$library_flags -lboost_thread" fi # # colpack if [ @cppad_has_colpack@ == 1 ] then include_flags="$include_flags -I @colpack_prefix@/include" # set_libdir @colpack_prefix@ ColPack fi # # cppad include_flags=$(echo $include_flags | \ sed -e 's| *-I *| -isystem |g' -e 's|^-I *|-isystem |' ) include_flags="-I $top_srcdir/include $include_flags" if [ @is_cppad_lib_dynamic@ == '1' ] then cppad_lib_path='@PROJECT_BINARY_DIR@/cppad_lib/libcppad_lib.so' else cppad_lib_path='@PROJECT_BINARY_DIR@/cppad_lib/libcppad_lib.a' fi library_flags="$library_flags $cppad_lib_path -lpthread" # cd build if [ -e 'build.ninja' ] && [ -e 'Makefile' ] then echo "bin/test_one.sh: Both build.ninja and Makefile are in $(pwd)" exit 1 elif [ -e 'build.ninja' ] then builder=ninja elif [ -e 'Makefile' ] then builder=make else echo "bin/test_one.sh: Neither build.ninja or Makefile is in $(pwd)" exit 1 fi if ! $builder cppad_lib then echo "test_one.sh: $builder cppad_lib failed: see errors above" exit 1 fi cd .. # -------------------------------------------------------------------------- # Create test_one.exe # # determine the function name fun=`grep "^bool *[a-zA-Z0-9_]* *( *void *)" $dir/$file | tail -1 | \ sed -e "s/^bool *\([a-zA-Z0-9_]*\) *( *void *)/\1/"` # # determine the main program main main=`echo *$dir | sed -e 's|.*/||' -e 's|$|.cpp|'` # sed < $dir/$main > test_one.cpp \ -e '/^ *Run( /d' \ -e "s/.*This line is used by test_one.sh.*/ Run( $fun, \"$fun\");/" # -------------------------------------------------------------------------- # Create test_one.exe # # compiler flags cxx_flags='@cppad_cxx_flags@' if [ "$dir" == 'test_more/cppad_for_tmb' ] then cxx_flags="$cxx_flags @OpenMP_CXX_FLAGS@ -DCPPAD_FOR_TMB" fi # # compile command compile_command="@CMAKE_CXX_COMPILER@ test_one.cpp -o test_one.exe $dir/$file $extra -g $cxx_flags $include_flags $library_flags " echo "$compile_command 2> test_one.err" if ! $compile_command 2> test_one.err then tail test_one.err echo 'test_one.sh: see test_one.err' exit 1 fi # -------------------------------------------------------------------------- # LD_LIBRARY_PATH my_path='@PROJECT_BINARY_DIR@/cppad_lib' if ! echo $LD_LIBRARY_PATH | grep "$my_path" > /dev/null then if [ "$LD_LIBRARY_PATH" == '' ] then LD_LIBRARY_PATH="$my_path" else LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$my_path" fi fi # -------------------------------------------------------------------------- # Execute the test echo "./test_one.exe" if ! ./test_one.exe then cat test_one.err echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH" exit 1 fi # -------------------------------------------------------------------------- # Check for compiler warnings cat << EOF > test_one.sed # Lines that describe where error is /^In file included from/d /: note:/d # # Ipopt has sign conversion warnings /\/coin\/.*-Wsign-conversion/d # # Adolc has multiple types of conversion warnings /\/adolc\/.*-W[a-z-]*conversion/d /\/adolc\/.*-Wshorten-64-to-32/d # # Lines describing the error begin with space /^ /d # # Lines summarizing results /^[0-9]* warnings generated/d EOF sed -f test_one.sed < test_one.err > test_one.tmp rm test_one.sed if [ -s test_one.tmp ] then cat test_one.tmp echo 'test_one.sh: unexpected warnings: see test_one.tmp, test_one.err' echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH" exit 1 fi # -------------------------------------------------------------------------- echo 'test_one.sh: OK' echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH" exit 0 ================================================ FILE: bin/trace.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- if [ $0 != "bin/trace.sh" ] then echo "bin/trace.sh: must be executed from its parent directory" exit 1 fi file="include/cppad/local/sweep/$1" option="$2" # ok='yes' if [ "$option" != '0' ] && [ "$option" != '1' ] then ok='no' fi if [ "$ok" == 'yes' ] then if ! grep '_TRACE [01]' $file > /dev/null then ok='no' fi fi if [ "$ok" == 'no' ] then echo 'usage: bin/trace.sh file (0|1)' echo 'Sets trace in file to off (0) or on (1) where the file is one of:' grep -l '_TRACE [01]' include/cppad/local/sweep/*.hpp | \ sed -e 's|^include/cppad/local/sweep/||' exit 1 fi old=`grep '_TRACE [01]' $file` sed -e "s|TRACE [01]|TRACE $option|" -i $file new=`grep '_TRACE [01]' $file` # echo "old: $old" echo "new: $new" # # ---------------------------------------------------------------------------- echo "$0: OK" exit 0 ================================================ FILE: bin/travis.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- if [ $0 != "bin/travis.sh" ] then echo 'bin/travis.sh: must be executed from its parent directory' exit 1 fi if [ "$1" != 'make' ] && [ "$1" != 'test_one' ] then echo 'usage: bin/travis.sh (make|test_one) target1 target2 ...' echo 'target: if make specified, is one of the available make commands' echo if test_one, specified, is the path to a test file. exit 1 fi cmd="$1" # ----------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ----------------------------------------------------------------------------- if [ -e 'build' ] then echo_eval rm -r build fi echo_eval mkdir build echo_eval cd build echo_eval cmake \ -D cppad_prefix=$(pwd)/prefix \ -D cppad_cxx_flags='-std=c++11' \ .. # ----------------------------------------------------------------------------- if [ "$cmd" == 'make' ] then shift while [ "$1" != '' ] do echo_eval make "$1" shift done else echo_eval cd .. shift while [ "$1" != '' ] do echo_eval bin/test_one.sh "$1" shift done fi # ----------------------------------------------------------------------------- echo 'bin/travis.sh: OK' exit 0 ================================================ FILE: bin/twine.sh ================================================ #! /usr/bin/env bash set -e -u # !! EDITS TO THIS FILE ARE LOST DURING UPDATES BY xrst.git/bin/dev_tools.sh !! # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2020-25 Bradley M. Bell # ----------------------------------------------------------------------------- # bin/twine.sh branch_or_tag # 1. This program muse be executed from the main or master branch # 2. branch_or_tag can be a branch or a tag. If it is a branch, it must be # main or master. # 3. If branch_or_tag is a tag, the corresponding version is uploaded to pypi. # Otherwise it is uploaded to testpipi. # ----------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ----------------------------------------------------------------------------- if [ "$0" != "bin/twine.sh" ] then echo "bin/twine.sh: must be executed from its parent directory" exit 1 fi if [ ! -e .git ] then echo 'bin/twine.sh: Cannot find .git in current working directory' exit 1 fi # # branch_or_tag if [ $# != 1 ] then echo 'usage: bin/twine.sh: branch_or_tag' echo 'where branch_or_tag is master, main, or a git tag' exit 1 fi branch_or_tag="$1" # # branch branch=$(git branch --show-current) if [ "$branch" != 'master' ] && [ "$branch" != 'main' ] then echo 'bin/twine.sh: must execute on the main or master branch' exit 1 fi # # repository if [ "$branch_or_tag" == "$branch" ] then repository='testpypi' else repository='pypi' fi # # TOKEN if [ -z "${TOKEN+x}" ] then echo echo "bin/twine.sh: must export TOKEN=" echo 'see https://pypi.org/help/#apitoken' exit 1 fi # # dist if [ -e dist ] then rm -r dist fi if [ "$branch_or_tag" == "$branch" ] then echo_eval python -m build else if ! git show-ref $branch_or_tag | grep '/tags/' > /dev/null then echo "bin/twine.sh: $branch_or_tag is not the current branch or a tag" exit 1 fi git checkout --quiet $branch_or_tag echo_eval python -m build git checkout $branch fi # # twind echo_eval twine upload --repository $repository dist/* -u __token__ -p $TOKEN # echo 'twine.sh: OK' exit 0 ================================================ FILE: bin/valgrind.sh ================================================ #! /bin/bash -eu # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ----------------------------------------------------------------------------- if [ "$0" != "bin/valgrind.sh" ] then echo "bin/valgrind.sh: must be executed from its parent directory" exit 1 fi if [ "$#" != '0' ] then echo 'usage: bin/valgrind.sh.sh' exit 1 fi # ----------------------------------------------------------------------------- list=`find build -perm -700 -type f | \ sed -e '/^build\/cppad-*/d' -e '/\/CMakeFiles\//d'` for program in $list do arguments='' if echo "$program" | grep '\/speed\/' > /dev/null then arguments='correct none' fi echo "valgrind $program $arguments 2> valgrind.log" valgrind $program $arguments 2> valgrind.log if ! grep '^==[0-9]*== ERROR SUMMARY: 0' valgrind.log then grep 'ERROR SUMMARY' valgrind.log exit 1 fi if ! grep '^==[0-9]*== *definitely lost: 0' valgrind.log then if grep '^==[0-9]*== *definitely lost:' valgrind.log then exit 1 fi fi if ! grep '^==[0-9]*== *indirectly lost: 0' valgrind.log then if grep '^==[0-9]*== *indirectly lost:' valgrind.log then exit 1 fi fi if ! grep '^==[0-9]*== *possibly lost: 0' valgrind.log then if grep '^==[0-9]*== *possibly lost:' valgrind.log then exit 1 fi fi if ! grep '^==[0-9]*== *suppressed: 0' valgrind.log then if grep '^==[0-9]*== *suppressed:' valgrind.log then exit 1 fi fi done # ----------------------------------------------------------------------------- echo 'bin/valgrind.sh: OK' exit 0 ================================================ FILE: bug/boost_lu.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- #!/bin/bash -e name=`echo $0 | sed -e 's|^bug/||' -e 's|\.sh$||'` if [ "$0" != "bug/$name.sh" ] then echo 'usage: bug/alloc_global.sh' exit 1 fi # ----------------------------------------------------------------------------- if [ -e build/bug ] then rm -r build/bug fi mkdir -p build/bug cd build/bug cmake ../.. # ----------------------------------------------------------------------------- cat << EOF This test results in a very long error message with boost-1.55.0 and gcc-4.9.2. Here is the gist of the message: /usr/include/boost/numeric/ublas/detail/matrix_assign.hpp:33:35: error: no match for ‘operator<’ ... snip ... return norm_inf (e1 - e2) < epsilon * ^ The following change to /usr/include/boost/numeric/ublas/detail/matrix_assign.hpp seems to fix the problem: Old Text: return norm_inf (e1 - e2) < epsilon * std::max (std::max (norm_inf (e1), norm_inf (e2)), min_norm); New Text: S norm_1 = norm_inf(e1); S norm_2 = norm_inf(e2); S norm_diff = norm_inf(e1 - e2); return norm_diff < epsilon * std::max( std::max(norm_1, norm_2) , min_norm ); EOF cat << EOF > boost_lu.cpp #include # define NUMERIC_LIMITS_FUN(name) \ static CppAD::AD name(void) \ { return static_cast< CppAD::AD > ( \ std::numeric_limits::name() \ ); \ } # define NUMERIC_LIMITS_BOOL(name) \ static const bool name = \ std::numeric_limits::name; # define NUMERIC_LIMITS_INT(name) \ static const int name = \ std::numeric_limits::name; namespace std { /// Specialization of numeric_limits< CppAD::AD > template <> class numeric_limits< CppAD::AD > { public: // has_denorm static const float_denorm_style has_denorm = std::numeric_limits::has_denorm; // round_style static const float_round_style round_style = std::numeric_limits::round_style; // bool NUMERIC_LIMITS_BOOL(is_specialized); NUMERIC_LIMITS_BOOL(is_signed); NUMERIC_LIMITS_BOOL(is_integer); NUMERIC_LIMITS_BOOL(is_exact); NUMERIC_LIMITS_BOOL(has_infinity); NUMERIC_LIMITS_BOOL(has_quiet_NaN); NUMERIC_LIMITS_BOOL(has_signaling_NaN); NUMERIC_LIMITS_BOOL(has_denorm_loss); NUMERIC_LIMITS_BOOL(is_iec559); NUMERIC_LIMITS_BOOL(is_bounded); NUMERIC_LIMITS_BOOL(is_modulo); NUMERIC_LIMITS_BOOL(traps); NUMERIC_LIMITS_BOOL(tinyness_before); // int NUMERIC_LIMITS_INT(digits); NUMERIC_LIMITS_INT(digits10); NUMERIC_LIMITS_INT(radix); NUMERIC_LIMITS_INT(min_exponent); NUMERIC_LIMITS_INT(min_exponent10); NUMERIC_LIMITS_INT(max_exponent); NUMERIC_LIMITS_INT(max_exponent10); /// functions NUMERIC_LIMITS_FUN( epsilon) NUMERIC_LIMITS_FUN( min ) NUMERIC_LIMITS_FUN( max ) }; } #include int main() { typedef CppAD::AD T; boost::numeric::ublas::matrix a(5,5); boost::numeric::ublas::permutation_matrix pert(5); // lu decomposition const std::size_t s = lu_factorize(a, pert); return 0; } EOF echo "g++ -I../../include --std=c++11 -g $name.cpp -o $name >& $name.log" if ! g++ -I../../include --std=c++11 -g $name.cpp -o $name >& $name.log then cat << EOF > $name.sed s|\\[|&\\n|g s|\\]|&\\n|g s|[‘{}]|&\\n|g s|[a-zA-Z0-9_]* *=|\\n&|g # s|boost::numeric::ublas::||g s|/usr/include/boost/numeric/ublas/||g # s|CppAD::AD *|AD|g s|, basic_unit_lower<> >||g s|, basic_upper<> >||g s|scalar_minus|scalar_minus_AD|g s|triangular_adaptor<\\([^<>]*\\)>|triangular_\\1|g s|triangular_matrix_AD_ulower|AD_ulower|g s|triangular_matrix_AD_upper|AD_upper|g s|matrix_matrix_prod *|AD_prod_ulower_upper|g s| *||g s|matrix_matrix_binary|AD_prod_ulower_upper|g EOF echo "sed -f $name.sed $name.log > ../$name.log" sed -f $name.sed $name.log > ../$name.log echo "$name.sh: Compilation Error: see build/bug/$name.log" exit 1 fi # echo "./$name" if ! ./$name then echo echo "$name.sh: Execution Error" exit 1 fi echo # ------------------------------------------------------------------------------ echo "$name.sh: OK" exit 0 ================================================ FILE: bug/clang_simple.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- name='clang_simple' if [ "$0" != "bug/$name.sh" ] then echo "usage: bug/$name.sh" exit 1 fi # ----------------------------------------------------------------------------- if [ -e build/bug ] then rm -r build/bug fi mkdir -p build/bug cd build/bug cmake ../.. # ----------------------------------------------------------------------------- cat << EOF > $name.cpp # include int main(void) { bool ok = true; using std::cout; using CppAD::AD; // std::cout << "Test for issue 31\n"; CppAD::CheckSimpleVector >(); // if( ok ) return 0; return 1; } EOF cxx_flags='-g -O0' eigen_dir="$HOME/prefix/eigen/include" echo "clang++ -I../../include -isystem $eigen_dir $cxx_flags $name.cpp -o $name" clang++ -I../../include -isystem $eigen_dir $cxx_flags $name.cpp -o $name # if ! ./$name then echo echo "build/bug/$name: Error" exit 1 fi echo # ------------------------------------------------------------------------------ echo "./$name.sh: OK" exit 0 ================================================ FILE: bug/cmake_target.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- #! /bin/bash -e echo 'This script does not work properly on el6; see' echo 'https://bugzilla.redhat.com/show_bug.cgi?id=896116' echo # if [ -e build ] then rm -r build fi mkdir build cd build cat << EOF > hello_one.cpp # include int main(void) { std::cout << "hello_one" << std::endl << std::endl; return 0; } EOF cat << EOF > hello_two.cpp # include int main(void) { std::cout << "hello_two" << std::endl << std::endl; return 0; } EOF cat << EOF > CMakeLists.txt CMAKE_MINIMUM_REQUIRED(VERSION 2.6) # PROJECT(hello) # ADD_EXECUTABLE(hello_one EXCLUDE_FROM_ALL hello_one.cpp ) ADD_EXECUTABLE(hello_two EXCLUDE_FROM_ALL hello_two.cpp ) # ADD_CUSTOM_TARGET(check_one hello_one DEPENDS hello_one) ADD_CUSTOM_TARGET(check_two hello_two DEPENDS hello_two) # ADD_CUSTOM_TARGET(check DEPENDS check_one check_two) EOF # uname -a cmake --version cmake . make check ================================================ FILE: bug/cppad_cg.sh ================================================ #! /bin/bash -e # $Id:$ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- #! /bin/bash -e web_page='https://github.com/joaoleal/CppADCodeGen' # ----------------------------------------------------------------------------- # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ----------------------------------------------------------------------------- cat << EOF This is not a bug but rather a demonstration of using conditional expressions with CppADCodeGen https://github.com/joaoleal/CppADCodeGen/ EOF # if [ ! -e build ] then echo_eval mkdir -p build fi echo_eval cd build cat << EOF > cppad_cg.cpp # include # include int main(void) { typedef CppAD::cg::CG cg_double; typedef CppAD::AD acg_double; // declare independent variables for f(x) size_t nx = 2; CppAD::vector ax(nx); ax[0] = 2.0; ax[1] = 3.0; CppAD::Independent(ax); // create dependent variables and values for f(x) // f(x) = x[0] / x[1] if (x[0] > 0 and x[1] >= x[0]) else 1.0 size_t nz = 1; CppAD::vector az(nz); acg_double acg_zero = acg_double(0.0); acg_double acg_one = acg_double(1.0); acg_double acg_temp_1 = CondExpGt(ax[1], ax[0], ax[0] / ax[1], acg_one); acg_double acg_temp_2 = CondExpGt(ax[0], acg_zero, acg_temp_1, acg_one); az[0] = acg_temp_2; // create AD function mapping independent to dependent variables CppAD::ADFun F(ax, az); // create the source code generator for function g(x) CppAD::cg::CodeHandler G; // declare the independent variables for g(x) CppAD::vector cg_x(nx); G.makeVariables(cg_x); // Compute the dependent variables and values for g(x) size_t ny = nz * nx; CppAD::vector cg_y(ny); cg_y = F.Jacobian(cg_x); // Mapping from variables in this program to variables in source_code // independent variable = x // dependent variable = y // temporary variable = v CppAD::cg::LanguageC langC("double"); CppAD::cg::LangCDefaultVariableNameGenerator nameGen; // generate the source code std::ostringstream source_code; G.generateCode(source_code, langC, cg_y, nameGen); // string corresponding to source code std::string source_str = source_code.str(); // C source code corresponding to y = g(x) std::cout << source_str; return 0; } EOF # # Compile and run cppad_cg.cpp echo_eval g++ \ -g \ -std=c++11 \ -I$HOME/prefix/cppad_cg/include \ -I../.. \ cppad_cg.cpp -o cppad_cg # # Determine the maximum v index v_max_index=`./cppad_cg | sed \ -e '/^ *v\[[0-9]*\]/! d' \ -e 's|^ *v\[\([0-9]*\)\].*|\1|' | sort | tail -1` # # # Wrap y = g(x) in C++ function and test it cat << EOF > tst_cppad_cg.cpp # include namespace { using CppAD::zdouble; typedef CppAD::vector zvector; // // f(x) = x[0] / x[1] if (x[0] > 0 and x[1] >= x[0]) else 1.0 // g(x) = d/dx f(x) void g(const zvector& x, zvector& y) { zvector v($v_max_index + 1); EOF ./cppad_cg | sed -e 's|^ *|\t\t|' >> tst_cppad_cg.cpp cat << EOF >> tst_cppad_cg.cpp return; } } int main(void) { // initialize flag bool ok = true; // numerical precision for tests zdouble eps = 10. * std::numeric_limits::epsilon(); // number of components in vectors size_t nx = 2; size_t nz = 1; size_t ny = nz * nx; zdouble zero = 0.0; // // compute y = g(x) case where x[0] == 0.0 zvector x(nx), y(ny); x[0] = 0.0; x[1] = 0.0; g(x, y); // // check results ok &= CppAD::NearEqual(y[0], zero, eps, eps); ok &= CppAD::NearEqual(y[1], zero, eps, eps); // // compute y = g(x) case where g(x) = x[0] / x[1] x[0] = 2.0; x[1] = 3.0; g(x, y); // // check results ok &= CppAD::NearEqual(y[0], 1.0/x[1], eps, eps); ok &= CppAD::NearEqual(y[1], -x[0]/(x[1]*x[1]), eps, eps); // if( ! ok ) return 1; return 0; } EOF # # Compile test echo_eval g++ \ -g \ -std=c++11 \ -I../.. \ tst_cppad_cg.cpp -o tst_cppad_cg # if ! ./tst_cppad_cg then file="$HOME/install/cppad_cg/build/tst_cppad_cg.cpp" echo "install_cppad_cg.sh: Error" exit 1 fi # echo 'cppad_cg.sh: OK' exit 0 ================================================ FILE: bug/doxy_member.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # Trying to figure out why ADFun::Forward appears twice where there is only # one implementation. # # ------------------------------------------------------------------------------ # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ----------------------------------------------- echo "$0" name=`echo $0 | sed -e 's|.*/||' -e 's|\..*||'` # ----------------------------------------------- for dir in build doxy_member do if [ ! -e $dir ] then mkdir $dir fi cd $dir done # ------------------------------------------------------------------------- cat << EOF > $name.hpp template class my_class { private: T value_; public: void set_value(T value = 0); T get_value(void); }; EOF cat << EOF > implement.hpp /*! \\file implement.hpp Implementation of member functions */ /*! Member function that sets the value. \\param value [in] New value. */ template void my_class::set_value(T value) { value_ = value; } /*! Member function that gets the value. \\return Current value. */ template T my_class::get_value(void) { return value_; } EOF cat << EOF > $name.cpp # include # include "$name.hpp" # include "implement.hpp" int main(void) { my_class x; x.set_value(2); std::cout << "x.value = " << x.get_value() << std::endl; return 0; } EOF # ------------------------------------------------------------------------- # echo_eval doxygen -g doxyfile cp ../../../doxyfile . sed \ -e 's|^\(INPUT *=\)|& .|' \ -e 's|^\(FILE_PATTERNS *=\)|& *.hpp *.cpp|' \ -i doxyfile # ------------------------------------------------------------------------- echo_eval doxygen doxyfile # ------------------------------------------------------------------------- echo_eval g++ $name.cpp -o name echo_eval ./name ================================================ FILE: bug/eigen_shadow.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # Eigen generates lots of warnings if -Wshadow is set of compile; e.g., # the first warning generated by this script is: # # warning: declaration of ‘value’ shadows a member of 'this' [-Wshadow] # explicit variable_if_dynamic(T value) : m_value(value) {} # ^ # ------------------------------------------------------------------------------ # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ----------------------------------------------- if [ ! -e build ] then mkdir build fi cd build echo "$0" name=`echo $0 | sed -e 's|.*/||' -e 's|\..*||'` # cat << EOF > $name.cpp # include # include int main() { using Eigen::Matrix; using Eigen::Dynamic; Matrix A(1,1); A(0,0) = 6.0; if( A(0,0) != 6.0 ) { std::cout << "$name: Error" << std::endl; return 1; } std::cout << "$name: OK" << std::endl; return 0; } EOF if [ -e "$name" ] then echo_eval rm $name fi echo_eval g++ \ $name.cpp \ -I$HOME/prefix/eigen/include \ -g \ -O0 \ -std=c++11 \ -Wshadow \ -o $name echo_eval ./$name ================================================ FILE: bug/gcc_complex.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- echo_eval() { echo $* eval $* } # ----------------------------------------------- echo "create gcc_complex.cpp" cat << EOF > gcc_complex.cpp # include # include # include int main(void) { double inf = std::numeric_limits::infinity(); std::complex c_inf( inf ); std::complex c_1( 1. ); std::cout << "c_inf = " << c_inf << std::endl; std::cout << "c_1 = " << c_1 << std::endl; std::cout << "c_inf / c1 = " << c_inf / c_1 << std::endl; return 0; } EOF echo_eval g++ gcc_complex.cpp -o gcc_complex echo_eval ./gcc_complex cat << EOF Explanation: (x + i*y) (x + i*y) * (a - i*b) (x*a - y*b) + i*(y*a - x*b) --------- = --------------------- = --------------------------- (a + i*b) (a + i*b) * (a - i*b) a*a + b*b In our case x = inf, y = 0, a = 1, b = 0, so we have (x + i*y) (inf*1 - 0*0) + i*(0*1 - inf*0) --------- = ------------------------------ = inf - i*nan (a + i*b) 1*1 + 0*0 EOF echo_eval rm gcc_complex.cpp gcc_complex ================================================ FILE: bug/numeric_limit.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- if [ ! -e ../include/cppad/configure.hpp ] then echo 'numeric_limits.sh: must first run bin/run_cmake.sh' echo 'from parent directory.' exit 1 fi cat << EOF This is not a bug, but rather a test of specialization of numeric_limits for AD types. EOF cat << EOF > bug.$$ # include /*! \\def CPPAD_STD_NUMERIC_LIMITS(Other, Base) This macro defines the specialization std::numeric_limits to have the same values and functions as the existing specialization std::numeric_limits. */ # define CPPAD_STD_NUMERIC_LIMITS(Other, Base) \\ namespace std {\\ template <> class numeric_limits\\ {\\ public:\\ static const bool is_specialized =\\ numeric_limits::is_specialized;\\ static const bool is_signed =\\ numeric_limits::is_signed;\\ static const bool is_integer =\\ numeric_limits::is_integer;\\ static const bool is_exact =\\ numeric_limits::is_exact;\\ static const bool has_infinity =\\ numeric_limits::has_infinity;\\ static const bool has_quiet_NaN =\\ numeric_limits::has_quiet_NaN;\\ static const bool has_signaling_NaN =\\ numeric_limits::has_signaling_NaN;\\ static const bool has_denorm_loss =\\ numeric_limits::has_denorm_loss;\\ static const bool is_iec559 =\\ numeric_limits::is_iec559;\\ static const bool is_bounded =\\ numeric_limits::is_bounded;\\ static const bool is_modulo =\\ numeric_limits::is_modulo;\\ static const bool traps =\\ numeric_limits::traps;\\ static const bool tinyness_before =\\ numeric_limits::tinyness_before;\\ static const int digits =\\ numeric_limits::digits;\\ static const int digits10 =\\ numeric_limits::digits10;\\ static const int radix =\\ numeric_limits::radix;\\ static const int min_exponent =\\ numeric_limits::min_exponent;\\ static const int min_exponent10 =\\ numeric_limits::min_exponent10;\\ static const int max_exponent =\\ numeric_limits::max_exponent;\\ static const int max_exponent10 =\\ numeric_limits::max_exponent10;\\ static const Base min(void)\\ { return static_cast( numeric_limits::min() ); }\\ static const Base max(void)\\ { return static_cast( numeric_limits::max() ); }\\ static const Base epsilon(void)\\ { return static_cast( numeric_limits::epsilon() ); }\\ static const Base round_error(void)\\ { return static_cast( numeric_limits::round_error() ); }\\ static const Base infinity(void)\\ { return static_cast( numeric_limits::infinity() ); }\\ static const Base quiet_NaN(void)\\ { return static_cast( numeric_limits::quiet_NaN() ); }\\ static const Base signaling_NaN(void)\\ { return static_cast( numeric_limits::signaling_NaN() ); }\\ static const Base denorm_min(void)\\ { return static_cast( numeric_limits::denorm_min() ); }\\ static const float_denorm_style has_denorm =\\ numeric_limits::has_denorm;\\ static const float_round_style round_style =\\ numeric_limits::round_style;\\ };\\ } CPPAD_STD_NUMERIC_LIMITS(double, CppAD::AD) # define PRINT_VAL(name) \\ std::cout << #name << " = " \\ << std::numeric_limits< CppAD::AD >::name << std::endl; # define PRINT_FUN(name) \\ std::cout << #name << " = " \\ << std::numeric_limits< CppAD::AD >::name() << std::endl; int main(void) { bool ok = true; // PRINT_VAL(is_specialized) PRINT_VAL(is_signed) PRINT_VAL(is_integer) PRINT_VAL(is_exact) PRINT_VAL(has_infinity) PRINT_VAL(has_quiet_NaN) PRINT_VAL(has_signaling_NaN) PRINT_VAL(has_denorm_loss) PRINT_VAL(is_iec559) PRINT_VAL(is_bounded) PRINT_VAL(is_modulo) PRINT_VAL(traps) PRINT_VAL(tinyness_before) // int PRINT_VAL(digits) PRINT_VAL(digits10) PRINT_VAL(radix) PRINT_VAL(min_exponent) PRINT_VAL(min_exponent10) PRINT_VAL(max_exponent) PRINT_VAL(max_exponent10) // function PRINT_FUN(min) PRINT_FUN(max) PRINT_FUN(epsilon) PRINT_FUN(round_error) PRINT_FUN(infinity) PRINT_FUN(quiet_NaN) PRINT_FUN(signaling_NaN) PRINT_FUN(denorm_min) // other PRINT_VAL(has_denorm) PRINT_VAL(round_style) // C++11 only // PRINT_VAL(max_digits10) // PRINT_FUN(lowest) // if( ok ) return 0; return 1; } EOF # ----------------------------------------------------------------------------- if [ ! -e build ] then mkdir build fi cd build echo "$0" name=`echo $0 | sed -e 's|.*/||' -e 's|\..*||'` mv ../bug.$$ $name.cpp echo "g++ -I../.. --std=c++11 -g $name.cpp -o $name" g++ -I../.. --std=c++11 -g $name.cpp -o $name # echo "./$name" if ! ./$name then echo echo "$name.sh: Error" exit 1 fi echo echo "$name.sh: OK" exit 0 ================================================ FILE: bug/pow.sh ================================================ #! /bin/bash -e # vim: set expandtab: # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- name=`echo $0 | sed -e 's|^bug/||' -e 's|\.sh$||'` if [ "$0" != "bug/$name.sh" ] then echo 'usage: bug/pow.sh' exit 1 fi # ----------------------------------------------------------------------------- if [ -e build/bug ] then rm -r build/bug fi mkdir -p build/bug cd build/bug # cmake ../.. # ----------------------------------------------------------------------------- cat << EOF Issue 43: f(x) = (x^0.5) * (x^0.5) = x, and differentiate at x = 0. Expect some NaN, Inf, or possibly one, but not zero. EOF cat << EOF > $name.cpp # include # include "cppad/cppad.hpp" int main(int argc, char** argv) { bool ok = true; using std::cout; using CppAD::AD; using CppAD::vector; // vector< double> x(1), y(1), w(1), dw(1); vector< AD > ax(1), ay(1); // ax[0] = 0.0; // CppAD::Independent(ax); ay[0] = pow(ax[0], 0.5) * pow(ax[0], 0.5); CppAD::ADFun f(ax, ay); // x[0] = 0.0; y = f.Forward(0, x); w[0] = 1.0; dw = f.Reverse(1, w); // cout << "dw = " << dw << "\n"; // ok &= y[0] == 0.0; ok &= dw[0] == 1.0 || ! std::isfinite( dw[0] ); // if( ! ok ) return 1; return 0; } EOF cxx_flags='-Wall -pedantic-errors -std=c++11 -Wshadow -Wconversion -g -O0' eigen_dir="$HOME/prefix/eigen/include" echo "g++ -I../../include -isystem $eigen_dir $cxx_flags $name.cpp -o $name" g++ -I../../include -isystem $eigen_dir $cxx_flags $name.cpp -o $name # echo "build/bug/$name" if ! ./$name then echo echo "build/bug/$name: Error" exit 1 fi echo # ----------------------------------------------------------------------------- echo "bug/$name.sh: OK" exit 0 ================================================ FILE: bug/sparsity.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- gigabytes='1.0' # memory limit in gigabytes # ----------------------------------------------------------------------------- kilobytes=`echo "($gigabytes * 10^9) / 1024" | bc` ulimit -Sv $kilobytes cat << EOF This is testing the idea that vector< std::set > sparsity; is inefficient compared to vector row, col; The results on one system for this script are: ./sparsity set 1e7 elapsed_seconds = 3.62206 ./sparsity set 2e7 std::bad_alloc ./sparsity vec 2e7 elapsed_seconds = 1.34243 ./sparsity vec 4e7 std::bad_alloc EOF cat << EOF > bug.$$ # include int main(int argc, char *argv[]) { using CppAD::elapsed_seconds; if( argc != 3 ) { std::cerr << "usage: $0 (set|vec) n" << std::endl; return 1; } bool set = std::strcmp(argv[1], "set") == 0; bool vec = std::strcmp(argv[1], "vec") == 0; bool ok = vec || set; if( ! ok ) { std::cerr << "usage: $0 (set|vec) n" << std::endl; return 1; } size_t n = size_t( std::atof( argv[2] ) ); const char* label; elapsed_seconds(); try { if( set ) { std::vector< std::set > my_set(n); for(size_t i = 0; i < n; i++) my_set[i].insert(i); } else { std::vector row; std::vector col; for(size_t i = 0; i < n; i++) { row.push_back(i); col.push_back(i); } } for(int i = 0; i < argc; i++) std::cout << argv[i] << " "; std::cout << "elapsed_seconds = " << elapsed_seconds() << std::endl; } catch( std::bad_alloc& bad ) { for(int i = 0; i < argc; i++) std::cout << argv[i] << " "; std::cout << bad.what() << std::endl; } return 0; } EOF # ----------------------------------------------------------------------------- if [ ! -e build ] then mkdir build fi cd build echo "$0" name=`echo $0 | sed -e 's|.*/||' -e 's|\..*||'` mv ../bug.$$ $name.cpp echo "g++ -I../.. -DNDEBUG --std=c++11 $name.cpp -o $name" g++ -I../.. -DNDEBUG --std=c++11 $name.cpp -o $name # ./$name set 1e7 ./$name set 2e7 ./$name vec 2e7 ./$name vec 4e7 ================================================ FILE: bug/std_vector.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # Using g++ 4.8.1 results in the following error message: # # std_vector.cpp:8:7: error: no match for ‘operator|=’ (operand types are # ‘std::vector::reference {aka std::_Bit_reference}’ and ‘bool’) # y[1] |= true; # ^ # ----------------------------------------------------------------------------- if [ ! -e build ] then mkdir build fi cd build echo "$0" name=`echo $0 | sed -e 's|.*/||' -e 's|\..*||'` cat << EOF > $name.cpp # include int main(void) { int N = 1; std::vector y(N); for(int i = 0; i < N; i++ ) y[i] = false; y[0] = y[0] | true; y[1] |= true; return 0; } EOF echo "g++ -g $name.cpp -o $name" g++ -g $name.cpp -o $name # echo "./$name" ./$name # echo "rm $name $name.cpp" rm $name $name.cpp ================================================ FILE: bug/subgraph.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- name=`echo $0 | sed -e 's|^bug/||' -e 's|\.sh$||'` if [ "$0" != "bug/$name.sh" ] then echo 'usage: bug/alloc_global.sh' exit 1 fi # ----------------------------------------------------------------------------- if [ -e build/bug ] then rm -r build/bug fi mkdir -p build/bug cd build/bug cmake ../.. # ----------------------------------------------------------------------------- cat << EOF Description: Test for bug in subgraph_jac_rev(x, subset). EOF cat << EOF > $name.cpp # include int main(void) { bool ok = true; using std::cout; using CppAD::AD; using CppAD::vector; typedef vector d_vector; typedef vector s_vector; // size_t n = 4; d_vector x(n); vector< AD > ax(n), ay(n); for(size_t j = 0; j < n; ++j) ax[j] = x[j] = double(j); CppAD::Independent(ax); for(size_t i = 0; i < n; ++i) { ay[i] = 0.0; for(size_t j = 0; j < n; ++j) ay[i] += double(i + j + 1) * ax[j]; } CppAD::ADFun f(ax, ay); // size_t nnz = (n * (n + 1)) / 2; CppAD::sparse_rc upper_triangle(n, n, nnz); size_t k = 0; for(size_t i = 0; i < n; ++i) { for(size_t j = i; j < n; ++j) upper_triangle.set(k++, i, j); } ok &= k == nnz; CppAD::sparse_rcv subset( upper_triangle ); // f.subgraph_jac_rev(x, subset); const d_vector& val = subset.val(); k = 0; for(size_t i = 0; i < n; ++i) { for(size_t j = i; j < n; ++j) ok &= val[k++] == double(i + j + 1); } ok &= k == nnz; // if( ok ) return 0; return 1; } EOF cxx_flags='-Wall -pedantic-errors -std=c++11 -Wshadow -Wconversion -g -O0' echo "g++ -I../../include $cxx_flags $name.cpp -o $name" g++ -I../../include $cxx_flags $name.cpp -o $name # echo "build/bug/$name" if ! ./$name then echo echo "build/bug/$name: Error" exit 1 fi echo # ----------------------------------------------------------------------------- echo "bug/$name.sh: OK" exit 0 ================================================ FILE: bug/template.sh ================================================ #! /usr/bin/env bash # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ----------------------------------------------------------------------------- set -e -u # # bash function that echos and executes a command echo_eval() { echo $* eval $* } # ----------------------------------------------------------------------------- # # name name=`echo $0 | sed -e 's|^bug/||' -e 's|\.sh$||'` if [ "$0" != "bug/$name.sh" ] then echo "usage: bug/$name.sh" exit 1 fi # # PKG_CONFIG_PATH, LD_LIBRARY_PATH prefix="$(pwd)/build/prefix" export PKG_CONFIG_PATH="$prefix/share/pkgconfig" export LD_LIBRARY_PATH="" for subdir in 'lib' 'lib64' do PKG_CONFIG_PATH+=":$prefix/$subdir/pkgconfig" LD_LIBRARY_PATH+=";$prefix/$subdir" done # # eigen_flags eigen_flags=$(pkg-config -cflags eigen3) # # ipopt_flags ipopt_flags=$(pkg-config -cflags ipopt | sed -e 's|/coin-or | |' ) # # link_flags link_flags=$(pkg-config -libs cppad) # # extra_flags extra_flags='-Wall -pedantic-errors -std=c++11 -Wshadow -Wconversion -g -O0' # # build/bug if [ -e build/bug ] then rm -r build/bug fi mkdir -p build/bug cd build/bug # # $name.cpp cat << EOF > $name.cpp # include int main(void) { bool ok = true; using std::cout; using CppAD::AD; using CppAD::vector; // cout << "Choose a name for this bug (called name below)\n"; cout << "1. Copy bug/template.sh to bug/name.sh\n"; cout << "2. Edit bug/name.sh replacing C++ source code\n"; cout << "3. Run bug/name.sh\n"; cout << "Test passes (fails) if bug/name.sh: OK (Error) echoed at end\n"; // if( ok ) return 0; return 1; } EOF # # $name cmd=" g++ $name.cpp -I../../include $extra_flags $eigen_flags $ipopt_flags -o $name $link_flags " # $cmd # echo "build/bug/$name" if ! ./$name then echo echo "build/bug/$name: Error" echo "export LD_LIBRARY_PATH='$LD_LIBRARY_PATH'" exit 1 fi echo # ----------------------------------------------------------------------------- echo "bug/$name.sh: OK" exit 0 ================================================ FILE: bug/test_install.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- cat << EOF This is not a bug but rather a test of installing with cppad_prefix=$HOME/prefix EOF cat << EOF > bug.$$ # include int main(void) { bool ok = true; using std::cout; using CppAD::AD; // CPPAD_TESTVECTOR( AD ) ax(1), ay(1); ax[0] = 1.0; CppAD::Independent(ax); ay[0] = sin( ax[0] ); CppAD::ADFun f(ax, ay); // std::vector< std::set > p(1); p[0].insert(0); CppAD::vector< size_t > row(1), col(1); row[0] = 0; col[0] = 0; CppAD::sparse_jacobian_work work; work.color_method = "colpack"; CPPAD_TESTVECTOR(double) x(1), jac(1); x[0] = 2.0; f.SparseJacobianForward(x, p, row, col, jac, work); // ok &= jac[0] == std::cos( x[0] ); // if( ok ) return 0; return 1; } EOF # ----------------------------------------------------------------------------- if [ ! -e build ] then mkdir build fi cd build echo "$0" name=`echo $0 | sed -e 's|.*/||' -e 's|\..*||'` mv ../bug.$$ $name.cpp cmd="g++ -I $HOME/prefix/cppad/include --std=c++11 -g $name.cpp -o $name" cmd="$cmd -L $HOME/prefix/cppad/lib64 -lcppad_lib" cmd="$cmd -L $HOME/prefix/colpack/lib64 -lColPack" echo "$cmd" eval $cmd # echo "./$name" if ! ./$name then echo echo "$name.sh: Error" exit 1 fi echo echo "$name.sh: OK" exit 0 ================================================ FILE: bug/vec_itr_speed.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- name=`echo $0 | sed -e 's|^bug/||' -e 's|\.sh$||'` error='none' if [ "$0" != "bug/$name.sh" ] then echo "program name is not bug/$name.sh" ok='no' fi compiler="$1" if [ "$compiler" != 'g++' ] && [ "$compiler" != 'clang++' ] then if [ "$compiler" != '' ] then echo 'compiler is not g++ or clang++' fi ok='no' fi debug="$2" if [ "$debug" != 'yes' ] && [ "$debug" != 'no' ] then if [ "$debug" != '' ] then echo 'debug is not yes or no' fi ok='no' fi opt_level="$3" if [[ "$opt_level" =~ "[^0-3]" ]] then if [ "$opt_level" != '' ] then echo 'opt_level is not 0, 1, 2, or 3' fi ok='no' fi if [ "$ok" == 'no' ] then echo echo "usage: bug/$name.sh compiler debug opt_level" echo 'compiler is: g++ or clang++' echo 'debug is: yes or no' echo 'opt_level is: 0, 1, 2, or 3.' exit 1 fi # ----------------------------------------------------------------------------- if [ -e build/bug ] then rm -r build/bug fi mkdir -p build/bug cd build/bug # cmake ../.. # ----------------------------------------------------------------------------- cat << EOF This is speed test (not a bug report) comparing the speed using CppAD vector iterators and raw pointer with the algorithms std::sort and std::reverse. EOF cat << EOF > $name.cpp # include # include namespace { // declared here so setup does not include allocation CppAD::vector vec; // ---------------------------------------------------------------------- // sort test functions void sort_itr(size_t size, size_t repeat) { // size and vec.size() are equal size_t* data = vec.data(); while( repeat-- ) { // sort a vector that is not in order for(size_t i = 0; i < size; ++i) data[i] = (size - i) % 21; std::sort(vec.begin(), vec.end()); } } void sort_ptr(size_t size, size_t repeat) { // size and vec.size() are equal size_t* data = vec.data(); while( repeat-- ) { // sort same vector as in sort_itr for(size_t i = 0; i < size; ++i) data[i] = (size - i) % 21; std::sort(vec.data(), vec.data() + vec.size()); } } // ---------------------------------------------------------------------- // reverse test functions void reverse_itr(size_t size, size_t repeat) { // size and vec.size() are equal size_t* data = vec.data(); while( repeat-- ) { // reverse a vector that is not in order for(size_t i = 0; i < size; ++i) data[i] = i; std::reverse(vec.begin(), vec.end()); } } void reverse_ptr(size_t size, size_t repeat) { // size and vec.size() are equal size_t* data = vec.data(); while( repeat-- ) { // reverse same vector as in reverse_itr for(size_t i = 0; i < size; ++i) data[i] = i; std::reverse(vec.data(), vec.data() + vec.size()); } } } int main(void) { bool ok = true; using CppAD::time_test; using std::cout; // size_t test_size = 100000; // size of vector in test double time_min = 1.0; // minimum time in seconds for each test vec.resize(test_size); // allocate memory outsize of test size_t repeat; // output by time_test function // ----------------------------------------------------------------------- // sort tests // // iterator double sort_itr_sec = time_test(sort_itr, time_min, test_size, repeat); for(size_t i = 1; i < test_size; ++i) ok &= vec[i-1] <= vec[i]; cout << "sort_itr_sec=" << sort_itr_sec << ", repeat=" << repeat << "\n"; // // pointer double sort_ptr_sec = time_test(sort_ptr, time_min, test_size, repeat); for(size_t i = 1; i < test_size; ++i) ok &= vec[i-1] <= vec[i]; cout << "sort_ptr_sec=" << sort_ptr_sec << ", repeat=" << repeat << "\n"; // ----------------------------------------------------------------------- // reverse tests // // iterator double rev_itr_sec = time_test(reverse_itr, time_min, test_size, repeat); for(size_t i = 1; i < test_size; ++i) ok &= vec[i] == test_size - 1 - i; cout << "rev_itr_sec=" << rev_itr_sec << ", repeat=" << repeat << "\n"; // // pointer double rev_ptr_sec = time_test(reverse_ptr, time_min, test_size, repeat); for(size_t i = 1; i < test_size; ++i) ok &= vec[i] == test_size - 1 - i; cout << "rev_ptr_sec=" << rev_ptr_sec << ", repeat=" << repeat; // ----------------------------------------------------------------------- if( ok ) return 0; return 1; } EOF cxx_flags="-Wall -std=c++11 -Wshadow -Wconversion -O$opt_level" if [ "$debug" == 'no' ] then cxx_flags="$cxx_flags -DNDEBUG" fi echo "$compiler -I../../include $cxx_flags $name.cpp -o $name" $compiler -I../../include $cxx_flags $name.cpp -o $name # echo "build/bug/$name" if ! ./$name then echo echo "build/bug/$name: Error" exit 1 fi echo # ----------------------------------------------------------------------------- echo "bug/$name.sh: OK" exit 0 ================================================ FILE: cmake/add_check_executable.cmake ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- # add_check_executable(parent_target short_name) # add_check_executable(parent_target short_name arguments) # # parent_target: (in) # The variable containing the name of the parent check target, # ${parent_target} must begin with "check". # # short_name: (in) # Is the non-empty short name of the target we are adding. # The full name of this target is ${parent_target}_${short_name}. # # arguments: (in) # This argument is optional. If it is present, it is # a string containing the arguments to the executable. # # 1. This macro creates the target ${parent_target}_${short_name}. # 2. This target depends on an executable with the same name except that # the "check_" at the beginning is removed. # 3. If the variable ${parent_target}_${short_name}_depends is defined, # it is a list that is included in the dependencies for this target. # 4. This target is add to the list ${parent_target}_depends in both # the current scope and its parent scope. # # This macros uses temporary variables with names that begin with # add_check_executable_. # MACRO(add_check_executable parent_target short_name) IF( NOT ${parent_target} MATCHES "^check" ) MESSAGE(FATAL_ERROR "add_check_executable: " "parent_target does not begin with 'check'" ) ENDIF( ) IF( "${short_name}" STREQUAL "" ) MESSAGE(FATAL_ERROR "add_check_target: short_name is empty") ENDIF( ) # # add_check_executable_full_name SET(add_check_executable_full_name "${parent_target}_${short_name}" ) # # add_check_executable_no_check STRING(REGEX REPLACE "^check_" "" add_check_executable_no_check "${add_check_executable_full_name}" ) # # add_check_executable_arguments IF( ${ARGC} EQUAL 2 ) SET(add_check_executable_arguments "") ELSEIF( ${ARGC} EQUAL 3 ) STRING( REGEX REPLACE "[ ]" ";" add_check_executable_arguments "${ARGV2}" ) ELSE( ) MESSAGE(FATAL_ERROR "add_check_executable: " "number of arguments = ${ARGC}" ) ENDIF( ) # # add_check_executable_depends IF( DEFINED ${add_check_executable_full_name}_depends ) SET(add_check_executable_depends ${${add_check_executable_full_name}_depends} ) add_to_list(add_check_executable_depends ${add_check_executable_no_check} ) ELSE ( ) SET(add_check_executable_depends ${add_check_executable_no_check} ) ENDIF( ) # # create this target ADD_CUSTOM_TARGET( ${add_check_executable_full_name} ${add_check_executable_no_check} ${add_check_executable_arguments} DEPENDS ${add_check_executable_depends} ) IF( "${CMAKE_GENERATOR}" STREQUAL "Ninja" ) SET(make_cmd "ninja" ) ELSEIF( "${CMAKE_GENERATOR}" STREQUAL "NMake Makefiles" ) SET(make_cmd "nmake" ) ELSE( ) SET(make_cmd "make" ) ENDIF( ) MESSAGE(STATUS "${make_cmd} ${add_check_executable_full_name}: available") # # add parent dependency add_to_list( ${parent_target}_depends ${add_check_executable_full_name} ) SET( ${parent_target}_depends ${${parent_target}_depends} PARENT_SCOPE ) # ENDMACRO() ================================================ FILE: cmake/add_to_list.cmake ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # ============================================================================= # add_to_list(variable_list constant_value) # # variables_list: (in/out) # The variable containing the list of values. # The original list may be ""; i.e., the empty list. # # constant_value: (in) # Is the value we are adding to the list. This value cannot be empty. # MACRO(add_to_list variable_list constant_value ) IF( "${${variable_list}}" STREQUAL "" ) SET( ${variable_list} ${constant_value} ) ELSE( "${${variable_list}}" STREQUAL "" ) SET( ${variable_list} ${${variable_list}} ${constant_value} ) ENDIF( "${${variable_list}}" STREQUAL "" ) ENDMACRO(add_to_list) ================================================ FILE: cmake/assert.cmake ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # ============================================================================= # assert(variable) # # variable: (in) # The variable is checked to make sure it is true, and if it is not true # a fatal error message is printed. # MACRO(assert variable) IF( NOT ${variable} ) MESSAGE(FATAL_ERROR "Error: ${variable} is false in ${CMAKE_CURRENT_LIST_FILE}" ) ENDIF( NOT ${variable} ) ENDMACRO(assert) ================================================ FILE: cmake/assert_value_in_set.cmake ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # assert_value_in_set(value element_1 ... element_n ) # # value: (in) # is the value we are searching for. # # element_j: (in) # is the j-th element of the set. # MACRO(assert_value_in_set) SET(argv ${ARGV}) LIST(GET argv 0 value) LIST(REMOVE_AT argv 0) SET(ok FALSE) FOREACH(entry ${argv} ) IF( "${${value}}" STREQUAL "${entry}" ) SET(ok TRUE) ENDIF( "${${value}}" STREQUAL "${entry}" ) ENDFOREACH(entry ${argv} ) IF(NOT ok) MESSAGE(FATAL_ERROR "${value} is not one of following: ${argv}" ) ENDIF(NOT ok) ENDMACRO(assert_value_in_set) ================================================ FILE: cmake/command_line_arg.cmake ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # command_line_arg(variable default type description) # # variable: (out) # is the variable we are setting to its default value. # The variable can be changed on the cmake command line (or in the camke gui). # The final value of the variable is printed with the cmake output. # # default: (in) # is the default value for this variable; i.e., # if it is not set by the cmake command line or gui. # # type: (in) # must be one of the following: # STRING, if the variable holds an arbitrary string. # PATH, if the variable holds a directory. # BOOL, if the variable only has the values true or false. # # description: (in) # Is a description of how the variable affects the CppAD install procedure. # MACRO(command_line_arg variable default type description) IF( NOT ( ${type} STREQUAL "STRING" ) ) IF( NOT ( ${type} STREQUAL "PATH" ) ) IF( NOT ( ${type} STREQUAL "BOOL" ) ) MESSAGE(FATAL_ERROR, "command_line_arg: bug in CppAD cmake commands") ENDIF( NOT ( ${type} STREQUAL "BOOL" ) ) ENDIF( NOT ( ${type} STREQUAL "PATH" ) ) ENDIF( NOT ( ${type} STREQUAL "STRING" ) ) # IF( NOT ${variable} ) SET(${variable} "${default}" CACHE ${type} "${description}") ENDIF( NOT ${variable} ) MESSAGE(STATUS "${variable} = ${${variable}}") # ENDMACRO( command_line_arg ) ================================================ FILE: cmake/compile_source_test.cmake ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- # compile_source_test(defined_ok source variable) # # defined_ok (in) # If this is true, it is OK for variable to be defined on input # (it will be replaced). Otherwise it is a fatal error if variable # is defined on input. # # source: (in) # contains the source for the program that will be compiled and linked. # # variable: (out) # Upon return, the value of this variable is 1 (0) if the program compiles # and links (does not compile and link). # # CMAKE_REQUIRED_name (in) # For name equal to DEFINITIONS, INCLUDES, LIBRARIES, FLAGS, the variable # CMAKE_REQUIRED_name is an input to routine; see CHECK_CXX_SOURCE_COMPILES # documentation. # MACRO(compile_source_test defined_ok source variable) # # # check that variable is not yet defined IF( NOT ${defined_ok} ) IF( DEFINED ${variable} ) MESSAGE(FATAL_ERROR "compile_source_test: ${variable} is defined before expected" ) ENDIF( DEFINED ${variable} ) ENDIF( NOT ${defined_ok} ) # SET(CMAKE_REQUIRED_DEFINITIONS "" ) SET(CMAKE_REQUIRED_INCLUDES "" ) SET(CMAKE_REQUIRED_LIBRARIES "" ) IF( cppad_cxx_flags ) SET(CMAKE_REQUIRED_FLAGS "${cppad_cxx_flags} ${CMAKE_CXX_FLAGS}" ) ELSE( cppad_cxx_flags ) SET(CMAKE_REQUIRED_FLAGS "" ) ENDIF( cppad_cxx_flags ) # # check that source code compiles CHECK_CXX_SOURCE_COMPILES("${source}" ${variable} ) # # change result variable to 0 (1) for fail (succeed). IF( ${variable} ) SET(${variable} 1) ELSE( ${variable} ) SET(${variable} 0) ENDIF( ${variable} ) # # check that variable is defined IF( NOT DEFINED ${variable} ) MESSAGE(FATAL_ERROR "compile_source_test: error in CMake script." ) ENDIF( NOT DEFINED ${variable} ) # MESSAGE(STATUS "${variable} = ${${variable}}" ) ENDMACRO( compile_source_test ) ================================================ FILE: cmake/cppad_uninstall.cmake ================================================ # Copyright (C) 2010 Olivier Stasse, JRL, CNRS, 2010 # # Original source: # https://github.com/jrl-umi3218/jrl-cmakemodules/blob/master/cmake_uninstall.cmake.in # # Contributor # https://jcarpent.github.io/ # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # set(MANIFEST "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt") if(EXISTS ${MANIFEST}) file(STRINGS ${MANIFEST} files) foreach(file ${files}) if(IS_SYMLINK ${file} OR EXISTS ${file}) message(STATUS "Removing file: '${file}'") execute_process( COMMAND ${CMAKE_COMMAND} -E remove ${file} OUTPUT_VARIABLE rm_out RESULT_VARIABLE rm_retval ) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Failed to remove file: '${file}'.") endif() else() message(STATUS "File '${file}' does not exist.") endif() endforeach(file) else() message(FATAL_ERROR "Cannot find install manifest: ${MANIFEST}") endif() ================================================ FILE: cmake/dos_path_to_unix.cmake ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # dos_path_to_unix(dos_path unix_path) # # dos_path: (in) # is the value of the path we are converting to unix format; i.e., # all \ characters are replaced by / characters. # # unix_path: (out) # is the variable where the result of the conversion is placed. # MACRO(dos_path_to_unix dos_path unix_path) STRING(REGEX REPLACE "[\\]" "/" ${unix_path} "${dos_path}" ) ENDMACRO(dos_path_to_unix dos_path unix_path) ================================================ FILE: cmake/eigen_info.cmake ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- # eigen_info() # # cppad_has_eigen: (out) # is 1 (0) if eigen is found (not found) # If this is 1, the include directory for eigen will automatically be # added using INCLUDE_DIRECTORIES with the SYSTEM flag. # # eigen_LINK_LIBRARIES: (out) # if cppad_has_eigen is 1, # is a list of absolute paths to libraries necessary to use eigen # (should be empty because eigen is a header only library). # # This macro may use variables with the name eigen_* MACRO(eigen_info) # # check for pkg-config IF( NOT PKG_CONFIG_FOUND ) FIND_PACKAGE(PkgConfig REQUIRED) ENDIF( ) # # IF( NOT ${use_cplusplus_2014_ok} ) MESSAGE(STATUS "Eigen not supportedL: because c++14 not supported") SET(cppad_has_eigen 0) ELSE( ) # # eigen_* pkg_check_modules(eigen QUIET eigen3 ) # IF( eigen_FOUND ) MESSAGE(STATUS "Eigen found") SET(cppad_has_eigen 1) INCLUDE_DIRECTORIES( SYSTEM ${eigen_INCLUDE_DIRS} ) ELSE( ) MESSAGE(STATUS "Eigen not Found: eigen3.pc not in PKG_CONFIG_PATH") SET(cppad_has_eigen 0) ENDIF( ) ENDIF( ) # print_variable( cppad_has_eigen ) ENDMACRO(eigen_info) ================================================ FILE: cmake/pkgconfig_info.cmake ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-26 Bradley M. Bell # ---------------------------------------------------------------------------- # pkgconfig_info(package system_include) # # package: (in) # is the name of an optional package. # # system_include: (in) # If this is true (false), the include directories for this package will # (will not) be treated as system directories (no warnings). # # include_${package}: (in) # is set by this macro to a cache BOOL variable initialized as false. # It can be changed by the cmake command line or gui. # If it is true, there must be a pkg-config file for this package and # the corresponding include directories added using INCLUDE_DIRECTORIES. # # cppad_has_${package}: (out) # is 1 (0) if include_${package} is true (false). # # ${package}_LINK_LIBRARIES: (out) # is a list of absolute paths to libraries necessary to use this package. # # This macros uses temporary variables with names that begin with # pkgconfig_info_ and ${package}_. # MACRO(pkgconfig_info package system_include) # # include_${package} SET( include_${package} FALSE CACHE BOOL "include ${package} using its pkgconfig file" ) print_variable( include_${package} ) IF( NOT include_${package} ) SET(cppad_has_${package} 0) ELSE( ) SET(cppad_has_${package} 1) # # check for pkg-config IF( NOT PKG_CONFIG_FOUND ) FIND_PACKAGE(PkgConfig REQUIRED) ENDIF( ) # # ${package} info pkg_check_modules( ${package} QUIET ${package} ) # IF( ${package}_FOUND ) MESSAGE(STATUS "Found ${package} pkg-config file") ELSE( ) MESSAGE(FATAL_ERROR "Can't find ${package} pkg-config file or one of its requirements." "\nPKG_CONFIG_PATH=$ENV{PKG_CONFIG_PATH}" ) ENDIF( ) # IF( ${package} STREQUAL eigen ) SET( eigen_LIBRARIES "${eigen3_LIBRARIES}" ) ENDIF() # # pkgconfig_info_dirs STRING( REGEX REPLACE "/coin-or" "" pkgconfig_info_dirs "${${package}_INCLUDE_DIRS}" ) # # INCLUDE_DIRECTORIES IF( ${system_include} ) INCLUDE_DIRECTORIES( SYSTEM ${pkgconfig_info_dirs} ) ELSE( ) INCLUDE_DIRECTORIES( ${pkgconfig_info_dirs} ) ENDIF( ) ENDIF( ) # print_variable( cppad_has_${package} ) ENDMACRO(pkgconfig_info) ================================================ FILE: cmake/prefix_info.cmake ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- # prefix_info(package system_include) # # package: (in) # is the name of an optional package. # # system_include: (in) # If this is true (false), the include directories for this package will # (will not) be treated as system directories (no warnings). # # ${package}_prefix: (in) # is set by this macro to a cache PATH variable initialized as NOTFOUND. # It can be changed by the cmake command line or gui. # If it is changed, it holds the install prefix for this package. # # cppad_has_${package}: (out) # is 1 if ${package}_prefix is changed by the cmake command line or gui, # and 0 otherwise. # # If ${package}_prefix is changed, the following are done: # 1. All the valid include subdirectories are added using INCLUDE_DIRECTORIES. # Valid include subdirectories are determined by cmake_install_includedirs. # 2. All the valid library subdirectories are added using LINK_DIRECTORIES. # Valid library subdirectories are determined by cmake_install_libdirs. # # This macros uses temporary variables with names that begin with # prefix_info_. # MACRO(prefix_info package system_include) # # ${package}_prefix SET(${package}_prefix NOTFOUND CACHE PATH "${package} install prefix") # # prefix_info_value SET( prefix_info_value "${${package}_prefix}" ) # SET(cppad_has_${package} 1) # IF( "${prefix_info_value}" STREQUAL "NOTFOUND" ) SET(cppad_has_${package} 0) ENDIF( ) # IF( "${prefix_info_value}" STREQUAL "" ) SET(cppad_has_${package} 0) ENDIF( ) # IF( cppad_has_${package} ) SET(cppad_has_${package} 0) # # prefix_info_subdir FOREACH(prefix_info_subdir ${cmake_install_includedirs}) # # prefix_info_dir SET( prefix_info_dir "${prefix_info_value}/${prefix_info_subdir}" ) IF(IS_DIRECTORY "${prefix_info_dir}" ) SET(cppad_has_${package} 1) MESSAGE(STATUS " Found ${prefix_info_dir}") IF( ${system_include} ) INCLUDE_DIRECTORIES( SYSTEM "${prefix_info_dir}" ) ELSE( ) INCLUDE_DIRECTORIES( "${prefix_info_dir}" ) ENDIF( ) ENDIF( ) ENDFOREACH( ) # # prefix_info_subdir FOREACH(prefix_info_subdir ${cmake_install_libdirs}) # # prefix_info_dir SET( prefix_info_dir "${prefix_info_value}/${prefix_info_subdir}" ) IF(IS_DIRECTORY "${prefix_info_dir}" ) SET(cppad_has_${package} 1) MESSAGE(STATUS " Found ${prefix_info_dir}") LINK_DIRECTORIES( "${prefix_info_dir}" ) ENDIF( ) ENDFOREACH() IF( NOT cppad_has_${package} ) MESSAGE(FATAL_ERROR "cppad_has_${package} = ${prefix_info_value} " "but did not find any include files or libraries there" ) ENDIF( ) ENDIF( ) # print_variable( cppad_has_${package} ) ENDMACRO( ) ================================================ FILE: cmake/print_variable.cmake ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # ============================================================================= # print_variable(variable) # # variable: (in) # The variable name and value is printed # MACRO(print_variable variable) MESSAGE(STATUS "${variable} = ${${variable}}" ) ENDMACRO(print_variable) ================================================ FILE: cmake/run_source_test.cmake ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # run_source_test(source variable) # # source: (in) # contains the source for the program that will be run. # # variable: (out) # This variable must not be defined when this macro is called. # Upon return, the value of this variable is 1 if the program runs and # returns a zero status. Otherwise its value is 0. # Note that this is the reverse of the status flag returned by the program. # # CMAKE_REQUORED_name (out) # For name equal to DEFINITIONS, INCLUDES, LIBRARIES, FLAGS, the variable # CMAKE_REQUIRED_name is set to the empty string. # MACRO(run_source_test source variable) IF( DEFINED ${variable} ) MESSAGE(ERROR "run_source_test: ${variable} is defined before expected" ) ENDIF( DEFINED ${variable} ) SET(CMAKE_REQUIRED_DEFINITIONS "" ) SET(CMAKE_REQUIRED_INCLUDES "" ) SET(CMAKE_REQUIRED_LIBRARIES "" ) IF( cppad_cxx_flags ) SET(CMAKE_REQUIRED_FLAGS "${cppad_cxx_flags} ${CMAKE_CXX_FLAGS}" ) ELSE( cppad_cxx_flags ) SET(CMAKE_REQUIRED_FLAGS "" ) ENDIF( cppad_cxx_flags ) CHECK_CXX_SOURCE_RUNS("${source}" ${variable} ) IF( ${variable} ) SET(${variable} 1) ELSE( ${variable} ) SET(${variable} 0) ENDIF( ${variable} ) MESSAGE(STATUS "${variable} = ${${variable}}" ) # SET(CMAKE_REQUIRED_FLAGS "" ) ENDMACRO( run_source_test ) ================================================ FILE: cmake/set_compile_flags.cmake ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # set_compile_flags( program_name debug_which source_list) # # program_name: (in) # Is the name of the program that we are building. This is only used # for debugging which files have debug and which have release properties. # # debug_which: (in) # Is one of the following cases: # # Case debug_even: # The files with an even (odd) index in source_list have debug (release) flags. # # Case debug_odd: # The files with an odd (even) index in source_list have debug (release) flags. # # Case debug_all, debug_none, or empty string: # The debug and release flags are not set by this routine. # # source_list: (in) # is a list of source files that get set to have debug or release # compile flags. The cppad_cxx_flags compile flags always get included. # FUNCTION(set_compile_flags program_name debug_which source_list) # debug compile flags SET(debug_flags "${cppad_cxx_flags} ${CMAKE_CXX_FLAGS_DEBUG}") # release compile flags SET(release_flags "${cppad_cxx_flags} ${CMAKE_CXX_FLAGS_RELEASE}") # # set alternate, report random number result, # set compile flags property when not alternating. IF( "${debug_which}" STREQUAL "debug_even" ) SET(alternate TRUE) SET(count_mod_2 0) ELSEIF( "${debug_which}" STREQUAL "debug_odd" ) SET(alternate TRUE) SET(count_mod_2 1) ELSE( "${debug_which}" ) SET(alternate FALSE) IF( NOT "${cppad_cxx_flags}" STREQUAL "" ) SET(alternate FALSE) SET_SOURCE_FILES_PROPERTIES( ${source_list} PROPERTIES COMPILE_FLAGS "${cppad_cxx_flags}" ) ENDIF( NOT "${cppad_cxx_flags}" STREQUAL "" ) ENDIF( "${debug_which}" STREQUAL "debug_even" ) # IF( alternate ) SET(debug_list "") SET(release_list "") FOREACH(source ${source_list}) MATH(EXPR count_mod_2 "(${count_mod_2} + 1) % 2") IF( count_mod_2 ) add_to_list(debug_list ${source}) ELSE( count_mod_2 ) add_to_list(release_list ${source}) ENDIF( count_mod_2 ) ENDFOREACH(source ${source_list}) SET_SOURCE_FILES_PROPERTIES( ${debug_list} PROPERTIES COMPILE_FLAGS "${debug_flags}" ) SET_SOURCE_FILES_PROPERTIES( ${release_list} PROPERTIES COMPILE_FLAGS "${release_flags}" ) # IF( ${program_name} STREQUAL "..." ) # print_variable( ... ) # ENDIF( ) ENDIF( alternate ) ENDFUNCTION(set_compile_flags program_name debug_which source_list) ================================================ FILE: configure ================================================ #! /usr/bin/env bash # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell set -e -u echo $0 $* # # ---------------------------------------------------------------------------- # {xrst_begin configure app} # {xrst_spell # adolc # adouble # ansi # badiff # boostvector # colpack # cxx # cygdrive # dist # eigenvector # fadbad # fortran # ip # ipopt # msvc # nmake # sacado # stdvector # sys # ublas # usr # } # {xrst_comment_ch #} # # Configure Test and Installation Script # ###################################### # # Deprecated 2012-12-26 # ********************* # This install procedure is deprecated. # You should use the :ref:`cmake-name` instructions to # configure and install CppAD. # # Distribution Directory # ********************** # You must first obtain a copy of the CppAD distribution directory # using the :ref:`download-name` instructions. # We refer to the corresponding # :ref:`download@Distribution Directory` as # *dist_dir* . # # Build Directory # *************** # Create the directory *dist_dir* / ``build`` , # which will be referred to as the build directory below. # (The build directory can be in any location # so long as it is not the *dist_dir* .) # # Configure # ********* # Execute the following command in the build directory: # # | ../ ``configure`` \\ # | |tab| ``--help`` \\ # | |tab| ``--prefix`` = *prefix_dir* \\ # | |tab| ``--with-`` *package* ``vector`` \\ # | |tab| ``--enable-msvc`` \\ # | |tab| ``--with-clang`` \\ # | |tab| ``--with-verbose-make`` \\ # | |tab| ``MAX_NUM_THREADS`` = *max_num_threads* \\ # | |tab| ``CXX_FLAGS`` = *cxx_flags* \\ # | |tab| ``POSTFIX_DIR`` = *postfix_dir* \\ # | |tab| ``ADOLC_DIR`` = *adolc_dir* \\ # | |tab| ``FADBAD_DIR`` = *fadbad_dir* \\ # | |tab| ``SADADO_DIR`` = *sacado_dir* \\ # | |tab| ``IPOPT_DIR`` = *ipopt_dir* \\ # | |tab| ``TAPE_ADDR_TYPE`` = *tape_addr_type* \\ # | |tab| ``TAPE_ID_TYPE`` = *tape_id_type* # # where only the ``configure`` line need appear; i.e., # the entries in all of the other lines are optional. # The text in italic above is replaced values that you choose # (see discussion below). # # make # **** # The following command, in the build directory, # creates the file ``include/cppad/configure.hpp`` in # the source directory and then builds # some object libraries that are used by the tests # :: # # make # # Examples and Tests # ================== # The following command will build and run all the correctness and speed tests. # :: # # make test # # prefix_dir # ********** # The default value for prefix directory is ``$HOME`` # i.e., by default the CppAD include files # will :ref:`install` below ``$HOME`` . # If you want to install elsewhere, you will have to use this option. # As an example of using the ``--prefix`` = *prefix_dir* option, # if you specify # :: # # ./configure --prefix=/usr/local # # the CppAD include files will be installed in the directory # # / ``usr/local/include/cppad`` # # --with-vector # ********************** # The :ref:`CPPAD_TESTVECTOR` # template class is used for many of the CppAD examples and tests. # The default for this template class is # # |tab| ``CppAD::vector<`` *Scalar* > . # # If one, and only one, of the following command line arguments is specified: # :: # # --with-stdvector # --with-boostvector # --with-eigenvector # # the corresponding of the following template classes is used # # | |tab| ``std::vector<`` *Scalar* > # | |tab| ``boost::numeric::ublas::vector<`` *Scalar* > # | |tab| ``Eigen::matrix<`` *Scalar* , ``Eigen::Dynamic`` , 1> # # --enable-msvc # ************* # If this flag is present, the Microsoft ``cl`` compiler will be # placed at the front of a list of compilers to search for. # In addition, nmake_ make files will be generated; i.e., # you will use ``nmake`` instead of ``make`` to build, test, and install. # The resulting C++, C, and Fortran compiler orders work Coin-OR specific; # see # `Building with Visual Studio in MSys2 # `_ . # # .. _nmake: https://learn.microsoft.com/en-us/cpp/build/reference/nmake-reference?view=msvc-170 # # --with-clang # ************ # If this flag is present, ``clang++`` ( ``clang`` ) # will be used for the C++ (C) compiler. # This option cannot appear when the --enable-msvc option appears. # # --with-verbose-make # ******************* # This will create verbose output during the make commands. # # max_num_threads # *************** # this specifies the value for the preprocessor symbol # :ref:`multi_thread@CPPAD_MAX_NUM_THREADS` . # It must be greater than or equal to four; i.e., # *max_num_threads* >= 4 . # The default value for this option is 64. # # cxx_flags # ********* # If the command line argument *cxx_flags* is present, # it specifies compiler flags. # For example, # # ``CXX_FLAGS`` = ``"-Wall -ansi"`` # # would specify that warning flags ``-Wall`` # and ``-ansi`` should be included # in all the C++ compile commands. # The error and warning flags chosen must be valid options # for the C++ compiler. # The default value for *cxx_flags* is the # empty string. # # postfix_dir # *********** # The default value for this directory is empty; i.e., there # is no postfix directory. # As an example of using the ``POSTFIX_DIR`` = *postfix_dir* option, # if you specify # :: # # ./configure --prefix=/usr/local POSTFIX_DIR=coin # # the CppAD include files will be # :ref:`installed` in the directory # # / ``usr/local/include/coin-or/cppad`` # # adolc_dir # ********* # The default value for this directory is empty; i.e., there # is no adolc directory. # If you have # `ADOL-C `_ # installed on your system, you can # specify a value for *adolc_dir* in the # :ref:`configure@Configure` command line. # The value of *adolc_dir* must be such that # # *adolc_dir* / ``include/adolc/adouble.h`` # # is a valid way to reference ``adouble.h`` . # In this case, you can run the Adolc speed correctness tests # by executing the following commands starting in the build directory # # make check_speed_adolc # # Note that these speed tests assume Adolc has been configure with # its sparse matrix computations enabled using # # ``--with-colpack`` = *colpack_dir* # # where *colpack_dir* is the same as *adolc_dir* . # # Linux # ===== # If you are using Linux, # you may have to add *adolc_dir* / ``lib`` to ``LD_LIBRARY_PATH`` . # For example, if you use the ``bash`` shell to run your programs, # you could include # # | |tab| ``LD_LIBRARY_PATH`` = *adolc_dir* / ``lib:$`` { ``LD_LIBRARY_PATH`` } # | |tab| ``export LD_LIBRARY_PATH`` # # in your ``$HOME/.bash_profile`` file. # # Cygwin # ====== # If you are using Cygwin, # you will have to add to following lines to the file # ``.bash_profile`` in your home directory: # # | |tab| ``PATH`` = *adolc_dir* / ``bin:$`` { ``PATH`` } # | |tab| ``export PATH`` # # in order for Adolc to run properly. # If *adolc_dir* begins with a disk specification, # you must use the Cygwin format for the disk specification. # For example, # if ``d:/adolc_base`` is the proper directory, # ``/cygdrive/d/adolc_base`` should be used for *adolc_dir* . # # fadbad_dir # ********** # The default value for this directory is empty; i.e., there # is no fadbad directory. # If you have # `Fadbad 2.1 `_ # installed on your system, you can # specify a value for *fadbad_dir* . # It must be such that # # *fadbad_dir* / ``include/FADBAD`` ++/ ``badiff.h`` # # is a valid reference to ``badiff.h`` . # In this case, you can run the Fadbad speed correctness tests # by executing the following commands starting in the build directory: # # make check_speed_fadbad # # # ipopt_dir # ********* # The default value for this directory is empty; i.e., there # is no ipopt directory. # If you have # `Ipopt `_ # installed on your system, you can # specify a value for *ipopt_dir* . # It must be such that # # *ipopt_dir* / ``include/coin-or/IpIpoptApplication.hpp`` # # is a valid reference to ``IpIpoptApplication.hpp`` . # In this case, the CppAD interface to Ipopt # examples can be built and tested # by executing the following commands starting in the build directory: # # make check_cppad_ipopt # # Once this has been done, you can execute the program # ``cppad_ipopt_speed`` in the ``build/cppad_ipopt/speed`` directory; # see ``ipopt_ode_speed.cpp`` . # # sacado_dir # ********** # The default value for this directory is empty; i.e., there # is no sacado directory. # If you have # `Sacado `_ # installed on your system, you can # specify a value for *sacado_dir* . # It must be such that # # *sacado_dir* / ``include/Sacado.hpp`` # # is a valid reference to ``Sacado.hpp`` . # In this case, you can run the Sacado speed correctness tests # by executing the following commands starting in the build directory: # :: # # make check_speed_sacado # # tape_addr_type # ************** # The default value for this type is ``size_t`` . # If the command line argument *tape_addr_type* is present, # it specifies the type used for address in the AD recordings (tapes). # The valid values for this argument are # ``unsigned short int`` , # ``unsigned int`` , # ``size_t`` . # The smaller the value of *sizeof* ( ``tape_addr_type`` ) , # the less memory is used. # On the other hand, the value # # ``std::numeric_limits<`` *tape_addr_type* >:: ``max`` () # # must be larger than any of the following: # :ref:`fun_property@size_op` , # :ref:`fun_property@size_op_arg` , # :ref:`size_par` , # :ref:`fun_property@size_par` , # :ref:`size_par` . # # tape_id_type # ************ # The default value for this type is ``size_t`` . # If the command line argument *tape_id_type* is present, # it specifies the type used for identifying tapes. # The valid values for this argument are # ``unsigned short int`` , # ``unsigned int`` , # ``size_t`` . # The smaller the value of *sizeof* ( ``tape_id_type`` ) , # the less memory is used. # On the other hand, the value # # ``std::numeric_limits<`` *tape_id_type* >:: ``max`` () # # must be larger than the maximum number of tapes per thread # times :ref:`configure@max_num_threads` . # # make install # ************ # Once you are satisfied that the tests are giving correct results, # you can install CppAD into easy to use directories by executing the command # :: # # make install # # This will install CppAD in the location specified by # :ref:`configure@prefix_dir` . # You must have permission to write in the *prefix_dir* # directory to execute this command. # You may optionally specify a destination directory for the install; i.e., # # ``make install DESTDIR`` = *DestinationDirectory* # # {xrst_end configure} # ---------------------------------------------------------------------------- function print_variable { echo "$1 = ${!1}" } # ---------------------------------------------------------------------------- # # --help if echo " $* " | grep ' --help ' > /dev/null then cat << EOF usage: /configure \\ [--help] \\ [--prefix=] \\ [--with-vector] \\ [--enable-msvc] \\ [--with-clang] \\ [--with-verbose-make] \\ [MAX_NUM_THREADS=max_num_threads] \\ [CXX_FLAGS=cxx_flags] \\ [POSTFIX_DIR=postfix_dir] \\ [ADOLC_DIR=adolc_dir] \\ [FADBAD_DIR=fadbad_dir] \\ [SACADO_DIR=sacado_dir] \\ [IPOPT_DIR=ipopt_dir] \\ [TAPE_ADDR_TYPE=tape_addr_type] \\ [TAPE_ID_TYPE=tape_id_type] The --help option just prints this message and exits. is one of he following: boost, eigen, std EOF exit 0 fi # ---------------------------------------------------------------------------- first_executable() { first_executable_result='' while [ "$first_executable_result" == '' ] && [ "$1" != '' ] do if which $1 >& /dev/null then first_executable_result="$(which $1)" fi shift done } # ---------------------------------------------------------------------------- # prefix="$HOME" testvector='cppad' enable_msvc='no' with_clang='n0' with_verbose_make='no' max_num_threads='64' cxx_flags='' postfix_dir='NOTFOUND' adolc_dir='NOTFOUND' fadbad_dir='NOTFOUND' sacado_dir='NOTFOUND' ipopt_dir='NOTFOUND' tape_addr_type='size_t' tape_id_type='size_t' while [ "$#" != 0 ] do case "$1" in --prefix=*) prefix=$( echo $1 | sed -e 's|^--prefix=||' ) ;; --with-boostvector) testvector='boost' ;; --with-eigenvector) testvector='eigen' ;; --with-stdvector) testvector='std' ;; --with-clang) with_clang='yes' ;; --enable-msvc) enable_msvc='yes' ;; --with-verbose-make) with_verbose_make='yes' ;; MAX_NUM_THREADS=*) max_num_threads=$( echo $1 | sed -e 's|MAX_NUM_THREADS=||' ) ;; CXX_FLAGS=*) cxx_flags=$( echo $1 | sed -e 's|CXX_FLAGS=||' ) ;; POSTFIX_DIR=*) postfix_dir=$( echo $1 | sed -e 's|POSTFIX_DIR=||' ) ;; ADOLC_DIR=*) adolc_dir=$( echo $1 | sed -e 's|ADOLC_DIR=||' ) ;; FADBAD_DIR=*) fadbad_dir=$( echo $1 | sed -e 's|FADBAD_DIR=||' ) ;; SACADO_DIR=*) sacado_dir=$( echo $1 | sed -e 's|SACADO_DIR=||' ) ;; IPOPT_DIR=*) ipopt_dir=$( echo $1 | sed -e 's|IPOPT_DIR=||' ) ;; TAPE_ADDR_TYPE=*) tape_addr_type=$( echo $1 | sed -e 's|TAPE_ADDR_TYPE=||' ) ;; TAPE_ID_TYPE=*) tape_id_type=$( echo $1 | sed -e 's|TAPE_ID_TYPE=||' ) ;; *) echo "configure: '$1' is not a valid option. Use following to get help:" echo "$0 --help" exit 1 esac shift done if [ "$with_clang" == 'yes' ] && [ "$enable_msvc" == 'yes' ] then echo 'configure: cannot specify both --enable-mcvc and --with-clang' exit 1 fi # ---------------------------------------------------------------------------- # type_makefile if [ "$enable_msvc" == 'yes' ] then type_makefile='NMake Makefiles' else if [[ $(uname -s) =~ ^MSYS_.* ]] then type_makefile='MSYS Makefiles' else type_makefile='Unix Makefiles' fi fi # # cxx_standard_year if echo $cxx_flags | grep 'std=c++' > /dev/null then cxx_standard_year=$(echo $cxx_flags | sed -e 's|.*std=c++\([0-9]*\).*|\1|') print_variable cxx_standard_year if [ "$cxx_standard_year" -lt 17 ] then if [ "$sacado_dir" != 'NOTFOUND' ] then echo 'Sacado requires c++17 or greater' exit 1 fi fi fi # # cmake_cxx_compiler cmake_cxx_compiler='' if [ "$enable_msvc" == 'yes' ] then first_executable icl cl g++ else first_executable g++ clang++ CC pgCC icpc gpp cxx cc++ fi if [ "$with_clang" == 'yes' ] then cmake_cxx_compiler='-D CMAKE_CXX_COMPILER=clang++' elif [ "$first_executable_result" != '' ] then cmake_cxx_compiler="-D CMAKE_CXX_COMPILER=$first_executable_result" fi # # cmake_c_compiler cmake_c_compiler='' if [ "$enable_msvc" == 'yes' ] then first_executable icl cl gcc else first_executable gcc clang cc pgcc icc fi if [ "$with_clang" == 'yes' ] then cmake_c_compiler='-D CMAKE_C_COMPILER=clang' elif [ "$first_executable_result" != '' ] then cmake_c_compiler="-D CMAKE_C_COMPILER=$first_executable_result" fi # # CMAKE_Fortran_COMPILER cmake_fortran_compiler='' if [ "$enable_msvc" == 'yes' ] then first_executable ifort gfortran else first_executable gfortran ifort g95 fort77 f77 g77 pgf90 pgf77 ifc frt af77 fi if [ "$first_executable_result" != '' ] then cmake_fortran_compiler="-D CMAKE_Fortran_COMPILER=$first_executable_result" fi # # source_dir source_dir=$( echo $0 | sed -e 's|/configure$||' ) if [ "$source_dir" == "$0" ] || [ "$source_dir" == '.' ] then echo 'configure: cannot run configure in the current working directory' pwd exit 1 fi PKG_CONFIG_PATH='' # # include_$package, PKG_CONFIG_PATH for package in adolc ipopt do package_dir="${package}_dir" if [ "${!package_dir}" == NOTFOUND ] then declare include_${package}='false' else declare include_${package}='true' pc_file="$package.pc" if [ "$package" == eigen ] then pc_file='eigen3.pc' fi pc_path=$(find ${!package_dir} -name "$pc_file" | head -1) if [ "$pc_path" == '' ] then echo "$package_dir = ${!package_dir}" echo "But cannot find $pc_file below that directory" exit 1 fi pc_dir=$(echo $pc_path | sed -e "s|/$pc_file||") if [ -z ${PKG_CONFIG_PATH+x} ] then export PKG_CONFIG_PATH="$pc_dir" elif [ "$PKG_CONFIG_PATH" == '' ] then export PKG_CONFIG_PATH="$pc_dir" else PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$pc_dir" fi fi print_variable PKG_CONFIG_PATH done # # cppad_cxx_flags cppad_cxx_flags=$( echo " $cxx_flags " | sed -e 's| -g ||' ) # # cmake_install_libdirs cmake_install_libdirs='lib;lib64' if [ -d '/usr/lib64' ] then cmake_install_libdirs='lib64;lib' fi list=' build.ninja CMakeCache.txt CMakeFiles ' for name in $list do if [ -e "$name" ] then echo_eval rm -r $name fi done echo cmake \ -U .+ \ -S "$source_dir" \ -B . \ \ -D CMAKE_VERBOSE_MAKEFILE=$with_verbose_make \ -G "'$type_makefile'" \ \ $cmake_c_compiler \ $cmake_fortran_compiler \ \ -D cppad_prefix="$prefix" \ -D cppad_postfix="$postfix_dir" \ \ -D cmake_install_includedirs=include \ -D cmake_install_libdirs=$cmake_install_libdirs \ \ -D cmake_install_datadir=share \ -D cmake_install_docdir=share/doc \ \ -D include_adolc=$include_adolc \ -D include_ipopt=$include_ipopt \ -D include_cppadcg=false \ \ -D colpack_prefix='NOTFOUND' \ -D fadbad_prefix="$fadbad_dir" \ -D sacado_prefix="$sacado_dir" \ \ -D cppad_cxx_flags="$cxx_flags" \ -D cppad_profile_flag='' \ -D cppad_testvector="$testvector" \ -D cppad_max_num_threads="$max_num_threads" \ -D cppad_tape_id_type="$tape_id_type" \ -D cppad_tape_addr_type="$tape_addr_type" \ -D cppad_debug_which='debug_none' \ -D cppad_debug_and_release='true' # cmake \ -U .+ \ -S "$source_dir" \ -B . \ \ -D CMAKE_VERBOSE_MAKEFILE=$with_verbose_make \ -G "$type_makefile" \ \ $cmake_cxx_compiler \ $cmake_fortran_compiler \ \ -D cppad_prefix="$prefix" \ -D cppad_postfix="$postfix_dir" \ \ -D cmake_install_includedirs=include \ -D cmake_install_libdirs=$cmake_install_libdirs \ \ -D cmake_install_datadir=share \ -D cmake_install_docdir=share/doc \ \ -D include_adolc=$include_adolc \ -D include_ipopt=$include_adolc \ -D include_cppadcg=false \ \ -D colpack_prefix='NOTFOUND' \ -D fadbad_prefix="$fadbad_dir" \ -D sacado_prefix="$sacado_dir" \ \ -D cppad_cxx_flags="$cxx_flags" \ -D cppad_profile_flag='' \ -D cppad_testvector="$testvector" \ -D cppad_max_num_threads="$max_num_threads" \ -D cppad_tape_id_type="$tape_id_type" \ -D cppad_tape_addr_type="$tape_addr_type" \ -D cppad_debug_which='debug_none' \ -D cppad_debug_and_release='true' # ---------------------------------------------------------------------------- echo 'configure: OK' exit 0 ================================================ FILE: cppad_ipopt/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # Initialize list of tests as empty SET(check_cppad_ipopt_depends "") # assert include_ipopt IF ( NOT include_ipopt ) MESSAGE(FATAL_ERROR "include_ipopt is not on the cmake command line" ) ENDIF ( NOT include_ipopt ) # The CMakeLists.txt file in the specified source directory is processed # before the current input file continues beyond this command. # add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL]) ADD_SUBDIRECTORY(src) ADD_SUBDIRECTORY(example) ADD_SUBDIRECTORY(speed) ADD_SUBDIRECTORY(test) # Add the check_cppad_ipopt target ADD_CUSTOM_TARGET(check_cppad_ipopt DEPENDS ${check_cppad_ipopt_depends} ) MESSAGE(STATUS "make check_cppad_ipopt: available") # Change check depends in parent environment add_to_list(check_depends check_cppad_ipopt) SET(check_depends "${check_depends}" PARENT_SCOPE) ================================================ FILE: cppad_ipopt/example/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-23 Bradley M. Bell # ---------------------------------------------------------------------------- # Inherit build type from ../CMakeList.txt # Local include directories to search (not in package_prefix/include) INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../src ) # Local link directories to search (not in external packages) LINK_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR}/../src ${ipopt_LIBRARY_DIRS} ) # add_executable( [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] # source1 source2 ... sourceN # ) # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list example.cpp get_started.cpp ode_check.cpp ode_check.hpp ode_fast.hpp ode_fast_check.cpp ode_problem.hpp ode_run.hpp ode_simple.hpp ode_simple_check.cpp ) # END_SORT_THIS_LINE_MINUS_2 set_compile_flags( cppad_ipopt_example "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE( cppad_ipopt_example EXCLUDE_FROM_ALL ${source_list} ) # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(cppad_ipopt_example cppad_ipopt ${cppad_lib} ${ipopt_LINK_LIBRARIES} ${colpack_libs} ) # # check_cppad_ipopt_example add_check_executable(check_cppad_ipopt example) ================================================ FILE: cppad_ipopt/example/example.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // system include files used for I/O # include // C style asserts # include // CppAD include file # include // test runner # include // external complied tests extern bool ipopt_get_started(void); extern bool ode_simple_check(void); extern bool ode_fast_check(void); // main program that runs all the tests int main(void) { std::string group = "cppad_ipopt/example"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // external compiled tests Run( ipopt_get_started, "ipopt_get_started" ); Run( ode_simple_check, "ode_simple_check" ); Run( ode_fast_check, "ode_fast_check" ); // // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } ================================================ FILE: cppad_ipopt/example/get_started.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ipopt_nlp_get_started.cpp dev} {xrst_spell lc } Nonlinear Programming Using CppAD and Ipopt: Getting Started ############################################################ Purpose ******* This example program demonstrates how to use the class cppad_ipopt_nlp to solve the example problem in the Ipopt documentation; i.e., the problem .. math:: \begin{array}{lc} {\rm minimize \; } & x_1 * x_4 * (x_1 + x_2 + x_3) + x_3 \\ {\rm subject \; to \; } & x_1 * x_2 * x_3 * x_4 \geq 25 \\ & x_1^2 + x_2^2 + x_3^2 + x_4^2 = 40 \\ & 1 \leq x_1, x_2, x_3, x_4 \leq 5 \end{array} Configuration Requirement ************************* This example will be compiled and tested provided that a ``include_ipopt=true`` is specified on the :ref:`cmake-name` command line. {xrst_literal // BEGIN C++ // END C++ } {xrst_end ipopt_nlp_get_started.cpp} */ // BEGIN C++ # include namespace { using namespace cppad_ipopt; class FG_info : public cppad_ipopt_fg_info { private: bool retape_; public: // derived class part of constructor FG_info(bool retape_in) : retape_ (retape_in) { } // Evaluation of the objective f(x), and constraints g(x) // using an Algorithmic Differentiation (AD) class. ADVector eval_r(size_t k, const ADVector& x) { ADVector fg(3); // Fortran style indexing ADNumber x1 = x[0]; ADNumber x2 = x[1]; ADNumber x3 = x[2]; ADNumber x4 = x[3]; // f(x) fg[0] = x1 * x4 * (x1 + x2 + x3) + x3; // g_1 (x) fg[1] = x1 * x2 * x3 * x4; // g_2 (x) fg[2] = x1 * x1 + x2 * x2 + x3 * x3 + x4 * x4; return fg; } bool retape(size_t k) { return retape_; } }; } bool ipopt_get_started(void) { bool ok = true; size_t j; // number of independent variables (domain dimension for f and g) size_t n = 4; // number of constraints (range dimension for g) size_t m = 2; // initial value of the independent variables NumberVector x_i(n); x_i[0] = 1.0; x_i[1] = 5.0; x_i[2] = 5.0; x_i[3] = 1.0; // lower and upper limits for x NumberVector x_l(n); NumberVector x_u(n); for(j = 0; j < n; j++) { x_l[j] = 1.0; x_u[j] = 5.0; } // lower and upper limits for g NumberVector g_l(m); NumberVector g_u(m); g_l[0] = 25.0; g_u[0] = 1.0e19; g_l[1] = 40.0; g_u[1] = 40.0; size_t icase; for(icase = 0; icase <= 1; icase++) { // Should cppad_ipopt_nlp retape the operation sequence for // every new x. Can test both true and false cases because // the operation sequence does not depend on x (for this case). bool retape = icase != 0; // object in derived class FG_info fg_info(retape); // create the Ipopt interface cppad_ipopt_solution solution; Ipopt::SmartPtr cppad_nlp = new cppad_ipopt_nlp( n, m, x_i, x_l, x_u, g_l, g_u, &fg_info, &solution ); // Create an instance of the IpoptApplication using Ipopt::IpoptApplication; Ipopt::SmartPtr app = new IpoptApplication(); // turn off any printing app->Options()->SetIntegerValue("print_level", 0); app->Options()->SetStringValue("sb", "yes"); // maximum number of iterations app->Options()->SetIntegerValue("max_iter", 10); // approximate accuracy in first order necessary conditions; // see Mathematical Programming, Volume 106, Number 1, // Pages 25-57, Equation (6) app->Options()->SetNumericValue("tol", 1e-9); // derivative testing app->Options()-> SetStringValue("derivative_test", "second-order"); app->Options()-> SetNumericValue( "point_perturbation_radius", 0. ); // Initialize the IpoptApplication and process the options Ipopt::ApplicationReturnStatus status = app->Initialize(); ok &= status == Ipopt::Solve_Succeeded; // Run the IpoptApplication status = app->OptimizeTNLP(cppad_nlp); ok &= status == Ipopt::Solve_Succeeded; /* Check some of the solution values */ ok &= solution.status == cppad_ipopt_solution::success; // double check_x[] = { 1.000000, 4.743000, 3.82115, 1.379408 }; double check_z_l[] = { 1.087871, 0., 0., 0. }; double check_z_u[] = { 0., 0., 0., 0. }; double rel_tol = 1e-6; // relative tolerance double abs_tol = 1e-6; // absolute tolerance for(j = 0; j < n; j++) { ok &= CppAD::NearEqual( check_x[j], solution.x[j], rel_tol, abs_tol ); ok &= CppAD::NearEqual( check_z_l[j], solution.z_l[j], rel_tol, abs_tol ); ok &= CppAD::NearEqual( check_z_u[j], solution.z_u[j], rel_tol, abs_tol ); } } return ok; } // END C++ ================================================ FILE: cppad_ipopt/example/ode1.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin ipopt_nlp_ode dev} Example Simultaneous Solution of Forward and Inverse Problem ############################################################ Contents ******** {xrst_toc_list cppad_ipopt/example/get_started.cpp cppad_ipopt/example/ode2.xrst cppad_ipopt/example/ode_run.hpp cppad_ipopt/example/ode_check.cpp cppad_ipopt/speed/ode_speed.cpp } {xrst_end ipopt_nlp_ode} ================================================ FILE: cppad_ipopt/example/ode2.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin ipopt_nlp_ode_problem dev} {xrst_spell ny nz } An ODE Inverse Problem Example ############################## Notation ******** The table below contains the name of a variable, the meaning of the variable value, and the value for this particular example. If the value is not specified in the table below, the corresponding value in ``ipopt_nlp_ode_problem.hpp`` can be changed and the example should still run (with no other changes). .. csv-table:: :widths: auto **Name**,**Meaning**,**Value** :math:`Na`,number of parameters to fit,3 :math:`Ny`,number components in ODE,2 :math:`Nz`,number of measurements,4 :math:`N(i)`,# of grid points between *i-1*-th and *i*-th measurement, :math:`S(i)`,# of grid points up to an including *i*-th measurement, Forward Problem *************** We consider the following ordinary differential equation: .. math:: :nowrap: \begin{eqnarray} \partial_t y_0 ( t , a ) & = & - a_1 * y_0 (t, a ) \\ \partial_t y_1 (t , a ) & = & + a_1 * y_0 (t, a ) - a_2 * y_1 (t, a ) \end{eqnarray} with the initial conditions .. math:: y_0 (0 , a) = F(a) = \left( \begin{array}{c} a_0 \\ 0 \end{array} \right) where :math:`Na` is the number of parameters, :math:`a \in \B{R}^{Na}` is an unknown parameter vector. The function and :math:`F : \B{R}^{Na} \rightarrow \B{R}^{Ny}` is defined by the equation above where :math:`Ny` is the number of components in :math:`y(t, a)`. Our forward problem is stated as follows: Given :math:`a \in \B{R}^{Na}` determine the value of :math:`y ( t , a )`, for :math:`t \in R`, that solves the initial value problem above. Measurements ************ We use :math:`Nz` to denote the number of measurements. Suppose we are also given a measurement vector :math:`z \in \B{R}^{Nz}` and for :math:`i = 1, \ldots, Nz`, we model :math:`z_i` by .. math:: z_i = y_1 ( s_i , a) + e_i where :math:`s_i \in \B{R}` is the time for the *i*-th measurement, :math:`e_i \sim {\bf N} (0 , \sigma^2 )` is the corresponding noise, and :math:`\sigma \in \B{R}_+` is the corresponding standard deviation. Simulation Analytic Solution ============================ The following analytic solution to the forward problem is used to simulate a data set: .. math:: :nowrap: \begin{eqnarray} y_0 (t , a) & = & a_0 * \exp( - a_1 * t ) \\ y_1 (t , a) & = & a_0 * a_1 * \frac{\exp( - a_2 * t ) - \exp( -a_1 * t )}{ a_1 - a_2 } \end{eqnarray} Simulation Parameter Values =========================== .. list-table:: :widths: auto * - :math:`\bar{a}_0 = 1` - initial value of :math:`y_0 (t, a)` * - :math:`\bar{a}_1 = 2` - transfer rate from compartment zero to compartment one * - :math:`\bar{a}_2 = 1` - transfer rate from compartment one to outside world * - :math:`\sigma = 0` - standard deviation of measurement noise * - :math:`e_i = 0` - simulated measurement noise, :math:`i = 1 , \ldots , Nz` * - :math:`s_i = i * .5` - time corresponding to the *i*-th measurement, :math:`i = 1 , \ldots , Nz` Simulated Measurement Values ============================ The simulated measurement values are given by the equation .. math:: :nowrap: \begin{eqnarray} z_i & = & e_i + y_1 ( s_i , \bar{a} ) \\ & = & e_i + \bar{a}_0 * \bar{a}_1 * \frac{\exp( - \bar{a}_2 * s_i ) - \exp( -\bar{a}_1 * s_i )} { \bar{a}_1 - \bar{a}_2 } \end{eqnarray} for :math:`k = 1, \ldots , Nz`. Inverse Problem *************** The maximum likelihood estimate for :math:`a` given :math:`z` solves the following inverse problem .. math:: :nowrap: \begin{eqnarray} {\rm minimize} \; & \sum_{i=1}^{Nz} H_i [ y( s_i , a ) , a ] & \;{\rm w.r.t} \; a \in \B{R}^{Na} \end{eqnarray} where the functions :math:`H_i : \B{R}^{Ny} \times \B{R}^{Na} \rightarrow \B{R}` is defined by .. math:: H_i (y, a) = ( z_i - y_1 )^2 Trapezoidal Approximation ************************* This example uses a trapezoidal approximation to solve the ODE. This approximation procedures starts with .. math:: y^0 = y(0, a) = \left( \begin{array}{c} a_0 \\ 0 \end{array} \right) Given a time grid :math:`\{ t_i \}` and an approximate value :math:`y^{i-1}` for :math:`y ( t_{i-1} , a )`, the a trapezoidal method approximates :math:`y ( t_i , a )` (denoted by :math:`y^i` ) by solving the equation .. math:: y^i = y^{i-1} + \left[ G( y^i , a ) + G( y^{i-1} , a ) \right] * \frac{t_i - t_{i-1} }{ 2 } where :math:`G : \B{R}^{Ny} \times \B{R}^{Na} \rightarrow \B{R}^{Ny}` is the function representing this ODE; i.e. .. math:: G(y, a) = \left( \begin{array}{c} - a_1 * y_0 \\ + a_1 * y_0 - a_2 * y_1 \end{array} \right) This :math:`G(y, a)` is linear with respect to :math:`y`, hence the implicit equation defining :math:`y^i` can be solved inverting the a set of linear equations. In the general case, where :math:`G(y, a)` is non-linear with respect to :math:`y`, an iterative procedure is used to calculate :math:`y^i` from :math:`y^{i-1}`. Trapezoidal Time Grid ===================== The discrete time grid, used for the trapezoidal approximation, is denoted by :math:`\{ t_i \}` which is defined by: :math:`t_0 = 0` and for :math:`i = 1 , \ldots , Nz` and for :math:`j = 1 , \ldots , N(i)`, .. math:: :nowrap: \begin{eqnarray} \Delta t_i & = & ( s_i - s_{i-1} ) / N(i) \\ t_{S(i-1)+j} & = & s_{i-1} + \Delta t_i * j \end{eqnarray} where :math:`s_0 = 0`, :math:`N(i)` is the number of time grid points between :math:`s_{i-1}` and :math:`s_i`, :math:`S(0) = 0`, and :math:`S(i) = N(1) + \ldots + N(i)`. Note that for :math:`i = 0 , \ldots , S(Nz)`, :math:`y^i` denotes our approximation for :math:`y( t_i , a )` and :math:`t_{S(i)}` is equal to :math:`s_i`. Black Box Method **************** A common approach to an inverse problem is to treat the forward problem as a black box (that we do not look inside of or try to understand). In this approach, for each value of the parameter vector :math:`a` one uses the :ref:`ipopt_nlp_ode_problem@Trapezoidal Approximation` (on a finer grid that :math:`\{ s_i \}`) to solve for :math:`y_1 ( s_i , a )` for :math:`i = 1 , \ldots , Nz`. Two levels of Iteration ======================= As noted above, the trapezoidal approximation often requires an iterative procedure. Thus, in this approach, there are two levels of iterations, one with respect to the parameter values during the minimization and the other for solving the trapezoidal approximation equation. Derivatives =========== In addition, in the black box approach, differentiating the ODE solution often involves differentiating an iterative procedure. Direct application of AD to compute these derivatives requires a huge amount of memory and calculations to differentiate the iterative procedure. (There are special techniques for applying AD to the solutions of iterative procedures, but that is outside the scope of this presentation). Simultaneous Method ******************* The simultaneous forward and inverse method uses constraints to include the solution of the forward problem in the inverse problem. To be specific for our example, .. math:: :nowrap: \begin{eqnarray} {\rm minimize} & \sum_{i=1}^{Nz} H_i ( y^{N(i)} , a ) & \; {\rm w.r.t} \; y^1 \in \B{R}^{Ny} , \ldots , y^{S(Nz)} \in \B{R}^{Ny} , \; a \in \B{R}^{Na} \\ {\rm subject \; to} & y^j = y^{j-1} + \left[ G( y^{j-1} , a ) + G( y^j , a ) \right] * \frac{ t_j - t_{j-1} }{ 2 } & \; {\rm for} \; j = 1 , \ldots , S(Nz) \\ & y^0 = F(a) \end{eqnarray} where for :math:`i = 1, \ldots , Nz`, :math:`N(i)` is the number of time intervals between :math:`s_{i-1}` and :math:`s_i` (with :math:`s_0 = 0`) and :math:`S(i) = N(1) + \ldots + N(i)`. Note that, in this form, the iterations of the optimization procedure also solve the forward problem equations. In addition, the functions that need to be differentiated do not involve an iterative procedure. {xrst_toc_hidden cppad_ipopt/example/ode_problem.hpp } Source ****** The file ``ipopt_nlp_ode_problem.hpp`` contains source code that defines the example values and functions defined above. {xrst_end ipopt_nlp_ode_problem} ----------------------------------------------------------------------------- {xrst_begin ipopt_nlp_ode_simple dev} {xrst_spell fg ny nz } ODE Fitting Using Simple Representation ####################################### Purpose ******* In this section we represent the objective and constraint functions, (in the simultaneous forward and reverse optimization problem) using the :ref:`cppad_ipopt_nlp@Simple Representation` in the sense of ``cppad_ipopt_nlp`` . Argument Vector *************** The argument vector that we are optimizing with respect to ( :math:`x` in :ref:`cppad_ipopt_nlp-name` ) has the following structure .. math:: x = ( y^0 , \cdots , y^{S(Nz)} , a ) Note that :math:`x \in \B{R}^{S(Nz) + Na}` and .. math:: :nowrap: \begin{eqnarray} y^i & = & ( x_{Ny * i} , \ldots , x_{Ny * i + Ny - 1} ) \\ a & = & ( x_{Ny *S(Nz) + Ny} , \ldots , x_{Ny * S(Nz) + Na - 1} ) \end{eqnarray} Objective Function ****************** The objective function ( :math:`fg_0 (x)` in :ref:`cppad_ipopt_nlp-name` ) has the following representation, .. math:: fg_0 (x) = \sum_{i=1}^{Nz} H_i ( y^{S(i)} , a ) Initial Condition Constraint **************************** For :math:`i = 1 , \ldots , Ny`, we define the component functions :math:`fg_i (x)`, and corresponding constraint equations, by .. math:: 0 = fg_i ( x ) = y_i^0 - F_i (a) Trapezoidal Approximation Constraint ************************************ For :math:`i = 1, \ldots , S(Nz)`, and for :math:`j = 1 , \ldots , Ny`, we define the component functions :math:`fg_{Ny*i + j} (x)`, and corresponding constraint equations, by .. math:: 0 = fg_{Ny*i + j } = y_j^{i} - y_j^{i-1} - \left[ G_j ( y^i , a ) + G_j ( y^{i-1} , a ) \right] * \frac{t_i - t_{i-1} }{ 2 } {xrst_toc_hidden cppad_ipopt/example/ode_simple.hpp } Source ****** The file ``ipopt_nlp_ode_simple.hpp`` contains source code for this representation of the objective and constraints. {xrst_end ipopt_nlp_ode_simple} ----------------------------------------------------------------------------- {xrst_begin ipopt_nlp_ode_fast dev} {xrst_spell fg ny nz } ODE Fitting Using Fast Representation ##################################### Purpose ******* In this section we represent a more complex representation of the simultaneous forward and reverse ODE fitting problem (described above). The representation defines the problem using simpler functions that are faster to differentiate (either by hand coding or by using AD). Objective Function ****************** We use the following representation for the :ref:`ipopt_nlp_ode_simple@Objective Function` : For :math:`k = 0 , \ldots , Nz - 1`, we define the function :math:`r^k : \B{R}^{Ny+Na} \rightarrow \B{R}` by .. math:: :nowrap: \begin{eqnarray} fg_0 (x) & = & \sum_{i=1}^{Nz} H_i ( y^{S(i)} , a ) \\ fg_0 (x) & = & \sum_{k=0}^{Nz-1} r^k ( u^{k,0} ) \end{eqnarray} where for :math:`k = 0 , \ldots , Nz-1`, :math:`u^{k,0} \in \B{R}^{Ny + Na}` is defined by :math:`u^{k,0} = ( y^{S(k+1)} , a )` Range Indices I(k,0) ==================== For :math:`k = 0 , \ldots , Nz - 1`, the range index in the vector :math:`fg (x)` corresponding to :math:`r^k ( u^{k,0} )` is 0. Thus, the range indices are given by :math:`I(k,0) = \{ 0 \}` for :math:`k = 0 , \ldots , Nz-1`. Domain Indices J(k,0) ===================== For :math:`k = 0 , \ldots , Nz - 1`, the components of the vector :math:`x` corresponding to the vector :math:`u^{k,0}` are .. math:: :nowrap: \begin{eqnarray} u^{k,0} & = & ( y^{S(k+1} , a ) \\ & = & ( x_{Ny * S(k+1)} \; , \; \ldots \; , \; x_{Ny * S(k+1) + Ny - 1} \; , \; x_{Ny * S(Nz) + Ny } \; , \; \ldots \; , \; x_{Ny * S(Nz) + Ny + Na - 1} ) \end{eqnarray} Thus, the domain indices are given by .. math:: J(k,0) = \{ Ny * S(k+1) \; , \; \ldots \; , \; Ny * S(k+1) + Ny - 1 \; , \; Ny * S(Nz) + Ny \; , \; \ldots \; , \; Ny * S(Nz) + Ny + Na - 1 \} Initial Condition ***************** We use the following representation for the :ref:`ipopt_nlp_ode_simple@Initial Condition Constraint` : For :math:`k = Nz` we define the function :math:`r^k : \B{R}^{Ny} \times \B{R}^{Na + Ny}` by .. math:: :nowrap: \begin{eqnarray} 0 & = & fg_i ( x ) = y_i^0 - F_i (a) \\ 0 & = & r_{i-1}^k ( u^{k,0} ) = y_i^0 - F_i(a) \end{eqnarray} where :math:`i = 1 , \ldots , Ny` and where :math:`u^{k,0} \in \B{R}^{Ny + Na}` is defined by :math:`u^{k,0} = ( y^0 , a)`. Range Indices I(k,0) ==================== For :math:`k = Nz`, the range index in the vector :math:`fg (x)` corresponding to :math:`r^k ( u^{k,0} )` are :math:`I(k,0) = \{ 1 , \ldots , Ny \}`. Domain Indices J(k,0) ===================== For :math:`k = Nz`, the components of the vector :math:`x` corresponding to the vector :math:`u^{k,0}` are .. math:: :nowrap: \begin{eqnarray} u^{k,0} & = & ( y^0 , a) \\ & = & ( x_0 \; , \; \ldots \; , \; x_{Ny-1} \; , \; x_{Ny * S(Nz) + Ny } \; , \; \ldots \; , \; x_{Ny * S(Nz) + Ny + Na - 1} ) \end{eqnarray} Thus, the domain indices are given by .. math:: J(k,0) = \{ 0 \; , \; \ldots \; , \; Ny - 1 \; , \; Ny * S(Nz) + Ny \; , \; \ldots \; , \; Ny * S(Nz) + Ny + Na - 1 \} Trapezoidal Approximation ************************* We use the following representation for the :ref:`ipopt_nlp_ode_simple@Trapezoidal Approximation Constraint` : For :math:`k = 1 , \ldots , Nz`, we define the function :math:`r^{Nz+k} : \B{R}^{2*Ny+Na} \rightarrow \B{R}^{Ny}` by .. math:: r^{Nz+k} ( y , w , a ) = y - w - [ G( y , a ) + G( w , a ) ] * \frac{ \Delta t_k }{ 2 } For :math:`\ell = 0 , \ldots , N(k)-1`, using the notation :math:`i = Ny * S(k-1) + \ell + 1`, the corresponding trapezoidal approximation is represented by .. math:: :nowrap: \begin{eqnarray} 0 & = & fg_{Ny+i} (x) \\ & = & y^i - y^{i-1} - \left[ G( y^i , a ) + G( y^{i-1} , a ) \right] * \frac{\Delta t_k }{ 2 } \\ & = & r^{Nz+k} ( u^{Nz+k , \ell} ) \end{eqnarray} where :math:`u^{Nz+k,\ell} \in \B{R}^{2*Ny + Na}` is defined by :math:`u^{Nz+k,\ell} = ( y^{i-1} , y^i , a)`. Range Indices I(k,0) ==================== For :math:`k = Nz + 1, \ldots , 2*Nz`, and :math:`\ell = 0 , \ldots , N(k)-1`, the range index in the vector :math:`fg (x)` corresponding to :math:`r^k ( u^{k,\ell} )` are :math:`I(k,\ell) = \{ Ny + i , \ldots , 2*Ny + i - 1 \}` where :math:`i = Ny * S(k-1) + \ell + 1`. Domain Indices J(k,0) ===================== For :math:`k = Nz + 1, \ldots , 2*Nz`, and :math:`\ell = 0 , \ldots , N(k)-1`, define :math:`i = Ny * S(k-1) + \ell + 1`. The components of the vector :math:`x` corresponding to the vector :math:`u^{k,\ell}` are (and the function :math:`fg (x)` in :ref:`cppad_ipopt_nlp-name` ) .. math:: :nowrap: \begin{eqnarray} u^{k, \ell} & = & ( y^{i-1} , y^i , a ) \\ & = & ( x_{Ny * (i-1)} \; , \; \ldots \; , \; x_{Ny * (i+1) - 1} \; , \; x_{Ny * S(Nz) + Ny } \; , \; \ldots \; , \; x_{Ny * S(Nz) + Ny + Na - 1} ) \end{eqnarray} Thus, the domain indices are given by .. math:: J(k,\ell) = \{ Ny * (i-1) \; , \; \ldots \; , \; Ny * (i+1) - 1 \; , \; Ny * S(Nz) + Ny \; , \; \ldots \; , \; Ny * S(Nz) + Ny + Na - 1 \} {xrst_toc_hidden cppad_ipopt/example/ode_fast.hpp } Source ****** The file ``ipopt_nlp_ode_fast.hpp`` contains source code for this representation of the objective and constraints. {xrst_end ipopt_nlp_ode_fast} ------------------------------------------------------------------------------ ================================================ FILE: cppad_ipopt/example/ode_check.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ipopt_nlp_ode_check.cpp dev} Correctness Check for Both Simple and Fast Representations ########################################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end ipopt_nlp_ode_check.cpp} */ // BEGIN C++ # include "ode_run.hpp" bool ode_check(const SizeVector& N, const NumberVector& x) { bool ok = true; size_t i, j; // number of components of x corresponding to values for y size_t ny_inx = x.size() - Na; // compute the partial sums of the number of grid points // and the maximum step size for the trapezoidal approximation SizeVector S(Nz+1); S[0] = 0; Number max_step = 0.; for(i = 1; i <= Nz; i++) { S[i] = S[i-1] + N[i]; max_step = std::max(max_step, Number(s[i] - s[i-1]) / Number(N[i]) ); } // split out return values NumberVector a(Na), y_0(Ny), y_1(Ny), y_2(Ny); for(j = 0; j < Na; j++) a[j] = x[ny_inx+j]; for(j = 0; j < Ny; j++) { y_0[j] = x[j]; y_1[j] = x[Ny + j]; y_2[j] = x[2 * Ny + j]; } // Check some of the optimal a value Number rel_tol = max_step * max_step; Number abs_tol = rel_tol; Number check_a[] = {a0, a1, a2}; // see the y_one function for(j = 0; j < Na; j++) { ok &= CppAD::NearEqual( check_a[j], a[j], rel_tol, abs_tol ); } // check accuracy of constraint equations rel_tol = 1e-9; abs_tol = 1e-9; // check the initial value constraint NumberVector F = eval_F(a); for(j = 0; j < Ny; j++) ok &= CppAD::NearEqual(F[j], y_0[j], rel_tol, abs_tol); // check the first trapezoidal equation NumberVector G_0 = eval_G(y_0, a); NumberVector G_1 = eval_G(y_1, a); Number dt = (s[1] - s[0]) / Number(N[1]); Number check; for(j = 0; j < Ny; j++) { check = y_1[j] - y_0[j] - (G_1[j]+G_0[j])*dt/2; ok &= CppAD::NearEqual( check, 0., rel_tol, abs_tol); } // // check the second trapezoidal equation NumberVector G_2 = eval_G(y_2, a); if( N[1] == 1 ) dt = (s[2] - s[1]) / Number(N[2]); for(j = 0; j < Ny; j++) { check = y_2[j] - y_1[j] - (G_2[j]+G_1[j])*dt/2; ok &= CppAD::NearEqual( check, 0., rel_tol, abs_tol); } // // check the objective function (specialized to this case) check = 0.; NumberVector y_i(Ny); for(size_t k = 0; k < Nz; k++) { for(j = 0; j < Ny; j++) y_i[j] = x[S[k+1] * Ny + j]; check += eval_H(k + 1, y_i, a); } Number obj_value = 0.; // optimal object (no noise in simulation) ok &= CppAD::NearEqual(check, obj_value, rel_tol, abs_tol); // Use this empty namespace function to avoid warning that it is not used static size_t ode_check_count = 0; ode_check_count++; ok &= count_eval_r() == ode_check_count; return ok; } // END C++ ================================================ FILE: cppad_ipopt/example/ode_check.hpp ================================================ # ifndef CPPAD_CPPAD_IPOPT_EXAMPLE_ODE_CHECK_HPP # define CPPAD_CPPAD_IPOPT_EXAMPLE_ODE_CHECK_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- extern bool ode_check(const SizeVector& N, const NumberVector& x); # endif ================================================ FILE: cppad_ipopt/example/ode_fast.hpp ================================================ # ifndef CPPAD_CPPAD_IPOPT_EXAMPLE_ODE_FAST_HPP # define CPPAD_CPPAD_IPOPT_EXAMPLE_ODE_FAST_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ipopt_nlp_ode_fast.hpp dev} ODE Fitting Using Fast Representation ##################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end ipopt_nlp_ode_fast.hpp} */ // BEGIN C++ # include "ode_problem.hpp" namespace { using namespace cppad_ipopt; class FG_fast : public cppad_ipopt_fg_info { private: bool retape_; SizeVector N_; SizeVector S_; public: // derived class part of constructor FG_fast(bool retape_in, const SizeVector& N) : retape_ (retape_in), N_(N) { assert( N_[0] == 0 ); S_.resize( N_.size() ); S_[0] = 0; for(size_t i = 1; i < N_.size(); i++) S_[i] = S_[i-1] + N_[i]; } // r^k for k = 0, 1, ..., Nz-1 used for measurements // r^k for k = Nz use for initial condition // r^k for k = Nz+1, ..., 2*Nz used for trapezoidal approx size_t number_functions(void) { return Nz + 1 + Nz; } ADVector eval_r(size_t k, const ADVector &u) { count_eval_r(); size_t j; ADVector y(Ny), a(Na); // objective function -------------------------------- if( k < Nz ) { // used for measurement with index k+1 ADVector r(1); // return value is a scalar // u is [y( s[k+1] ) , a] for(j = 0; j < Ny; j++) y[j] = u[j]; for(j = 0; j < Na; j++) a[j] = u[Ny + j]; r[0] = eval_H(k+1, y, a); return r; } // initial condition --------------------------------- if( k == Nz ) { ADVector r(Ny), F(Ny); // u is [y(t), a] at t = 0 for(j = 0; j < Ny; j++) y[j] = u[j]; for(j = 0; j < Na; j++) a[j] = u[Ny + j]; F = eval_F(a); for(j = 0; j < Ny; j++) r[j] = y[j] - F[j]; return r; } // trapezoidal approximation ------------------------- ADVector ym(Ny), G(Ny), Gm(Ny), r(Ny); // r^k for k = Nz+1, ... , 2*Nz // interval between data samples Number T = s[k-Nz] - s[k-Nz-1]; // integration step size Number dt = T / Number( N_[k-Nz] ); // u = [ y(t[i-1], a) , y(t[i], a), a ) for(j = 0; j < Ny; j++) { ym[j] = u[j]; y[j] = u[Ny + j]; } for(j = 0; j < Na; j++) a[j] = u[2 * Ny + j]; Gm = eval_G(ym, a); G = eval_G(y, a); for(j = 0; j < Ny; j++) r[j] = y[j] - ym[j] - (G[j] + Gm[j]) * dt / 2.; return r; } // The operations sequence for r_eval does not depend on u, // hence retape = false should work and be faster. bool retape(size_t k) { return retape_; } // size of the vector u in eval_r size_t domain_size(size_t k) { if( k < Nz ) return Ny + Na; // objective function if( k == Nz ) return Ny + Na; // initial value constraint return 2 * Ny + Na; // trapezodial constraints } // size of the return value from eval_r size_t range_size(size_t k) { if( k < Nz ) return 1; return Ny; } // number of terms that use this value of k size_t number_terms(size_t k) { if( k <= Nz ) return 1; // r^k used once for k <= Nz // r^k used N_[k-Nz] times for k > Nz return N_[k-Nz]; } void index(size_t k, size_t ell, SizeVector& I, SizeVector& J) { size_t i, j; // # of components of x corresponding to values for y size_t ny_inx = (S_[Nz] + 1) * Ny; // objective function ------------------------------- if( k < Nz ) { // index in fg corresponding to objective I[0] = 0; // u = [ y(t, a) , a ] // The first Ny components of u is y(t) at // t = s[k+1] = t[S_[k+1]] // x indices corresponding to this value of y for(j = 0; j < Ny; j++) J[j] = S_[k + 1] * Ny + j; // components of x corresponding to a for(j = 0; j < Na; j++) J[Ny + j] = ny_inx + j; return; } // initial conditions -------------------------------- if( k == Nz ) { // index in fg for inidial condition constraint for(j = 0; j < Ny; j++) I[j] = 1 + j; // u = [ y(t, a) , a ] where t = 0 // x indices corresponding to this value of y for(j = 0; j < Ny; j++) J[j] = j; // following that, u contains the vector a for(j = 0; j < Na; j++) J[Ny + j] = ny_inx + j; return; } // trapoziodal approximation ------------------------- // index of first grid point in this approximation i = S_[k - Nz - 1] + ell; // There are Ny difference equations for each time // point. Add one for the objective function, and Ny // for the initial value constraints. for(j = 0; j < Ny; j++) I[j] = 1 + Ny + i * Ny + j; // u = [ y(t, a) , y(t+dt, a) , a ] at t = t[i] for(j = 0; j < Ny; j++) { J[j] = i * Ny + j; // y^i indices J[Ny + j] = J[j] + Ny; // y^{i+1} indices } for(j = 0; j < Na; j++) J[2 * Ny + j] = ny_inx + j; // a indices } }; } // END C++ # endif ================================================ FILE: cppad_ipopt/example/ode_fast_check.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include "ode_run.hpp" # include "ode_fast.hpp" # include "ode_check.hpp" bool ode_fast_check(void) { bool ok = true; bool retape; size_t i; // solution vector NumberVector x; // number of time grid intervals between measurement values SizeVector N(Nz + 1); N[0] = 0; for(i = 1; i <= Nz; i++) N[i] = 5; for(i = 0; i < 2; i++) { retape = bool(i); ipopt_ode_case(retape, N, x); ok &= ode_check(N, x); } return ok; } ================================================ FILE: cppad_ipopt/example/ode_problem.hpp ================================================ # ifndef CPPAD_CPPAD_IPOPT_EXAMPLE_ODE_PROBLEM_HPP # define CPPAD_CPPAD_IPOPT_EXAMPLE_ODE_PROBLEM_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ipopt_nlp_ode_problem.hpp dev} ODE Inverse Problem Definitions: Source Code ############################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end ipopt_nlp_ode_problem.hpp} ------------------------------------------------------------------------------ */ // BEGIN C++ # include "../src/cppad_ipopt_nlp.hpp" namespace { //------------------------------------------------------------------ typedef Ipopt::Number Number; Number a0 = 1.; // simulation value for a[0] Number a1 = 2.; // simulation value for a[1] Number a2 = 1.; // simulatioln value for a[2] // function used to simulate data Number y_one(Number t) { Number y_1 = a0*a1 * (exp(-a2*t) - exp(-a1*t)) / (a1 - a2); return y_1; } // time points were we have data (no data at first point) double s[] = { 0.0, 0.5, 1.0, 1.5, 2.0 }; // Simulated data for case with no noise (first point is not used) double z[] = { 0.0, y_one(0.5), y_one(1.0), y_one(1.5), y_one(2.0) }; // Number of measurement values size_t Nz = sizeof(z) / sizeof(z[0]) - 1; // Number of components in the function y(t, a) size_t Ny = 2; // Number of components in the vectro a size_t Na = 3; // Initial Condition function, F(a) = y(t, a) at t = 0 // (for this particular example) template Vector eval_F(const Vector &a) { Vector F(Ny); // y_0 (t) = a[0]*exp(-a[1] * t) F[0] = a[0]; // y_1 (t) = // a[0]*a[1]*(exp(-a[2] * t) - exp(-a[1] * t))/(a[1] - a[2]) F[1] = 0.; return F; } // G(y, a) = \partial_t y(t, a); i.e. the differential equation // (for this particular example) template Vector eval_G(const Vector &y , const Vector &a) { Vector G(Ny); // y_0 (t) = a[0]*exp(-a[1] * t) G[0] = -a[1] * y[0]; // y_1 (t) = // a[0]*a[1]*(exp(-a[2] * t) - exp(-a[1] * t))/(a[1] - a[2]) G[1] = +a[1] * y[0] - a[2] * y[1]; return G; } // H(i, y, a) = contribution to objective at i-th data point // (for this particular example) template Scalar eval_H(size_t i, const Vector &y, const Vector &a) { // This particular H is for a case where y_1 (t) is measured Scalar diff = z[i] - y[1]; return diff * diff; } // function used to count the number of calls to eval_r size_t count_eval_r(void) { static size_t count = 0; ++count; return count; } } // END C++ # endif ================================================ FILE: cppad_ipopt/example/ode_run.hpp ================================================ # ifndef CPPAD_CPPAD_IPOPT_EXAMPLE_ODE_RUN_HPP # define CPPAD_CPPAD_IPOPT_EXAMPLE_ODE_RUN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ipopt_nlp_ode_run.hpp dev} Driver for Running the Ipopt ODE Example ######################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end ipopt_nlp_ode_run.hpp} */ // BEGIN C++ # include "ode_problem.hpp" namespace { // BEGIN empty namespace ----------------------------------------- using namespace cppad_ipopt; template void ipopt_ode_case( bool retape , const SizeVector& N , NumberVector& x ) { size_t i, j; // compute the partial sums of the number of grid points assert( N.size() == Nz + 1); assert( N[0] == 0 ); SizeVector S(Nz+1); S[0] = 0; for(i = 1; i <= Nz; i++) S[i] = S[i-1] + N[i]; // number of components of x corresponding to values for y size_t ny_inx = (S[Nz] + 1) * Ny; // number of constraints (range dimension of g) size_t m = ny_inx; // number of components in x (domain dimension for f and g) size_t n = ny_inx + Na; // the argument vector for the optimization is // y(t) at t[0] , ... , t[S[Nz]] , followed by a NumberVector x_i(n), x_l(n), x_u(n); for(j = 0; j < ny_inx; j++) { x_i[j] = 0.; // initial y(t) for optimization x_l[j] = -1.0e19; // no lower limit x_u[j] = +1.0e19; // no upper limit } for(j = 0; j < Na; j++) { x_i[ny_inx + j ] = .5; // initiali a for optimization x_l[ny_inx + j ] = -1.e19; // no lower limit x_u[ny_inx + j ] = +1.e19; // no upper } // all of the difference equations are constrained to the value zero NumberVector g_l(m), g_u(m); for(i = 0; i < m; i++) { g_l[i] = 0.; g_u[i] = 0.; } // object defining the objective f(x) and constraints g(x) FG_info fg_info(retape, N); // create the CppAD Ipopt interface cppad_ipopt_solution solution; Ipopt::SmartPtr cppad_nlp = new cppad_ipopt_nlp( n, m, x_i, x_l, x_u, g_l, g_u, &fg_info, &solution ); // Create an Ipopt application using Ipopt::IpoptApplication; Ipopt::SmartPtr app = new IpoptApplication(); // turn off any printing app->Options()->SetIntegerValue("print_level", 0); app->Options()->SetStringValue("sb", "yes"); // maximum number of iterations app->Options()->SetIntegerValue("max_iter", 30); // approximate accuracy in first order necessary conditions; // see Mathematical Programming, Volume 106, Number 1, // Pages 25-57, Equation (6) app->Options()->SetNumericValue("tol", 1e-9); // Derivative testing is very slow for large problems // so comment this out if you use a large value for N[]. app->Options()-> SetStringValue( "derivative_test", "second-order"); app->Options()-> SetNumericValue( "point_perturbation_radius", 0.); # ifndef NDEBUG bool ok = true; // // Initialize the application and process the options Ipopt::ApplicationReturnStatus status = app->Initialize(); ok &= status == Ipopt::Solve_Succeeded; // // Run the application status = app->OptimizeTNLP(cppad_nlp); ok &= status == Ipopt::Solve_Succeeded; // assert(ok); # else app->Initialize(); app->OptimizeTNLP(cppad_nlp); # endif // return the solution x.resize( solution.x.size() ); for(j = 0; j < x.size(); j++) x[j] = solution.x[j]; return; } } // END empty namespace ---------------------------------------------------- // END C++ # endif ================================================ FILE: cppad_ipopt/example/ode_simple.hpp ================================================ # ifndef CPPAD_CPPAD_IPOPT_EXAMPLE_ODE_SIMPLE_HPP # define CPPAD_CPPAD_IPOPT_EXAMPLE_ODE_SIMPLE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ipopt_nlp_ode_simple.hpp dev} ODE Fitting Using Simple Representation ####################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end ipopt_nlp_ode_simple.hpp} */ // BEGIN C++ # include "ode_problem.hpp" // define in the empty namespace namespace { using namespace cppad_ipopt; class FG_simple : public cppad_ipopt_fg_info { private: bool retape_; SizeVector N_; SizeVector S_; public: // derived class part of constructor FG_simple(bool retape_in, const SizeVector& N) : retape_ (retape_in), N_(N) { assert( N_[0] == 0 ); S_.resize( N.size() ); S_[0] = 0; for(size_t i = 1; i < N_.size(); i++) S_[i] = S_[i-1] + N_[i]; } // Evaluation of the objective f(x), and constraints g(x) // using an Algorithmic Differentiation (AD) class. ADVector eval_r(size_t not_used, const ADVector& x) { count_eval_r(); // temporary indices size_t i, j, k; // # of components of x corresponding to values for y size_t ny_inx = (S_[Nz] + 1) * Ny; // # of constraints (range dimension of g) size_t m = ny_inx; // # of components in x (domain dimension for f and g) assert ( x.size() == ny_inx + Na ); // vector for return value ADVector fg(m + 1); // vector of parameters ADVector a(Na); for(j = 0; j < Na; j++) a[j] = x[ny_inx + j]; // vector for value of y(t) ADVector y(Ny); // objective function ------------------------------- fg[0] = 0.; for(k = 0; k < Nz; k++) { for(j = 0; j < Ny; j++) y[j] = x[Ny*S_[k+1] + j]; fg[0] += eval_H(k+1, y, a); } // initial condition --------------------------------- ADVector F = eval_F(a); for(j = 0; j < Ny; j++) { y[j] = x[j]; fg[1+j] = y[j] - F[j]; } // trapezoidal approximation -------------------------- ADVector ym(Ny), G(Ny), Gm(Ny); G = eval_G(y, a); ADNumber dy; for(k = 0; k < Nz; k++) { // interval between data points Number T = s[k+1] - s[k]; // integration step size Number dt = T / Number( N_[k+1] ); for(j = 0; j < N_[k+1]; j++) { size_t Index = (j + S_[k]) * Ny; // y(t) at end of last step ym = y; // G(y, a) at end of last step Gm = G; // value of y(t) at end of this step for(i = 0; i < Ny; i++) y[i] = x[Ny + Index + i]; // G(y, a) at end of this step G = eval_G(y, a); // trapezoidal approximation residual for(i = 0; i < Ny; i++) { dy = (G[i] + Gm[i]) * dt / 2; fg[1+Ny+Index+i] = y[i] - ym[i] - dy; } } } return fg; } // The operations sequence for r_eval does not depend on u, // hence retape = false should work and be faster. bool retape(size_t k) { return retape_; } }; } // END C++ # endif ================================================ FILE: cppad_ipopt/example/ode_simple_check.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include "ode_run.hpp" # include "ode_simple.hpp" # include "ode_check.hpp" bool ode_simple_check(void) { bool ok = true; bool retape; size_t i; // solution vector NumberVector x; // number of time grid intervals between measurement values SizeVector N(Nz + 1); N[0] = 0; for(i = 1; i <= Nz; i++) N[i] = 4; for(i = 0; i < 2; i++) { retape = bool(i); ipopt_ode_case(retape, N, x); ok &= ode_check(N, x); } return ok; } ================================================ FILE: cppad_ipopt/example/test.sh.in ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- export LD_LIBRARY_PATH=@CPPAD_IPOPT_LD_PATH@ ./example ================================================ FILE: cppad_ipopt/speed/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-23 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the cppad_ipopt/speed directory tests # Inherit build type from ../CMakeList.txt # Local include directories to search (not in package_prefix/include) INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../src ) # Local link directories to search (not in external packages) # (cannot use ../src/cppad_ipopt library because it is linked with debugging) LINK_DIRECTORIES( ${ipopt_LIBRARY_DIRS} ) # add_executable( [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] # source1 source2 ... sourceN # ) # Include source for cppad_ipopt library so we build it without debugging # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list ../src/cppad_ipopt_nlp.cpp ../src/fun_record.hpp ../src/hes_fg_map.cpp ../src/jac_g_map.cpp ../src/sparse_map2vec.cpp ../src/vec_fun_pattern.cpp ode_speed.cpp speed.cpp ) # END_SORT_THIS_LINE_MINUS_2 set_compile_flags( cppad_ipopt_example "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE( cppad_ipopt_speed EXCLUDE_FROM_ALL ${source_list} ) # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES( cppad_ipopt_speed cppad_ipopt ${cppad_lib} ${ipopt_LINK_LIBRARIES} ${colpack_libs} ) # check_cppad_ipopt_speed add_check_executable(check_cppad_ipopt speed) ================================================ FILE: cppad_ipopt/speed/ode_speed.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ipopt_ode_speed.cpp dev} Speed Test for Both Simple and Fast Representations ################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end ipopt_ode_speed.cpp} */ // BEGIN C++ # include "../example/ode_run.hpp" # include "../example/ode_simple.hpp" # include "../example/ode_fast.hpp" # include # include # if CPPAD_HAS_GETTIMEOFDAY & CPPAD_NO_MICROSOFT # include # else # include # endif namespace { double current_second(void) { # if CPPAD_HAS_GETTIMEOFDAY & CPPAD_NOT_MICOROSOFT struct timeval value; gettimeofday(&value, 0); return double(value.tv_sec) + double(value.tv_usec) * 1e-6; # else return (double) clock() / (double) CLOCKS_PER_SEC; # endif } } double ode_speed(const char* name, size_t& count) { // determine simple and retape flags bool simple = true, retape = true; if( std::strcmp(name, "simple_retape_no") == 0 ) { simple = true; retape = false; } else if( std::strcmp(name, "simple_retape_yes") == 0 ) { simple = true; retape = true; } else if( std::strcmp(name, "fast_retape_no") == 0 ) { simple = false; retape = false; } else if( std::strcmp(name, "fast_retape_yes") == 0 ) { simple = false; retape = true; } else assert(false); size_t i; double s0, s1; size_t c0, c1; // solution vector NumberVector x; // number of time grid intervals between measurement values SizeVector N(Nz + 1); N[0] = 0; for(i = 1; i <= Nz; i++) { N[i] = 10; // n += N[i] * Ny; } // n += Na; s0 = current_second(); c0 = count_eval_r(); if( simple ) ipopt_ode_case(retape, N, x); else ipopt_ode_case(retape, N, x); s1 = current_second(); c1 = count_eval_r(); count = c1 - c0 - 1; return s1 - s0; } // END C++ ================================================ FILE: cppad_ipopt/speed/speed.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include // system include files used for I/O # include // std::string // external complied tests extern double ode_speed(const char* name, size_t& count); // main program that runs all the cppad_ipopt speed tests int main(void) { using std::printf; const char* name; double seconds; size_t count; name = "simple_retape_yes"; seconds = ode_speed(name, count); printf("ode %20s: seconds = %5.2f: eval_r_count = %d\n", name, seconds, int(count) ); name = "simple_retape_no"; seconds = ode_speed(name, count); printf("ode %20s: seconds = %5.2f: eval_r_count = %d\n", name, seconds, int(count) ); name = "fast_retape_yes"; seconds = ode_speed(name, count); printf("ode %20s: seconds = %5.2f: eval_r_count = %d\n", name, seconds, int(count) ); name = "fast_retape_no"; seconds = ode_speed(name, count); printf("ode %20s: seconds = %5.2f: eval_r_count = %d\n", name, seconds, int(count) ); return 0; } ================================================ FILE: cppad_ipopt/speed/test.sh.in ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- export LD_LIBRARY_PATH=@CPPAD_IPOPT_LD_PATH@ ./speed ================================================ FILE: cppad_ipopt/src/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the cppad_ipopt/src library # Inherit build type from ../CMakeList.txt # add_library( [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] # source1 source2 ... sourceN) # ) # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list cppad_ipopt_nlp.cpp hes_fg_map.cpp jac_g_map.cpp sparse_map2vec.cpp vec_fun_pattern.cpp ) # END_SORT_THIS_LINE_MINUS_2 # set_compiler_flags set_compile_flags( cppad_ipopt "${cppad_debug_which}" "${source_list}" ) # # add_library IF( "${is_cppad_lib_dynamic}" STREQUAL "0" ) ADD_LIBRARY( cppad_ipopt STATIC ${source_list} ) ELSEIF( "${is_cppad_lib_dynamic}" STREQUAL "1" ) ADD_LIBRARY( cppad_ipopt SHARED ${source_list} ) ENDIF( ) # install(FILES files... DESTINATION # [PERMISSIONS permissions...] # [CONFIGURATIONS [Debug|Release|...]] # [COMPONENT ] # [RENAME ] [OPTIONAL]) INSTALL(FILES cppad_ipopt_nlp.hpp DESTINATION ${cppad_abs_includedir}) # install(TARGETS myExe mySharedLib myStaticLib # RUNTIME DESTINATION bin # LIBRARY DESTINATION lib # ARCHIVE DESTINATION lib/static) INSTALL(TARGETS cppad_ipopt DESTINATION ${cppad_abs_libdir}) ================================================ FILE: cppad_ipopt/src/cppad_ipopt_nlp.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include "cppad_ipopt_nlp.hpp" # include "sparse_map2vec.hpp" # include "jac_g_map.hpp" # include "hes_fg_map.hpp" # include "vec_fun_pattern.hpp" # include "fun_record.hpp" /// If 0 tracing is off, otherwise tracing is on. # define CPPAD_IPOPT_NLP_TRACE 0 # if CPPAD_IPOPT_NLP_TRACE # include # endif // --------------------------------------------------------------------------- namespace cppad_ipopt { // --------------------------------------------------------------------------- /*! \{ \file cppad_ipopt_nlp.cpp \brief Member functions for the cppad_ipopt_nlp class. */ /*! Constructor for the \ref Nonlinear_Programming_Problem. \param n dimension of the domain space for f(x) and g(x). \param m dimension of the range space for g(x) \param x_i initial value of x during the optimization procedure (size n). \param x_l lower limit for x (size n). \param x_u upper limit for x (size n). \param g_l lower limit for g(x) (size m). \param g_u upper limit for g(x) (size m). \param fg_info pointer to base class version of derived class object used to get information about the user's representation for f(x) and g(x). (The object pointed to must not be deleted before this cppad_ipopt_nlp object). \param solution pointer to object where final results are stored. (The object pointed to must not be deleted before this cppad_ipopt_nlp object). \par Constants The following values are set by the constructor and are const or effectively const; i.e., they are set by the constructor and should not be changed: \verbatim n_, m_, x_i_, x_l_, x_u_, g_l_, g_u_, K_, L_, p_, q_, retape_, pattern_jac_r_, pattern_hes_r_, index_jac_g_, index_hes_fg_, nnz_jac_g_, iRow_jac_g_, jCol_jac_g_, nnz_h_lag_, iRow_h_lag_, jCol_h_lag_, \endverbatim In addition, the function calls fg_info->set_n(n) and fg_info->set_m(m) are used to set the values of n and m in fg_info. \par Variables The following arrays have fixed size which is set during this constructor: \li tape_ok_ has size K_. It is initialized as true for indices k such that retape[k] is false. \li r_fun_ has size K_. It is initialize with the default ADFun constructor. Then, for indices k such that retape[k] is false, the operation sequence corresponding to \f$ r_k (u) \f$ is stored in r_fun_[k]. \li I_ has size equal to the maximum of p[k] w.r.t k. \li J_ has size equal to the maximum of q[k] w.r.t k. \par NDEBUG If the preprocessor symbol NEBUG is not defined, certain of the assumptions about the function calls of the form \verbatim fg_info->index(k, ell, I, J) \endverbatim are checked to make sure they hold. */ cppad_ipopt_nlp::cppad_ipopt_nlp( size_t n , size_t m , const NumberVector &x_i , const NumberVector &x_l , const NumberVector &x_u , const NumberVector &g_l , const NumberVector &g_u , cppad_ipopt_fg_info* fg_info , cppad_ipopt_solution* solution ) : n_ ( n ), m_ ( m ), x_i_ ( x_i ), x_l_ ( x_l ), x_u_ ( x_u ), g_l_ ( g_l ), g_u_ ( g_u ), fg_info_ ( fg_info ) , solution_ (solution) , infinity_ ( std::numeric_limits::infinity() ) { size_t k; // set information needed in cppad_ipopt_fg_info fg_info_->set_n(n); fg_info_->set_m(m); // get information from derived class version of fg_info K_ = fg_info_->number_functions(); L_.resize(K_); p_.resize(K_); q_.resize(K_); r_fun_.resize(K_); retape_.resize(K_); tape_ok_.resize(K_); pattern_jac_r_.resize(K_); pattern_hes_r_.resize(K_); size_t max_p = 0; size_t max_q = 0; for(k = 0; k < K_; k++) { L_[k] = fg_info_->number_terms(k); p_[k] = fg_info_->range_size(k); q_[k] = fg_info_->domain_size(k); retape_[k] = fg_info_->retape(k); max_p = std::max(max_p, p_[k]); max_q = std::max(max_q, q_[k]); pattern_jac_r_[k].resize( p_[k] * q_[k] ); pattern_hes_r_[k].resize( q_[k] * q_[k] ); } I_.resize(max_p); J_.resize(max_q); # ifndef NDEBUG size_t i, j, ell; // check for valid range and domain indices for(k = 0; k < K_; k++) for(ell = 0; ell < L_[k]; ell++) { for( i = 0; i < p_[k]; i++) I_[i] = m+1; // an invalid range index for( j = 0; j < q_[k]; j++) J_[j] = n; // an invalid domain index fg_info_->index(k, ell, I_, J_); for( i = 0; i < p_[k]; i++) if( I_[i] > m ) { std::cerr << "k=" << k << ", ell=" << ell << ", I[" << i << "]=" << I_[i] << std::endl; CPPAD_ASSERT_KNOWN( I_[i] <= m, "cppad_ipopt_nlp: invalid value in index vector I" ); } for( j = 0; j < q_[k]; j++) if( J_[j] >= n ) { std::cerr << "k=" << k << ", ell=" << ell << ", J[" << j << "]=" << J_[j] << std::endl; CPPAD_ASSERT_KNOWN( J_[j] < n, "cppad_ipopt_nlp: invalid value in index vector J" ); } } # endif // record r[k] for functions that do not need retaping for(k = 0; k < K_; k++) { tape_ok_[k] = false; if( ! retape_[k] ) { // Operation sequence does not depend on value // of u so record it once here in the constructor. fg_info_->index(k, 0, I_, J_); fun_record( fg_info_ , // inputs k , p_ , q_ , n_ , x_i_ , J_ , r_fun_ // output ); // take time to optimize because only recording once r_fun_[k].optimize(); // ok and will stay that way tape_ok_[k] = true; } } // compute a sparsity patterns for each r_k (u) vec_fun_pattern( K_, p_, q_, retape_, r_fun_, // inputs pattern_jac_r_, pattern_hes_r_ // outputs ); // mapping from (i,j) to Ipopt sparsity index for Jacobian of g jac_g_map( fg_info_, m_, n_, K_, L_, p_, q_, pattern_jac_r_, // inputs I_, J_, // work index_jac_g_ // outputs ); // mapping from (i,j) to Ipopt sparsity index for Hessian of Lagragian hes_fg_map( fg_info_, m_, n_, K_, L_, p_, q_, pattern_hes_r_, // inputs I_, J_, // work index_hes_fg_ // outputs ); // Compute Ipopt sparsity structure for Jacobian of g sparse_map2vec( index_jac_g_, // inputs nnz_jac_g_, iRow_jac_g_, jCol_jac_g_ // outputs ); // Compute Ipopt sparsity structure for Hessian of Lagragian sparse_map2vec( index_hes_fg_, // inputs nnz_h_lag_, iRow_h_lag_, jCol_h_lag_ // outputs ); } /// The destructor takes no special action. cppad_ipopt_nlp::~cppad_ipopt_nlp() {} /*! Return dimension information about optimization problem. \param[out] n is set to the value n_. \param[out] m is set to the value m_. \param[out] nnz_jac_g is set to the value of nnz_jac_g_. \param[out] nnz_h_lag is set to the value of nnz_h_lag_. \param[out] index_style is set to C_STYLE; i.e., zeoro based indexing is used in the information passed to Ipopt. */ bool cppad_ipopt_nlp::get_nlp_info(Index& n, Index& m, Index& nnz_jac_g, Index& nnz_h_lag, IndexStyleEnum& index_style) { n = static_cast( n_ ); m = static_cast( m_ ); nnz_jac_g = static_cast( nnz_jac_g_ ); nnz_h_lag = static_cast( nnz_h_lag_ ); // use the fortran index style for row/col entries index_style = C_STYLE; return true; } /*! Return bound information about optimization problem. \param[in] n is the dimension of the domain space for f(x) and g(x); i.e., it must be equal to n_. \param[out] x_l is a vector of size n. The input value of its elements does not matter. On output, it is a copy of the lower bound for \f$ x \f$; i.e., x_l_. \param[out] x_u is a vector of size n. The input value of its elements does not matter. On output, it is a copy of the upper bound for \f$ x \f$; i.e., x_u_. \param[in] m is the dimension of the range space for g(x). i.e., it must be equal to m_. \param[out] g_l is a vector of size m. The input value of its elements does not matter. On output, it is a copy of the lower bound for \f$ g(x) \f$; i.e., g_l_. \param[out] g_u is a vector of size m. The input value of its elements does not matter. On output, it is a copy of the upper bound for \f$ g(x) \f$; i.e, g_u_. */ bool cppad_ipopt_nlp::get_bounds_info(Index n, Number* x_l, Number* x_u, Index m, Number* g_l, Number* g_u) { size_t i, j; // here, the n and m we gave IPOPT in get_nlp_info are passed back CPPAD_ASSERT_UNKNOWN(size_t(n) == n_); CPPAD_ASSERT_UNKNOWN(size_t(m) == m_); // pass back bounds for(j = 0; j < n_; j++) { x_l[j] = x_l_[j]; x_u[j] = x_u_[j]; } for(i = 0; i < m_; i++) { g_l[i] = g_l_[i]; g_u[i] = g_u_[i]; } return true; } /*! Return initial x value where optimization is started. \param[in] n must be equal to the domain dimension for f(x) and g(x); i.e., it must be equal to n_. \param[in] init_x must be equal to true. \param[out] x is a vector of size n. The input value of its elements does not matter. On output, it is a copy of the initial value for \f$ x \f$; i.e. x_i_. \param[in] init_z must be equal to false. \param z_L is not used. \param z_U is not used. \param[in] m must be equal to the range dimension for g(x); i.e., it must be equal to m_. \param init_lambda must be equal to false. \param lambda is not used. */ bool cppad_ipopt_nlp::get_starting_point(Index n, bool init_x, Number* x, bool init_z, Number* z_L, Number* z_U, Index m, bool init_lambda, Number* lambda) { size_t j; CPPAD_ASSERT_UNKNOWN(size_t(n) == n_ ); CPPAD_ASSERT_UNKNOWN(size_t(m) == m_ ); CPPAD_ASSERT_UNKNOWN(init_x == true); CPPAD_ASSERT_UNKNOWN(init_z == false); CPPAD_ASSERT_UNKNOWN(init_lambda == false); for(j = 0; j < n_; j++) x[j] = x_i_[j]; return true; } /*! Evaluate the objective function f(x). \param[in] n is the dimension of the argument space for f(x); i.e., must be equal n_. \param[in] x is a vector of size n containing the point at which to evaluate the function f(x). \param[in] new_x is false if the previous call to any one of the \ref Deprecated_Evaluation_Methods used the same value for x. \param[out] obj_value is the value of the objective f(x) at this value of x. \return The return value is always true; see \ref Deprecated_Evaluation_Methods. \par Efficiency This routine could be more efficient (for certain when when L[k] > 1 and retape[k] is true) if the users also provided a version of the function fg_info->eval_r(k, u) where u was of type NumberVector. */ bool cppad_ipopt_nlp::eval_f( Index n, const Number* x, bool new_x, Number& obj_value ) { CPPAD_ASSERT_UNKNOWN(size_t(n) == n_ ); size_t iobj, j, k, ell; // initialize summation obj_value = 0.; // update tape_ok_ flag for(k = 0; k < K_; k++) { if( retape_[k] && (new_x || L_[k] > 1) ) tape_ok_[k] = false; } for(k = 0; k < K_; k++) for(ell = 0; ell < L_[k]; ell++) { fg_info_->index(k, ell, I_, J_); for(iobj = 0; iobj < p_[k]; iobj++) if( I_[iobj] == 0 ) { if( ! tape_ok_[k] ) { // Record r_k for value of u corresponding to x fun_record( fg_info_ , // inputs k , p_ , q_ , n_ , x , J_ , r_fun_ // output ); tape_ok_[k] = ! (retape_[k] || L_[k] > 1); } NumberVector u(q_[k]); NumberVector r(p_[k]); for(j = 0; j < q_[k]; j++) { CPPAD_ASSERT_UNKNOWN( J_[j] < n_ ); u[j] = x[ J_[j] ]; } r = r_fun_[k].Forward(0, u); obj_value += r[iobj]; } } # if CPPAD_IPOPT_NLP_TRACE using std::printf; for(j = 0; j < n_; j++) printf("cppad_ipopt_nlp::eval_f::x[%d] = %20.14g\n", j, x[j]); printf("cppad_ipopt_nlp::eval_f::obj_value = %20.14g\n", obj_value); # endif # ifndef NDEBUG CPPAD_ASSERT_KNOWN( (-infinity_ < obj_value) && (obj_value < infinity_), "cppad_ipopt_nlp::eval_f:: objective value is not finite" ); # endif return true; } /*! Evaluate the gradient of f(x). \param[in] n is the dimension of the argument space for f(x); i.e., must be equal n_. \param[in] x has a vector of size n containing the point at which to evaluate the gradient of f(x). \param[in] new_x is false if the previous call to any one of the \ref Deprecated_Evaluation_Methods used the same value for x. \param[out] grad_f is a vector of size n. The input value of its elements does not matter. The output value of its elements is the gradient of f(x) at this value of. \return The return value is always true; see \ref Deprecated_Evaluation_Methods. */ bool cppad_ipopt_nlp::eval_grad_f( Index n, const Number* x, bool new_x, Number* grad_f ) { CPPAD_ASSERT_UNKNOWN(size_t(n) == n_ ); size_t iobj, i, j, k, ell; // initialize summation for(j = 0; j < n_; j++) grad_f[j] = 0.; // update tape_ok_ flag for(k = 0; k < K_; k++) { if( retape_[k] && (new_x || L_[k] > 1) ) tape_ok_[k] = false; } for(k = 0; k < K_; k++) for(ell = 0; ell < L_[k]; ell++) { fg_info_->index(k, ell, I_, J_); for(iobj = 0; iobj < p_[k]; iobj++) if( I_[iobj] == 0 ) { if( ! tape_ok_[k] ) { // Record r_k for value of u corresponding to x fun_record( fg_info_ , // inputs k , p_ , q_ , n_ , x , J_ , r_fun_ // output ); tape_ok_[k] = ! (retape_[k] || L_[k] > 1); } NumberVector u(q_[k]); NumberVector w(p_[k]); NumberVector r_grad(q_[k]); for(j = 0; j < q_[k]; j++) { CPPAD_ASSERT_UNKNOWN( J_[j] < n_ ); u[j] = x[ J_[j] ]; } r_fun_[k].Forward(0, u); for(i = 0; i < p_[k]; i++) w[i] = 0.; w[iobj] = 1.; r_grad = r_fun_[k].Reverse(1, w); for(j = 0; j < q_[k]; j++) { CPPAD_ASSERT_UNKNOWN( J_[j] < n_ ); grad_f[ J_[j] ] += r_grad[j]; } } } # if CPPAD_IPOPT_NLP_TRACE using std::printf; for(j = 0; j < n_; j++) printf( "cppad_ipopt_nlp::eval_grad_f::x[%d] = %20.14g\n", j, x[j] ); for(j = 0; j < n_; j++) printf( "cppad_ipopt_nlp::eval_grad_f::grad_f[%d] = %20.14g\n", j, grad_f[j] ); # endif # ifndef NDEBUG for(j = 0; j < n_; j++) CPPAD_ASSERT_KNOWN( (-infinity_ < grad_f[j]) && (grad_f[j] < infinity_), "cppad_ipopt_nlp::grad_f:: gradient of objective is not finite" ); # endif return true; } /*! Evaluate the function g(x). \param[in] n is the dimension of the argument space for g(x); i.e., must be equal n_. \param[in] x has a vector of size n containing the point at which to evaluate the constraint function g(x). \param[in] new_x is false if the previous call to any one of the \ref Deprecated_Evaluation_Methods used the same value for x. \param[in] m is the dimension of the range space for g(x); i.e., must be equal to m_. \param[out] g is a vector of size m. The input value of its elements does not matter. The output value of its elements is the value of the function g(x) at this value of x. \return The return value is always true; see \ref Deprecated_Evaluation_Methods. */ bool cppad_ipopt_nlp::eval_g( Index n, const Number* x, bool new_x, Index m, Number* g ) { CPPAD_ASSERT_UNKNOWN(size_t(n) == n_ ); size_t i, j, k, ell; // initialize summation for(i = 0; i < m_; i++) g[i] = 0.; // update tape_ok_ flag for(k = 0; k < K_; k++) { if( retape_[k] && (new_x || L_[k] > 1) ) tape_ok_[k] = false; } for(k = 0; k < K_; k++) for(ell = 0; ell < L_[k]; ell++) { fg_info_->index(k, ell, I_, J_); if( ! tape_ok_[k] ) { // Record r_k for value of u corresponding to x fun_record( fg_info_ , // inputs k , p_ , q_ , n_ , x , J_ , r_fun_ // output ); } tape_ok_[k] = ! (retape_[k] || L_[k] > 1); NumberVector u(q_[k]); NumberVector r(p_[k]); for(j = 0; j < q_[k]; j++) { CPPAD_ASSERT_UNKNOWN( J_[j] < n_ ); u[j] = x[ J_[j] ]; } r = r_fun_[k].Forward(0, u); for(i = 0; i < p_[k]; i++) { CPPAD_ASSERT_UNKNOWN( I_[i] <= m_ ); if( I_[i] >= 1 ) g[ I_[i] - 1 ] += r[i]; } } # if CPPAD_IPOPT_NLP_TRACE using std::printf; for(j = 0; j < n_; j++) printf("cppad_ipopt_nlp::eval_g::x[%d] = %20.14g\n", j, x[j]); for(i = 0; i < m_; i++) printf("cppad_ipopt_nlp::eval_g::g[%d] = %20.14g\n", i, g[i]); # endif # ifndef NDEBUG for(i = 0; i < m_; i++) CPPAD_ASSERT_KNOWN( (-infinity_ < g[i]) && (g[i] < infinity_), "cppad_ipopt_nlp::eval_g:: not all constraints are not finite" ); # endif return true; } /*! Evaluate the Jacobian of g(x). \param[in] n is the dimension of the argument space for g(x); i.e., must be equal n_. \param x if values is not NULL, x is a vector of size n containing the point at which to evaluate the gradient of g(x). \param[in] new_x is false if the previous call to any one of the \ref Deprecated_Evaluation_Methods used the same value for x. \param[in] m is the dimension of the range space for g(x); i.e., must be equal to m_. \param[in] nele_jac is the number of possibly non-zero elements in the Jacobian of g(x); i.e., must be equal to nnz_jac_g_. \param iRow if values is not NULL, iRow is not defined. if values is NULL, iRow is a vector with size nele_jac. The input value of its elements does not matter. On output, For k = 0 , ... , nele_jac-1, iRow[k] is the base zero row index for the k-th possibly non-zero entry in the Jacobian of g(x). \param jCol if values is not NULL, jCol is not defined. if values is NULL, jCol is a vector with size nele_jac. The input value of its elements does not matter. On output, For k = 0 , ... , nele_jac-1, jCol[k] is the base zero column index for the k-th possibly non-zero entry in the Jacobian of g(x). \param values if values is not NULL, values is a vector with size nele_jac. The input value of its elements does not matter. On output, For k = 0 , ... , nele_jac-1, values[k] is the value for the k-th possibly non-zero entry in the Jacobian of g(x). \return The return value is always true; see \ref Deprecated_Evaluation_Methods. */ bool cppad_ipopt_nlp::eval_jac_g(Index n, const Number* x, bool new_x, Index m, Index nele_jac, Index* iRow, Index *jCol, Number* values) { CPPAD_ASSERT_UNKNOWN(size_t(m) == m_ ); CPPAD_ASSERT_UNKNOWN(size_t(n) == n_ ); CPPAD_ASSERT_UNKNOWN( size_t(nele_jac) == nnz_jac_g_ ); size_t i, j, k, ell, l; std::map::iterator index_ij; if (values == NULL) { for(k = 0; k < nnz_jac_g_; k++) { iRow[k] = static_cast( iRow_jac_g_[k] ); jCol[k] = static_cast( jCol_jac_g_[k] ); } return true; } // initialize summation l = nnz_jac_g_; while(l--) values[l] = 0.; // update tape_ok_ flag for(k = 0; k < K_; k++) { if( retape_[k] && (new_x || L_[k] > 1) ) tape_ok_[k] = false; } for(k = 0; k < K_; k++) for(ell = 0; ell < L_[k]; ell++) { fg_info_->index(k, ell, I_, J_); if( ! tape_ok_[k] ) { // Record r_k for value of u corresponding to x fun_record( fg_info_ , // inputs k , p_ , q_ , n_ , x , J_ , r_fun_ // output ); } tape_ok_[k] = ! (retape_[k] || L_[k] > 1); NumberVector u(q_[k]); NumberVector jac_r(p_[k] * q_[k]); for(j = 0; j < q_[k]; j++) { CPPAD_ASSERT_UNKNOWN( J_[j] < n_ ); u[j] = x[ J_[j] ]; } if( retape_[k] ) jac_r = r_fun_[k].Jacobian(u); else jac_r = r_fun_[k].SparseJacobian(u, pattern_jac_r_[k]); for(i = 0; i < p_[k]; i++) if( I_[i] != 0 ) { CPPAD_ASSERT_UNKNOWN( I_[i] <= m_ ); for(j = 0; j < q_[k]; j++) { index_ij = index_jac_g_[I_[i]-1].find(J_[j]); if( index_ij != index_jac_g_[I_[i]-1].end() ) { l = index_ij->second; values[l] += jac_r[i * q_[k] + j]; } else CPPAD_ASSERT_UNKNOWN( jac_r[i * q_[k] + j] == 0. ); } } } # ifndef NDEBUG for(l = 0; l < nnz_jac_g_; l++) CPPAD_ASSERT_KNOWN( (-infinity_ < values[l]) && (values[l] < infinity_), "cppad_ipopt_nlp::eval_jac_g:: a component of " "gradient of g is not finite" ); # endif return true; } /*! Evaluate the Hessian of the Lagragian \section Deprecated_Hessian_of_the_Lagragian The Hessian of the Lagragian The Hessian of the Lagragian is defined as \f[ H(x, \sigma, \lambda ) = \sigma \nabla^2 f(x) + \sum_{i=0}^{m-1} \lambda_i \nabla^2 g(x)_i \f] \param[in] n is the dimension of the argument space for g(x); i.e., must be equal n_. \param x if values is not NULL, x is a vector of size n containing the point at which to evaluate the Hessian of the Lagrangian. \param[in] new_x is false if the previous call to any one of the \ref Deprecated_Evaluation_Methods used the same value for x. \param[in] obj_factor the value \f$ \sigma \f$ multiplying the Hessian of f(x) in the expression for \ref Deprecated_Hessian_of_the_Lagragian. \param[in] m is the dimension of the range space for g(x); i.e., must be equal to m_. \param[in] lambda if values is not NULL, lambda is a vector of size m specifying the value of \f$ \lambda \f$ in the expression for \ref Deprecated_Hessian_of_the_Lagragian. \param[in] new_lambda is true if the previous call to eval_h had the same value for lambda and false otherwise. (Not currently used.) \param[in] nele_hess is the number of possibly non-zero elements in the Hessian of the Lagragian; i.e., must be equal to nnz_h_lag_. \param iRow if values is not NULL, iRow is not defined. if values is NULL, iRow is a vector with size nele_jac. The input value of its elements does not matter. On output, For k = 0 , ... , nele_jac-1, iRow[k] is the base zero row index for the k-th possibly non-zero entry in the Hessian of the Lagragian. \param jCol if values is not NULL, jCol is not defined. if values is NULL, jCol is a vector with size nele_jac. The input value of its elements does not matter. On output, For k = 0 , ... , nele_jac-1, jCol[k] is the base zero column index for the k-th possibly non-zero entry in the Hessian of the Lagragian. \param values if values is not NULL, it is a vector with size nele_jac. The input value of its elements does not matter. On output, For k = 0 , ... , nele_jac-1, values[k] is the value for the k-th possibly non-zero entry in the Hessian of the Lagragian. \return The return value is always true; see \ref Deprecated_Evaluation_Methods. */ bool cppad_ipopt_nlp::eval_h(Index n, const Number* x, bool new_x, Number obj_factor, Index m, const Number* lambda, bool new_lambda, Index nele_hess, Index* iRow, Index* jCol, Number* values) { CPPAD_ASSERT_UNKNOWN(size_t(m) == m_ ); CPPAD_ASSERT_UNKNOWN(size_t(n) == n_ ); size_t i, j, k, ell, l; std::map::iterator index_ij; if (values == NULL) { for(k = 0; k < nnz_h_lag_; k++) { iRow[k] = static_cast( iRow_h_lag_[k] ); jCol[k] = static_cast( jCol_h_lag_[k] ); } return true; } // initialize summation l = nnz_h_lag_; while(l--) values[l] = 0.; // update tape_ok_ flag for(k = 0; k < K_; k++) { if( retape_[k] && (new_x || L_[k] > 1) ) tape_ok_[k] = false; } for(k = 0; k < K_; k++) for(ell = 0; ell < L_[k]; ell++) { fg_info_->index(k, ell, I_, J_); bool in_use = false; for(i = 0; i < p_[k]; i++) { if( I_[i] == 0 ) in_use |= obj_factor > 0.; else in_use |= lambda[ I_[i] - 1 ] > 0; } if( in_use ) { if( ! tape_ok_[k] ) { // Record r_k for value of u corresponding to x fun_record( fg_info_ , // inputs k , p_ , q_ , n_ , x , J_ , r_fun_ // output ); tape_ok_[k] = ! (retape_[k] || L_[k] > 1); } NumberVector w(p_[k]); NumberVector r_hes(q_[k] * q_[k]); NumberVector u(q_[k]); for(j = 0; j < q_[k]; j++) { CPPAD_ASSERT_UNKNOWN( J_[j] < n_ ); u[j] = x[ J_[j] ]; } for(i = 0; i < p_[k]; i++) { CPPAD_ASSERT_UNKNOWN( I_[i] <= m_ ); if( I_[i] == 0 ) w[i] = obj_factor; else w[i] = lambda[ I_[i] - 1 ]; } if( retape_[k] ) r_hes = r_fun_[k].Hessian(u, w); else r_hes = r_fun_[k].SparseHessian( u, w, pattern_hes_r_[k] ); for(i = 0; i < q_[k]; i++) for(j = 0; j < q_[k]; j++) if( J_[j] <= J_[i] ) { index_ij = index_hes_fg_[J_[i]].find(J_[j]); if( index_ij != index_hes_fg_[J_[i]].end() ) { l = index_ij->second; values[l] += r_hes[i * q_[k] + j]; } else CPPAD_ASSERT_UNKNOWN( r_hes[i * q_[k] + j] == 0. ); } } } # ifndef NDEBUG for(l = 0; l < nnz_h_lag_; l++) CPPAD_ASSERT_KNOWN( (-infinity_ < values[l]) && (values[l] < infinity_), "cppad_ipopt_nlp::eval_h:: a component of " "Hessian of Lagragian is not finite" ); # endif return true; } /*! Pass solution information from Ipopt to users solution structure. \param[in] status is value that the Ipopt solution status which gets mapped to a corresponding value for \n solution_->status. \param[in] n is the dimension of the domain space for f(x) and g(x); i.e., it must be equal to n_. \param[in] x is a vector with size n specifying the final solution. \n solution_->x is set to be a vector with size n and to have the same element values. \param[in] z_L is a vector with size n specifying the Lagragian multipliers for the constraint \f$ x^l \leq x \f$. \n solution_->z_l is set to be a vector with size n and to have the same element values. \param[in] z_U is a vector with size n specifying the Lagragian multipliers for the constraint \f$ x \leq x^u \f$. \n solution_->z_u is set to be a vector with size n and to have the same element values. \param[in] m is the dimension of the range space for g(x). i.e., it must be equal to m_. \param[in] g is a vector with size m containing the value of the constraint function g(x) at the final solution for x. \n solution_->g is set to be a vector with size m and to have the same element values. \param[in] lambda is a vector with size m specifying the Lagragian multipliers for the constraints \f$ g^l \leq g(x) \leq g^u \f$. \n solution_->lambda is set to be a vector with size m and to have the same element values. \param[in] obj_value is the value of the objective function f(x) at the final solution for x. \n solution_->obj_value is set to have the same value. \param[in] ip_data is unspecified (by Ipopt) and hence not used. \param[in] ip_cq is unspecified (by Ipopt) and hence not used. \par solution_[out] the pointer solution_ , which is equal to the pointer solution in the constructor for cppad_ipopt_nlp, is used to set output values (see documentation above). */ void cppad_ipopt_nlp::finalize_solution( Ipopt::SolverReturn status , Index n , const Number* x , const Number* z_L , const Number* z_U , Index m , const Number* g , const Number* lambda , Number obj_value , const Ipopt::IpoptData* ip_data , Ipopt::IpoptCalculatedQuantities* ip_cq ) { size_t i, j; CPPAD_ASSERT_UNKNOWN(size_t(n) == n_ ); CPPAD_ASSERT_UNKNOWN(size_t(m) == m_ ); switch(status) { // convert status from Ipopt enum to cppad_ipopt_solution enum case Ipopt::SUCCESS: solution_->status = cppad_ipopt_solution::success; break; case Ipopt::MAXITER_EXCEEDED: solution_->status = cppad_ipopt_solution::maxiter_exceeded; break; case Ipopt::STOP_AT_TINY_STEP: solution_->status = cppad_ipopt_solution::stop_at_tiny_step; break; case Ipopt::STOP_AT_ACCEPTABLE_POINT: solution_->status = cppad_ipopt_solution::stop_at_acceptable_point; break; case Ipopt::LOCAL_INFEASIBILITY: solution_->status = cppad_ipopt_solution::local_infeasibility; break; case Ipopt::USER_REQUESTED_STOP: solution_->status = cppad_ipopt_solution::user_requested_stop; break; case Ipopt::DIVERGING_ITERATES: solution_->status = cppad_ipopt_solution::diverging_iterates; break; case Ipopt::RESTORATION_FAILURE: solution_->status = cppad_ipopt_solution::restoration_failure; break; case Ipopt::ERROR_IN_STEP_COMPUTATION: solution_->status = cppad_ipopt_solution::error_in_step_computation; break; case Ipopt::INVALID_NUMBER_DETECTED: solution_->status = cppad_ipopt_solution::invalid_number_detected; break; case Ipopt::INTERNAL_ERROR: solution_->status = cppad_ipopt_solution::internal_error; break; default: solution_->status = cppad_ipopt_solution::unknown; } solution_->x.resize(n_); solution_->z_l.resize(n_); solution_->z_u.resize(n_); for(j = 0; j < n_; j++) { solution_->x[j] = x[j]; solution_->z_l[j] = z_L[j]; solution_->z_u[j] = z_U[j]; } solution_->g.resize(m_); solution_->lambda.resize(m_); for(i = 0; i < m_; i++) { solution_->g[i] = g[i]; solution_->lambda[i] = lambda[i]; } solution_->obj_value = obj_value; return; } // This routine is defined, but not yet used // (trying to figure out a problem with Ipopt-3.9.1 and dismod4). bool cppad_ipopt_nlp::intermediate_callback( Ipopt::AlgorithmMode mode, Index iter, Number obj_value, Number inf_pr, Number inf_du, Number mu, Number d_norm, Number regularization_size, Number alpha_du, Number alpha_pr, Index ls_trials, const Ipopt::IpoptData* ip_data, Ipopt::IpoptCalculatedQuantities* ip_cq ) { // std::cout << "intermediate_callback called" << std::endl; return true; } // --------------------------------------------------------------------------- } // end namespace cppad_ipopt // --------------------------------------------------------------------------- ================================================ FILE: cppad_ipopt/src/cppad_ipopt_nlp.hpp ================================================ # ifndef CPPAD_CPPAD_IPOPT_SRC_CPPAD_IPOPT_NLP_HPP # define CPPAD_CPPAD_IPOPT_SRC_CPPAD_IPOPT_NLP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppad_ipopt_nlp app} {xrst_spell doesn fg libs lipopt maxiter naninf retape rll } Nonlinear Programming Using the CppAD Interface to Ipopt ######################################################## Deprecated 2012-11-28 ********************* This interface to Ipopt is deprecated, use :ref:`ipopt_solve-name` instead. Syntax ****** | # ``include`` ``"cppad_ipopt_nlp.hpp"`` | ``cppad_ipopt_solution`` *solution* ; | ``cppad_ipopt_nlp`` *cppad_nlp* ( | |tab| *n* , *m* , *x_i* , *x_l* , *x_u* , *g_l* , *g_u* , & *fg_info* , & *solution* | ) | ``export LD_LIBRARY_PATH`` = ``$LD_LIBRARY_PATH:`` *ipopt_library_paths* Purpose ******* The class ``cppad_ipopt_nlp`` is used to solve nonlinear programming problems of the form .. math:: \begin{array}{rll} {\rm minimize} & f(x) \\ {\rm subject \; to} & g^l \leq g(x) \leq g^u \\ & x^l \leq x \leq x^u \end{array} This is done using `Ipopt `_ optimizer and `CppAD `_ Algorithmic Differentiation package. cppad_ipopt namespace ********************* All of the declarations for these routines are in the ``cppad_ipopt`` namespace (not the ``CppAD`` namespace). For example; :ref:`cppad_ipopt_nlp@SizeVector` below actually denotes the type ``cppad_ipopt::SizeVector`` . ipopt_library_paths ******************* If you are linking to a shared version of the Ipopt library, you may have to add a path to the ``LD_LIBRARY_PATH`` . You can determine the directory you need to add using the command ``pkg-config ipopt --libs`` The output will have the following form ``-L`` *dir* ``-lipopt`` You may need to add the directory %dir% to ``LD_LIBRARY_PATH%`` ; e.g., ``export LD_LIBRARY_PATH`` =" *dir* : ``$LD_LIBRARY_PATH`` " fg(x) ***** The function :math:`fg : \B{R}^n \rightarrow \B{R}^{m+1}` is defined by .. math:: :nowrap: \begin{eqnarray} fg_0 (x) & = & f(x) \\ fg_1 (x) & = & g_0 (x) \\ & \vdots & \\ fg_m (x) & = & g_{m-1} (x) \end{eqnarray} Index Vector ============ We define an *index vector* as a vector of non-negative integers for which none of the values are equal; i.e., it is both a vector and a set. If :math:`I` is an index vector :math:`|I|` is used to denote the number of elements in :math:`I` and :math:`\| I \|` is used to denote the value of the maximum element in :math:`I`. Projection ========== Given an index vector :math:`J` and a positive integer :math:`n` where :math:`n > \| J \|`, we use :math:`J \otimes n` for the mapping :math:`( J \otimes n ) : \B{R}^n \rightarrow \B{R}^{|J|}` defined by .. math:: [ J \otimes n ] (x)_j = x_{J(j)} for :math:`j = 0 , \ldots |J| - 1`. Injection ========= Given an index vector :math:`I` and a positive integer :math:`m` where :math:`m > \| I \|`, we use :math:`m \otimes I` for the mapping :math:`( m \otimes I ): \B{R}^{|I|} \rightarrow \B{R}^m` defined by .. math:: [ m \otimes I ] (y)_i = \left\{ \begin{array}{ll} y_k & {\rm if} \; i = I(k) \; {\rm for \; some} \; k \in \{ 0 , \cdots, |I|-1 \} \\ 0 & {\rm otherwise} \end{array} \right. Representation ============== In many applications, each of the component functions of :math:`fg(x)` only depend on a few of the components of :math:`x`. In this case, expressing :math:`fg(x)` in terms of simpler functions with fewer arguments can greatly reduce the amount of work required to compute its derivatives. We use the functions :math:`r_k : \B{R}^{q(k)} \rightarrow \B{R}^{p(k)}` for :math:`k = 0 , \ldots , K` to express our representation of :math:`fg(x)` in terms of simpler functions as follows .. math:: fg(x) = \sum_{k=0}^{K-1} \; \sum_{\ell=0}^{L(k) - 1} [ (m+1) \otimes I_{k,\ell} ] \; \circ \; r_k \; \circ \; [ J_{k,\ell} \otimes n ] \; (x) where :math:`\circ` represents function composition, for :math:`k = 0 , \ldots , K - 1`, and :math:`\ell = 0 , \ldots , L(k)`, :math:`I_{k,\ell}` and :math:`J_{k,\ell}` are index vectors with :math:`| J_{k,\ell} | = q(k)`, :math:`\| J_{k,\ell} \| < n`, :math:`| I_{k,\ell} | = p(k)`, and :math:`\| I_{k,\ell} \| \leq m`. Simple Representation ********************* In the simple representation, :math:`r_0 (x) = fg(x)`, :math:`K = 1`, :math:`q(0) = n`, :math:`p(0) = m+1`, :math:`L(0) = 1`, :math:`I_{0,0} = (0 , \ldots , m)`, and :math:`J_{0,0} = (0 , \ldots , n-1)`. SizeVector ********** The type ``SizeVector`` is defined by the ``cppad_ipopt_nlp.hpp`` include file to be a :ref:`SimpleVector-name` class with elements of type ``size_t`` . NumberVector ************ The type ``NumberVector`` is defined by the ``cppad_ipopt_nlp.hpp`` include file to be a :ref:`SimpleVector-name` class with elements of type ``Ipopt::Number`` . ADNumber ******** The type ``ADNumber`` is defined by the ``cppad_ipopt_nlp.hpp`` include file to be a an AD type that can be used to compute derivatives. ADVector ******** The type ``ADVector`` is defined by the ``cppad_ipopt_nlp.hpp`` include file to be a :ref:`SimpleVector-name` class with elements of type ``ADNumber`` . n * The argument *n* has prototype ``size_t`` *n* It specifies the dimension of the argument space; i.e., :math:`x \in \B{R}^n`. m * The argument *m* has prototype ``size_t`` *m* It specifies the dimension of the range space for :math:`g`; i.e., :math:`g : \B{R}^n \rightarrow \B{R}^m`. x_i *** The argument *x_i* has prototype ``const NumberVector&`` *x_i* and its size is equal to :math:`n`. It specifies the initial point where Ipopt starts the optimization process. x_l *** The argument *x_l* has prototype ``const NumberVector&`` *x_l* and its size is equal to :math:`n`. It specifies the lower limits for the argument in the optimization problem; i.e., :math:`x^l`. x_u *** The argument *x_u* has prototype ``const NumberVector&`` *x_u* and its size is equal to :math:`n`. It specifies the upper limits for the argument in the optimization problem; i.e., :math:`x^u`. g_l *** The argument *g_l* has prototype ``const NumberVector&`` *g_l* and its size is equal to :math:`m`. It specifies the lower limits for the constraints in the optimization problem; i.e., :math:`g^l`. g_u *** The argument *g_u* has prototype ``const NumberVector&`` *g_u* and its size is equal to :math:`n`. It specifies the upper limits for the constraints in the optimization problem; i.e., :math:`g^u`. fg_info ******* The argument *fg_info* has prototype *FG_info fg_info* where the class *FG_info* is derived from the base class ``cppad_ipopt_fg_info`` . Certain virtual member functions of *fg_info* are used to compute the value of :math:`fg(x)`. The specifications for these member functions are given below: fg_info.number_functions ======================== This member function has prototype ``virtual size_t cppad_ipopt_fg_info::number_functions`` ( ``void`` ) If *K* has type ``size_t`` , the syntax *K* = *fg_info* . ``number_functions`` () sets *K* to the number of functions used in the representation of :math:`fg(x)`; i.e., :math:`K` in the :ref:`cppad_ipopt_nlp@fg(x)@Representation` above. The ``cppad_ipopt_fg_info`` implementation of this function corresponds to the simple representation mentioned above; i.e. *K* = 1 . fg_info.eval_r ============== This member function has the prototype ``virtual ADVector cppad_ipopt_fg_info::eval_r`` ( ``size_t`` *k* , ``const ADVector&`` *u* ) = 0; Thus it is a pure virtual function and must be defined in the derived class *FG_info* . This function computes the value of :math:`r_k (u)` used in the :ref:`cppad_ipopt_nlp@fg(x)@Representation` for :math:`fg(x)`. If *k* in :math:`\{0 , \ldots , K-1 \}` has type ``size_t`` , *u* is an ``ADVector`` of size *q* ( *k* ) and *r* is an ``ADVector`` of size *p* ( *k* ) the syntax *r* = *fg_info* . ``eval_r`` ( *k* , *u* ) set *r* to the vector :math:`r_k (u)`. fg_info.retape ============== This member function has the prototype ``virtual bool cppad_ipopt_fg_info::retape`` ( ``size_t`` *k* ) If *k* in :math:`\{0 , \ldots , K-1 \}` has type ``size_t`` , and *retape* has type ``bool`` , the syntax *retape* = *fg_info* . ``retape`` ( *k* ) sets *retape* to true or false. If *retape* is true, ``cppad_ipopt_nlp`` will retape the operation sequence corresponding to :math:`r_k (u)` for every value of *u* . An ``cppad_ipopt_nlp`` object should use much less memory and run faster if *retape* is false. You can test both the true and false cases to make sure the operation sequence does not depend on *u* . The ``cppad_ipopt_fg_info`` implementation of this function sets *retape* to true (while slower it is also safer to always retape). fg_info.domain_size =================== This member function has prototype ``virtual size_t cppad_ipopt_fg_info::domain_size`` ( ``size_t`` *k* ) If *k* in :math:`\{0 , \ldots , K-1 \}` has type ``size_t`` , and *q* has type ``size_t`` , the syntax *q* = *fg_info* . ``domain_size`` ( *k* ) sets *q* to the dimension of the domain space for :math:`r_k (u)`; i.e., :math:`q(k)` in the :ref:`cppad_ipopt_nlp@fg(x)@Representation` above. The ``cppad_ipopt_h_base`` implementation of this function corresponds to the simple representation mentioned above; i.e., :math:`q = n`. fg_info.range_size ================== This member function has prototype ``virtual size_t cppad_ipopt_fg_info::range_size`` ( ``size_t`` *k* ) If *k* in :math:`\{0 , \ldots , K-1 \}` has type ``size_t`` , and *p* has type ``size_t`` , the syntax *p* = *fg_info* . ``range_size`` ( *k* ) sets *p* to the dimension of the range space for :math:`r_k (u)`; i.e., :math:`p(k)` in the :ref:`cppad_ipopt_nlp@fg(x)@Representation` above. The ``cppad_ipopt_h_base`` implementation of this function corresponds to the simple representation mentioned above; i.e., :math:`p = m+1`. fg_info.number_terms ==================== This member function has prototype ``virtual size_t cppad_ipopt_fg_info::number_terms`` ( ``size_t`` *k* ) If *k* in :math:`\{0 , \ldots , K-1 \}` has type ``size_t`` , and *L* has type ``size_t`` , the syntax *L* = *fg_info* . ``number_terms`` ( *k* ) sets *L* to the number of terms in representation for this value of *k* ; i.e., :math:`L(k)` in the :ref:`cppad_ipopt_nlp@fg(x)@Representation` above. The ``cppad_ipopt_h_base`` implementation of this function corresponds to the simple representation mentioned above; i.e., :math:`L = 1`. fg_info.index ============= This member function has prototype | |tab| ``virtual void cppad_ipopt_fg_info::index`` ( | |tab| |tab| ``size_t`` *k* , ``size_t`` *ell* , ``SizeVector&`` *I* , ``SizeVector&`` *J* | |tab| ) The argument *k* has type ``size_t`` and is a value between zero and :math:`K-1` inclusive. The argument *ell* has type ``size_t`` and is a value between zero and :math:`L(k)-1` inclusive. The argument *I* is a :ref:`SimpleVector-name` with elements of type ``size_t`` and size greater than or equal to :math:`p(k)`. The input value of the elements of *I* does not matter. The output value of the first :math:`p(k)` elements of *I* must be the corresponding elements of :math:`I_{k,ell}` in the :ref:`cppad_ipopt_nlp@fg(x)@Representation` above. The argument *J* is a :ref:`SimpleVector-name` with elements of type ``size_t`` and size greater than or equal to :math:`q(k)`. The input value of the elements of *J* does not matter. The output value of the first :math:`q(k)` elements of *J* must be the corresponding elements of :math:`J_{k,ell}` in the :ref:`cppad_ipopt_nlp@fg(x)@Representation` above. The ``cppad_ipopt_h_base`` implementation of this function corresponds to the simple representation mentioned above; i.e., for :math:`i = 0 , \ldots , m`, *I* [ *i* ] = *i* , and for :math:`j = 0 , \ldots , n-1`, *J* [ *j* ] = *j* . solution ******** After the optimization process is completed, *solution* contains the following information: status ====== The *status* field of *solution* has prototype ``cppad_ipopt_solution::solution_status`` *solution* . ``status`` It is the final Ipopt status for the optimizer. Here is a list of the possible values for the status: .. list-table:: :widths: auto * - *status* - Meaning * - not_defined - The optimizer did not return a final status to this ``cppad_ipopt_nlp`` object. * - unknown - The status returned by the optimizer is not defined in the Ipopt documentation for ``finalize_solution`` . * - success - Algorithm terminated successfully at a point satisfying the convergence tolerances (see Ipopt options). * - maxiter_exceeded - The maximum number of iterations was exceeded (see Ipopt options). * - stop_at_tiny_step - Algorithm terminated because progress was very slow. * - stop_at_acceptable_point - Algorithm stopped at a point that was converged, not to the 'desired' tolerances, but to 'acceptable' tolerances (see Ipopt options). * - local_infeasibility - Algorithm converged to a non-feasible point (problem may have no solution). * - user_requested_stop - This return value should not happen. * - diverging_iterates - It the iterates are diverging. * - restoration_failure - Restoration phase failed, algorithm doesn't know how to proceed. * - error_in_step_computation - An unrecoverable error occurred while Ipopt tried to compute the search direction. * - invalid_number_detected - Algorithm received an invalid number (such as ``nan`` or ``inf`` ) from the users function *fg_info* . ``eval`` or from the CppAD evaluations of its derivatives (see the Ipopt option ``check_derivatives_for_naninf`` ). * - internal_error - An unknown Ipopt internal error occurred. Contact the Ipopt authors through the mailing list. x = The ``x`` field of *solution* has prototype ``NumberVector`` *solution* . ``x`` and its size is equal to :math:`n`. It is the final :math:`x` value for the optimizer. z_l === The ``z_l`` field of *solution* has prototype ``NumberVector`` *solution* . ``z_l`` and its size is equal to :math:`n`. It is the final Lagrange multipliers for the lower bounds on :math:`x`. z_u === The ``z_u`` field of *solution* has prototype ``NumberVector`` *solution* . ``z_u`` and its size is equal to :math:`n`. It is the final Lagrange multipliers for the upper bounds on :math:`x`. g = The ``g`` field of *solution* has prototype ``NumberVector`` *solution* . ``g`` and its size is equal to :math:`m`. It is the final value for the constraint function :math:`g(x)`. lambda ====== The ``lambda`` field of *solution* has prototype ``NumberVector`` *solution* . ``lambda`` and its size is equal to :math:`m`. It is the final value for the Lagrange multipliers corresponding to the constraint function. obj_value ========= The ``obj_value`` field of *solution* has prototype ``Number`` *solution* . ``obj_value`` It is the final value of the objective function :math:`f(x)`. {xrst_end cppad_ipopt_nlp} ----------------------------------------------------------------------------- */ # include # include # include /*! \file cppad_ipopt_nlp.hpp \brief CppAD interface to Ipopt \ingroup cppad_ipopt_nlp_cpp */ // --------------------------------------------------------------------------- namespace cppad_ipopt { // --------------------------------------------------------------------------- /// A scalar value used to record operation sequence. typedef CppAD::AD ADNumber; /// A simple vector of values used to record operation sequence typedef CppAD::vector ADVector; /// A simple vector of size_t values. typedef CppAD::vector SizeVector; /// A simple vector of values used by Ipopt typedef CppAD::vector NumberVector; /*! Abstract base class user derives from to define the functions in the problem. */ class cppad_ipopt_fg_info { /// allow cppad_ipopt_nlp class complete access to this class friend class cppad_ipopt_nlp; private: /// domain space dimension for the functions f(x), g(x) size_t n_; /// range space dimension for the function g(x) size_t m_; /// the cppad_ipopt_nlp constructor uses this method to set n_ void set_n(size_t n) { n_ = n; } /// the cppad_ipopt_nlp constructor uses this method to set m_ void set_m(size_t m) { m_ = m; } public: /// destructor virtual so user derived class destructor gets called virtual ~cppad_ipopt_fg_info(void) { } /// number_functions; i.e. K (simple representation uses 1) virtual size_t number_functions(void) { return 1; } /// function that evaluates the users representation for f(x) and /// and g(x) is pure virtual so user must define it in derived class virtual ADVector eval_r(size_t k, const ADVector& u) = 0; /// should the function r_k (u) be retaped when ever the argument /// u changes (default is true which is safe but slow) virtual bool retape(size_t k) { return true; } /// domain_size q[k] for r_k (u) (simple representation uses n) virtual size_t domain_size(size_t k) { return n_; } /// range_size p[k] for r_k (u) (simple representation uses m+1) virtual size_t range_size(size_t k) { return m_ + 1; } /// number_terms that use r_k (u) (simple representation uses 1) virtual size_t number_terms(size_t k) { return 1; } /// return the index vectors I_{k,ell} and J_{k,ell} /// (simple representation uses I[i] = i and J[j] = j) virtual void index(size_t k, size_t ell, SizeVector& I, SizeVector& J) { assert( I.size() >= m_ + 1 ); assert( J.size() >= n_ ); for(size_t i = 0; i <= m_; i++) I[i] = i; for(size_t j = 0; j < n_; j++) J[j] = j; } }; /*! Class that contains information about the problem solution \section Nonlinear_Programming_Problem Nonlinear Programming Problem We are give smooth functions \f$ f : {\bf R}^n \rightarrow {\bf R} \f$ and \f$ g : {\bf R}^n \rightarrow {\bf R}^m \f$ and wish to solve the problem \f[ \begin{array}{rcl} {\rm minimize} & f(x) & {\rm w.r.t.} \; x \in {\bf R}^n \\ {\rm subject \; to} & g^l \leq g(x) \leq g^u \\ & x^l \leq x \leq x^u \end{array} \f] \section Users_Representation Users Representation The functions \f$ f : {\bf R}^n \rightarrow {\bf R} \f$ and \f$ g : {\bf R}^n \rightarrow {\bf R}^m \f$ are defined by \f[ \left( \begin{array}{c} f(x) \\ g(x) \end{array} \right) = \sum_{k=0}^{K-1} \; \sum_{\ell=0}^{L(k) - 1} [ (m+1) \otimes I_{k,\ell} ] \; \circ \; r_k \; \circ \; [ J_{k,\ell} \otimes n ] \; (x) \f] where for \f$ k = 0 , \ldots , K-1\f$, \f$ r_k : {\bf R}^{q(k)} \rightarrow {\bf R}^{p(k)} \f$. \section Deprecated_Evaluation_Methods Evaluation Methods The set of evaluation methods for this class is \verbatim { eval_f, eval_grad_f, eval_g, eval_jac_g, eval_h } \endverbatim Note that the bool return flag for the evaluations methods does not appear in the Ipopt documentation. Looking at the code, it seems to be a flag telling Ipopt to abort when the flag is false. */ class cppad_ipopt_solution { public: /// possible values for he solution status enum solution_status { not_defined, success, maxiter_exceeded, stop_at_tiny_step, stop_at_acceptable_point, local_infeasibility, user_requested_stop, feasible_point_found, diverging_iterates, restoration_failure, error_in_step_computation, invalid_number_detected, too_few_degrees_of_freedom, internal_error, unknown } status; /// the approximation solution NumberVector x; /// Lagrange multipliers corresponding to lower bounds on x NumberVector z_l; /// Lagrange multipliers corresponding to upper bounds on x NumberVector z_u; /// value of g(x) NumberVector g; /// Lagrange multipliers correspondiing constraints on g(x) NumberVector lambda; /// value of f(x) Ipopt::Number obj_value; /// constructor initializes solution status as not yet defined cppad_ipopt_solution(void) { status = not_defined; } }; /*! Class connects Ipopt to CppAD for derivative and sparsity pattern calculations. */ class cppad_ipopt_nlp : public Ipopt::TNLP { private: /// A Scalar value used by Ipopt typedef Ipopt::Number Number; /// An index value used by Ipopt typedef Ipopt::Index Index; /// Indexing style used in Ipopt sparsity structure typedef Ipopt::TNLP::IndexStyleEnum IndexStyleEnum; /// A simple vector of boolean values typedef CppAD::vectorBool BoolVector; /// A simple vector of AD function objects typedef CppAD::vector< CppAD::ADFun > ADFunVector; /// A simple vector of simple vectors of boolean values typedef CppAD::vector BoolVectorVector; /// A mapping that is dense in i, sparse in j, and maps (i, j) /// to the corresponding sparsity index in Ipopt. typedef CppAD::vector< std::map > IndexMap; // ------------------------------------------------------------------ // Values directly passed in to constructor // ------------------------------------------------------------------ /// dimension of the domain space for f(x) and g(x) /// (passed to ctor) const size_t n_; /// dimension of the range space for g(x) /// (passed to ctor) const size_t m_; /// dimension of the range space for g(x) /// (passed to ctor) const NumberVector x_i_; /// lower limit for x /// (size n_), (passed to ctor) const NumberVector x_l_; /// upper limit for x /// (size n_) (passed to ctor) const NumberVector x_u_; /// lower limit for g(x) /// (size m_) (passed to ctor) const NumberVector g_l_; /// upper limit for g(x) /// (size m_) (passed to ctor) const NumberVector g_u_; /// pointer to base class version of derived class object used to get /// information about the user's representation for f(x) and g(x) /// (passed to ctor) cppad_ipopt_fg_info* const fg_info_; /// pointer to object where final results are stored /// (passed to ctor) cppad_ipopt_solution* const solution_; /// plus infinity as a value of type Number const Number infinity_; // ------------------------------------------------------------------ // Effectively const values determined during constructor using calls // to fg_info: // ------------------------------------------------------------------ /// The value of \f$ K \f$ in the representation. /// (effectively const) size_t K_; /// Does operation sequence for \f$ r_k (u) \f$ depend on \f$ u \f$. /// (size K_) (effectively const) BoolVector retape_; /// q_[k] is the domain space dimension for \f$ r_k (u) \f$ /// (size K_) (effectively const) SizeVector q_; /// p_[k] is the range space dimension for \f$ r_k (u) \f$ /// (size K_) (effectively const) SizeVector p_; /// L_[k] is number of times \f$ r_k (u) \f$ appears in /// the representation summation /// (size K_) (effectively const) SizeVector L_; // ------------------------------------------------------------------- // Other effectively const values determined by the constructor: // ------------------------------------------------------------------- /*! CppAD sparsity patterns for \f$ \{ r_k^{(1)} (u) \} \f$ (set by ctor). For k = 0 , ... , K_-1, pattern_jac_r_[k] is a CppAD sparsity pattern for the Jacobian of \f$ r_k (u) \f$ and as such it has size p_[k]*q_[k]. (effectively const) */ BoolVectorVector pattern_jac_r_; /*! CppAD sparsity patterns for \f$ \{ r_k^{(2)} (u) \} \f$ (set by ctor). For k = 0 , ... , K_-1, pattern_jac_r_[k] is a CppAD sparsity pattern for the Hessian of \f[ R(u) = \sum_{i=0}^{p[k]-1} r_k (u)_i \f] and as such it has size q_[k]*q_[k]. (effectively const) */ BoolVectorVector pattern_hes_r_; /// number non-zero is Ipopt sparsity structor for Jacobian of g(x) /// (effectively const) size_t nnz_jac_g_; /// row indices in Ipopt sparsity structor for Jacobian of g(x) /// (effectively const) SizeVector iRow_jac_g_; /// column indices in Ipopt sparsity structor for Jacobian of g(x) /// (effectively const) SizeVector jCol_jac_g_; /// number non-zero is Ipopt sparsity structor for Hessian of Lagragian /// (effectively const) size_t nnz_h_lag_; /// row indices in Ipopt sparsity structor for Hessian of Lagragian /// (effectively const) SizeVector iRow_h_lag_; /// column indices in Ipopt sparsity structor for Hessian of Lagragian /// (effectively const) SizeVector jCol_h_lag_; /*! Mapping from (i, j) in Jacobian of g(x) to Ipopt sparsity structure For i = 0 , ... , m_-1, index_jac_g_[i] is a standard map from column index values j to the corresponding index in the Ipopt sparsity structure for the Jacobian of g(x). */ IndexMap index_jac_g_; /*! Mapping from (i, j) in Hessian of fg(x) to Ipopt sparsity structure For i = 0 , ... , n_-1, index_hes_fg_[i] is a standard map from column index values j to the corresponding index in the Ipopt sparsity structure for the Hessian of the Lagragian. */ IndexMap index_hes_fg_; // ----------------------------------------------------------------- // Values that are changed by routine other than the constructor: // ----------------------------------------------------------------- /// For k = 0 , ... , K_-1, r_fun_[k] /// is a the CppAD function object corresponding to \f$ r_k (u) \f$. ADFunVector r_fun_; /*! Is r_fun[k] OK for current x. For k = 0 , ... , K_-1, tape_ok_[k] is true if current operations sequence in r_fun_[k] OK for this value of \f$ x \f$. Note that \f$ u = [ J_{k,\ell} \otimes n ] (x) \f$ may depend on the value of \f$ \ell \f$. */ BoolVector tape_ok_; /// work space of size equal maximum of q[k] w.r.t k. SizeVector J_; /// work space of size equal maximum of p[k] w.r.t k. SizeVector I_; // ------------------------------------------------------------ // Private Methods // ------------------------------------------------------------ /// block the default constructor from use cppad_ipopt_nlp(const cppad_ipopt_nlp&); /// blocks the assignment operator from use cppad_ipopt_nlp& operator=(const cppad_ipopt_nlp&); public: // ---------------------------------------------------------------- // See cppad_ipopt_nlp.cpp for doxygen documentation of these methods // ---------------------------------------------------------------- /// only constructor for cppad_ipopot_nlp cppad_ipopt_nlp( size_t n , size_t m , const NumberVector &x_i , const NumberVector &x_l , const NumberVector &x_u , const NumberVector &g_l , const NumberVector &g_u , cppad_ipopt_fg_info* fg_info , cppad_ipopt_solution* solution ); // use virtual so that derived class destructor gets called. virtual ~cppad_ipopt_nlp(); // return info about the nlp virtual bool get_nlp_info( Index& n , Index& m , Index& nnz_jac_g , Index& nnz_h_lag , IndexStyleEnum& index_style ); // return bounds for my problem virtual bool get_bounds_info( Index n , Number* x_l , Number* x_u , Index m , Number* g_l , Number* g_u ); // return the starting point for the algorithm virtual bool get_starting_point( Index n , bool init_x , Number* x , bool init_z , Number* z_L , Number* z_U , Index m , bool init_lambda , Number* lambda ); // return the objective value virtual bool eval_f( Index n , const Number* x , bool new_x , Number& obj_value ); // Method to return the gradient of the objective virtual bool eval_grad_f( Index n , const Number* x , bool new_x , Number* grad_f ); // return the constraint residuals virtual bool eval_g( Index n , const Number* x , bool new_x , Index m , Number* g ); // Method to return: // 1) The structure of the jacobian (if "values" is NULL) // 2) The values of the jacobian (if "values" is not NULL) virtual bool eval_jac_g( Index n , const Number* x , bool new_x , Index m , Index nele_jac , Index* iRow , Index* jCol , Number* values ); // Method to return: // 1) structure of hessian of the lagrangian (if "values" is NULL) // 2) values of hessian of the lagrangian (if "values" is not NULL) virtual bool eval_h( Index n , const Number* x , bool new_x , Number obj_factor , Index m , const Number* lambda , bool new_lambda , Index nele_hess , Index* iRow , Index* jCol , Number* values ); // called when the algorithm is completed so the TNLP can // store/write the solution virtual void finalize_solution( Ipopt::SolverReturn status , Index n , const Number* x , const Number* z_L , const Number* z_U , Index m , const Number* g , const Number* lambda , Number obj_value , const Ipopt::IpoptData* ip_data , Ipopt::IpoptCalculatedQuantities* ip_cq ); virtual bool intermediate_callback( Ipopt::AlgorithmMode mode, Index iter, Number obj_value, Number inf_pr, Number inf_du, Number mu, Number d_norm, Number regularization_size, Number alpha_du, Number alpha_pr, Index ls_trials, const Ipopt::IpoptData* ip_data, Ipopt::IpoptCalculatedQuantities* ip_cq ); }; // --------------------------------------------------------------------------- } // end namespace cppad_ipopt // --------------------------------------------------------------------------- # endif ================================================ FILE: cppad_ipopt/src/fun_record.hpp ================================================ # ifndef CPPAD_CPPAD_IPOPT_SRC_FUN_RECORD_HPP # define CPPAD_CPPAD_IPOPT_SRC_FUN_RECORD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include "cppad_ipopt_nlp.hpp" // --------------------------------------------------------------------------- namespace cppad_ipopt { // --------------------------------------------------------------------------- /*! \{ \file fun_record.hpp \brief Records operation sequence for r_k (u) */ /*! Records operation sequence for \f$ r_k (u) \f$ at \f$u = [ J \circ n ] (x)\f$. \tparam NumVector is the type of the argument x. It can either be Ipopt::Number* or CppAD::vector; i.e., NumberVector. \param fg_info Given a value \f$ u \in {\bf R}^{q[k]} \f$, fg_info returns the value \f$ r_k (u) \in {\bf R}^{p[k]} \f$. using the syntax \verbatim fg_info->eval_r(k, u); \endverbatim No other use is made of fg_info. \param k is a value less that K specifying the index value for k in the evaluation eval_r. \param p p[k] is dimension of the range space for \f$ r_k (u) \f$; i.e., \f$ r_k (u) \in {\bf R}^{p(k)} \f$. \param q q[k] is dimension of the domain space for \f$ r_k (u) \f$; i.e., \f$ u \in {\bf R}^{q(k)} \f$. \param n is the length of the vector x. \param x the length of x is equal to n and the point \f[ u = [ J \circ n ] (x) \f] is the point at which the operation sequence for \f$ r_k \f$ is recorded. \param J is a vector with length q[k] that projects from \f$ {\bf R}^n \f$ to \f$ {\bf R}^{q[k]} \f$ by selecting an ordered subset of the possible indices \f$ \{ 0 , \ldots , n-1 \} \f$. Hence, 0 <= J[j] < n for j = 0 , ... , q[k]-1. \param r_fun is the vector of AD function objects which has size size greater than k. Only the function object r_fun[k] is referenced. The input value of this function object does not matter. On output it is a recording of the function \f$ r_k (u) \f$ at the value of \f$ u \f$ specified by x and J. */ template void fun_record( cppad_ipopt_fg_info* fg_info , size_t k , const SizeVector& p , const SizeVector& q , size_t n , const NumVector& x , const SizeVector& J , CppAD::vector< CppAD::ADFun >& r_fun ) { size_t j; // extract u from x ADVector u(q[k]); for(j = 0; j < q[k]; j++) { // when NDEBUG is not defined, this error should be caught // during the cppad_ipopt_nlp constructor. CPPAD_ASSERT_UNKNOWN( J[j] < n ); u[j] = x[ J[j] ]; } // start the recording CppAD::Independent(u); // record the evaluation of r_k (u) ADVector r_k = fg_info->eval_r(k, u); CPPAD_ASSERT_KNOWN( r_k.size() == p[k] , "cppad_ipopt_nlp: eval_r return value size not equal to p[k]." ); // stop the recording and store operation sequence in r_fun[k].Dependent(u, r_k); } // --------------------------------------------------------------------------- } // end namespace cppad_ipopt // --------------------------------------------------------------------------- # endif ================================================ FILE: cppad_ipopt/src/hes_fg_map.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include "cppad_ipopt_nlp.hpp" # include "hes_fg_map.hpp" // --------------------------------------------------------------------------- namespace cppad_ipopt { // --------------------------------------------------------------------------- /*! \{ \file hes_fg_map.cpp \brief Creates a mapping between two representations for Hessian of fg. */ /*! Create mapping from CppAD to Ipopt sparse representations of Hessian of F(x). The functions \f$ f : {\bf R}^n \rightarrow {\bf R} \f$ and \f$ g : {\bf R}^n \rightarrow {\bf R}^m \f$ are defined by the \ref Users_Representation. We define the function \f$ F : {\bf R}^n \rightarrow {\bf R} \f$ by \f[ F(x) = \sum_{i=0}^m fg(x)_i \f] \param fg_info For k = 0 , ... , K-1, for ell = 0 , ... , L[k], the function call \verbatim fg_info->index(k, ell, I, J); \endverbatim is made by hes_fg_map. The values k and ell are inputs. The input size of I ( J ) is greater than or equal p[k] ( q[k] ) and this size is not changed. The input values of the elements of I and J are not specified. The output value of the elements of I define \f[ I_{k, \ell} = ( {\rm I[0]} , \cdots , {\rm I[p[k]-1]} ) \f] The output value of the elements of J define \f[ J_{k, \ell} = ( {\rm J[0]} , \cdots , {\rm J[q[k]-1]} ) \f] \param m is the dimension of the range space for \f$ g(x) \f$; i.e., \f$ g(x) \in {\bf R}^m \f$. \param n is the dimension of the domain space for \f$ f(x) \f$ and \f$ g(x) \f$; i.e., \f$ x \in {\bf R}^n \f$. \param K is the number of functions \f$ r_k ( u ) \f$ used for the representation of \f$ f(x) \f$ and \f$ g(x) \f$. \param L is a vector with size K. For k = 0 , ... , K-1, L[k] is the number of terms that use \f$ r_k (u) \f$ in the representation of \f$ f(x) \f$ and \f$ g(x) \f$. \param p is a vector with size K. For k = 0 , ... , K-1, p[k] is dimension of the range space for \f$ r_k (u) \f$; i.e., \f$ r_k (u) \in {\bf R}^{p(k)} \f$. \param q is a vector with size K. For k = 0 , ... , K-1, q[k] is dimension of the domain space for \f$ r_k (u) \f$; i.e., \f$ u \in {\bf R}^{q(k)} \f$. \param pattern_hes_r is a vector with size K. For k = 0 , ... , K-1, pattern_jac_r[k] is a CppAD sparsity pattern for the Hessian of the function \f[ R(u) = \sum_{i=0}^{p[k]-1} r_k (u)_i \f] As such, pattern_hes_r[k].size() == q[k] * q[k]. \param I is a work vector of length greater than or equal p[k] for all k. The input and output value of its elements are unspecified. The size of I is not changed. \param J is a work vector of length greater than or equal q[k] for all k. The input and output value of its elements are unspecified. The size of J is not changed. \param index_hes_fg: On input, this is empty; i.e., index_jac_g.size() == 0. On output, it is the index mapping from \f$ (i, j) \f$ in the Jacobian of \f$ g(x) \f$ to the corresponding index value used by Ipopt to represent the Jacobian. Furthermore, if index_jac_g[i].find(j) == index_jac_g[i].end(), then the \f$ (i, j)\f$ entry in the Jacobian of \f$ g(x) \f$ is always zero. */ void hes_fg_map( cppad_ipopt_fg_info* fg_info , size_t m , size_t n , size_t K , const CppAD::vector& L , const CppAD::vector& p , const CppAD::vector& q , const CppAD::vector& pattern_hes_r , CppAD::vector& I , CppAD::vector& J , CppAD::vector< std::map >& index_hes_fg ) { using CppAD::vectorBool; size_t i, j, ij, k, ell; CPPAD_ASSERT_UNKNOWN( K == L.size() ); CPPAD_ASSERT_UNKNOWN( K == p.size() ); CPPAD_ASSERT_UNKNOWN( K == q.size() ); CPPAD_ASSERT_UNKNOWN( K == pattern_hes_r.size() ); # ifndef NDEBUG for(k = 0; k < K; k++) { CPPAD_ASSERT_UNKNOWN( p[k] <= I.size() ); CPPAD_ASSERT_UNKNOWN( q[k] <= J.size() ); CPPAD_ASSERT_UNKNOWN( q[k]*q[k] == pattern_hes_r[k].size() ); } # endif // Now compute pattern for fg // (use standard set representation because can be huge). CppAD::vector< std::set > pattern_hes_fg(n); for(k = 0; k < K; k++) for(ell = 0; ell < L[k]; ell++) { fg_info->index(k, ell, I, J); for(i = 0; i < q[k]; i++) { for(j = 0; j < q[k]; j++) { ij = i * q[k] + j; if( pattern_hes_r[k][ij] ) pattern_hes_fg[J[i]].insert(J[j]); } } } // Now compute the mapping from (i, j) in the Hessian of fg to the // corresponding index value used by Ipopt to represent the Hessian. CPPAD_ASSERT_UNKNOWN( index_hes_fg.size() == 0 ); index_hes_fg.resize(n); std::set::const_iterator itr; ell = 0; for(i = 0; i < n; i++) { for(itr = pattern_hes_fg[i].begin(); itr != pattern_hes_fg[i].end(); itr++) { index_hes_fg[i][*itr] = ell++; } } return; } // --------------------------------------------------------------------------- } // end namespace cppad_ipopt // --------------------------------------------------------------------------- ================================================ FILE: cppad_ipopt/src/hes_fg_map.hpp ================================================ # ifndef CPPAD_CPPAD_IPOPT_SRC_HES_FG_MAP_HPP # define CPPAD_CPPAD_IPOPT_SRC_HES_FG_MAP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include "cppad_ipopt_nlp.hpp" /*! \file hes_fg_map.hpp \brief Create a mapping between two representations for Hessian of fg. \ingroup hes_fg_map_cpp */ // --------------------------------------------------------------------------- namespace cppad_ipopt { // --------------------------------------------------------------------------- extern void hes_fg_map( cppad_ipopt_fg_info* fg_info , size_t m , size_t n , size_t K , const CppAD::vector& L , const CppAD::vector& p , const CppAD::vector& q , const CppAD::vector& pattern_hes_r , CppAD::vector& I , CppAD::vector& J , CppAD::vector< std::map >& index_hes_fg ); // --------------------------------------------------------------------------- } // end namespace cppad_ipopt // --------------------------------------------------------------------------- # endif ================================================ FILE: cppad_ipopt/src/jac_g_map.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include "cppad_ipopt_nlp.hpp" # include "jac_g_map.hpp" // --------------------------------------------------------------------------- namespace cppad_ipopt { // --------------------------------------------------------------------------- /*! \{ \file jac_g_map.cpp \brief Creates a mapping between two representations for Jacobian of g. */ /*! Create mapping from CppAD to Ipopt sparse representations of Jacobian of g. The functions \f$ f : {\bf R}^n \rightarrow {\bf R} \f$ and \f$ g : {\bf R}^n \rightarrow {\bf R}^m \f$ are defined by the \ref Users_Representation. \param fg_info For k = 0 , ... , K-1, for ell = 0 , ... , L[k], the function call \verbatim fg_info->index(k, ell, I, J); \endverbatim is made by jac_g_map. The values k and ell are inputs. The input size of I ( J ) is greater than or equal p[k] ( q[k] ) and this size is not changed. The input values of the elements of I and J are not specified. The output value of the elements of I define \f[ I_{k, \ell} = ( {\rm I[0]} , \cdots , {\rm I[p[k]-1]} ) \f] The output value of the elements of J define \f[ J_{k, \ell} = ( {\rm J[0]} , \cdots , {\rm J[q[k]-1]} ) \f] \param m is the dimension of the range space for \f$ g(x) \f$; i.e., \f$ g(x) \in {\bf R}^m \f$. \param n is the dimension of the domain space for \f$ f(x) \f$ and \f$ g(x) \f$; i.e., \f$ x \in {\bf R}^n \f$. \param K is the number of functions \f$ r_k ( u ) \f$ used for the representation of \f$ f(x) \f$ and \f$ g(x) \f$. \param L is a vector with size K. For k = 0 , ... , K-1, L[k] is the number of terms that use \f$ r_k (u) \f$ in the representation of \f$ f(x) \f$ and \f$ g(x) \f$. \param p is a vector with size K. For k = 0 , ... , K-1, p[k] is dimension of the range space for \f$ r_k (u) \f$; i.e., \f$ r_k (u) \in {\bf R}^{p(k)} \f$. \param q is a vector with size K. For k = 0 , ... , K-1, q[k] is dimension of the domain space for \f$ r_k (u) \f$; i.e., \f$ u \in {\bf R}^{q(k)} \f$. \param pattern_jac_r is a vector with size K. For k = 0 , ... , K-1, pattern_jac_r[k] is a CppAD sparsity pattern for the Jacobian of the function \f$ r_k : {\bf R}^{q(k)} \rightarrow {\bf R}^{p(k)} \f$. As such, pattern_jac_r[k].size() == p[k] * q[k]. \param I is a work vector of length greater than or equal p[k] for all k. The input and output value of its elements are unspecified. The size of I is not changed. \param J is a work vector of length greater than or equal q[k] for all k. The input and output value of its elements are unspecified. The size of J is not changed. \param index_jac_g: On input, this is empty; i.e., index_jac_g.size() == 0. On output, it is the index mapping from \f$ (i, j) \f$ in the Jacobian of \f$ g(x) \f$ to the corresponding index value used by Ipopt to represent the Jacobian. Furthermore, if index_jac_g[i].find(j) == index_jac_g[i].end(), then the \f$ (i, j)\f$ entry in the Jacobian of \f$ g(x) \f$ is always zero. */ void jac_g_map( cppad_ipopt_fg_info* fg_info , size_t m , size_t n , size_t K , const CppAD::vector& L , const CppAD::vector& p , const CppAD::vector& q , const CppAD::vector& pattern_jac_r , CppAD::vector& I , CppAD::vector& J , CppAD::vector< std::map >& index_jac_g ) { using CppAD::vectorBool; size_t i, j, ij, k, ell; CPPAD_ASSERT_UNKNOWN( K == L.size() ); CPPAD_ASSERT_UNKNOWN( K == p.size() ); CPPAD_ASSERT_UNKNOWN( K == q.size() ); CPPAD_ASSERT_UNKNOWN( K == pattern_jac_r.size() ); # ifndef NDEBUG for(k = 0; k < K; k++) { CPPAD_ASSERT_UNKNOWN( p[k] <= I.size() ); CPPAD_ASSERT_UNKNOWN( q[k] <= J.size() ); CPPAD_ASSERT_UNKNOWN( p[k]*q[k] == pattern_jac_r[k].size() ); } # endif // Now compute pattern for g // (use standard set representation because can be huge). CppAD::vector< std::set > pattern_jac_g(m); for(k = 0; k < K; k++) for(ell = 0; ell < L[k]; ell++) { fg_info->index(k, ell, I, J); for(i = 0; i < p[k]; i++) if( I[i] != 0 ) { for(j = 0; j < q[k]; j++) { ij = i * q[k] + j; if( pattern_jac_r[k][ij] ) pattern_jac_g[I[i]-1].insert(J[j]); } } } // Now compute the mapping from (i, j) in the Jacobian of g to the // corresponding index value used by Ipopt to represent the Jacobian. CPPAD_ASSERT_UNKNOWN( index_jac_g.size() == 0 ); index_jac_g.resize(m); std::set::const_iterator itr; ell = 0; for(i = 0; i < m; i++) { for(itr = pattern_jac_g[i].begin(); itr != pattern_jac_g[i].end(); itr++) { index_jac_g[i][*itr] = ell++; } } return; } // --------------------------------------------------------------------------- } // end namespace cppad_ipopt // --------------------------------------------------------------------------- ================================================ FILE: cppad_ipopt/src/jac_g_map.hpp ================================================ # ifndef CPPAD_CPPAD_IPOPT_SRC_JAC_G_MAP_HPP # define CPPAD_CPPAD_IPOPT_SRC_JAC_G_MAP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include "cppad_ipopt_nlp.hpp" /*! \file jac_g_map.hpp \brief Create a mapping between two representations for Jacobian of g. \ingroup jac_g_map_cpp */ // --------------------------------------------------------------------------- namespace cppad_ipopt { // --------------------------------------------------------------------------- extern void jac_g_map( cppad_ipopt_fg_info* fg_info , size_t m , size_t n , size_t K , const CppAD::vector& L , const CppAD::vector& p , const CppAD::vector& q , const CppAD::vector& pattern_jac_r , CppAD::vector& I , CppAD::vector& J , CppAD::vector< std::map >& index_jac_g ); // --------------------------------------------------------------------------- } // end namespace cppad_ipopt // --------------------------------------------------------------------------- # endif ================================================ FILE: cppad_ipopt/src/sparse_map2vec.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include "cppad_ipopt_nlp.hpp" # include "sparse_map2vec.hpp" // --------------------------------------------------------------------------- namespace cppad_ipopt { // --------------------------------------------------------------------------- /*! \{ \file sparse_map2vec.cpp \brief Create a two vector sparsity representation from a vector of maps. */ /*! Create a two vector sparsity representation from a vector of maps. \param sparse Is a vector of maps representation of sparsity as well as the index in the two vector representation. To be specific; \verbatim for(i = 0; i < sparse.size(); i++) { for(itr = sparse[i].begin(); itr != sparse[i].end(); itr++) { j = itr->first; // (i, j) is a possibly non-zero entry in sparsity pattern // k == itr->second, is corresponding index in i_row and j_col k++; } } \endverbatim \param n_nz is the total number of possibly non-zero entries. \param i_row The input size and element values for i_row do not matter. On output, it has size n_nz and i_row[k] contains the row index corresponding to the k-th possibly non-zero entry. \param j_col The input size and element values for j_col do not matter. On output, it has size n_nz and j_col[k] contains the column index corresponding to the k-th possibly non-zero entry. */ void sparse_map2vec( const CppAD::vector< std::map > sparse, size_t& n_nz , CppAD::vector& i_row , CppAD::vector& j_col ) { size_t i, j, k, m; // number of rows in sparse m = sparse.size(); // itererator for one row std::map::const_iterator itr; // count the number of possibly non-zeros in sparse n_nz = 0; for(i = 0; i < m; i++) for(itr = sparse[i].begin(); itr != sparse[i].end(); itr++) ++n_nz; // resize the return vectors to accommodate n_nz entries i_row.resize(n_nz); j_col.resize(n_nz); // set the row and column indices and check assumptions on sparse k = 0; for(i = 0; i < m; i++) { for(itr = sparse[i].begin(); itr != sparse[i].end(); itr++) { j = itr->first; CPPAD_ASSERT_UNKNOWN( k == itr->second ); i_row[k] = i; j_col[k] = j; ++k; } } return; } // --------------------------------------------------------------------------- } // end namespace cppad_ipopt // --------------------------------------------------------------------------- ================================================ FILE: cppad_ipopt/src/sparse_map2vec.hpp ================================================ # ifndef CPPAD_CPPAD_IPOPT_SRC_SPARSE_MAP2VEC_HPP # define CPPAD_CPPAD_IPOPT_SRC_SPARSE_MAP2VEC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // --------------------------------------------------------------------------- namespace cppad_ipopt { // --------------------------------------------------------------------------- /*! \file sparse_map2vec.hpp \brief Create a two vector sparsity representation from a vector of maps. \ingroup sparese_map2vec_cpp */ extern void sparse_map2vec( const CppAD::vector< std::map > sparse, size_t& n_nz , CppAD::vector& i_row , CppAD::vector& j_col ); // --------------------------------------------------------------------------- } // end namespace cppad_ipopt // --------------------------------------------------------------------------- # endif ================================================ FILE: cppad_ipopt/src/vec_fun_pattern.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include "cppad_ipopt_nlp.hpp" # include "vec_fun_pattern.hpp" // --------------------------------------------------------------------------- namespace cppad_ipopt { // --------------------------------------------------------------------------- /*! \{ \file vec_fun_pattern.cpp \brief Determine a sparsity pattern for a vector of AD function objects. */ /*! Determine a sparsity patterns for each function in a vector of functions. \param K is the number of functions that we are computing the sparsity pattern for. \param p is a vector with size K. For k = 0 , ... , K-1, p[k] is dimension of the range space for \f$ r_k (u) \f$; i.e., \f$ r_k (u) \in {\bf R}^{p(k)} \f$. \param q is a vector with size K. For k = 0 , ... , K-1, q[k] is dimension of the domain space for \f$ r_k (u) \f$; i.e., \f$ u \in {\bf R}^{q(k)} \f$. \param retape is a vector with size K. For k = 0 , ... , K-1, if retape[k] is true, the function object r[k] is a valid representation for \f$ r_k (u) \f$ for all \f$ u \in {\bf R}^{q(k)} \f$. Otherwise, the function object must be retaped for each value of \f$ u \f$. \param r_fun is the vector of AD function objects which has size size K. For k = 0 , ... , K-1, if retape[k] is true, r_fun[k] is not used. If retape[k] is false, r_fun[k] is not used. is a CppAD function object correspopnding to the function \f$ r_k : {\bf R}^{q[k]} \rightarrow {\bf R}^{p[k]} \f$. The following non-constant member functions will be called: \verbatim r_fun[k].ForSparseJac(q[k], pattern_domain) r_fun[k].RevSparseHes(p[k], pattern_range) \endverbatim The following const member functions r_fun[k].Range() and r_fun[k].Domain() may also be called. \param pattern_jac_r is a vector with size K. On input, For k = 0 , ... , K-1, pattern_jac_r[k] is a vector of length p[k] * q[k] and the value of its elements does not matter. On output it is a CppAD sparsity pattern for the Jacobian of \f$ r_k (u) \f$. \param pattern_hes_r is a vector with size K. On input, For k = 0 , ... , K-1, pattern_hes_r[k] is a vector of length q[k] * q[k] and the value of its elements does not matter. On output it is a CppAD sparsity pattern for the Hessian of \f$ R : {\bf R}^{q[k]} \rightarrow {\bf R} \f$ which is defined by \f[ R(u) = \sum_{i=0}^{p[k]-1} r_k (u)_i \f] */ void vec_fun_pattern( size_t K , const CppAD::vector& p , const CppAD::vector& q , const CppAD::vectorBool& retape , CppAD::vector< CppAD::ADFun >& r_fun , CppAD::vector& pattern_jac_r , CppAD::vector& pattern_hes_r ) { // check some assumptions CPPAD_ASSERT_UNKNOWN( K == p.size() ); CPPAD_ASSERT_UNKNOWN( K == q.size() ); CPPAD_ASSERT_UNKNOWN( K == retape.size() ); CPPAD_ASSERT_UNKNOWN( K == r_fun.size() ); CPPAD_ASSERT_UNKNOWN( K == pattern_jac_r.size() ); CPPAD_ASSERT_UNKNOWN( K == pattern_hes_r.size() ); using CppAD::vectorBool; size_t i, j, k; for(k = 0; k < K; k++) { // check some k specific assumptions CPPAD_ASSERT_UNKNOWN( pattern_jac_r[k].size() == p[k] * q[k] ); CPPAD_ASSERT_UNKNOWN( pattern_hes_r[k].size() == q[k] * q[k] ); if( retape[k] ) { for(i = 0; i < p[k]; i++) { for(j = 0; j < q[k]; j++) pattern_jac_r[k][i*q[k] + j] = true; } for(i = 0; i < q[k]; i++) { for(j = 0; j < q[k]; j++) pattern_hes_r[k][i*q[k] + j] = true; } } else { // check assumptions about r_k CPPAD_ASSERT_UNKNOWN( r_fun[k].Range() == p[k] ); CPPAD_ASSERT_UNKNOWN( r_fun[k].Domain() == q[k] ); // pattern for the identity matrix CppAD::vectorBool pattern_domain(q[k] * q[k]); for(i = 0; i < q[k]; i++) { for(j = 0; j < q[k]; j++) pattern_domain[i*q[k] + j] = (i == j); } // use forward mode to compute Jacobian sparsity pattern_jac_r[k] = r_fun[k].ForSparseJac(q[k], pattern_domain); // user reverse mode to compute Hessian sparsity CppAD::vectorBool pattern_ones(p[k]); for(i = 0; i < p[k]; i++) pattern_ones[i] = true; pattern_hes_r[k] = r_fun[k].RevSparseHes(q[k], pattern_ones); } } } // --------------------------------------------------------------------------- } // end namespace cppad_ipopt // --------------------------------------------------------------------------- ================================================ FILE: cppad_ipopt/src/vec_fun_pattern.hpp ================================================ # ifndef CPPAD_CPPAD_IPOPT_SRC_VEC_FUN_PATTERN_HPP # define CPPAD_CPPAD_IPOPT_SRC_VEC_FUN_PATTERN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include "cppad_ipopt_nlp.hpp" // --------------------------------------------------------------------------- namespace cppad_ipopt { // --------------------------------------------------------------------------- /*! \file vec_fun_pattern.hpp \brief Determine sparsity pattern for a vector of AD function objects. \ingroup vec_fun_pattern_cpp */ extern void vec_fun_pattern( size_t K , const CppAD::vector& p , const CppAD::vector& q , const CppAD::vectorBool& retape , CppAD::vector< CppAD::ADFun >& r_fun , CppAD::vector& pattern_jac_r , CppAD::vector& pattern_hes_r ); // --------------------------------------------------------------------------- } // end namespace cppad_ipopt // --------------------------------------------------------------------------- # endif ================================================ FILE: cppad_ipopt/test/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-23 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the cppad_ipopt/test directory tests # Inherit build type from ../CMakeList.txt # Local include directories to search (not in package_prefix/include) INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../src ) # Local link directories to search (not in external packages) LINK_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR}/../src ${ipopt_LIBRARY_DIRS} ) # add_executable( [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] # source1 source2 ... sourceN # ) SET(source_list test_more.cpp k_gt_one.cpp multiple_solution.cpp retape_k1_l1.cpp retape_k1_l2.cpp ) set_compile_flags( cppad_ipopt_test "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE( cppad_ipopt_test EXCLUDE_FROM_ALL ${source_list} ) # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(cppad_ipopt_test cppad_ipopt ${cppad_lib} ${ipopt_LINK_LIBRARIES} ${colpack_libs} ) # check_cppad_ipopt_test add_check_executable(check_cppad_ipopt test) ================================================ FILE: cppad_ipopt/test/k_gt_one.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace { // Begin empty namespace using namespace cppad_ipopt; // --------------------------------------------------------------------------- /* This solve the same problem as ../cppad_ipopt/cppad_ipopt_simple.cpp (repository revision 1276) in a convoluted way in order to test the representation code. */ class FG_K_gt_one : public cppad_ipopt_fg_info { private: bool retape_; public: // derived class part of constructor FG_K_gt_one(bool retape_in) : retape_ (retape_in) { } // Evaluation of the objective f(x), and constraints g(x) // using an Algorithmic Differentiation (AD) class. ADVector eval_r(size_t k, const ADVector& u) { // Fortran style indexing ADNumber x1 = u[3]; ADNumber x2 = u[2]; ADNumber x3 = u[1]; ADNumber x4 = u[0]; if( k == 0 ) { ADVector r(1); // f(x) r[0] = x1 * x4 * (x1 + x2 + x3) + x3; return r; } ADVector r(2); // g_1 (x) r[0] = x1 * x2 * x3 * x4; // g_2 (x) r[1] = x1 * x1 + x2 * x2 + x3 * x3 + x4 * x4; return r; } bool retape(size_t k) { return retape_; } size_t number_functions(void) { return 2; } size_t domain_size(size_t k) { return 4; } size_t range_size(size_t k) { if( k == 0 ) return 1; return 2; } size_t number_terms(size_t k) { return 1; } void index(size_t k, size_t ell, SizeVector& I, SizeVector& J) { if( k == 0 ) I[0] = 0; else { I[0] = 1; I[1] = 2; } // reverse the order of the variables in u from that in x for(size_t j = 0; j < 4; j++) J[j] = 3-j; } }; } // end empty namespace bool k_gt_one(void) { bool ok = true; size_t j; // number of independent variables (domain dimension for f and g) size_t n = 4; // number of constraints (range dimension for g) size_t m = 2; // initial value of the independent variables NumberVector x_i(n); x_i[0] = 1.0; x_i[1] = 5.0; x_i[2] = 5.0; x_i[3] = 1.0; // lower and upper limits for x NumberVector x_l(n); NumberVector x_u(n); for(j = 0; j < n; j++) { x_l[j] = 1.0; x_u[j] = 5.0; } // lower and upper limits for g NumberVector g_l(m); NumberVector g_u(m); g_l[0] = 25.0; g_u[0] = 1.0e19; g_l[1] = 40.0; g_u[1] = 40.0; // known solution to check against double check_x[] = { 1.000000, 4.743000, 3.82115, 1.379408 }; size_t icase; for(icase = 0; icase <= 1; icase++) { // Should cppad_ipopt_nlp retape the operation sequence for // every new x. Can test both true and false cases because // the operation sequence does not depend on x (for this case). bool retape = bool(icase); // check case where upper and lower limits are equal if( icase == 1 ) { x_l[2] = check_x[2]; x_u[2] = check_x[2]; } // object in derived class FG_K_gt_one my_fg_info(retape); cppad_ipopt_fg_info *fg_info = &my_fg_info; // create the Ipopt interface cppad_ipopt_solution solution; Ipopt::SmartPtr cppad_nlp = new cppad_ipopt_nlp( n, m, x_i, x_l, x_u, g_l, g_u, fg_info, &solution ); // Create an instance of the IpoptApplication using Ipopt::IpoptApplication; Ipopt::SmartPtr app = new IpoptApplication(); // turn off any printing app->Options()->SetIntegerValue("print_level", 0); app->Options()->SetStringValue("sb", "yes"); // maximum number of iterations app->Options()->SetIntegerValue("max_iter", 10); // approximate accuracy in first order necessary conditions; // see Mathematical Programming, Volume 106, Number 1, // Pages 25-57, Equation (6) app->Options()->SetNumericValue("tol", 1e-9); // derivative testing app->Options()-> SetStringValue("derivative_test", "second-order"); // Initialize the IpoptApplication and process the options Ipopt::ApplicationReturnStatus status = app->Initialize(); ok &= status == Ipopt::Solve_Succeeded; // Run the IpoptApplication status = app->OptimizeTNLP(cppad_nlp); ok &= status == Ipopt::Solve_Succeeded; /* Check some of the solution values */ ok &= solution.status == cppad_ipopt_solution::success; // double check_z_l[] = { 1.087871, 0., 0., 0. }; double check_z_u[] = { 0., 0., 0., 0. }; double rel_tol = 1e-6; // relative tolerance double abs_tol = 1e-6; // absolute tolerance for(j = 0; j < n; j++) { ok &= CppAD::NearEqual( check_x[j], solution.x[j], rel_tol, abs_tol ); ok &= CppAD::NearEqual( check_z_l[j], solution.z_l[j], rel_tol, abs_tol ); ok &= CppAD::NearEqual( check_z_u[j], solution.z_u[j], rel_tol, abs_tol ); } } return ok; } ================================================ FILE: cppad_ipopt/test/multiple_solution.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace { // Begin empty namespace using namespace cppad_ipopt; // --------------------------------------------------------------------------- /* f(x) = x[1]; k=0, ell=0, I[0] = 0, J[0] = 1 g_0 (x) = x[0]; k=0, ell=1, I[0] = 1, J[0] = 0 g_1 (x) = x[1]; k=0, ell=2, I[0] = 2, J[0] = 1 minimize f(x) subject to -1 <= g_0(x) <= 0 0 <= g_1 (x) <= 1 The solution is x[1] = 0 and x[0] arbitrary. */ class FG_J_changes : public cppad_ipopt_fg_info { private: bool retape_; public: // constructor FG_J_changes(bool retape_in) : retape_ (retape_in) { } size_t number_functions(void) { return 1; } size_t domain_size(size_t k) { size_t q = 1; assert(k == 0); return q; } size_t range_size(size_t k) { size_t p = 1; assert(k == 0); return p; } size_t number_terms(size_t k) { size_t L = 3; assert(k == 0); return L; } void index(size_t k, size_t ell, SizeVector&I, SizeVector& J) { assert( I.size() >= 1 ); assert( J.size() >= 1 ); I[0] = ell; if( ell == 0 ) { J[0] = 1; return; } J[0] = ell - 1; return; } // retape function bool retape(size_t k) { return retape_; } ADVector eval_r(size_t k, const ADVector& u) { assert( u.size() == 1 ); ADVector r(1); r[0] = u[0] ; return r; } }; } // end empty namespace bool multiple_solution(void) { bool ok = true; // number of independent variables (domain dimension for f and g) size_t n = 2; // number of constraints (range dimension for g) size_t m = 2; // initial value of the independent variables NumberVector x_i(n); NumberVector x_l(n); NumberVector x_u(n); size_t i = 0; for(i = 0; i < n; i++) { x_i[i] = 0.; x_l[i] = -1.0; x_u[i] = +1.0; } // lower and upper limits for g NumberVector g_l(m); NumberVector g_u(m); g_l[0] = -1; g_u[0] = 0.; g_l[1] = 0.; g_u[1] = 1.; // object for evaluating function bool retape = false; FG_J_changes my_fg_info(retape); cppad_ipopt_fg_info *fg_info = &my_fg_info; cppad_ipopt_solution solution; Ipopt::SmartPtr cppad_nlp = new cppad_ipopt_nlp( n, m, x_i, x_l, x_u, g_l, g_u, fg_info, &solution ); // Create an instance of the IpoptApplication using Ipopt::IpoptApplication; Ipopt::SmartPtr app = new IpoptApplication(); // turn off any printing app->Options()->SetIntegerValue("print_level", 0); app->Options()->SetStringValue("sb", "yes"); // approximate accuracy in first order necessary conditions; // see Mathematical Programming, Volume 106, Number 1, // Pages 25-57, Equation (6) app->Options()->SetNumericValue("tol", 1e-9); app->Options()-> SetStringValue("derivative_test", "second-order"); // Initialize the IpoptApplication and process the options Ipopt::ApplicationReturnStatus status = app->Initialize(); ok &= status == Ipopt::Solve_Succeeded; // Run the IpoptApplication status = app->OptimizeTNLP(cppad_nlp); ok &= status == Ipopt::Solve_Succeeded; /* Check solution status */ ok &= solution.status == cppad_ipopt_solution::success; ok &= CppAD::NearEqual(solution.x[1], 0., 1e-6, 1e-6); return ok; } ================================================ FILE: cppad_ipopt/test/retape_k1_l1.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace { // Begin empty namespace using namespace cppad_ipopt; // --------------------------------------------------------------------------- class FG_retape : public cppad_ipopt_fg_info { public: // derived class part of constructor FG_retape(void) { } // Evaluation of the objective f(x), and constraints g(x) // using an Algorithmic Differentiation (AD) class. ADVector eval_r(size_t k, const ADVector& x) { ADVector fg(2); // f(x) if( x[0] >= 1. ) fg[0] = .5 * (x[0] * x[0] + x[1] * x[1]); else fg[0] = x[0] + .5 * x[1] * x[1]; // g (x) fg[1] = x[0]; return fg; } bool retape(size_t k) { return true; } }; } // end of empty namespace bool retape_k1_l1(void) { bool ok = true; size_t j; // number of independent variables (domain dimension for f and g) size_t n = 2; // number of constraints (range dimension for g) size_t m = 1; // initial value of the independent variables NumberVector x_i(n); x_i[0] = 2.0; x_i[1] = 2.0; // lower and upper limits for x NumberVector x_l(n); NumberVector x_u(n); for(j = 0; j < n; j++) { x_l[j] = -10.; x_u[j] = +10.; } // lower and upper limits for g NumberVector g_l(m); NumberVector g_u(m); g_l[0] = -1.; g_u[0] = 1.0e19; // object in derived class FG_retape fg_retape; cppad_ipopt_fg_info *fg_info = &fg_retape; // create the Ipopt interface cppad_ipopt_solution solution; Ipopt::SmartPtr cppad_nlp = new cppad_ipopt_nlp( n, m, x_i, x_l, x_u, g_l, g_u, fg_info, &solution ); // Create an instance of the IpoptApplication using Ipopt::IpoptApplication; Ipopt::SmartPtr app = new IpoptApplication(); // turn off any printing app->Options()->SetIntegerValue("print_level", 0); app->Options()->SetStringValue("sb", "yes"); // maximum number of iterations app->Options()->SetIntegerValue("max_iter", 10); // approximate accuracy in first order necessary conditions; // see Mathematical Programming, Volume 106, Number 1, // Pages 25-57, Equation (6) app->Options()->SetNumericValue("tol", 1e-9); // derivative testing app->Options()-> SetStringValue("derivative_test", "second-order"); // Initialize the IpoptApplication and process the options Ipopt::ApplicationReturnStatus status = app->Initialize(); ok &= status == Ipopt::Solve_Succeeded; // Run the IpoptApplication status = app->OptimizeTNLP(cppad_nlp); ok &= status == Ipopt::Solve_Succeeded; /* Check some of the solution values */ ok &= solution.status == cppad_ipopt_solution::success; // double check_x[] = { -1., 0. }; double rel_tol = 1e-6; // relative tolerance double abs_tol = 1e-6; // absolute tolerance for(j = 0; j < n; j++) { ok &= CppAD::NearEqual( check_x[j], solution.x[j], rel_tol, abs_tol ); } return ok; } ================================================ FILE: cppad_ipopt/test/retape_k1_l2.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace { // Begin empty namespace using namespace cppad_ipopt; // --------------------------------------------------------------------------- /* A test case were retaping is required, no constraints, and L[0] > 1. */ class FG_info : public cppad_ipopt_fg_info { public: // derived class part of constructor FG_info (void) { } // r_0 (u) = u if u > 1 and u^2 otherwise. ADVector eval_r(size_t k, const ADVector& u) { ADVector r(1); if( u[0] > 1 ) r[0] = u[0]; else r[0] = u[0] * u[0]; return r; } // operation sequence depends on u bool retape(size_t k) { return true; } // K = 1 size_t number_functions(void) { return 1; } // q[k] = 1 size_t domain_size(size_t k) { return 1; } // p[k] = 1 size_t range_size(size_t k) { return 1; } // L[k] = 2 size_t number_terms(size_t k) { return 2; } // I_{k,ell} = 0 // objective function index // J_{k,ell} = ell // argument index void index(size_t k, size_t ell, SizeVector& I, SizeVector& J) { I[0] = 0; J[0] = ell; } }; } // end empty namespace bool retape_k1_l2(void) { bool ok = true; size_t j; // number of independent variables (domain dimension for f and g) size_t n = 2; // no constraints (range dimension for g) size_t m = 0; // initial value of the independent variables NumberVector x_i(n); x_i[0] = 0.0; // below break in eval_r definition x_i[1] = 2.0; // above break in eval_r definition // lower and upper limits for x NumberVector x_l(n); NumberVector x_u(n); for(j = 0; j < n; j++) { x_l[j] = -5.0; x_u[j] = +5.0; } // lower and upper limits for g NumberVector g_l; NumberVector g_u; // object in derived class FG_info my_fg_info; cppad_ipopt_fg_info *fg_info = &my_fg_info; // create the Ipopt interface cppad_ipopt_solution solution; Ipopt::SmartPtr cppad_nlp = new cppad_ipopt_nlp( n, m, x_i, x_l, x_u, g_l, g_u, fg_info, &solution ); // Create an instance of the IpoptApplication using Ipopt::IpoptApplication; Ipopt::SmartPtr app = new IpoptApplication(); // turn off any printing app->Options()->SetIntegerValue("print_level", 0); app->Options()->SetStringValue("sb", "yes"); // maximum number of iterations app->Options()->SetIntegerValue("max_iter", 10); // approximate accuracy in first order necessary conditions; // see Mathematical Programming, Volume 106, Number 1, // Pages 25-57, Equation (6) app->Options()->SetNumericValue("tol", 1e-9); // derivative testing app->Options()-> SetStringValue("derivative_test", "second-order"); app->Options()-> SetNumericValue("point_perturbation_radius", 0.); // Initialize the IpoptApplication and process the options Ipopt::ApplicationReturnStatus status = app->Initialize(); ok &= status == Ipopt::Solve_Succeeded; // Run the IpoptApplication status = app->OptimizeTNLP(cppad_nlp); ok &= status == Ipopt::Solve_Succeeded; /* Check the solution values */ ok &= solution.status == cppad_ipopt_solution::success; // double rel_tol = 1e-6; // relative tolerance double abs_tol = 1e-6; // absolute tolerance for(j = 0; j < n; j++) ok &= CppAD::NearEqual( 0., solution.x[j], rel_tol, abs_tol); return ok; } ================================================ FILE: cppad_ipopt/test/test.sh.in ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- export LD_LIBRARY_PATH=@CPPAD_IPOPT_LD_PATH@ ./test_more ================================================ FILE: cppad_ipopt/test/test_more.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // system include files used for I/O # include // C style asserts # include // CppAD include file # include // test runner # include // external complied tests extern bool k_gt_one(void); extern bool multiple_solution(void); extern bool retape_k1_l1(void); extern bool retape_k1_l2(void); // main program that runs all the tests int main(void) { std::string group = "cppad_ipopt/test"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // external compiled tests Run( k_gt_one, "k_gt_one" ); Run( multiple_solution, "multiple_solution" ); Run( retape_k1_l1, "retape_k1_l1" ); Run( retape_k1_l2, "retape_k1_l2" ); // // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } ================================================ FILE: cppad_lib/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- # Build and install the cppad_lib shared library # # split cppad_version into year;month;day;release STRING(REGEX REPLACE "([0-9][0-9][0-9][0-9])([0-9][0-9])([0-9][0-9])[.]*([0-9]*)" "\\1;\\2;\\3;\\4" version_list ${cppad_version} ) LIST(GET version_list 0 year) LIST(GET version_list 1 month) LIST(GET version_list 2 day) LIST(GET version_list 3 release) # # soversion: dynamic library version number MATH(EXPR major "${day} - 1 + 31 * ( ${month} - 1 + 12 * ( ${year} - 2019))") IF( "${release}" STREQUAL "" ) SET(soversion "${major}") ELSE( "${release}" STREQUAL "" ) SET(soversion "${major}.${release}") ENDIF( "${release}" STREQUAL "" ) print_variable(soversion) # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list cpp_graph_op.cpp cppad_colpack.cpp csrc_writer.cpp json_lexer.cpp json_parser.cpp json_writer.cpp link_dll_lib.cpp temp_file.cpp ) # END_SORT_THIS_LINE_MINUS_2 IF( cppad_has_cppadcg ) SET(source_list ${source_list} code_gen_fun.cpp) ENDIF( cppad_has_cppadcg ) # # set_compile_flags set_compile_flags(cppad_lib "${cppad_debug_which}" "${source_list}" ) # # add_library IF( "${is_cppad_lib_dynamic}" STREQUAL "0" ) MESSAGE( STATUS "Building static cppad_lib") ADD_LIBRARY( cppad_lib STATIC ${source_list} ) ELSEIF( "${is_cppad_lib_dynamic}" STREQUAL "1" ) MESSAGE( STATUS "Building shared cppad_lib") ADD_LIBRARY( cppad_lib SHARED ${source_list} ) SET_TARGET_PROPERTIES( cppad_lib PROPERTIES SOVERSION ${soversion} LINK_OPTIONS "${cppad_link_flags}" ) # FIND_LIBRARY(dl_LIBRARY dl) IF( dl_LIBRARY ) TARGET_LINK_LIBRARIES(cppad_lib ${dl_LIBRARY}) ENDIF( dl_LIBRARY ) ELSE( ) MESSAGE(FATAL_ERROR "CMkakeLists.txt Error: invalid is_cppad_lib_dynamic") ENDIF( ) # # install(TARGETS myExe mySharedLib myStaticLib # RUNTIME DESTINATION bin # LIBRARY DESTINATION lib # ARCHIVE DESTINATION lib/static) INSTALL(TARGETS cppad_lib DESTINATION ${cppad_abs_libdir}) ================================================ FILE: cppad_lib/code_gen_fun.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin code_gen_fun} {xrst_spell cg jrcv } Generate Source Code and Compile an AD Function ############################################### Syntax ****** | # ``include `` Constructors ============ | ``code_gen_fun`` *fun_name* () | ``code_gen_fun`` *fun_name* ( *file_name* ) | ``code_gen_fun`` *fun_name* ( *file_name* , *cg_fun* ) | ``code_gen_fun`` *fun_name* ( *file_name* , *cg_fun* , *eval_jac* ) swap ==== *fun_name* . ``swap`` ( *other_fun* ) function ======== *y* = *fun_name* ( *x* ) jacobian ======== *J* = *fun_name* . ``jacobian`` ( *x* ) sparse_jacobian =============== *Jrcv* = *fun_name* . ``sparse_jacobian`` ( *x* ) Prototype ********* Constructors ============ {xrst_literal // BEGIN_CTOR_VOID // END_CTOR_VOID } {xrst_literal // BEGIN_CTOR_FILE_NAME // END_CTOR_FILE_NAME } {xrst_literal // BEGIN_CTOR_CG_FUN // END_CTOR_CG_FUN } Operations ========== {xrst_literal // BEGIN_SWAP_OTHER_FUN // END_SWAP_OTHER_FUN } {xrst_literal // BEGIN_FUN_NAME_X // END_FUN_NAME_X } {xrst_literal // BEGIN_JACOBIAN // END_JACOBIAN } {xrst_literal // BEGIN_SPARSE_JACOBIAN // END_SPARSE_JACOBIAN } CppAD::cg::CG ********************* This is the CppAD *Base* type for the function *cg_fun* . It is defined by `CppADCodeGen `_. and used to convert the *cg_fun* function object to source code, compile the source code, and then link the corresponding function evaluation *y* = ``cg_fun.Forward`` (0, *x* ) Speed ***** The conversion to source and linking is expected to take a significant amount of time and the evaluation of the function is expected to be much faster; see the following speed tests: .. csv-table:: :widths: auto cppadcg_det_minor.cpp,:ref:`cppadcg_det_minor.cpp-title` cppadcg_sparse_jacobian.cpp,:ref:`cppadcg_sparse_jacobian.cpp-title` fun_name ******** This is the name of the ``code_gen_fun`` object. other_fun ********* This is the name of another ``code_gen_fun`` object. file_name ********* This is the absolute or relative path for the file that contains the dynamic library. It does not include the files extension at the end that is used for dynamic libraries on this system. If *cg_fun* is not present in the constructor, it must have been present in a previous constructor with the same *file_name* . cg_fun ****** This is a CppAD function object that corresponds to a function :math:`f : \B{R}^n \rightarrow \B{R}^m`. If this arguments is present in the constructor, a new dynamic library is created. eval_jac ******** If this argument is present in the constructor, it determines which type of Jacobian :math:`f'(x)` will be enabled. The possible choices for *eval_jac* are: .. csv-table:: :widths: auto *eval_jac*,Available Jacobian ``code_gen_fun::none_enum``,none ``code_gen_fun::dense_enum``,*fun_name* . ``jacobian`` The default value for *eval_jac* is none. swap **** This exchanges the library in *fun_name* with the library in *other_fun* . x * is a vector of size *n* specifying the argument value at which the function will be evaluated. y * This return value has size *m* and is the value of :math:`f(x)`. jacobian ******** J = This return value has size *m* * *n* and is the value of the Jacobian :math:`f'(x)` where .. math:: J[ i \cdot n + j ] = ( \partial f_i / \partial x_j ) (x) Speed ===== The speed test :ref:`cppadcg_det_minor.cpp-name` has the option to pass the determinant function, or the Jacobian of the determinant function, to CppADCodeGen (for the same eventual calculation); see :ref:`cppadcg_det_minor.cpp@PASS_JACOBIAN_TO_CODE_GEN` . This test indicates that both methods have similar setup and derivative calculation times. sparse_jacobian *************** Jrcv **** This return value is a :ref:`sparse_rcv-name` sparse matrix representation of the Jacobian. Speed ===== The speed test :ref:`cppadcg_sparse_jacobian.cpp-name` has the option to pass a function (:ref:`sparse_jac_fun-name` ) or it's Jacobian to CppADCodeGen (for the same eventual calculation); see :ref:`cppadcg_sparse_jacobian.cpp@PASS_SPARSE_JACOBIAN_TO_CODE_GEN` . THis test indicates that both methods have similar setup and derivative calculation times. {xrst_toc_hidden example/cppad_code_gen/function.cpp example/cppad_code_gen/file.cpp example/cppad_code_gen/jacobian.cpp example/cppad_code_gen/jac_as_fun.cpp example/cppad_code_gen/sparse_jacobian.cpp example/cppad_code_gen/sparse_jac_as_fun.cpp } Examples ******** .. csv-table:: :widths: auto code_gen_fun_function.cpp,:ref:`code_gen_fun_function.cpp-title` code_gen_fun_file.cpp,:ref:`code_gen_fun_file.cpp-title` code_gen_fun_jacobian.cpp,:ref:`code_gen_fun_jacobian.cpp-title` code_gen_fun_jac_as_fun.cpp,:ref:`code_gen_fun_jac_as_fun.cpp-title` code_gen_fun_sparse_jacobian.cpp,:ref:`code_gen_fun_sparse_jacobian.cpp-title` code_gen_fun_sparse_jac_as_fun.cpp,:ref:`code_gen_fun_sparse_jac_as_fun.cpp-title` Implementation ************** see :ref:`code_gen_fun.hpp-name` and :ref:`code_gen_fun.cpp-name` {xrst_end code_gen_fun} ----------------------------------------------------------------------------- {xrst_begin code_gen_fun.hpp} code_gen_fun Class Include File ############################### See Also ******** :ref:`code_gen_fun-name` , :ref:`code_gen_fun.cpp-name` Source ****** {xrst_literal include/cppad/example/code_gen_fun.hpp // BEGIN C++ // END C++ } {xrst_end code_gen_fun.hpp} ----------------------------------------------------------------------------- {xrst_begin code_gen_fun.cpp} code_gen_fun Class Member Implementation ######################################## See Also ******** :ref:`code_gen_fun-name` , :ref:`code_gen_fun.hpp-name` Source ****** {xrst_literal // BEGIN_C++ // END_C++ } {xrst_end code_gen_fun.cpp} */ // BEGIN_C++ # include // --------------------------------------------------------------------------- // code_gen_fun fun_name(file_name, cg_name, eval_jac) // --------------------------------------------------------------------------- // BEGIN_CTOR_CG_FUN code_gen_fun::code_gen_fun( const std::string& file_name , CppAD::ADFun< CppAD::cg::CG >& cg_fun , evaluation_enum eval_jac ) // END_CTOR_CG_FUN { // Generate source code CppAD::cg::ModelCSourceGen cgen(cg_fun, "model"); switch(eval_jac) { case none_enum: break; case dense_enum: cgen.setCreateJacobian(true); break; case sparse_enum: cgen.setCreateSparseJacobian(true); break; } CppAD::cg::ModelLibraryCSourceGen libcgen(cgen); // Compile source, create the library file, and load the library CppAD::cg::DynamicModelLibraryProcessor proc(libcgen, file_name); CppAD::cg::ClangCompiler compiler; bool loadLib = true; dynamic_lib_ = proc.createDynamicLibrary(compiler, loadLib); // // create the model object model_ = dynamic_lib_->model("model"); } // --------------------------------------------------------------------------- // code_gen_fun fun_name(file_name) // --------------------------------------------------------------------------- // BEGIN_CTOR_FILE_NAME code_gen_fun::code_gen_fun(const std::string& file_name ) // END_CTOR_FILE_NAME { // file name plus extension used for dynamic libraries on this system std::string file_name_ext = file_name + CppAD::cg::system::SystemInfo<>::DYNAMIC_LIB_EXTENSION; // load the library CppAD::cg::DynamicLib* ptr = new CppAD::cg::LinuxDynamicLib(file_name_ext); dynamic_lib_ = std::unique_ptr< CppAD::cg::DynamicLib >(ptr); // // create the model object model_ = dynamic_lib_->model("model"); } // --------------------------------------------------------------------------- // code_gen_fun fun_name // --------------------------------------------------------------------------- // BEGIN_CTOR_VOID code_gen_fun::code_gen_fun(void) // END_CTOR_VOID { } // -------------------------------------------------------------------------- // fun_name.swap(other_fun) // -------------------------------------------------------------------------- // BEGIN_SWAP_OTHER_FUN void code_gen_fun::swap(code_gen_fun& other_fun) // END_SWAP_OTHER_FUN { std::swap(dynamic_lib_, other_fun.dynamic_lib_); std::swap(model_, other_fun.model_ ); } // -------------------------------------------------------------------------- // y = fun_name(x) // -------------------------------------------------------------------------- // BEGIN_FUN_NAME_X CppAD::vector code_gen_fun::operator()(const CppAD::vector& x) // END_FUN_NAME_X { return model_->ForwardZero(x); } // -------------------------------------------------------------------------- // J = fun_name.jacobian(x) // -------------------------------------------------------------------------- // BEGIN_JACOBIAN CppAD::vector code_gen_fun::jacobian(const CppAD::vector& x) // END_JACOBIAN { CPPAD_ASSERT_KNOWN( model_->isJacobianAvailable() , "code_gen_fun: dense jacobian not enables during constructor" ); return model_-> Jacobian(x); } // -------------------------------------------------------------------------- // Jrcv = fun_name.sparse_jacobian(x) // -------------------------------------------------------------------------- // BEGIN_SPARSE_JACOBIAN CppAD::sparse_rcv< CppAD::vector, CppAD::vector > code_gen_fun::sparse_jacobian(const CppAD::vector& x) // END_SPARSE_JACOBIAN { CPPAD_ASSERT_KNOWN( model_->isSparseJacobianAvailable() , "code_gen_fun: sparse jacobian not enabled during constructor" ); // x_std size_t n = model_->Domain(); std::vector x_std(n); for(size_t j = 0; j < n; ++j) x_std[j] = x[j]; // // 2DO: Prepahs CppAD should have a sparse_rcv constructor (jac, row, col) // that uses swap to swap the vectors // // jac, row, col std::vector jac; std::vector row, col; model_-> SparseJacobian(x_std, jac, row, col); // // sparse_rc size_t nr = model_->Range(); size_t nc = model_->Domain(); size_t nnz = row.size(); CppAD::sparse_rc< CppAD::vector > pattern(nr, nc, nnz); for(size_t k = 0; k < nnz; ++k) pattern.set(k, row[k], col[k]); // sparse_rcv CppAD::sparse_rcv< CppAD::vector, CppAD::vector > Jrcv(pattern); for(size_t k = 0; k < nnz; ++k) Jrcv.set(k, jac[k]); // return Jrcv; } // END_C++ ================================================ FILE: cppad_lib/cpp_graph_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include // documentations for this routintes are in the file below # include // BEGIN_CPPAD_LOCAL_GRAPH_NAMESPACE namespace CppAD { namespace local { namespace graph { // mapping from operator name to graph_op_enum value std::map op_name2enum; // map from operator enum to name const char* op_enum2name[n_graph_op]; // map from operator enum to n_arg (when fixed number of arguments) size_t op_enum2fixed_n_arg[n_graph_op]; // This routine is called by the first use of the cpp_graph constructor // see cpp_graph.hpp. void set_operator_info(void) { // This routine cannot be called in parallel mode CPPAD_ASSERT_UNKNOWN( ! ( CppAD::thread_alloc::in_parallel() )); // typedef std::pair pair; struct op_info { graph_op_enum code; const char* name; size_t n_arg; }; /* If n_arg is zero in the table below, the table contains a comment as to the heading in graph_op_enum.hpp below which you can find the specifications for n_arg, n_result for the corresponding operator. */ // BEGIN_SORT_THIS_LINE_PLUS_2 op_info op_info_vec[] = { { abs_graph_op, "abs", 1 }, // 1 result { acos_graph_op, "acos", 1 }, // 1 result { acosh_graph_op, "acosh", 1 }, // 1 result { add_graph_op, "add", 2 }, // 1 result { asin_graph_op, "asin", 1 }, // 1 result { asinh_graph_op, "asinh", 1 }, // 1 result { atan_graph_op, "atan", 1 }, // 1 result { atanh_graph_op, "atanh", 1 }, // 1 result { atom4_graph_op, "atom4", 0 }, // See Atomic Function { atom_graph_op, "atom", 0 }, // See Atomic Function { azmul_graph_op, "azmul", 2 }, // 1 result { cexp_eq_graph_op, "cexp_eq", 4 }, // 1 result { cexp_le_graph_op, "cexp_le", 4 }, // 1 result { cexp_lt_graph_op, "cexp_lt", 4 }, // 1 result { comp_eq_graph_op, "comp_eq", 0 }, // See Comparisons { comp_le_graph_op, "comp_le", 0 }, // ... { comp_lt_graph_op, "comp_lt", 0 }, // ... { comp_ne_graph_op, "comp_ne", 0 }, // ... { cos_graph_op, "cos", 1 }, // 1 result { cosh_graph_op, "cosh", 1 }, // 1 result { discrete_graph_op, "discrete", 0 }, // See Discrete Function { div_graph_op, "div", 2 }, // 1 result { erf_graph_op, "erf", 1 }, // 1 result { erfc_graph_op, "erfc", 1 }, // 1 result { exp_graph_op, "exp", 1 }, // 1 result { expm1_graph_op, "expm1", 1 }, // 1 result { log1p_graph_op, "log1p", 1 }, // 1 result { log_graph_op, "log", 1 }, // 1 result { mul_graph_op, "mul", 2 }, // 1 result { neg_graph_op, "neg", 1 }, // 1 result { pow_graph_op, "pow", 2 }, // 1 result { print_graph_op, "print", 0 }, // See Print { sign_graph_op, "sign", 1 }, // 1 result { sin_graph_op, "sin", 1 }, // 1 result { sinh_graph_op, "sinh", 1 }, // 1 result { sqrt_graph_op, "sqrt", 1 }, // 1 result { sub_graph_op, "sub", 2 }, // 1 result { sum_graph_op, "sum", 0 }, // See Summation { tan_graph_op, "tan", 1 }, // 1 result { tanh_graph_op, "tanh", 1 } // 1 result }; // END_SORT_THIS_LINE_MINUS_2 CPPAD_ASSERT_UNKNOWN( size_t(n_graph_op) == sizeof(op_info_vec) / sizeof(op_info_vec[0]) ); for(size_t i = 0; i < size_t(n_graph_op); ++i) { graph_op_enum code = op_info_vec[i].code; const char* name = op_info_vec[i].name; size_t n_arg = op_info_vec[i].n_arg; CPPAD_ASSERT_UNKNOWN( size_t(code) == i ); // op_enum2name[code] = name; op_enum2fixed_n_arg[code] = n_arg; op_name2enum.insert( pair(name, code) ); } } } } } // END_CPPAD_LOCAL_GRAPH_NAMESPACE ================================================ FILE: cppad_lib/cppad_colpack.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include # if CPPAD_HAS_COLPACK == 0 namespace CppAD { namespace local { void this_routine_should_never_get_called(void) { CPPAD_ASSERT_UNKNOWN(false); } } } # else // CPPAD_HAS_COLPACK # include namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE /*! \file cppad_colpack.cpp The CppAD interface to the Colpack coloring algorithms. */ /*! Determine which rows of a general sparse matrix can be computed together. \param color is a vector with color.size() == m. For i = 0 , ... , m-1, color[i] is the color for the corresponding row of the matrix. If color[i1]==color[i2], (i1, j1) is in sparsity pattern, and (i2, j2) is in sparsity pattern, then j1 is not equal to j2. \param m is the number of rows in the matrix. \param n is the number of columns in the matrix. \param adolc_pattern is a vector with adolc_pattern.size() == m. For i = 0 , ... , m-1, and for k = 1, ... ,adolc_pattern[i][0], the entry with index (i, adolc_pattern[i][k]) is a non-zero in the sparsity pattern for the matrix. */ // ---------------------------------------------------------------------- void cppad_colpack_general( CppAD::vector& color , size_t m , size_t n , const CppAD::vector& adolc_pattern ) { size_t i, k; CPPAD_ASSERT_UNKNOWN( adolc_pattern.size() == m ); CPPAD_ASSERT_UNKNOWN( color.size() == m ); // Use adolc sparsity pattern to create corresponding bipartite graph ColPack::BipartiteGraphPartialColoringInterface graph( SRC_MEM_ADOLC, adolc_pattern.data(), m, n ); // row ordered Partial-Distance-Two-Coloring of the bipartite graph graph.PartialDistanceTwoColoring( "SMALLEST_LAST", "ROW_PARTIAL_DISTANCE_TWO" ); // ---------------------------------------------------------------------- // If we had access to BipartiteGraphPartialColoring::m_vi_LeftVertexColors // we could access the coloring and not need to go through seed matrix; see // BipartiteGraphPartialColoring::GetLeftSeedMatrix_unmanaged in colpack // and cppad_colpack_symmetric below. // ---------------------------------------------------------------------- // Use coloring information to create seed matrix int n_seed_row; int n_seed_col; double** seed_matrix = graph.GetSeedMatrix(&n_seed_row, &n_seed_col); CPPAD_ASSERT_UNKNOWN( size_t(n_seed_col) == m ); // now return coloring in format required by CppAD for(i = 0; i < m; i++) color[i] = m; for(k = 0; k < size_t(n_seed_row); k++) { for(i = 0; i < m; i++) { if( seed_matrix[k][i] != 0.0 ) { // check that entries in the seed matrix are zero or one CPPAD_ASSERT_UNKNOWN( seed_matrix[k][i] == 1.0 ); // check that no row appears twice in the coloring CPPAD_ASSERT_UNKNOWN( color[i] == m ); // only need include rows with non-zero entries if( adolc_pattern[i][0] != 0 ) { // set color for this row color[i] = k; } } } } # ifndef NDEBUG // check non-zero versus color for each row for(i = 0; i < m; i++) { // if there is a color for row i, check that it has non-zero entries if(color[i] < m ) CPPAD_ASSERT_UNKNOWN( adolc_pattern[i][0] != 0 ); // if there is no color for row i, check that it is empty if( color[i] == m ) CPPAD_ASSERT_UNKNOWN( adolc_pattern[i][0] == 0 ); } // check that no rows with the same color have non-zero entries // with the same column index CppAD::vector found(n); for(k = 0; k < size_t(n_seed_row); k++) { // k is the color index // found: column already has a non-zero entries for this color for(size_t j = 0; j < n; j++) found[j] = false; // for each row with color k for(i = 0; i < m; i++) if( color[i] == k ) { // for each non-zero entry in this row for(size_t ell = 0; ell < adolc_pattern[i][0]; ell++) { // column index for this entry size_t j = adolc_pattern[i][1 + ell]; // check that this is the first non-zero in this column CPPAD_ASSERT_UNKNOWN( ! found[j] ); // found a non-zero in this column found[j] = true; } } } # endif return; } // ---------------------------------------------------------------------- /*! Determine which rows of a symmetrix sparse matrix can be computed together. \param color is a vector with color.size() == m. For i = 0 , ... , m-1, color[i] is the color for the corresponding row of the matrix. We say that a sparsity pattern entry (i, j) is valid if for all i1, such that i1 != i and color[i1]==color[i], and all j1, such that (i1, j1) is in sparsity pattern, j1 != j. The coloring is chosen so that for all (i, j) in the sparsity pattern; either (i, j) or (j, i) is valid (possibly both). \param m is the number of rows (and columns) in the matrix. \param adolc_pattern is a vector with adolc_pattern.size() == m. For i = 0 , ... , m-1, and for k = 1, ... ,adolc_pattern[i][0], the entry with index (i, adolc_pattern[i][k]) is in the sparsity pattern for the symmetric matrix. */ void cppad_colpack_symmetric( CppAD::vector& color , size_t m , const CppAD::vector& adolc_pattern ) { size_t i; CPPAD_ASSERT_UNKNOWN( adolc_pattern.size() == m ); CPPAD_ASSERT_UNKNOWN( color.size() == m ); // Use adolc sparsity pattern to create corresponding bipartite graph ColPack::GraphColoringInterface graph( SRC_MEM_ADOLC, adolc_pattern.data(), m ); // Use STAR coloring because it has a direct recovery scheme; i.e., // not necessary to solve equations to extract values. graph.Coloring("SMALLEST_LAST", "STAR"); // pointer to Colpack coloring solution const std::vector* vertex_colors_ptr = graph.GetVertexColorsPtr(); CPPAD_ASSERT_UNKNOWN( vertex_colors_ptr->size() == m ); // now return coloring in format required by CppAD for(i = 0; i < m; i++) { if( adolc_pattern[i][0] == 0 ) { // no entries in row of of sparsity patter color[i] = m; } else { // there are non-zero entries in row i color[i] = size_t( (*vertex_colors_ptr)[i] ); CPPAD_ASSERT_UNKNOWN( color[i] < m ); } } # ifndef NDEBUG // check that every entry in the symmetric matrix can be directly recovered size_t i1, i2, j1, j2, k1, k2, nz1, nz2; for(i1 = 0; i1 < m; i1++) { nz1 = size_t(adolc_pattern[i1][0]); for(k1 = 1; k1 <= nz1; k1++) { j1 = adolc_pattern[i1][k1]; // (i1, j1) is a non-zero in the sparsity pattern // check of a forward on color[i1] followed by a reverse // can recover entry (i1, j1) bool color_i1_ok = true; for(i2 = 0; i2 < m; i2++) if( i1 != i2 && color[i1] == color[i2] ) { nz2 = adolc_pattern[i2][0]; for(k2 = 1; k2 <= nz2; k2++) { j2 = adolc_pattern[i2][k2]; color_i1_ok &= (j1 != j2); } } // check of a forward on color[j1] followed by a reverse // can recover entry (j1, i1) bool color_j1_ok = true; for(j2 = 0; j2 < m; j2++) if( j1 != j2 && color[j1] == color[j2] ) { nz2 = adolc_pattern[j2][0]; for(k2 = 1; k2 <= nz2; k2++) { i2 = adolc_pattern[j2][k2]; color_j1_ok &= (i1 != i2); } } CPPAD_ASSERT_UNKNOWN( color_i1_ok || color_j1_ok ); } } # endif return; } } } // END_CPPAD_LOCAL_NAMESPACE # endif // CPPAD_HAS_COLPACK ================================================ FILE: cppad_lib/csrc_writer.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cpp_csrc_writer dev} Converts Cpp Graph to C Source ############################## Syntax ****** | ``csrc_writer`` ( *os* , *graph_obj* , *c_type* ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } os ** The C source code corresponding to the function is written to *os* . graph ***** is the C++ graph representation of the function. c_type ****** this specifies the type corresponding to the C source code and must be one of the following: ``float`` , ``double`` , or ``long_double`` . {xrst_end cpp_csrc_writer} */ # include # include # include # include // documentation for this routine is in the file below # include /* Optimizations 2DO: 1. Reduce size of v by removing x and y from the v vector. 2. Reduce size of v by reusing elements when they are no longer needed. */ namespace { // // element std::string element(const std::string& array_name, size_t array_index) { return array_name + "[" + CppAD::to_string(array_index) + "]"; } // // binary_function void binary_function( std::ostream& os , const char* op_csrc , size_t result_node , size_t left_node , size_t right_node ) { os << "\t" + element("v", result_node) + " = "; os << op_csrc; os << "( " + element("v", left_node); os << ", " + element("v", right_node) + " );\n"; } // // binary_operator void binary_operator( std::ostream& os , const char* op_csrc , size_t result_node , size_t left_node , size_t right_node ) { os << "\t" + element("v", result_node) + " = "; os << element("v", left_node) + " " + op_csrc + " "; os << element("v", right_node) + ";\n"; } // // compare_operator void compare_operator( std::ostream& os , const char* op_csrc , size_t left_node , size_t right_node ) { os << "\tif( " + element("v", left_node) + " " + op_csrc + " "; os << element("v", right_node) + " )\n"; os << "\t\t++(*compare_change);\n"; } // // unary_function void unary_function( std::ostream& os , const char* op_csrc , size_t result_node , size_t arg_node ) { os << "\t" + element("v", result_node) + " = "; os << op_csrc; os << "( " + element("v", arg_node) + " );\n"; } // // sum_operator void sum_operator( std::ostream& os , size_t result_node , const CppAD::vector& arg_node ) { std::string rhs = "\t" + element("v", result_node) + " = "; os << rhs; if( arg_node.size() == 1 ) { // can have subtraction terms with no addition terms os << "(float_point_t) 0;\n"; return; } for(size_t i = 0; i < arg_node.size(); ++i) { if( i % 5 == 0 && i != 0 ) { os << "\n\t"; for(size_t j = 0; j < rhs.size() - 4; ++j) os << ' '; } if( 0 < i ) os << " + "; os << element("v", arg_node[i]); } os << ";\n"; } // // atomic_function void atomic_function( std::ostream& os , size_t result_node , const std::string& atomic_name , size_t call_id , size_t n_result , const CppAD::vector& arg_node ) { using CppAD::to_string; std::string complete_name = "cppad_atomic_" + atomic_name; size_t nu = arg_node.size(); size_t nw = n_result; os << "\t{\t// call " + atomic_name + "\n"; os << "\t\tint flag;\n"; os << "\t\tfloat_point_t " + element("u", nu) + ";\n"; os << "\t\tfloat_point_t* w = v + " + to_string(result_node) + ";\n"; for(size_t j = 0; j < nu; ++j) { size_t i = arg_node[j]; os << "\t\t" + element("u",j) + " = " + element("v",i) + ";\n"; } // os << "\t\tflag = " + complete_name + "("; os << to_string(call_id) + ", "; os << to_string(nu) + ", u, "; os << to_string(nw) + ", w, "; os << "compare_change);\n"; os << "\t\tif( flag == 1 || flag == 2 ) return 3;\n"; os << "\t\tif( flag != 0 ) return flag;\n"; // os << "\t}\n"; } // // discrete_function void discrete_function( std::ostream& os , size_t result_node , const std::string& discrete_name , size_t arg_node ) { using CppAD::to_string; std::string complete_name = "cppad_discrete_" + discrete_name; os << "\t{\t// call " + discrete_name + "\n"; os << "\t\t" + element("v", result_node) + " = "; os << complete_name + "( " + element("v", arg_node) + " );\n"; os << "\t}\n"; } } // BEGIN_PROTOTYPE void CppAD::local::graph::csrc_writer( std::ostream& os , const cpp_graph& graph_obj , const std::string& c_type ) // END_PROTOTYPE { using std::string; using CppAD::to_string; // // -------------------------------------------------------------------- string function_name = graph_obj.function_name_get(); size_t n_dynamic_ind = graph_obj.n_dynamic_ind_get(); size_t n_variable_ind = graph_obj.n_variable_ind_get(); size_t n_constant = graph_obj.constant_vec_size(); size_t n_dependent = graph_obj.dependent_vec_size(); size_t n_usage = graph_obj.operator_vec_size(); // -------------------------------------------------------------------- CPPAD_ASSERT_KNOWN( function_name != "" , "to_csrc: Cannot convert a function with no name" ); // // graph_itr // defined here because not using as loop index cpp_graph::const_iterator graph_itr; // // first_result_node size_t first_result_node = 1 + n_dynamic_ind + n_variable_ind + n_constant; // // n_node size_t n_node = first_result_node; for(size_t op_index = 0; op_index < n_usage; ++op_index) { // graph_itr if( op_index == 0 ) graph_itr = graph_obj.begin(); else ++graph_itr; // // nv cpp_graph::const_iterator::value_type itr_value = *graph_itr; n_node += itr_value.n_result; } // // includes os << "// includes\n" "# include \n" "# include \n" "\n" ; // // typedefs string tmp_type = c_type; if( c_type == "long_double" ) tmp_type = "long double"; os << "// typedefs\n" "typedef " + tmp_type + " float_point_t;\n" "\n" ; // // externals os << "// externals\n"; size_t n_atomic = graph_obj.atomic_name_vec_size(); for(size_t i_atomic = 0; i_atomic < n_atomic; ++i_atomic) { string atomic_name = graph_obj.atomic_name_vec_get(i_atomic); os << "extern int cppad_atomic_" + atomic_name + "(\n"; os << "\tsize_t call_id ,\n" "\tsize_t nu ,\n" "\tconst float_point_t* u ,\n" "\tsize_t ny ,\n" "\tfloat_point_t* y ,\n" "\tsize_t* compare_change\n" ");\n" ; } size_t n_discrete = graph_obj.discrete_name_vec_size(); for(size_t i_discrete = 0; i_discrete < n_discrete; ++i_discrete) { string discrete_name = graph_obj.discrete_name_vec_get(i_discrete); os << "extern float_point_t cppad_discrete_" + discrete_name; os << "( float_point_t x );\n"; } // // azmul os << "// azmul\n" "static float_point_t azmul(float_point_t x, float_point_t y)\n" "{\tif( x == 0.0 ) return 0.0;\n" "\treturn x * y;\n" "}\n\n" ; // // sign os << "// sign\n" "static float_point_t sign(float_point_t x)\n" "{\tif( x > 0.0 ) return 1.0;\n" "\tif( x == 0.0 ) return 0.0;\n" "\treturn -1.0;\n" "}\n\n" ; // // This JIT function os << "// This JIT function\n" # ifdef _MSC_VER "__declspec(dllexport) int __cdecl " # else "int " # endif "cppad_jit_" + function_name + "(\n" "\tsize_t nu ,\n" "\tconst float_point_t* u ,\n" "\tsize_t ny ,\n" "\tfloat_point_t* y ,\n" "\tsize_t* compare_change )\n" ; // // begin function body os << "{\t// begin function body \n" "\n" ; // // declare variables // v, i, nan os << "\t// declare variables\n" "\tfloat_point_t v[" + to_string(n_node) + "];\n" "\tsize_t i;\n" "\n" "\t// check nu, ny\n" ; // // nx size_t nu = n_dynamic_ind + n_variable_ind; os << "\tif( nu != " + to_string(nu) + ") return 1;\n"; // // ny size_t ny = n_dependent; os << "\tif( ny != " + to_string(ny) + ") return 2;\n"; // // initialize // compare_change, v[0] os << "\n" "\t// initialize\n" "\tv[0] = NAN; // const \n" ; // // independent variables // set v[1+i] for i = 0, ..., nx-1" os << "\n" "\t// independent variables\n" "\t// set v[1+i] for i = 0, ..., nu-1\n" "\tfor(i = 0; i < nu; ++i)\n" "\t\tv[1+i] = u[i];\n" ; // // cosntants // set v[1+nu+i] for i = 0, ..., nc-1 size_t nc = n_constant; os << "\n" "\t// constants\n" "\t// set v[1+nu+i] for i = 0, ..., nc-1\n" "\t// nc = " + to_string(nc) + "\n" ; for(size_t i = 0; i < nc; ++i) { double c_i = graph_obj.constant_vec_get(i); os << "\tv[1+nu+" + to_string(i) + "] = " + to_string(c_i) + ";\n" ; } // // result nodes // set v[1+nu+nc+i] for i = 0, ..., n_result_node-1 size_t n_result_node = n_node - first_result_node; os << "\n" "\t// result nodes\n" "\t// set v[1+nu+nc+i] for i = 0, ..., n_result_node-1\n" "\t// n_result_node = " + to_string(n_result_node) + "\n" ; // // result_node size_t result_node = first_result_node; // // op_index for(size_t op_index = 0; op_index < n_usage; ++op_index) { // // graph_itr if( op_index == 0 ) graph_itr = graph_obj.begin(); else ++graph_itr; // // str_index, op_enum, call_id, n_result, arg_node cpp_graph::const_iterator::value_type itr_value = *graph_itr; const vector& str_index( *itr_value.str_index_ptr ); const vector& arg_node( *itr_value.arg_node_ptr ); graph_op_enum op_enum = itr_value.op_enum; size_t call_id = itr_value.call_id; size_t n_result = itr_value.n_result; CPPAD_ASSERT_UNKNOWN( arg_node.size() > 0 ); // // op_csrc const char* op_csrc = nullptr; switch( op_enum ) { // ------------------------------------------------------------- // binary functions // ------------------------------------------------------------- case azmul_graph_op: case pow_graph_op: op_csrc = op_enum2name[op_enum]; break; // ------------------------------------------------------------- // binary operators // ------------------------------------------------------------- case add_graph_op: op_csrc = "+"; break; case div_graph_op: op_csrc = "/"; break; case mul_graph_op: op_csrc = "*"; break; case sub_graph_op: op_csrc = "-"; break; // ------------------------------------------------------------- // comparison operators // ------------------------------------------------------------- case comp_eq_graph_op: op_csrc = "!="; // not eq break; case comp_le_graph_op: op_csrc = ">"; // not le break; case comp_lt_graph_op: op_csrc = ">="; // not lt break; case comp_ne_graph_op: op_csrc = "=="; // not ne break; // ------------------------------------------------------------- // unary functions // ------------------------------------------------------------- case abs_graph_op: op_csrc = "fabs"; break; // case acos_graph_op: case acosh_graph_op: case asin_graph_op: case asinh_graph_op: case atan_graph_op: case atanh_graph_op: case cos_graph_op: case cosh_graph_op: case erf_graph_op: case erfc_graph_op: case exp_graph_op: case expm1_graph_op: case log1p_graph_op: case log_graph_op: case sign_graph_op: case sin_graph_op: case sinh_graph_op: case sqrt_graph_op: case tan_graph_op: case tanh_graph_op: op_csrc = op_enum2name[op_enum]; break; // --------------------------------------------------------------- // operators that do not use op_csrc // --------------------------------------------------------------- case atom4_graph_op: case discrete_graph_op: case sum_graph_op: op_csrc = ""; break; default: { string msg = op_enum2name[op_enum]; msg = "f.to_csrc: The " + msg + " is not yet implemented."; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } break; } // // csrc switch( op_enum ) { // // binary functions case azmul_graph_op: case pow_graph_op: CPPAD_ASSERT_UNKNOWN( arg_node.size() == 2 ); CPPAD_ASSERT_UNKNOWN( n_result == 1 ); binary_function( os, op_csrc, result_node, arg_node[0], arg_node[1] ); break; // // binary operators case add_graph_op: case div_graph_op: case mul_graph_op: case sub_graph_op: CPPAD_ASSERT_UNKNOWN( arg_node.size() == 2 ); CPPAD_ASSERT_UNKNOWN( n_result == 1 ); binary_operator( os, op_csrc, result_node, arg_node[0], arg_node[1] ); break; // // comparison operators case comp_eq_graph_op: case comp_le_graph_op: case comp_lt_graph_op: case comp_ne_graph_op: CPPAD_ASSERT_UNKNOWN( arg_node.size() == 2 ); CPPAD_ASSERT_UNKNOWN( n_result == 0 ); compare_operator( os, op_csrc, arg_node[0], arg_node[1]) ; break; // // unary functions case abs_graph_op: case acos_graph_op: case acosh_graph_op: case asin_graph_op: case asinh_graph_op: case atan_graph_op: case atanh_graph_op: case cos_graph_op: case cosh_graph_op: case erf_graph_op: case erfc_graph_op: case exp_graph_op: case expm1_graph_op: case log1p_graph_op: case log_graph_op: case sign_graph_op: case sin_graph_op: case sinh_graph_op: case sqrt_graph_op: case tan_graph_op: case tanh_graph_op: CPPAD_ASSERT_UNKNOWN( arg_node.size() == 1 ); CPPAD_ASSERT_UNKNOWN( n_result == 1 ); unary_function( os, op_csrc, result_node, arg_node[0] ); break; // // atom4 case atom4_graph_op: { size_t index = str_index[0]; string atomic_name = graph_obj.atomic_name_vec_get(index); atomic_function(os, result_node, atomic_name, call_id, n_result, arg_node ); } break; // // discrete case discrete_graph_op: CPPAD_ASSERT_UNKNOWN( arg_node.size() == 1 ); CPPAD_ASSERT_UNKNOWN( n_result == 1 ); { size_t index = str_index[0]; string discrete_name = graph_obj.discrete_name_vec_get(index); discrete_function(os, result_node, discrete_name, arg_node[0] ); } break; // // sum case sum_graph_op: CPPAD_ASSERT_UNKNOWN( n_result == 1 ); sum_operator(os, result_node, arg_node); break; // // default default: CPPAD_ASSERT_UNKNOWN(false); break; } // // result_node result_node += n_result; } // ---------------------------------------------------------------------- // dependent os << "\n" "\t// dependent variables\n" "\t// set y[i] for i = 0, ny-1\n" ; for(size_t i = 0; i < ny; ++i) { size_t node = graph_obj.dependent_vec_get(i); os << "\t" + element("y", i) + " = " + element("v", node) + ";\n"; } // ---------------------------------------------------------------------- // end function body os << "\n"; os << "\treturn 0;\n"; os << "}\n"; // return; } ================================================ FILE: cppad_lib/json_lexer.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include # include // BEGIN_CPPAD_LOCAL_GRAPH_NAMESPACE namespace CppAD { namespace local { namespace graph { // report_error void json_lexer::report_error( const std::string& expected , const std::string& found ) { size_t pos = index_; size_t count_newline = 0; while(0 < pos && count_newline < 2 ) { --pos; count_newline += json_[pos] == '\n'; } std::string recent_input = json_.substr( pos, index_ - pos + 1); std::string msg = "Error occurred while parsing Json AD graph"; if( function_name_ != "" ) msg += " for the function " + function_name_; msg += ".\n"; msg += "Expected a " + expected + " token but found " + found + "\n"; msg += "Detected at end of following input:"; msg += recent_input + "\n"; msg += "This end is character " + to_string(char_number_); msg += " in line " + to_string(line_number_) + " of the json.\n"; msg += "See https://coin-or.github.io/CppAD/doc/json_ad_graph.htm."; // // use this source code as point of detection bool known = true; int line = __LINE__; const char* file = __FILE__; const char* exp = "false"; // // CppAD error handler ErrorHandler::Call(known, line, file, exp, msg.c_str()); } // next_index void json_lexer::next_index(void) { CPPAD_ASSERT_UNKNOWN( index_ < json_.size() ); if( json_[index_] == '\n' ) { ++line_number_; char_number_ = 0; } ++index_; ++char_number_; } // skip_white_space void json_lexer::skip_white_space(void) { while( index_ < json_.size() && isspace( json_[index_] ) ) next_index(); } // constructor json_lexer::json_lexer(const std::string& json) : json_(json), index_(0), line_number_(1), char_number_(1), token_(""), function_name_("") { skip_white_space(); if( index_ < json_.size() ) token_ = json_[index_]; if( token_ != "{" ) { std::string expected = "'{'"; std::string found = "'"; if( index_ < json_.size() ) found += json_[index_]; found += "'"; report_error(expected, found); } return; } // token const std::string& json_lexer::token(void) const { return token_; } // line_number size_t json_lexer::line_number(void) const { return line_number_; } // char_number size_t json_lexer::char_number(void) const { return char_number_; } // set_function_name void json_lexer::set_function_name(const std::string& function_name) { function_name_ = function_name; } // token2size_t size_t json_lexer::token2size_t(void) const { return size_t( std::atoi( token_.c_str() ) ); } // token2double double json_lexer::token2double(void) const { return std::atof( token_.c_str() ); } // check_next_char void json_lexer::check_next_char(char ch) { // advance to next character if( index_ < json_.size() ) next_index(); skip_white_space(); // bool ok = false; if( index_ < json_.size() ) { token_.resize(1); token_[0] = json_[index_]; ok = (token_[0] == ch) || (ch == '\0'); } if( ! ok ) { std::string expected = "a character that is not white space"; if( ch != '\0' ) { expected = "'"; expected += ch; expected += "'"; } // std::string found = "'"; if( index_ < json_.size() ) found += json_[index_];; found += "'"; report_error(expected, found); } } // check_next_string void json_lexer::check_next_string(const std::string& expected) { // advance to next character bool found_first_quote = index_ < json_.size(); if( found_first_quote ) { next_index(); skip_white_space(); found_first_quote = index_ < json_.size(); } // check for " if( found_first_quote ) found_first_quote = json_[index_] == '"'; // // set value of token token_.resize(0); if( found_first_quote ) { next_index(); while( index_ < json_.size() && json_[index_] != '"' ) { token_.push_back( json_[index_] ); next_index(); } } // check for " bool found_second_quote = false; if( found_first_quote && index_ < json_.size() ) found_second_quote = json_[index_] == '"'; // bool ok = found_first_quote & found_second_quote; if( ok & (expected != "" ) ) ok = expected == token_; if( ! ok ) { std::string expected_token; if( expected == "" ) expected_token = "string"; else { expected_token = '"'; expected_token += expected; expected_token += '"'; } // std::string found; if( ! found_first_quote ) { found = "'"; if( index_ < json_.size() ) found += json_[index_]; found += "'"; } else { found += '"'; found += token_; if( found_second_quote ) found += '"'; } report_error(expected_token, found); } } // next_non_neg_int void json_lexer::next_non_neg_int(void) { // advance to next character bool ok = index_ < json_.size(); if( ok ) { next_index(); skip_white_space(); ok = index_ < json_.size(); } if( ok ) ok = std::isdigit( json_[index_] ); if( ! ok ) { std::string expected_token = "non-negative integer"; std::string found = "'"; if( index_ < json_.size() ) found += json_[index_]; found += "'"; report_error(expected_token, found); } // token_.resize(0); while( ok ) { token_.push_back( json_[index_] ); ok = index_ + 1 < json_.size(); if( ok ) ok = isdigit( json_[index_ + 1] ); if( ok ) next_index(); } } // next_float void json_lexer::next_float(void) { // advance to next character bool ok = index_ < json_.size(); if( ok ) { next_index(); skip_white_space(); ok = index_ < json_.size(); } if( ok ) { char ch = json_[index_]; ok = std::isdigit(ch); ok |= (ch == '.') || (ch == '+') || (ch == '-'); ok |= (ch == 'e') || (ch == 'E'); } if( ! ok ) { std::string expected_token = "floating point number"; std::string found = "'"; if( index_ < json_.size() ) found += json_[index_]; found += "'"; report_error(expected_token, found); } // token_.resize(0); while( ok ) { token_.push_back( json_[index_] ); ok = index_ + 1 < json_.size(); if( ok ) { char ch = json_[index_ + 1]; ok = isdigit(ch); ok |= (ch == '.') || (ch == '+') || (ch == '-'); ok |= (ch == 'e') || (ch == 'E'); } if( ok ) next_index(); } return; } } } } // END_CPPAD_LOCAL_GRAPH_NAMESPACE ================================================ FILE: cppad_lib/json_parser.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include // documentation for this routine is in the file below # include void CppAD::local::graph::json_parser( const std::string& json , cpp_graph& graph_obj ) { using std::string; // // // match_any_string const string match_any_string = ""; // // initialize atomic_name_vec graph_obj.initialize(); // // The values in this vector will be set while parsing op_define_vec. // Note that the values in op_code2enum[0] are not used. CppAD::vector op_code2enum(1); // // ----------------------------------------------------------------------- // json_lexer constructor checks for { at beginning CppAD::local::graph::json_lexer json_lexer(json); // // "function_name" : function_name json_lexer.check_next_string("function_name"); json_lexer.check_next_char(':'); json_lexer.check_next_string(match_any_string); std::string function_name = json_lexer.token(); graph_obj.function_name_set(function_name); json_lexer.set_function_name(function_name); json_lexer.check_next_char(','); // // ----------------------------------------------------------------------- // "op_define_vec" : [ n_define, [ json_lexer.check_next_string("op_define_vec"); json_lexer.check_next_char(':'); json_lexer.check_next_char('['); // json_lexer.next_non_neg_int(); size_t n_define = json_lexer.token2size_t(); json_lexer.check_next_char(','); // json_lexer.check_next_char('['); for(size_t i = 0; i < n_define; ++i) { // { json_lexer.check_next_char('{'); // // "op_code" : op_code, json_lexer.check_next_string("op_code"); json_lexer.check_next_char(':'); json_lexer.next_non_neg_int(); # ifndef NDEBUG size_t op_code = json_lexer.token2size_t(); CPPAD_ASSERT_UNKNOWN( op_code == op_code2enum.size() ); # endif json_lexer.check_next_char(','); // // "name" : name json_lexer.check_next_string("name"); json_lexer.check_next_char(':'); json_lexer.check_next_string(match_any_string); string name = json_lexer.token(); graph_op_enum op_enum = op_name2enum[name]; // // op_code2enum for this op_code op_code2enum.push_back(op_enum); // size_t n_arg = op_enum2fixed_n_arg[op_enum]; if( n_arg > 0 ) { // , "narg" : n_arg json_lexer.check_next_char(','); json_lexer.check_next_string("n_arg"); json_lexer.check_next_char(':'); json_lexer.next_non_neg_int(); if( n_arg != json_lexer.token2size_t() ) { string expected = CppAD::to_string(n_arg); string found = json_lexer.token(); json_lexer.report_error(expected, found); } } json_lexer.check_next_char('}'); // // , (if not last entry) if( i + 1 < n_define ) json_lexer.check_next_char(','); } json_lexer.check_next_char(']'); // ], json_lexer.check_next_char(']'); json_lexer.check_next_char(','); // // ----------------------------------------------------------------------- // "n_dynamic_ind" : n_dynamic_ind , json_lexer.check_next_string("n_dynamic_ind"); json_lexer.check_next_char(':'); // json_lexer.next_non_neg_int(); size_t n_dynamic_ind = json_lexer.token2size_t(); graph_obj.n_dynamic_ind_set(n_dynamic_ind); // json_lexer.check_next_char(','); // ----------------------------------------------------------------------- // "n_variable_ind" : n_variable_ind , json_lexer.check_next_string("n_variable_ind"); json_lexer.check_next_char(':'); // json_lexer.next_non_neg_int(); size_t n_variable_ind = json_lexer.token2size_t(); graph_obj.n_variable_ind_set(n_variable_ind); // json_lexer.check_next_char(','); // ----------------------------------------------------------------------- // "constant_vec": [ n_constant, [ first_constant, ..., last_constant ] ], json_lexer.check_next_string("constant_vec"); json_lexer.check_next_char(':'); json_lexer.check_next_char('['); // json_lexer.next_non_neg_int(); size_t n_constant = json_lexer.token2size_t(); // json_lexer.check_next_char(','); // // [ first_constant, ... , last_constant ] json_lexer.check_next_char('['); for(size_t i = 0; i < n_constant; ++i) { json_lexer.next_float(); graph_obj.constant_vec_push_back( json_lexer.token2double() ); // if( i + 1 < n_constant ) json_lexer.check_next_char(','); } json_lexer.check_next_char(']'); json_lexer.check_next_char(']'); json_lexer.check_next_char(','); // ----------------------------------------------------------------------- // "op_usage_vec": [ n_usage, [ first_op_usage, ..., last_op_usage ] ], json_lexer.check_next_string("op_usage_vec"); json_lexer.check_next_char(':'); json_lexer.check_next_char('['); // json_lexer.next_non_neg_int(); size_t n_usage = json_lexer.token2size_t(); // json_lexer.check_next_char(','); json_lexer.check_next_char('['); // // index for strings in current operator CppAD::vector str_index; for(size_t i = 0; i < n_usage; ++i) { str_index.resize(0); // // [ op_code, json_lexer.check_next_char('['); // // op_enum json_lexer.next_non_neg_int(); graph_op_enum op_enum = op_code2enum[ json_lexer.token2size_t() ]; json_lexer.check_next_char(','); // size_t call_id = std::numeric_limits::max(); size_t n_result = 1; size_t n_arg = op_enum2fixed_n_arg[op_enum]; // // check if number of arguments is fixed bool fixed = n_arg > 0; if( ! fixed ) { if( op_enum == discrete_graph_op ) { // name, json_lexer.check_next_string(match_any_string); string name = json_lexer.token(); json_lexer.check_next_char(','); // size_t name_index = graph_obj.discrete_name_vec_find(name); if( name_index == graph_obj.discrete_name_vec_size() ) graph_obj.discrete_name_vec_push_back( name ); str_index.push_back(name_index); } else if( op_enum == atom_graph_op || op_enum == atom4_graph_op ) { // name json_lexer.check_next_string(match_any_string); string name = json_lexer.token(); json_lexer.check_next_char(','); // size_t name_index = graph_obj.atomic_name_vec_find(name); if( name_index == graph_obj.atomic_name_vec_size() ) graph_obj.atomic_name_vec_push_back( name ); str_index.push_back(name_index); } else if( op_enum == print_graph_op ) { // before json_lexer.check_next_string(match_any_string); string before = json_lexer.token(); json_lexer.check_next_char(','); // size_t before_index = graph_obj.print_text_vec_find(before); if( before_index == graph_obj.print_text_vec_size() ) graph_obj.print_text_vec_push_back(before); str_index.push_back(before_index); // // aftere json_lexer.check_next_string(match_any_string); string after = json_lexer.token(); json_lexer.check_next_char(','); // size_t after_index = graph_obj.print_text_vec_find(after); if( after_index == graph_obj.print_text_vec_size() ) graph_obj.print_text_vec_push_back(after); str_index.push_back(after_index); } else CPPAD_ASSERT_UNKNOWN( op_enum == comp_eq_graph_op || op_enum == comp_le_graph_op || op_enum == comp_lt_graph_op || op_enum == comp_ne_graph_op || op_enum == sum_graph_op ); if( op_enum == atom4_graph_op ) { json_lexer.next_non_neg_int(); call_id = json_lexer.token2size_t(); json_lexer.check_next_char(','); } // n_result, json_lexer.next_non_neg_int(); n_result = json_lexer.token2size_t(); json_lexer.check_next_char(','); // // n_arg, [ json_lexer.next_non_neg_int(); n_arg = json_lexer.token2size_t(); json_lexer.check_next_char(','); json_lexer.check_next_char('['); } // // atom_graph_op: name_index, n_result, n_arg // come before first function argument // // atom4_graph_op: name_index, call_id, n_result, n_arg // come before first function argument if( op_enum == atom_graph_op || op_enum == atom4_graph_op ) { // name_index, n_result, n_arg come before first_node size_t name_index = str_index[0]; CPPAD_ASSERT_UNKNOWN( name_index < graph_obj.atomic_name_vec_size() ); if( op_enum == atom4_graph_op ) graph_obj.operator_arg_push_back( call_id ); graph_obj.operator_arg_push_back( name_index ); graph_obj.operator_arg_push_back( n_result ); graph_obj.operator_arg_push_back( n_arg ); } // discrete_op: name_index comes before first argument if( op_enum == discrete_graph_op ) { size_t name_index = str_index[0]; graph_obj.operator_arg_push_back( name_index ); } // print_op: before_index, after_index come before first argument if( op_enum == print_graph_op ) { size_t before_index = str_index[0]; size_t after_index = str_index[1]; graph_obj.operator_arg_push_back( before_index ); graph_obj.operator_arg_push_back( after_index ); } // // sum_graph_op: n_arg comes before first argument if( op_enum == sum_graph_op ) graph_obj.operator_arg_push_back( n_arg ); // // operator_vec graph_op_enum op_usage; op_usage = op_enum; graph_obj.operator_vec_push_back( op_usage ); for(size_t j = 0; j < n_arg; ++j) { // next_arg json_lexer.next_non_neg_int(); size_t argument_node = json_lexer.token2size_t(); graph_obj.operator_arg_push_back( argument_node ); // // , (if not last entry) if( j + 1 < n_arg ) json_lexer.check_next_char(','); } json_lexer.check_next_char(']'); if( ! fixed ) json_lexer.check_next_char(']'); // // , (if not last entry) if( i + 1 < n_usage ) json_lexer.check_next_char(','); } json_lexer.check_next_char(']'); json_lexer.check_next_char(']'); json_lexer.check_next_char(','); // ----------------------------------------------------------------------- // "dependent_vec": [ n_dependent, [first_dependent, ..., last_dependent] ] json_lexer.check_next_string("dependent_vec"); json_lexer.check_next_char(':'); json_lexer.check_next_char('['); // json_lexer.next_non_neg_int(); size_t n_dependent = json_lexer.token2size_t(); json_lexer.check_next_char(','); // json_lexer.check_next_char('['); for(size_t i = 0; i < n_dependent; ++i) { json_lexer.next_float(); graph_obj.dependent_vec_push_back( json_lexer.token2size_t() ); // if( i + 1 < n_dependent ) json_lexer.check_next_char(','); } json_lexer.check_next_char(']'); json_lexer.check_next_char(']'); // ----------------------------------------------------------------------- // end of Json object json_lexer.check_next_char('}'); // return; } ================================================ FILE: cppad_lib/json_writer.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include // documentation for this routine is in the file below # include void CppAD::local::graph::json_writer( std::string& json , const cpp_graph& graph_obj ) { using std::string; // -------------------------------------------------------------------- const string& function_name( graph_obj.function_name_get() ); const size_t& n_dynamic_ind( graph_obj.n_dynamic_ind_get() ); const size_t& n_variable_ind( graph_obj.n_variable_ind_get() ); // -------------------------------------------------------------------- // // set: n_usage size_t n_usage = graph_obj.operator_vec_size(); // // set: is_graph_op_used pod_vector is_graph_op_used(n_graph_op); for(size_t i = 0; i < n_graph_op; ++i) is_graph_op_used[i] = false; for(size_t i = 0; i < n_usage; ++i) is_graph_op_used[ graph_obj.operator_vec_get(i) ] = true; // // set: n_define and graph_code size_t n_define = 0; pod_vector graph_code(n_graph_op); for(size_t i = 0; i < n_graph_op; ++i) { graph_code[i] = 0; if( is_graph_op_used[i] ) graph_code[i] = ++n_define; } // ---------------------------------------------------------------------- // output: starting '{' for this graph json = "{\n"; // // output: function_name json += "'function_name' : '" + function_name + "',\n"; // // output: op_define_vec json += "'op_define_vec' : [ " + to_string(n_define) + ", [\n"; size_t count_define = 0; for(size_t i = 0; i < n_graph_op; ++i) { if( is_graph_op_used[i] ) { ++count_define; const string name = op_enum2name[i]; size_t op_code = graph_code[i]; size_t n_arg = op_enum2fixed_n_arg[i]; json += "{ 'op_code':" + to_string(op_code); json += ", 'name':'" + name + "'"; if( n_arg != 0 ) json += ", 'n_arg':" + to_string(n_arg); json += " }"; if( count_define < n_define ) json += ",\n"; } } json += " ]\n] ,\n"; // // output: n_dynamic_ind json += "'n_dynamic_ind' : " + to_string( n_dynamic_ind ) + ",\n"; // // output: n_variable_ind json += "'n_variable_ind' : " + to_string( n_variable_ind ) + ",\n"; // // output: constant_vec size_t n_constant = graph_obj.constant_vec_size(); json += "'constant_vec' : [ " + to_string(n_constant) + ", [\n"; for(size_t i = 0; i < n_constant; ++i) { json += to_string( graph_obj.constant_vec_get(i) ); if( i + 1 < n_constant ) json += ",\n"; } json += " ] ],\n"; // ----------------------------------------------------------------------- // // defined here to avoid memory re-allocation for each operator vector arg; // // defined here because not using as loop index cpp_graph::const_iterator graph_itr; // // output: op_usage_vec json += "'op_usage_vec' : [ " + to_string(n_usage) + ", [\n"; for(size_t op_index = 0; op_index < n_usage; ++op_index) { // op_enum, str_index, n_result, arg_node if( op_index == 0 ) graph_itr = graph_obj.begin(); else ++graph_itr; // cpp_graph::const_iterator::value_type itr_value = *graph_itr; const vector& str_index( *itr_value.str_index_ptr ); graph_op_enum op_enum = itr_value.op_enum; size_t call_id = itr_value.call_id; size_t n_result = itr_value.n_result; size_t n_arg = itr_value.arg_node_ptr->size(); arg.resize(n_arg); arg = *(itr_value.arg_node_ptr); CPPAD_ASSERT_UNKNOWN( n_arg > 0 ); // // op_code size_t op_code = graph_code[op_enum]; // switch( op_enum ) { // -------------------------------------------------------------- // sum case sum_graph_op: json += "[ " + to_string(op_code) + ", 1, "; json += to_string(n_arg) + ", [ "; for(size_t j = 0; j < n_arg; ++j) { json += to_string( arg[j] ); if( j + 1 < n_arg ) json += ", "; } json += "] ]"; break; // -------------------------------------------------------------- // atom, atom4 case atom_graph_op: case atom4_graph_op: { size_t name_index = str_index[0]; string name = graph_obj.atomic_name_vec_get(name_index); json += "[ " + to_string(op_code) + ", "; json += "'" + name + "', "; } if( op_enum == atom4_graph_op ) json += to_string(call_id) + ", "; json += to_string(n_result) + ", "; json += to_string(n_arg) + ", ["; for(size_t j = 0; j < n_arg; ++j) { json += to_string( arg[j] ); if( j + 1 < n_arg ) json += ", "; else json += " ]"; } json += " ]"; break; // -------------------------------------------------------------- // comparison operators case comp_eq_graph_op: case comp_ne_graph_op: case comp_lt_graph_op: case comp_le_graph_op: CPPAD_ASSERT_UNKNOWN( n_result == 0 ); CPPAD_ASSERT_UNKNOWN( n_arg == 2 ); json += "[ " + to_string(op_code) + ", 0, 2, [ "; json += to_string( arg[0] ) + ", "; json += to_string( arg[1] ) + " ] ]"; break; // -------------------------------------------------------------- // discrete case discrete_graph_op: CPPAD_ASSERT_UNKNOWN( n_result == 1 ); CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); { size_t name_index = str_index[0]; string name = graph_obj.discrete_name_vec_get(name_index); json += "[ " + to_string(op_code) + ", "; json += "'" + name + "', "; } json += to_string(n_result) + ", "; json += to_string(n_arg) + ", [ "; json += to_string( arg[0] ) + " ] ]"; break; // -------------------------------------------------------------- // print_op case print_graph_op: CPPAD_ASSERT_UNKNOWN( n_result == 0 ); CPPAD_ASSERT_UNKNOWN( n_arg == 2 ); { size_t before_index = str_index[0]; size_t after_index = str_index[1]; string before = graph_obj.print_text_vec_get(before_index); string after = graph_obj.print_text_vec_get(after_index); json += "[ " + to_string(op_code) + ", "; json += "'" + before + "', "; json += "'" + after + "', 0, 2, [ "; json += to_string( arg[0] ) + ", "; json += to_string( arg[1] ) + " ] ]"; } break; // -------------------------------------------------------------- default: CPPAD_ASSERT_UNKNOWN( n_result == 1 ); CPPAD_ASSERT_UNKNOWN( op_enum2fixed_n_arg[op_enum] == n_arg ); json += "[ " + to_string(op_code) + ", "; for(size_t j = 0; j < n_arg; ++j) { json += to_string( arg[j] ); if( j + 1 < n_arg ) json += ", "; else json += " ]"; } break; } // end switch if( op_index + 1 < n_usage ) json += ",\n"; } json += "\n] ],\n"; // ---------------------------------------------------------------------- // output: dependent_vec size_t n_dependent = graph_obj.dependent_vec_size(); json += "'dependent_vec' : [ " + to_string(n_dependent) + ", [ "; for(size_t i = 0; i < n_dependent; ++i) { json += to_string( graph_obj.dependent_vec_get(i) ); if( i + 1 < n_dependent ) json += ", "; } json += " ] ]\n"; // // output: ending '}' for this graph json += "}\n"; // ---------------------------------------------------------------------- // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // return; } ================================================ FILE: cppad_lib/link_dll_lib.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # ifndef _WIN32 // dlopen, dlsym, dlerror, dlclose, RTLD_LAZY # include # else // LoadLibrary, GetProcAddress, FreeLibrary, GetLastError, RTLD_LAZY # ifndef NOMINMAX # define NOMINMAX # endif # include # define RTLD_LAZY 0 # endif namespace CppAD { // BEGIN_CPPAD_NAMESPACE # ifdef _WIN32 // // dlopen void* link_dll_lib::dlopen(const char *filename, int flag) { HINSTANCE hinstance = LoadLibraryA(filename); return reinterpret_cast( hinstance ); } // dlsym void* link_dll_lib::dlsym(void* handle, const char* symbol) { HINSTANCE hinstance = reinterpret_cast( handle ); FARPROC farproc = GetProcAddress(hinstance, symbol); return reinterpret_cast( farproc ); } // dlclose int link_dll_lib::dlclose(void* handle) { HINSTANCE hinstance = reinterpret_cast( handle ); int result = FreeLibrary(hinstance); return result; } // dlerror const char* link_dll_lib::dlerror(void) { // should have a different result for each thread static char result[100]; // DWORD dw = GetLastError(); std::string str = CppAD::to_string(dw); str = "Microsoft GetLastError() = " + str; size_t i = 0; while(i < 99 && i < str.size()) result[i] = str[i]; result[i] = '\0'; // return result; } # endif // link_dll_lib::link_dll_lib(const std::string& dll_file, std::string& err_msg) { handle_ = dlopen(dll_file.c_str(), RTLD_LAZY); if( handle_ != nullptr ) err_msg = ""; else { const char *err_str = dlerror(); err_msg = "Error opening dll_file =" + dll_file; if( err_str != nullptr ) { err_msg += "\n"; err_msg += err_str; } } ctor_err_msg_ = err_msg; } link_dll_lib::~link_dll_lib(void) { if( handle_ != nullptr ) dlclose(handle_); } void* link_dll_lib::operator() (const std::string& function_name, std::string& err_msg) const // END_OPERATOR { if( handle_ == nullptr ) { err_msg = ctor_err_msg_; return nullptr; } void* fun_ptr = dlsym(handle_, function_name.c_str()); if( fun_ptr == nullptr ) err_msg = "Error finding function_name = " + function_name; else err_msg = ""; return fun_ptr; } } // END_CPPAD_NAMESPACE ================================================ FILE: cppad_lib/temp_file.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin temp_file dev} {xrst_spell mkstemp } Create a New Empty Temporary File ################################# Syntax ****** | *file_name* = ``temp_file`` () file_name ********* Is the name of a new temporary file. This file did not exist when ``temp_file`` was called. It has been created and is empty upon return. Empty ===== This routine failed if the return value *file_name* is empty. Thread Safe *********** This routine is thread safe when C++17 and ``mkstemp`` are available to CppAD. {xrst_end temp_file} */ # include # include # include # include # if CPPAD_HAS_MKSTEMP && CPPAD_USE_CPLUSPLUS_2017 # include # else # include # endif # if _MSC_VER # include # else # include # endif # ifdef _WIN32 # define DIR_SEP '\\' # else # define DIR_SEP '/' # endif namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE std::string temp_file(void) # if CPPAD_HAS_MKSTEMP && CPPAD_USE_CPLUSPLUS_2017 { // path using std::filesystem::path; // // tmp_dir_path path tmp_dir_path = std::filesystem::temp_directory_path(); // // tmp_dir_str std::string tmp_dir_str = tmp_dir_path.string(); if( tmp_dir_str.back() != DIR_SEP ) tmp_dir_str += DIR_SEP; // // pattern_str std::string pattern_str = tmp_dir_str; pattern_str += "fileXXXXXX"; // // pattern_vec std::vector pattern_vec( pattern_str.size() + 1 ); for(size_t i = 0; i < pattern_str.size(); ++i) pattern_vec[i] = pattern_str[i]; pattern_vec[ pattern_str.size() ] = '\0'; // // fd, pattrern_vec int fd = mkstemp( pattern_vec.data() ); if( fd < 0 ) return ""; close(fd); // // file_name std::string file_name = pattern_vec.data(); return file_name; } # else // CPPAD_HAS_MKSTEMP && CPPAD_USE_CPLUSPLUS_2017 { # if CPPAD_HAS_TMPNAM_S char c_str[L_tmpnam_s]; tmpnam_s(c_str, L_tmpnam_s ); # else char c_str[L_tmpnam]; tmpnam(c_str); # endif # ifdef __MINGW32__ // https://stackoverflow.com/questions/38868858/ // fopen-of-file-name-created-by-tmpnam-fails-on-mingw std::string file_name = c_str + 1; # else std::string file_name = c_str; # endif FILE* fp = fopen(file_name.c_str(), "r"); if( fp != NULL ) return ""; fp = fopen(file_name.c_str(), "w"); if( fp == NULL ) return ""; fclose(fp); return file_name; } # endif // CPPAD_HAS_MKSTEMP && CPPAD_USE_CPLUSPLUS_2017 } } // END_CPPAD_LOCAL_NAMESPACE ================================================ FILE: epl-2.0.txt ================================================ Eclipse Public License - v 2.0 THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 1. DEFINITIONS "Contribution" means: a) in the case of the initial Contributor, the initial content Distributed under this Agreement, and b) in the case of each subsequent Contributor: i) changes to the Program, and ii) additions to the Program; where such changes and/or additions to the Program originate from and are Distributed by that particular Contributor. A Contribution "originates" from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include changes or additions to the Program that are not Modified Works. "Contributor" means any person or entity that Distributes the Program. "Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. "Program" means the Contributions Distributed in accordance with this Agreement. "Recipient" means anyone who receives the Program under this Agreement or any Secondary License (as applicable), including Contributors. "Derivative Works" shall mean any work, whether in Source Code or other form, that is based on (or derived from) the Program and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. "Modified Works" shall mean any work in Source Code or other form that results from an addition to, deletion from, or modification of the contents of the Program, including, for purposes of clarity any new file in Source Code form that contains any contents of the Program. Modified Works shall not include works that contain only declarations, interfaces, types, classes, structures, or files of the Program solely in each case in order to link to, bind by name, or subclass the Program or Modified Works thereof. "Distribute" means the acts of a) distributing or b) making available in any manner that enables the transfer of a copy. "Source Code" means the form of a Program preferred for making modifications, including but not limited to software source code, documentation source, and configuration files. "Secondary License" means either the GNU General Public License, Version 2.0, or any later versions of that license, including any exceptions or additional permissions as identified by the initial Contributor. 2. GRANT OF RIGHTS a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, Distribute and sublicense the Contribution of such Contributor, if any, and such Derivative Works. b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in Source Code or other form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to Distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. e) Notwithstanding the terms of any Secondary License, no Contributor makes additional grants to any Recipient (other than those set forth in this Agreement) as a result of such Recipient's receipt of the Program under the terms of a Secondary License (if permitted under the terms of Section 3). 3. REQUIREMENTS 3.1 If a Contributor Distributes the Program in any form, then: a) the Program must also be made available as Source Code, in accordance with section 3.2, and the Contributor must accompany the Program with a statement that the Source Code for the Program is available under this Agreement, and informs Recipients how to obtain it in a reasonable manner on or through a medium customarily used for software exchange; and b) the Contributor may Distribute the Program under a license different than this Agreement, provided that such license: i) effectively disclaims on behalf of all other Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; ii) effectively excludes on behalf of all other Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; iii) does not attempt to limit or alter the recipients' rights in the Source Code under section 3.2; and iv) requires any subsequent distribution of the Program by any party to be under a license that satisfies the requirements of this section 3. 3.2 When the Program is Distributed as Source Code: a) it must be made available under this Agreement, or if the Program (i) is combined with other material in a separate file or files made available under a Secondary License, and (ii) the initial Contributor attached to the Source Code the notice described in Exhibit A of this Agreement, then the Program may be made available under the terms of such Secondary Licenses, and b) a copy of this Agreement must be included with each copy of the Program. 3.3 Contributors may not remove or alter any copyright, patent, trademark, attribution notices, disclaimers of warranty, or limitations of liability ("notices") contained within the Program from any copy of the Program which they Distribute, provided that Contributors may add their own appropriate notices. 4. COMMERCIAL DISTRIBUTION Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. 5. NO WARRANTY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. 6. DISCLAIMER OF LIABILITY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. GENERAL If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be Distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to Distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. Nothing in this Agreement is intended to be enforceable by any entity that is not a Contributor or Recipient. No third-party beneficiary rights are created under this Agreement. Exhibit A - Form of Secondary Licenses Notice "This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), version(s), and exceptions or additional permissions here}." Simply including a copy of this Agreement, including this Exhibit A is not sufficient to license the Source Code under Secondary Licenses. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. ================================================ FILE: example/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the example directory tests # Inherit environment from ../CMakeList.txt # initialize check_example_depends SET(check_example_depends "") # # BEGIN_SORT_THIS_LINE_PLUS_1 ADD_SUBDIRECTORY(abs_normal) ADD_SUBDIRECTORY(atomic_four) ADD_SUBDIRECTORY(atomic_three) ADD_SUBDIRECTORY(atomic_two) ADD_SUBDIRECTORY(chkpoint_two) ADD_SUBDIRECTORY(compare_change) ADD_SUBDIRECTORY(general) ADD_SUBDIRECTORY(get_started) ADD_SUBDIRECTORY(graph) ADD_SUBDIRECTORY(json) ADD_SUBDIRECTORY(multi_thread) ADD_SUBDIRECTORY(optimize) ADD_SUBDIRECTORY(print_for) ADD_SUBDIRECTORY(sparse) ADD_SUBDIRECTORY(utility) ADD_SUBDIRECTORY(valvector) # END_SORT_THIS_LINE_MINUS_1 # # cppad_code_gen examples IF( cppad_has_cppadcg ) ADD_SUBDIRECTORY(cppad_code_gen) ENDIF( cppad_has_cppadcg ) # # ipopt_solve examples IF( cppad_has_ipopt) ADD_SUBDIRECTORY(ipopt_solve) ENDIF( cppad_has_ipopt) # # jit examples IF( NOT cppad_link_flags_has_m32 ) ADD_SUBDIRECTORY(jit) ENDIF( ) # Add the check_example target ADD_CUSTOM_TARGET(check_example "" DEPENDS ${check_example_depends}) MESSAGE(STATUS "make check_example: available") # Change check depends in parent environment add_to_list(check_depends check_example) SET(check_depends "${check_depends}" PARENT_SCOPE) ================================================ FILE: example/abs_normal/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the example/print_for directory tests # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list abs_eval.cpp abs_min_linear.cpp abs_min_quad.cpp abs_normal.cpp get_started.cpp lp_box.cpp min_nso_linear.cpp min_nso_quad.cpp qp_box.cpp qp_interior.cpp simplex_method.cpp ) # END_SORT_THIS_LINE_MINUS_2 # set_compile_flags( example_abs_normal "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(example_abs_normal EXCLUDE_FROM_ALL ${source_list}) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_abs_normal ${cppad_lib} ${colpack_libs} ) # # check_example_abs_normal add_check_executable(check_example abs_normal) ================================================ FILE: example/abs_normal/abs_eval.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin abs_eval.cpp} abs_eval: Example and Test ########################## Purpose ******* The function :math:`f : \B{R}^3 \rightarrow \B{R}` defined by .. math:: f( x_0, x_1, x_2 ) = | x_0 + x_1 | + | x_1 + x_2 | is affine, except for its absolute value terms. For this case, the abs_normal approximation should be equal to the function itself. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end abs_eval.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include # include "abs_eval.hpp" namespace { CPPAD_TESTVECTOR(double) join( const CPPAD_TESTVECTOR(double)& x , const CPPAD_TESTVECTOR(double)& u ) { size_t n = x.size(); size_t s = u.size(); CPPAD_TESTVECTOR(double) xu(n + s); for(size_t j = 0; j < n; j++) xu[j] = x[j]; for(size_t j = 0; j < s; j++) xu[n + j] = u[j]; return xu; } } bool abs_eval(void) { bool ok = true; // using CppAD::AD; using CppAD::ADFun; // typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR( AD ) ad_vector; // double eps99 = 99.0 * std::numeric_limits::epsilon(); // size_t n = 3; // size of x size_t m = 1; // size of y size_t s = 2; // number of absolute value terms // // record the function f(x) ad_vector ad_x(n), ad_y(m); for(size_t j = 0; j < n; j++) ad_x[j] = double(j + 1); Independent( ad_x ); // for this example, we ensure first absolute value is | x_0 + x_1 | AD ad_0 = abs( ad_x[0] + ad_x[1] ); // and second absolute value is | x_1 + x_2 | AD ad_1 = abs( ad_x[1] + ad_x[2] ); ad_y[0] = ad_0 + ad_1; ADFun f(ad_x, ad_y); // create its abs_normal representation in g, a ADFun g, a; f.abs_normal_fun(g, a); // check dimension of domain and range space for g ok &= g.Domain() == n + s; ok &= g.Range() == m + s; // check dimension of domain and range space for a ok &= a.Domain() == n; ok &= a.Range() == s; // -------------------------------------------------------------------- // Choose a point x_hat d_vector x_hat(n); for(size_t j = 0; j < n; j++) x_hat[j] = double(j - 1); // value of a_hat = a(x_hat) d_vector a_hat = a.Forward(0, x_hat); // (x_hat, a_hat) d_vector xu_hat = join(x_hat, a_hat); // value of g[ x_hat, a_hat ] d_vector g_hat = g.Forward(0, xu_hat); // Jacobian of g[ x_hat, a_hat ] d_vector g_jac = g.Jacobian(xu_hat); // value of delta_x d_vector delta_x(n); delta_x[0] = 1.0; delta_x[1] = -2.0; delta_x[2] = +2.0; // value of x d_vector x(n); for(size_t j = 0; j < n; j++) x[j] = x_hat[j] + delta_x[j]; // value of f(x) d_vector y = f.Forward(0, x); // value of g_tilde d_vector g_tilde = CppAD::abs_eval(n, m, s, g_hat, g_jac, delta_x); // should be equal because f is affine, except for abs terms ok &= CppAD::NearEqual(y[0], g_tilde[0], eps99, eps99); return ok; } // END C++ ================================================ FILE: example/abs_normal/abs_eval.hpp ================================================ # ifndef CPPAD_EXAMPLE_ABS_NORMAL_ABS_EVAL_HPP # define CPPAD_EXAMPLE_ABS_NORMAL_ABS_EVAL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin abs_eval} abs_normal: Evaluate First Order Approximation ############################################## Syntax ****** | *g_tilde* = ``abs_eval`` ( *n* , *m* , *s* , *g_hat* , *g_jac* , *delta_x* ) Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Source ****** This following is a link to the source code for this example: :ref:`abs_eval.hpp-name` . Purpose ******* Given a current that abs-normal representation at a point :math:`\hat{x} \in \B{R}^n`, and a :math:`\Delta x \in \B{R}^n`, this routine evaluates the abs-normal :ref:`approximation for f(x)` where :math:`x = \hat{x} + \Delta x`. Vector ****** The type *Vector* is a simple vector with elements of type ``double`` . f * We use the notation *f* for the original function; see :ref:`abs_normal_fun@f` . n * This is the dimension of the domain space for *f* ; see :ref:`abs_normal_fun@f@n` . m * This is the dimension of the range space for *f* ; see :ref:`abs_normal_fun@f@m` . s * This is the number of absolute value terms in *f* ; see g * We use the notation *g* for the abs-normal representation of *f* ; see :ref:`abs_normal_fun@g` . g_hat ***** This vector has size *m* + *s* and is the value of *g* ( *x* , *u* ) at :math:`x = \hat{x}` and :math:`u = a( \hat{x} )`. g_jac ***** This vector has size ( *m* + *s* ) * ( *n* + *s* ) and is the Jacobian of :math:`g(x, u)` at :math:`x = \hat{x}` and :math:`u = a( \hat{x} )`. delta_x ******* This vector has size *n* and is the difference :math:`\Delta x = x - \hat{x}`, where :math:`x` is the point that we are approximating :math:`f(x)`. g_tilde ******* This vector has size *m* + *s* and is a the first order approximation for :ref:`abs_normal_fun@g` that corresponds to the point :math:`x = \hat{x} + \Delta x` and :math:`u = a(x)`. {xrst_toc_hidden example/abs_normal/abs_eval.cpp example/abs_normal/abs_eval.xrst } Example ******* The file :ref:`abs_eval.cpp-name` contains an example and test of ``abs_eval`` . {xrst_end abs_eval} ----------------------------------------------------------------------------- */ // BEGIN C++ namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN PROTOTYPE template Vector abs_eval( size_t n , size_t m , size_t s , const Vector& g_hat , const Vector& g_jac , const Vector& delta_x ) // END PROTOTYPE { using std::fabs; // CPPAD_ASSERT_KNOWN( size_t(delta_x.size()) == n, "abs_eval: size of delta_x not equal to n" ); CPPAD_ASSERT_KNOWN( size_t(g_hat.size()) == m + s, "abs_eval: size of g_hat not equal to m + s" ); CPPAD_ASSERT_KNOWN( size_t(g_jac.size()) == (m + s) * (n + s), "abs_eval: size of g_jac not equal to (m + s)*(n + s)" ); # ifndef NDEBUG // Check that partial_u z(x, u) is strictly lower triangular for(size_t i = 0; i < s; i++) { for(size_t j = i; j < s; j++) { // index in g_jac of partial of z_i w.r.t u_j // (note that g_jac has n + s elements in each row) size_t index = (m + i) * (n + s) + (n + j); CPPAD_ASSERT_KNOWN( g_jac[index] == 0.0, "abs_eval: partial z_i w.r.t u_j non-zero for i <= j" ); } } # endif // return value Vector g_tilde(m + s); // // compute z_tilde, the last s components of g_tilde for(size_t i = 0; i < s; i++) { // start at z_hat_i g_tilde[m + i] = g_hat[m + i]; // contribution for change x for(size_t j = 0; j < n; j++) { // index in g_jac of partial of z_i w.r.t x_j size_t index = (m + i) * (n + s) + j; // add contribution for delta_x_j to z_tilde_i g_tilde[m + i] += g_jac[index] * delta_x[j]; } // contribution for change in u_j for j < i for(size_t j = 0; j < i; j++) { // approixmation for change in absolute value double delta_a_j = fabs(g_tilde[m + j]) - fabs(g_hat[m + j]); // index in g_jac of partial of z_i w.r.t u_j size_t index = (m + i) * (n + s) + n + j; // add contribution for delta_a_j to s_tilde_i g_tilde[m + i] += g_jac[index] * delta_a_j; } } // // compute y_tilde, the first m components of g_tilde for(size_t i = 0; i < m; i++) { // start at y_hat_i g_tilde[i] = g_hat[i]; // contribution for change x for(size_t j = 0; j < n; j++) { // index in g_jac of partial of y_i w.r.t x_j size_t index = i * (n + s) + j; // add contribution for delta_x_j to y_tilde_i g_tilde[i] += g_jac[index] * delta_x[j]; } // contribution for change in u_j for(size_t j = 0; j < s; j++) { // approximation for change in absolute value double delta_a_j = fabs(g_tilde[m + j]) - fabs(g_hat[m + j]); // index in g_jac of partial of y_i w.r.t u_j size_t index = i * (n + s) + n + j; // add contribution for delta_a_j to s_tilde_i g_tilde[i] += g_jac[index] * delta_a_j; } } return g_tilde; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: example/abs_normal/abs_eval.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin abs_eval.hpp} abs_eval Source Code #################### {xrst_literal example/abs_normal/abs_eval.hpp // BEGIN C++ // END C++ } {xrst_end abs_eval.hpp} ================================================ FILE: example/abs_normal/abs_min_linear.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin abs_min_linear.cpp} abs_min_linear: Example and Test ################################ Purpose ******* The function :math:`f : \B{R}^3 \rightarrow \B{R}` defined by .. math:: :nowrap: \begin{eqnarray} f( x_0, x_1 ) & = & | d_0 - x_0 | + | d_1 - x_0 | + | d_2 - x_0 | \\ & + & | d_3 - x_1 | + | d_4 - x_1 | + | d_5 - x_1 | \\ \end{eqnarray} is affine, except for its absolute value terms. For this case, the abs_normal approximation should be equal to the function itself. In addition, the function is convex and :ref:`abs_min_linear-name` should find its global minimizer. The minimizer of this function is :math:`x_0 = \R{median}( d_0, d_1, d_2 )` and :math:`x_1 = \R{median}( d_3, d_4, d_5 )` Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end abs_min_linear.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include # include "abs_min_linear.hpp" namespace { CPPAD_TESTVECTOR(double) join( const CPPAD_TESTVECTOR(double)& x , const CPPAD_TESTVECTOR(double)& u ) { size_t n = x.size(); size_t s = u.size(); CPPAD_TESTVECTOR(double) xu(n + s); for(size_t j = 0; j < n; j++) xu[j] = x[j]; for(size_t j = 0; j < s; j++) xu[n + j] = u[j]; return xu; } } bool abs_min_linear(void) { bool ok = true; // using CppAD::AD; using CppAD::ADFun; // typedef CPPAD_TESTVECTOR(size_t) s_vector; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR( AD ) ad_vector; // size_t dpx = 3; // number of data points per x variable size_t level = 0; // level of tracing size_t n = 2; // size of x size_t m = 1; // size of y size_t s = dpx * n; // number of data points and absolute values // data points d_vector data(s); for(size_t i = 0; i < s; i++) data[i] = double(s - i) + 5.0 - double(i % 2) / 2.0; // // record the function f(x) ad_vector ad_x(n), ad_y(m); for(size_t j = 0; j < n; j++) ad_x[j] = double(j + 1); Independent( ad_x ); AD sum = 0.0; for(size_t j = 0; j < n; j++) for(size_t k = 0; k < dpx; k++) sum += abs( data[j * dpx + k] - ad_x[j] ); ad_y[0] = sum; ADFun f(ad_x, ad_y); // create its abs_normal representation in g, a ADFun g, a; f.abs_normal_fun(g, a); // check dimension of domain and range space for g ok &= g.Domain() == n + s; ok &= g.Range() == m + s; // check dimension of domain and range space for a ok &= a.Domain() == n; ok &= a.Range() == s; // -------------------------------------------------------------------- // Choose a point x_hat d_vector x_hat(n); for(size_t j = 0; j < n; j++) x_hat[j] = double(0.0); // value of a_hat = a(x_hat) d_vector a_hat = a.Forward(0, x_hat); // (x_hat, a_hat) d_vector xu_hat = join(x_hat, a_hat); // value of g[ x_hat, a_hat ] d_vector g_hat = g.Forward(0, xu_hat); // Jacobian of g[ x_hat, a_hat ] d_vector g_jac = g.Jacobian(xu_hat); // trust region bound (make large enough to include solutuion) d_vector bound(n); for(size_t j = 0; j < n; j++) bound[j] = 10.0; // convergence criteria d_vector epsilon(2); double eps99 = 99.0 * std::numeric_limits::epsilon(); epsilon[0] = eps99; epsilon[1] = eps99; // maximum number of iterations s_vector maxitr(2); maxitr[0] = 10; // maximum number of abs_min_linear iterations maxitr[1] = 35; // maximum number of qp_interior iterations // minimize the approximation for f, which is equal to f because // f is affine, except for absolute value terms d_vector delta_x(n); ok &= CppAD::abs_min_linear( level, n, m, s, g_hat, g_jac, bound, epsilon, maxitr, delta_x ); // number of data points per variable is odd ok &= dpx % 2 == 1; // check that the solution is the median of the corresponding data` for(size_t j = 0; j < n; j++) { // data[j * dpx + 0] , ... , data[j * dpx + dpx - 1] corresponds to x[j] // the median of this data has index j * dpx + dpx / 2 size_t j_median = j * dpx + (dpx / 2); // ok &= CppAD::NearEqual( delta_x[j], data[j_median], eps99, eps99 ); } return ok; } // END C++ ================================================ FILE: example/abs_normal/abs_min_linear.hpp ================================================ # ifndef CPPAD_EXAMPLE_ABS_NORMAL_ABS_MIN_LINEAR_HPP # define CPPAD_EXAMPLE_ABS_NORMAL_ABS_MIN_LINEAR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin abs_min_linear} {xrst_spell dbl lll maxitr } abs_normal: Minimize a Linear Abs-normal Approximation ###################################################### Syntax ****** | *ok* = ``abs_min_linear`` ( | |tab| *level* , *n* , *m* , *s* , | |tab| *g_hat* , *g_jac* , *bound* , *epsilon* , *maxitr* , *delta_x* | ) Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Source ****** This following is a link to the source code for this example: :ref:`abs_min_linear.hpp-name` . Purpose ******* We are given a point :math:`\hat{x} \in \B{R}^n` and use the notation :math:`\tilde{f} (x)` for the abs-normal :ref:`approximation for f(x)` near :math:`\hat{x}`. We are also given a vector :math:`b \in \B{R}_+^n`. This routine solves the problem .. math:: \begin{array}{lll} \R{minimize} & \tilde{f}(x) & \R{w.r.t} \; x \in \B{R}^n \\ \R{subject \; to} & | x_j - \hat{x}_j | \leq b_j & j = 0 , \ldots , n-1 \end{array} DblVector ********* is a :ref:`SimpleVector-name` class with elements of type ``double`` . SizeVector ********** is a :ref:`SimpleVector-name` class with elements of type ``size_t`` . f * We use the notation *f* for the original function; see :ref:`abs_normal_fun@f` . level ***** This value is less that or equal 4. If *level* == 0 , no tracing of the optimization is printed. If *level* >= 1 , a trace of each iteration of ``abs_min_linear`` is printed. If *level* >= 2 , a trace of the :ref:`lp_box-name` sub-problem is printed. If *level* >= 3 , a trace of the objective and primal variables :math:`x` are printed at each :ref:`simplex_method-name` iteration. If *level* == 4 , the simplex tableau is printed at each simplex iteration. n * This is the dimension of the domain space for *f* ; see :ref:`abs_normal_fun@f@n` . m * This is the dimension of the range space for *f* ; see :ref:`abs_normal_fun@f@m` . This must be one so that :math:`f` is an objective function. s * This is the number of absolute value terms in *f* ; see :ref:`abs_normal_fun@f@s` . g * We use the notation *g* for the abs-normal representation of *f* ; see :ref:`abs_normal_fun@g` . g_hat ***** This vector has size *m* + *s* and is the value of *g* ( *x* , *u* ) at :math:`x = \hat{x}` and :math:`u = a( \hat{x} )`. g_jac ***** This vector has size ( *m* + *s* ) * ( *n* + *s* ) and is the Jacobian of :math:`g(x, u)` at :math:`x = \hat{x}` and :math:`u = a( \hat{x} )`. bound ***** This vector has size *n* and we denote its value by :math:`b \in \B{R}^n`. The trust region is defined as the set of :math:`x` such that .. math:: | x_j - \hat{x}_j | \leq b_j for :math:`j = 0 , \ldots , n-1`, where :math:`x` is the point that we are approximating :math:`f(x)`. epsilon ******* The value *epsilon* [0] is convergence criteria in terms of the infinity norm of the difference of *delta_x* between iterations. The value *epsilon* [1] is convergence criteria in terms of the derivative of the objective; i.e., :math:`\tilde{f}(x)`. maxitr ****** This is a vector with size 2. The value *maxitr* [0] is the maximum number of ``abs_min_linear`` iterations to try before giving up on convergence. The value *maxitr* [1] is the maximum number of iterations in the :ref:`simplex_method` sub-problems. delta_x ******* This vector :math:`\Delta x` has size *n* . The input value of its elements does not matter. Upon return, the approximate minimizer of :math:`\tilde{f}(x)` with respect to the trust region is :math:`x = \hat{x} + \Delta x`. Method ****** sigma ===== We use the notation .. math:: \sigma (x) = \R{sign} ( z[ x , a(x) ] ) where :ref:`abs_normal_fun@a@a(x)` and :ref:`abs_normal_fun@g@z(x, u)` are as defined in the abs-normal representation of :math:`f(x)`. Cutting Planes ============== At each iteration, we are given affine functions :math:`p_k (x)` such that :math:`p_k ( x_k ) = \tilde{f}( x_k )` and :math:`p_k^{(1)} ( x_k )` is the derivative :math:`\tilde{f}^{(1)} ( x_k )` corresponding to :math:`\sigma ( x_k )`. Iteration ========= At iteration :math:`k`, we solve the problem .. math:: \begin{array}{lll} \R{minimize} & \max \{ p_k (x) \W{:} k = 0 , \ldots , K-1 \} & \R{w.r.t} \; x \\ \R{subject \; to} & - b \leq x \leq + b \end{array} The solution is the new point :math:`x_K` at which the new affine approximation :math:`p_K (x)` is constructed. This process is iterated until the difference :math:`x_K - x_{K-1}` is small enough. {xrst_toc_hidden example/abs_normal/abs_min_linear.cpp example/abs_normal/abs_min_linear.xrst } Example ******* The file :ref:`abs_min_linear.cpp-name` contains an example and test of ``abs_min_linear`` . {xrst_end abs_min_linear} ----------------------------------------------------------------------------- */ # include # include "lp_box.hpp" # include "abs_eval.hpp" // BEGIN C++ namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN PROTOTYPE template bool abs_min_linear( size_t level , size_t n , size_t m , size_t s , const DblVector& g_hat , const DblVector& g_jac , const DblVector& bound , const DblVector& epsilon , const SizeVector& maxitr , DblVector& delta_x ) // END PROTOTYPE { using std::fabs; bool ok = true; double inf = std::numeric_limits::infinity(); // CPPAD_ASSERT_KNOWN( level <= 4, "abs_min_linear: level is not less that or equal 4" ); CPPAD_ASSERT_KNOWN( size_t(epsilon.size()) == 2, "abs_min_linear: size of epsilon not equal to 2" ); CPPAD_ASSERT_KNOWN( size_t(maxitr.size()) == 2, "abs_min_linear: size of maxitr not equal to 2" ); CPPAD_ASSERT_KNOWN( m == 1, "abs_min_linear: m is not equal to 1" ); CPPAD_ASSERT_KNOWN( size_t(delta_x.size()) == n, "abs_min_linear: size of delta_x not equal to n" ); CPPAD_ASSERT_KNOWN( size_t(bound.size()) == n, "abs_min_linear: size of bound not equal to n" ); CPPAD_ASSERT_KNOWN( size_t(g_hat.size()) == m + s, "abs_min_linear: size of g_hat not equal to m + s" ); CPPAD_ASSERT_KNOWN( size_t(g_jac.size()) == (m + s) * (n + s), "abs_min_linear: size of g_jac not equal to (m + s)*(n + s)" ); CPPAD_ASSERT_KNOWN( size_t(bound.size()) == n, "abs_min_linear: size of bound is not equal to n" ); if( level > 0 ) { std::cout << "start abs_min_linear\n"; CppAD::abs_print_mat("bound", n, 1, bound); CppAD::abs_print_mat("g_hat", m + s, 1, g_hat); CppAD::abs_print_mat("g_jac", m + s, n + s, g_jac); } // partial y(x, u) w.r.t x (J in reference) DblVector py_px(n); for(size_t j = 0; j < n; j++) py_px[ j ] = g_jac[ j ]; // // partial y(x, u) w.r.t u (Y in reference) DblVector py_pu(s); for(size_t j = 0; j < s; j++) py_pu[ j ] = g_jac[ n + j ]; // // partial z(x, u) w.r.t x (Z in reference) DblVector pz_px(s * n); for(size_t i = 0; i < s; i++) { for(size_t j = 0; j < n; j++) { pz_px[ i * n + j ] = g_jac[ (n + s) * (i + m) + j ]; } } // partial z(x, u) w.r.t u (L in reference) DblVector pz_pu(s * s); for(size_t i = 0; i < s; i++) { for(size_t j = 0; j < s; j++) { pz_pu[ i * s + j ] = g_jac[ (n + s) * (i + m) + n + j ]; } } // initialize delta_x for(size_t j = 0; j < n; j++) delta_x[j] = 0.0; // // value of approximation for g(x, u) at current delta_x DblVector g_tilde = CppAD::abs_eval(n, m, s, g_hat, g_jac, delta_x); // // value of sigma at delta_x = 0; i.e., sign( z(x, u) ) CppAD::vector sigma(s); for(size_t i = 0; i < s; i++) sigma[i] = CppAD::sign( g_tilde[m + i] ); // // current set of cutting planes DblVector C(maxitr[0] * n), c(maxitr[0]); // // size_t n_plane = 0; for(size_t itr = 0; itr < maxitr[0]; itr++) { // Equation (5), Proposition 3.1 of reference // dy_dx = py_px + py_pu * Sigma * (I - pz_pu * Sigma)^-1 * pz_px // // tmp_ss = I - pz_pu * Sigma DblVector tmp_ss(s * s); for(size_t i = 0; i < s; i++) { for(size_t j = 0; j < s; j++) tmp_ss[i * s + j] = - pz_pu[i * s + j] * sigma[j]; tmp_ss[i * s + i] += 1.0; } // tmp_sn = (I - pz_pu * Sigma)^-1 * pz_px double logdet; DblVector tmp_sn(s * n); LuSolve(s, n, tmp_ss, pz_px, tmp_sn, logdet); // // tmp_sn = Sigma * (I - pz_pu * Sigma)^-1 * pz_px for(size_t i = 0; i < s; i++) { for(size_t j = 0; j < n; j++) tmp_sn[i * n + j] *= sigma[i]; } // dy_dx = py_px + py_pu * Sigma * (I - pz_pu * Sigma)^-1 * pz_px DblVector dy_dx(n); for(size_t j = 0; j < n; j++) { dy_dx[j] = py_px[j]; for(size_t k = 0; k < s; k++) dy_dx[j] += py_pu[k] * tmp_sn[ k * n + j]; } // // check for case where derivative of hyperplane is zero // (in convex case, this is the minimizer) bool near_zero = true; for(size_t j = 0; j < n; j++) near_zero &= std::fabs( dy_dx[j] ) < epsilon[1]; if( near_zero ) { if( level > 0 ) std::cout << "end abs_min_linear: local derivative near zero\n"; return true; } // value of hyperplane at delta_x double plane_at_zero = g_tilde[0]; // value of hyperplane at 0 for(size_t j = 0; j < n; j++) plane_at_zero -= dy_dx[j] * delta_x[j]; // // add a cutting plane with value g_tilde[0] at delta_x // and derivative dy_dx c[n_plane] = plane_at_zero; for(size_t j = 0; j < n; j++) C[n_plane * n + j] = dy_dx[j]; ++n_plane; // // variables for cutting plane problem are (dx, w) // c[i] + C[i,:]*dx <= w DblVector b_box(n_plane), A_box(n_plane * (n + 1)); for(size_t i = 0; i < n_plane; i++) { b_box[i] = c[i]; for(size_t j = 0; j < n; j++) A_box[i * (n+1) + j] = C[i * n + j]; A_box[i *(n+1) + n] = -1.0; } // w is the objective DblVector c_box(n + 1); for(size_t i = 0; i < size_t(c_box.size()); i++) c_box[i] = 0.0; c_box[n] = 1.0; // // d_box DblVector d_box(n+1); for(size_t j = 0; j < n; j++) d_box[j] = bound[j]; d_box[n] = inf; // // solve the cutting plane problem DblVector xout_box(n + 1); size_t level_box = 0; if( level > 0 ) level_box = level - 1; ok &= CppAD::lp_box( level_box, A_box, b_box, c_box, d_box, maxitr[1], xout_box ); if( ! ok ) { if( level > 0 ) { CppAD::abs_print_mat("delta_x", n, 1, delta_x); std::cout << "end abs_min_linear: lp_box failed\n"; } return false; } // // check for convergence double max_diff = 0.0; for(size_t j = 0; j < n; j++) { double diff = delta_x[j] - xout_box[j]; max_diff = std::max( max_diff, std::fabs(diff) ); } // // check for descent in value of approximation objective DblVector delta_new(n); for(size_t j = 0; j < n; j++) delta_new[j] = xout_box[j]; DblVector g_new = CppAD::abs_eval(n, m, s, g_hat, g_jac, delta_new); if( level > 0 ) { std::cout << "itr = " << itr << ", max_diff = " << max_diff << ", y_cur = " << g_tilde[0] << ", y_new = " << g_new[0] << "\n"; CppAD::abs_print_mat("delta_new", n, 1, delta_new); } // g_tilde = g_new; delta_x = delta_new; // // value of sigma at new delta_x; i.e., sign( z(x, u) ) for(size_t i = 0; i < s; i++) sigma[i] = CppAD::sign( g_tilde[m + i] ); // if( max_diff < epsilon[0] ) { if( level > 0 ) std::cout << "end abs_min_linear: change in delta_x near zero\n"; return true; } } if( level > 0 ) std::cout << "end abs_min_linear: maximum number of iterations exceeded\n"; return false; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: example/abs_normal/abs_min_linear.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin abs_min_linear.hpp} abs_min_linear Source Code ########################## {xrst_literal example/abs_normal/abs_min_linear.hpp // BEGIN C++ // END C++ } {xrst_end abs_min_linear.hpp} ================================================ FILE: example/abs_normal/abs_min_quad.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin abs_min_quad.cpp} abs_min_quad: Example and Test ############################## Purpose ******* The function :math:`f : \B{R}^3 \rightarrow \B{R}` defined by .. math:: f( x_0, x_1 ) = ( x_0^2 + x_1^2 ) / 2 + | x_0 - 5 | + | x_1 + 5 | For this case, the :ref:`abs_min_quad-name` object should be equal to the function itself. In addition, the function is convex and :ref:`abs_min_quad-name` should find its global minimizer. The minimizer of this function is :math:`x_0 = 1`, :math:`x_1 = -1`. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end abs_min_quad.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include # include "abs_min_quad.hpp" namespace { CPPAD_TESTVECTOR(double) join( const CPPAD_TESTVECTOR(double)& x , const CPPAD_TESTVECTOR(double)& u ) { size_t n = x.size(); size_t s = u.size(); CPPAD_TESTVECTOR(double) xu(n + s); for(size_t j = 0; j < n; j++) xu[j] = x[j]; for(size_t j = 0; j < s; j++) xu[n + j] = u[j]; return xu; } } bool abs_min_quad(void) { bool ok = true; // using CppAD::AD; using CppAD::ADFun; // typedef CPPAD_TESTVECTOR(size_t) s_vector; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR( AD ) ad_vector; // size_t level = 0; // level of tracing size_t n = 2; // size of x size_t m = 1; // size of y size_t s = 2 ; // number of data points and absolute values // // record the function f(x) ad_vector ad_x(n), ad_y(m); for(size_t j = 0; j < n; j++) ad_x[j] = double(j + 1); Independent( ad_x ); AD sum = 0.0; sum += ad_x[0] * ad_x[0] / 2.0 + abs( ad_x[0] - 5 ); sum += ad_x[1] * ad_x[1] / 2.0 + abs( ad_x[1] + 5 ); ad_y[0] = sum; ADFun f(ad_x, ad_y); // create its abs_normal representation in g, a ADFun g, a; f.abs_normal_fun(g, a); // check dimension of domain and range space for g ok &= g.Domain() == n + s; ok &= g.Range() == m + s; // check dimension of domain and range space for a ok &= a.Domain() == n; ok &= a.Range() == s; // -------------------------------------------------------------------- // Choose the point x_hat = 0 d_vector x_hat(n); for(size_t j = 0; j < n; j++) x_hat[j] = 0.0; // value of a_hat = a(x_hat) d_vector a_hat = a.Forward(0, x_hat); // (x_hat, a_hat) d_vector xu_hat = join(x_hat, a_hat); // value of g[ x_hat, a_hat ] d_vector g_hat = g.Forward(0, xu_hat); // Jacobian of g[ x_hat, a_hat ] d_vector g_jac = g.Jacobian(xu_hat); // trust region bound d_vector bound(n); for(size_t j = 0; j < n; j++) bound[j] = 10.0; // convergence criteria d_vector epsilon(2); double eps99 = 99.0 * std::numeric_limits::epsilon(); epsilon[0] = eps99; epsilon[1] = eps99; // maximum number of iterations s_vector maxitr(2); maxitr[0] = 10; // maximum number of abs_min_quad iterations maxitr[1] = 35; // maximum number of qp_interior iterations // set Hessian equal to identity matrix I d_vector hessian(n * n); for(size_t i = 0; i < n; i++) { for(size_t j = 0; j < n; j++) hessian[i * n + j] = 0.0; hessian[i * n + i] = 1.0; } // minimize the approximation for f (which is equal to f for this case) d_vector delta_x(n); ok &= CppAD::abs_min_quad( level, n, m, s, g_hat, g_jac, hessian, bound, epsilon, maxitr, delta_x ); // check that the solution ok &= CppAD::NearEqual( delta_x[0], +1.0, eps99, eps99 ); ok &= CppAD::NearEqual( delta_x[1], -1.0, eps99, eps99 ); return ok; } // END C++ ================================================ FILE: example/abs_normal/abs_min_quad.hpp ================================================ # ifndef CPPAD_EXAMPLE_ABS_NORMAL_ABS_MIN_QUAD_HPP # define CPPAD_EXAMPLE_ABS_NORMAL_ABS_MIN_QUAD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin abs_min_quad} {xrst_spell dbl lll maxitr } abs_normal: Minimize a Linear Abs-normal Approximation ###################################################### Syntax ****** | *ok* = ``abs_min_quad`` ( | |tab| *level* , *n* , *m* , *s* , | |tab| *g_hat* , *g_jac* , *hessian* , *bound* , *epsilon* , *maxitr* , *delta_x* | ) Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Source ****** This following is a link to the source code for this example: :ref:`abs_min_quad.hpp-name` . Purpose ******* We are given a point :math:`\hat{x} \in \B{R}^n` and use the notation :math:`\tilde{f} (x)` for the abs-normal :ref:`approximation for f(x)` near :math:`\hat{x}`. We are also given a vector :math:`b \in \B{R}_+^n` and a positive definite matrix :math:`H \in \B{R}^{n \times n}`. This routine solves the problem .. math:: \begin{array}{lll} \R{minimize} & \Delta x^T H \Delta x / 2 + \tilde{f}( \hat{x} + \Delta x ) & \R{w.r.t} \; \Delta x \in \B{R}^n \\ \R{subject \; to} & | \Delta x_j | \leq b_j & j = 0 , \ldots , n-1 \end{array} DblVector ********* is a :ref:`SimpleVector-name` class with elements of type ``double`` . SizeVector ********** is a :ref:`SimpleVector-name` class with elements of type ``size_t`` . f * We use the notation *f* for the original function; see :ref:`abs_normal_fun@f` . level ***** This value is less that or equal 3. If *level* == 0 , no tracing of the optimization is printed. If *level* >= 1 , a trace of each iteration of ``abs_min_quad`` is printed. If *level* >= 2 , a trace of the :ref:`qp_box-name` sub-problem is printed. If *level* >= 3 , a trace of the :ref:`qp_interior-name` sub-problem is printed. n * This is the dimension of the domain space for *f* ; see :ref:`abs_normal_fun@f@n` . m * This is the dimension of the range space for *f* ; see :ref:`abs_normal_fun@f@m` . This must be one so that :math:`f` is an objective function. s * This is the number of absolute value terms in *f* ; see :ref:`abs_normal_fun@f@s` . g * We use the notation *g* for the abs-normal representation of *f* ; see :ref:`abs_normal_fun@g` . g_hat ***** This vector has size *m* + *s* and is the value of *g* ( *x* , *u* ) at :math:`x = \hat{x}` and :math:`u = a( \hat{x} )`. g_jac ***** This vector has size ( *m* + *s* ) * ( *n* + *s* ) and is the Jacobian of :math:`g(x, u)` at :math:`x = \hat{x}` and :math:`u = a( \hat{x} )`. hessian ******* This vector has size *n* * *n* . It is a :ref:`row-major` representation of the matrix :math:`H \in \B{R}^{n \times n}`. bound ***** This vector has size *n* and is the vector :math:`b \in \B{R}^n`. The trust region is defined as the set of :math:`\Delta x` such that .. math:: | \Delta x | \leq b_j for :math:`j = 0 , \ldots , n-1`. epsilon ******* The value *epsilon* [0] is convergence criteria in terms of the infinity norm of the difference of *delta_x* between iterations. The value *epsilon* [1] is convergence criteria in terms of the derivative of the objective; i.e. .. math:: \Delta x^T H \Delta x / 2 + \tilde{f}( \hat{x} + \Delta x) maxitr ****** This is a vector with size 2. The value *maxitr* [0] is the maximum number of ``abs_min_quad`` iterations to try before giving up on convergence. The value *maxitr* [1] is the maximum number of iterations in the :ref:`qp_interior` sub-problems. delta_x ******* This vector :math:`\Delta x` has size *n* . The input value of its elements does not matter. Upon return, the approximate minimizer of the objective with respect to the trust region. Method ****** sigma ===== We use the notation .. math:: \sigma (x) = \R{sign} ( z[ x , a(x) ] ) where :ref:`abs_normal_fun@a@a(x)` and :ref:`abs_normal_fun@g@z(x, u)` are as defined in the abs-normal representation of :math:`f(x)`. Cutting Planes ============== At each iteration, we are given affine functions :math:`p_k (x)` such that :math:`p_k ( x_k ) = \tilde{f}( x_k )` and :math:`p_k^{(1)} ( x_k )` is the derivative :math:`\tilde{f}^{(1)} ( x_k )` corresponding to :math:`\sigma ( x_k )`. Iteration ========= At iteration :math:`k`, we solve the problem .. math:: \begin{array}{lll} \R{minimize} & \Delta x^T H \Delta x / 2 + \max \{ p_k ( \hat{x} + \Delta x) \W{:} k = 0 , \ldots , K-1 \} & \R{w.r.t} \; \Delta x \\ \R{subject \; to} & - b \leq \Delta x \leq + b \end{array} The solution is the new point :math:`x_K` at which the new affine approximation :math:`p_K (x)` is constructed. This process is iterated until the difference :math:`x_K - x_{K-1}` is small enough. {xrst_toc_hidden example/abs_normal/abs_min_quad.cpp example/abs_normal/abs_min_quad.xrst } Example ******* The file :ref:`abs_min_quad.cpp-name` contains an example and test of ``abs_min_quad`` . {xrst_end abs_min_quad} ----------------------------------------------------------------------------- */ # include # include "qp_box.hpp" # include "abs_eval.hpp" // BEGIN C++ namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN PROTOTYPE template bool abs_min_quad( size_t level , size_t n , size_t m , size_t s , const DblVector& g_hat , const DblVector& g_jac , const DblVector& hessian , const DblVector& bound , const DblVector& epsilon , const SizeVector& maxitr , DblVector& delta_x ) // END PROTOTYPE { using std::fabs; bool ok = true; double inf = std::numeric_limits::infinity(); // CPPAD_ASSERT_KNOWN( level <= 4, "abs_min_quad: level is not less that or equal 3" ); CPPAD_ASSERT_KNOWN( size_t(epsilon.size()) == 2, "abs_min_quad: size of epsilon not equal to 2" ); CPPAD_ASSERT_KNOWN( size_t(maxitr.size()) == 2, "abs_min_quad: size of maxitr not equal to 2" ); CPPAD_ASSERT_KNOWN( m == 1, "abs_min_quad: m is not equal to 1" ); CPPAD_ASSERT_KNOWN( size_t(delta_x.size()) == n, "abs_min_quad: size of delta_x not equal to n" ); CPPAD_ASSERT_KNOWN( size_t(bound.size()) == n, "abs_min_quad: size of bound not equal to n" ); CPPAD_ASSERT_KNOWN( size_t(g_hat.size()) == m + s, "abs_min_quad: size of g_hat not equal to m + s" ); CPPAD_ASSERT_KNOWN( size_t(g_jac.size()) == (m + s) * (n + s), "abs_min_quad: size of g_jac not equal to (m + s)*(n + s)" ); CPPAD_ASSERT_KNOWN( size_t(hessian.size()) == n * n, "abs_min_quad: size of hessian not equal to n * n" ); CPPAD_ASSERT_KNOWN( size_t(bound.size()) == n, "abs_min_quad: size of bound is not equal to n" ); if( level > 0 ) { std::cout << "start abs_min_quad\n"; CppAD::abs_print_mat("g_hat", m + s, 1, g_hat); CppAD::abs_print_mat("g_jac", m + s, n + s, g_jac); CppAD::abs_print_mat("hessian", n, n, hessian); CppAD::abs_print_mat("bound", n, 1, bound); } // partial y(x, u) w.r.t x (J in reference) DblVector py_px(n); for(size_t j = 0; j < n; j++) py_px[ j ] = g_jac[ j ]; // // partial y(x, u) w.r.t u (Y in reference) DblVector py_pu(s); for(size_t j = 0; j < s; j++) py_pu[ j ] = g_jac[ n + j ]; // // partial z(x, u) w.r.t x (Z in reference) DblVector pz_px(s * n); for(size_t i = 0; i < s; i++) { for(size_t j = 0; j < n; j++) { pz_px[ i * n + j ] = g_jac[ (n + s) * (i + m) + j ]; } } // partial z(x, u) w.r.t u (L in reference) DblVector pz_pu(s * s); for(size_t i = 0; i < s; i++) { for(size_t j = 0; j < s; j++) { pz_pu[ i * s + j ] = g_jac[ (n + s) * (i + m) + n + j ]; } } // initialize delta_x for(size_t j = 0; j < n; j++) delta_x[j] = 0.0; // // current set of cutting planes DblVector C(maxitr[0] * n), c(maxitr[0]); // // value of abs-normal approximation at x_hat + delta_x DblVector g_tilde = CppAD::abs_eval(n, m, s, g_hat, g_jac, delta_x); // // value of sigma at delta_x = 0; i.e., sign( z(x, u) ) CppAD::vector sigma(s); for(size_t i = 0; i < s; i++) sigma[i] = CppAD::sign( g_tilde[m + i] ); // // initial value of the objective double obj_cur = g_tilde[0]; // // initial number of cutting planes size_t n_plane = 0; // if( level > 0 ) { std::cout << "obj = " << obj_cur << "\n"; CppAD::abs_print_mat("delta_x", n, 1, delta_x); } for(size_t itr = 0; itr < maxitr[0]; itr++) { // Equation (5), Proposition 3.1 of reference // dy_dx = py_px + py_pu * Sigma * (I - pz_pu * Sigma)^-1 * pz_px // // tmp_ss = I - pz_pu * Sigma DblVector tmp_ss(s * s); for(size_t i = 0; i < s; i++) { for(size_t j = 0; j < s; j++) tmp_ss[i * s + j] = - pz_pu[i * s + j] * sigma[j]; tmp_ss[i * s + i] += 1.0; } // tmp_sn = (I - pz_pu * Sigma)^-1 * pz_px double logdet; DblVector tmp_sn(s * n); LuSolve(s, n, tmp_ss, pz_px, tmp_sn, logdet); // // tmp_sn = Sigma * (I - pz_pu * Sigma)^-1 * pz_px for(size_t i = 0; i < s; i++) { for(size_t j = 0; j < n; j++) tmp_sn[i * n + j] *= sigma[i]; } // dy_dx = py_px + py_pu * Sigma * (I - pz_pu * Sigma)^-1 * pz_px DblVector dy_dx(n); for(size_t j = 0; j < n; j++) { dy_dx[j] = py_px[j]; for(size_t k = 0; k < s; k++) dy_dx[j] += py_pu[k] * tmp_sn[ k * n + j]; } // // compute derivative of the quadratic term DblVector dq_dx(n); for(size_t j = 0; j < n; j++) { dq_dx[j] = 0.0; for(size_t i = 0; i < n; i++) dq_dx[j] += delta_x[i] * hessian[i * n + j]; } // // check for case where derivative of objective is zero // (in convex case, this is the minimizer) bool near_zero = true; for(size_t j = 0; j < n; j++) near_zero &= std::fabs( dq_dx[j] + dy_dx[j] ) < epsilon[1]; if( near_zero ) { if( level > 0 ) std::cout << "end abs_min_quad: local derivative near zero\n"; return true; } // value of hyperplane at delta_x double plane_at_zero = g_tilde[0]; // // value of hyperplane at 0 for(size_t j = 0; j < n; j++) plane_at_zero -= dy_dx[j] * delta_x[j]; // // add a cutting plane with value g_tilde[0] at delta_x // and derivative dy_dx c[n_plane] = plane_at_zero; for(size_t j = 0; j < n; j++) C[n_plane * n + j] = dy_dx[j]; ++n_plane; // // variables for cutting plane problem are (dx, w) // c[i] + C[i,:] * dx <= w DblVector c_box(n_plane), C_box(n_plane * (n + 1)); for(size_t i = 0; i < n_plane; i++) { c_box[i] = c[i]; for(size_t j = 0; j < n; j++) C_box[i * (n+1) + j] = C[i * n + j]; C_box[i * (n+1) + n] = -1.0; } // // w is the objective DblVector g_box(n + 1); for(size_t i = 0; i < size_t(c_box.size()); i++) g_box[i] = 0.0; g_box[n] = 1.0; // // a_box, b_box DblVector a_box(n+1), b_box(n+1); for(size_t j = 0; j < n; j++) { a_box[j] = - bound[j]; b_box[j] = + bound[j]; } a_box[n] = - inf; b_box[n] = + inf; // // initial delta_x in qp_box is zero DblVector xin_box(n + 1); for(size_t j = 0; j < n; j++) xin_box[j] = 0.0; // initial w in qp_box is 1 + max_i c[i] xin_box[n] = 1.0 + c_box[0]; for(size_t i = 1; i < n_plane; i++) xin_box[n] = std::max( xin_box[n], 1.0 + c_box[i] ); // DblVector hessian_box( (n+1) * (n+1) ); for(size_t i = 0; i < n+1; i++) { for(size_t j = 0; j < n+1; j++) { if( i == n || j == n ) hessian_box[i * (n+1) + j] = 0.0; else hessian_box[i * (n+1) + j] = hessian[i * n + j]; } } // // solve the cutting plane problem DblVector xout_box(n + 1); size_t level_box = 0; if( level > 0 ) level_box = level - 1; ok &= CppAD::qp_box( level_box, a_box, b_box, c_box, C_box, g_box, hessian_box, epsilon[1], maxitr[1], xin_box, xout_box ); if( ! ok ) { if( level > 0 ) { CppAD::abs_print_mat("delta_x", n, 1, delta_x); std::cout << "end abs_min_quad: qp_box failed\n"; } return false; } DblVector delta_new(n); for(size_t j = 0; j < n; j++) delta_new[j] = xout_box[j]; // // check for convergence double max_diff = 0.0; for(size_t j = 0; j < n; j++) { double diff = delta_x[j] - delta_new[j]; max_diff = std::max( max_diff, std::fabs(diff) ); } // // new value of the objective DblVector g_new = CppAD::abs_eval(n, m, s, g_hat, g_jac, delta_new); double obj_new = g_new[0]; for(size_t i = 0; i < n; i++) { for(size_t j = 0; j < n; j++) obj_new += delta_new[i] * hessian[i * n + j] * delta_new[j]; } g_tilde = g_new; obj_cur = obj_new; delta_x = delta_new; // if( level > 0 ) { std::cout << "itr = " << itr << ", max_diff = " << max_diff << ", obj_cur = " << obj_cur << "\n"; CppAD::abs_print_mat("delta_x", n, 1, delta_x); } // // value of sigma at new delta_x; i.e., sign( z(x, u) ) for(size_t i = 0; i < s; i++) sigma[i] = CppAD::sign( g_tilde[m + i] ); // if( max_diff < epsilon[0] ) { if( level > 0 ) std::cout << "end abs_min_quad: change in delta_x near zero\n"; return true; } } if( level > 0 ) std::cout << "end abs_min_quad: maximum number of iterations exceeded\n"; return false; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: example/abs_normal/abs_min_quad.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin abs_min_quad.hpp} abs_min_quad Source Code ######################## {xrst_literal example/abs_normal/abs_min_quad.hpp // BEGIN C++ // END C++ } {xrst_end abs_min_quad.hpp} ================================================ FILE: example/abs_normal/abs_normal.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin abs_normal.cpp} Abs-Normal Form Examples and Tests Driver ######################################### Running These Tests ******************* After executing the :ref:`cmake-name` command form the :ref:`download@Distribution Directory`, you can build and run these tests with the commands:: cd build make check_example_abs_normal Note that your choice of :ref:`cmake@generator` may require using an different version of make; e.g., ``ninja`` . {xrst_literal // BEGIN C++ // END C++ } {xrst_end abs_normal.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ // CPPAD_HAS_* defines # include // for thread_alloc # include // test runner # include // external complied tests extern bool abs_eval(void); extern bool abs_min_linear(void); extern bool abs_min_quad(void); extern bool get_started(void); extern bool lp_box(void); extern bool min_nso_linear(void); extern bool min_nso_quad(void); extern bool qp_box(void); extern bool qp_interior(void); extern bool simplex_method(void); // main program that runs all the tests int main(void) { std::string group = "example/abs_norml"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // external compiled tests Run( abs_eval, "abs_eval" ); Run( abs_min_linear, "abs_min_linear" ); Run( abs_min_quad, "abs_min_quad" ); Run( get_started, "get_started" ); Run( lp_box, "lp_box" ); Run( min_nso_linear, "min_nso_linear" ); Run( min_nso_quad, "min_nso_quad" ); Run( qp_box, "qp_box" ); Run( qp_interior, "qp_interior" ); Run( simplex_method, "simplex_method" ); // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } // END C++ ================================================ FILE: example/abs_normal/abs_normal.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin example_abs_normal} {xrst_spell andreas bernt griewank jens radons streubel uwe vol } Examples and Tests: Abs-normal Representation of Non-Smooth Functions ##################################################################### Reference ********* Andreas Griewank, Jens-Uwe Bernt, Manuel Radons, Tom Streubel, *Solving piecewise linear systems in abs-normal form* , Linear Algebra and its Applications, vol. 471 (2015), pages 500-530. Contents ******** {xrst_toc_table example/abs_normal/get_started.cpp example/abs_normal/abs_print_mat.hpp example/abs_normal/abs_eval.hpp example/abs_normal/simplex_method.hpp example/abs_normal/lp_box.hpp example/abs_normal/abs_min_linear.hpp example/abs_normal/min_nso_linear.hpp example/abs_normal/qp_interior.hpp example/abs_normal/qp_box.hpp example/abs_normal/abs_min_quad.hpp example/abs_normal/min_nso_quad.hpp } {xrst_end example_abs_normal} ================================================ FILE: example/abs_normal/abs_print_mat.hpp ================================================ # ifndef CPPAD_EXAMPLE_ABS_NORMAL_ABS_PRINT_MAT_HPP # define CPPAD_EXAMPLE_ABS_NORMAL_ABS_PRINT_MAT_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin abs_print_mat} {xrst_spell cout nr } abs_normal: Print a Vector or Matrix #################################### Syntax ****** | ``abs_print_mat`` ( *name* , *nr* , *nc* , *mat* ) Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Purpose ******* This routine is used by the :ref:`abs_normal` examples to print vectors and matrices. A new-line is printed at the end of this output. name **** This is a name that is printed before the vector or matrix. nr ** This is the number of rows in the matrix. Use *nr* = 1 for row vectors. nc ** This is the number of columns in the matrix. Use *nc* = 1 for column vectors. mat *** This is a :ref:`row-major` representation of the matrix (hence a :ref:`SimpleVector-name` ). The syntax *std::cout <<* ``mat`` [ ``i`` ] must output the *i*-th element of the simple vector *mat* . {xrst_end abs_print_mat} ----------------------------------------------------------------------------- */ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN PROTOTYPE template void abs_print_mat( const std::string& name , size_t nr , size_t nc , const Vector& mat ) // END PROTOTYPE { CPPAD_ASSERT_KNOWN( size_t(mat.size()) == nr * nc, "abs_print_mat: size of mat is not nr * nc" ); // output name std::cout << name << " ="; // // handle empty case if( nr == 0 || nc == 0 ) { std::cout << " " << nr << " by " << nc << " empty matrix\n"; return; } // // handle vector case if( nr == 1 || nc == 1 ) { std::cout << " ["; for(size_t i = 0; i < nr * nc; i++) { if( i > 0 ) std::cout << ", "; std::cout << mat[i]; } std::cout << "]"; // // column vectors are printed as row vectors with a transpose at end if( nr > 1 ) std::cout << "^T"; // std::cout << "\n"; return; } // non-empty matrix std::cout << "\n"; for(size_t i = 0; i < nr; i++) { std::cout << "["; for(size_t j = 0; j < nc; j++) { if( j > 0 ) std::cout << ", "; std::cout << mat[i * nc + j]; } std::cout << "]\n"; } return; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: example/abs_normal/get_started.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin abs_get_started.cpp} {xrst_spell rclrcl } abs_normal Getting Started: Example and Test ############################################ Purpose ******* Creates an :ref:`abs_normal` representation :math:`g` for the function :math:`f : \B{R}^3 \rightarrow \B{R}` defined by .. math:: f( x_0, x_1, x_2 ) = | x_0 + x_1 | + | x_1 + x_2 | The corresponding :ref:`abs_normal_fun@g` :math:`: \B{R}^5 \rightarrow \B{R}^3` is given by .. math:: \begin{array}{rclrcl} g_0 ( x_0, x_1, x_2, u_0, u_1 ) & = & u_0 + u_1 & = & y_0 (x, u) \\ g_1 ( x_0, x_1, x_2, u_0, u_1 ) & = & x_0 + x_1 & = & z_0 (x, u) \\ g_1 ( x_0, x_1, x_2, u_0, u_1 ) & = & x_1 + x_2 & = & z_1 (x, u) \end{array} Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end abs_get_started.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include namespace { CPPAD_TESTVECTOR(double) join( const CPPAD_TESTVECTOR(double)& x , const CPPAD_TESTVECTOR(double)& u ) { size_t n = x.size(); size_t s = u.size(); CPPAD_TESTVECTOR(double) xu(n + s); for(size_t j = 0; j < n; j++) xu[j] = x[j]; for(size_t j = 0; j < s; j++) xu[n + j] = u[j]; return xu; } } bool get_started(void) { bool ok = true; // using CppAD::AD; using CppAD::ADFun; // size_t n = 3; // size of x size_t m = 1; // size of y size_t s = 2; // size of u and z // // record the function f(x) CPPAD_TESTVECTOR( AD ) ax(n), ay(m); for(size_t j = 0; j < n; j++) ax[j] = double(j + 1); Independent( ax ); // for this example, we ensure first absolute value is | x_0 + x_1 | AD a0 = abs( ax[0] + ax[1] ); // and second absolute value is | x_1 + x_2 | AD a1 = abs( ax[1] + ax[2] ); ay[0] = a0 + a1; ADFun f(ax, ay); // create its abs_normal representation in g, a ADFun g, a; f.abs_normal_fun(g, a); // check dimension of domain and range space for g ok &= g.Domain() == n + s; ok &= g.Range() == m + s; // check dimension of domain and range space for a ok &= a.Domain() == n; ok &= a.Range() == s; // -------------------------------------------------------------------- // a(x) has all the operations used to compute f(x), but the sum of the // absolute values is not needed for a(x), so optimize it out. size_t n_op = f.size_op(); ok &= a.size_op() == n_op; a.optimize(); ok &= a.size_op() < n_op; // -------------------------------------------------------------------- // zero order forward mode calculation using g(x, u) CPPAD_TESTVECTOR(double) x(n), u(s), xu(n+s), yz(m+s); for(size_t j = 0; j < n; j++) x[j] = double(j + 2); for(size_t j = 0; j < s; j++) u[j] = double(j + n + 2); xu = join(x, u); yz = g.Forward(0, xu); // check y_0(x, u) double y0 = u[0] + u[1]; ok &= y0 == yz[0]; // check z_0 (x, u) double z0 = x[0] + x[1]; ok &= z0 == yz[1]; // check z_1 (x, u) double z1 = x[1] + x[2]; ok &= z1 == yz[2]; // -------------------------------------------------------------------- // check that y(x, a(x) ) == f(x) CPPAD_TESTVECTOR(double) y(m); y = f.Forward(0, x); // y = f(x) u = a.Forward(0, x); // u = a(x) xu = join(x, u); // xu = ( x, a(x) ) yz = g.Forward(0, xu); // yz = ( y(x, a(x)), z(x, a(x)) ) ok &= yz[0] == y[0]; return ok; } // END C++ ================================================ FILE: example/abs_normal/lp_box.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin lp_box.cpp} {xrst_spell rl } abs_normal lp_box: Example and Test ################################### Problem ******* Our original problem is .. math:: \begin{array}{rl} \R{minimize} & x_0 - x_1 \; \R{w.r.t} \; x \in \B{R}^2 \\ \R{subject \; to} & -2 \leq x_0 \leq +2 \; \R{and} \; -2 \leq x_1 \leq +2 \end{array} Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end lp_box.cpp} */ // BEGIN C++ # include # include # include "lp_box.hpp" bool lp_box(void) { bool ok = true; typedef CppAD::vector vector; double eps99 = 99.0 * std::numeric_limits::epsilon(); // size_t n = 2; size_t m = 0; vector A(m), b(m), c(n), d(n), xout(n); c[0] = +1.0; c[1] = -1.0; // d[0] = +2.0; d[1] = +2.0; // size_t level = 0; size_t maxitr = 20; // ok &= CppAD::lp_box(level, A, b, c, d, maxitr, xout); // // check optimal value for x ok &= std::fabs( xout[0] + 2.0 ) < eps99; ok &= std::fabs( xout[1] - 2.0 ) < eps99; // return ok; } // END C++ ================================================ FILE: example/abs_normal/lp_box.hpp ================================================ # ifndef CPPAD_EXAMPLE_ABS_NORMAL_LP_BOX_HPP # define CPPAD_EXAMPLE_ABS_NORMAL_LP_BOX_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin lp_box} {xrst_spell maxitr rl xout } abs_normal: Solve a Linear Program With Box Constraints ####################################################### Syntax ****** | *ok* = ``lp_box`` ( | |tab| *level* , *A* , *b* , *c* , *d* , *maxitr* , *xout* | ) Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Source ****** This following is a link to the source code for this example: :ref:`lp_box.hpp-name` . Problem ******* We are given :math:`A \in \B{R}^{m \times n}`, :math:`b \in \B{R}^m`, :math:`c \in \B{R}^n`, :math:`d \in \B{R}^n`, This routine solves the problem .. math:: \begin{array}{rl} \R{minimize} & c^T x \; \R{w.r.t} \; x \in \B{R}^n \\ \R{subject \; to} & A x + b \leq 0 \; \R{and} \; - d \leq x \leq d \end{array} Vector ****** The type *Vector* is a simple vector with elements of type ``double`` . level ***** This value is less that or equal two. If *level* == 0 , no tracing is printed. If *level* >= 1 , a trace of the ``lp_box`` operations is printed. If *level* >= 2 , the objective and primal variables :math:`x` are printed at each :ref:`simplex_method-name` iteration. If *level* == 3 , the simplex tableau is printed at each simplex iteration. A * This is a :ref:`row-major` representation of the matrix :math:`A` in the problem. b * This is the vector :math:`b` in the problem. c * This is the vector :math:`c` in the problem. d * This is the vector :math:`d` in the problem. If :math:`d_j` is infinity, there is no limit for the size of :math:`x_j`. maxitr ****** This is the maximum number of newton iterations to try before giving up on convergence. xout **** This argument has size is *n* and the input value of its elements does no matter. Upon return it is the primal variables :math:`x` corresponding to the problem solution. ok ** If the return value *ok* is true, an optimal solution was found. {xrst_toc_hidden example/abs_normal/lp_box.cpp example/abs_normal/lp_box.xrst } Example ******* The file :ref:`lp_box.cpp-name` contains an example and test of ``lp_box`` . {xrst_end lp_box} ----------------------------------------------------------------------------- */ # include "simplex_method.hpp" // BEGIN C++ namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN PROTOTYPE template bool lp_box( size_t level , const Vector& A , const Vector& b , const Vector& c , const Vector& d , size_t maxitr , Vector& xout ) // END PROTOTYPE { double inf = std::numeric_limits::infinity(); // size_t m = b.size(); size_t n = c.size(); // CPPAD_ASSERT_KNOWN( level <= 3, "lp_box: level is greater than 3"); CPPAD_ASSERT_KNOWN( size_t(A.size()) == m * n, "lp_box: size of A is not m * n" ); CPPAD_ASSERT_KNOWN( size_t(d.size()) == n, "lp_box: size of d is not n" ); if( level > 0 ) { std::cout << "start lp_box\n"; CppAD::abs_print_mat("A", m, n, A); CppAD::abs_print_mat("b", m, 1, b); CppAD::abs_print_mat("c", n, 1, c); CppAD::abs_print_mat("d", n, 1, d); } // // count number of limits size_t n_limit = 0; for(size_t j = 0; j < n; j++) { if( d[j] < inf ) n_limit += 1; } // // A_simplex and b_simplex define the extended constraints Vector A_simplex((m + 2 * n_limit) * (2 * n) ), b_simplex(m + 2 * n_limit); for(size_t i = 0; i < size_t(A_simplex.size()); i++) A_simplex[i] = 0.0; // // put A * x + b <= 0 in A_simplex, b_simplex for(size_t i = 0; i < m; i++) { b_simplex[i] = b[i]; for(size_t j = 0; j < n; j++) { // x_j^+ coefficient (positive component) A_simplex[i * (2 * n) + 2 * j] = A[i * n + j]; // x_j^- coefficient (negative component) A_simplex[i * (2 * n) + 2 * j + 1] = - A[i * n + j]; } } // // put | x_j | <= d_j in A_simplex, b_simplex size_t i_limit = 0; for(size_t j = 0; j < n; j++) if( d[j] < inf ) { // x_j^+ <= d_j constraint b_simplex[ m + 2 * i_limit] = - d[j]; A_simplex[(m + 2 * i_limit) * (2 * n) + 2 * j] = 1.0; // // x_j^- <= d_j constraint b_simplex[ m + 2 * i_limit + 1] = - d[j]; A_simplex[(m + 2 * i_limit + 1) * (2 * n) + 2 * j + 1] = 1.0; // ++i_limit; } // // c_simples Vector c_simplex(2 * n); for(size_t j = 0; j < n; j++) { // x_j+ component c_simplex[2 * j] = c[j]; // x_j^- component c_simplex[2 * j + 1] = - c[j]; } size_t level_simplex = 0; if( level >= 2 ) level_simplex = level - 1; // Vector x_simplex(2 * n); bool ok = CppAD::simplex_method( level_simplex, A_simplex, b_simplex, c_simplex, maxitr, x_simplex ); for(size_t j = 0; j < n; j++) xout[j] = x_simplex[2 * j] - x_simplex[2 * j + 1]; if( level > 0 ) { CppAD::abs_print_mat("xout", n, 1, xout); if( ok ) std::cout << "end lp_box: ok = true\n"; else std::cout << "end lp_box: ok = false\n"; } return ok; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: example/abs_normal/lp_box.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin lp_box.hpp} lp_box Source Code ################## {xrst_literal example/abs_normal/lp_box.hpp // BEGIN C++ // END C++ } {xrst_end lp_box.hpp} ================================================ FILE: example/abs_normal/min_nso_linear.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin min_nso_linear.cpp} abs_normal min_nso_linear: Example and Test ########################################### Purpose ******* We minimize the function :math:`f : \B{R}^3 \rightarrow \B{R}` defined by .. math:: :nowrap: \begin{eqnarray} f( x_0, x_1, x_2 ) & = & x_0^2 + 2 (x_0 + x_1)^2 + | x_2 | \end{eqnarray} Discussion ********** This routine uses :ref:`abs_min_linear-name` which uses :ref:`lp_box-name` , a linear programming algorithm. It is mean to be compared with :ref:`min_nso_quad.cpp-name` which uses a quadratic programing algorithm for the same problem. To see this comparison, set *level* = 1 is both examples. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end min_nso_linear.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include # include "min_nso_linear.hpp" bool min_nso_linear(void) { bool ok = true; // using CppAD::AD; using CppAD::ADFun; // typedef CPPAD_TESTVECTOR(size_t) s_vector; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR( AD ) ad_vector; // size_t level = 0; // level of tracing size_t n = 3; // size of x size_t m = 1; // size of y size_t s = 1; // number of data points and absolute values // // start recording the function f(x) ad_vector ax(n), ay(m); for(size_t j = 0; j < n; j++) ax[j] = double(j + 1); Independent( ax ); // ay[0] = ax[0] * ax[0]; ay[0] += 2.0 * (ax[0] + ax[1]) * (ax[0] + ax[1]); ay[0] += fabs( ax[2] ); ADFun f(ax, ay); // // create its abs_normal representation in g, a ADFun g, a; f.abs_normal_fun(g, a); // check dimension of domain and range space for g ok &= g.Domain() == n + s; ok &= g.Range() == m + s; // check dimension of domain and range space for a ok &= a.Domain() == n; ok &= a.Range() == s; // epsilon d_vector epsilon(2); double eps = 1e-3; epsilon[0] = eps; epsilon[1] = eps; // maxitr s_vector maxitr(3); maxitr[0] = 100; maxitr[1] = 20; maxitr[2] = 20; // b_in double b_in = 1.0; // call min_nso_linear d_vector x_in(n), x_out(n); for(size_t j = 0; j < n; j++) x_in[j] = double(j + 1); // ok &= CppAD::min_nso_linear( level, g, a, epsilon, maxitr, b_in, x_in, x_out ); // for(size_t j = 0; j < n; j++) ok &= std::fabs( x_out[j] ) < eps; return ok; } // END C++ ================================================ FILE: example/abs_normal/min_nso_linear.hpp ================================================ # ifndef CPPAD_EXAMPLE_ABS_NORMAL_MIN_NSO_LINEAR_HPP # define CPPAD_EXAMPLE_ABS_NORMAL_MIN_NSO_LINEAR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin min_nso_linear} {xrst_spell dbl maxitr } Non-Smooth Optimization Using Abs-normal Linear Approximations ############################################################## Syntax ****** | *ok* = ``min_nso_linear`` ( | |tab| *level* , *g* , *a* , *epsilon* , *maxitr* , *b_in* , *x_in* , *x_out* | ) Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Source ****** This following is a link to the source code for this example: :ref:`min_nso_linear.hpp-name` . Purpose ******* Given a current that abs-normal representation :ref:`abs_normal_fun@g` , :ref:`abs_normal_fun@a` , for a function :math:`f(x)`, this routine minimizes :math:`f(x)`. DblVector ********* is a :ref:`SimpleVector-name` class with elements of type ``double`` . SizeVector ********** is a :ref:`SimpleVector-name` class with elements of type ``size_t`` . f * We use the notation *f* for the original function; see :ref:`abs_normal_fun@f` . n = We use *n* to denote the dimension of the domain for *f* ; i.e., *f* . ``Domain`` () . m = We use *m* to denote the dimension of the range for *f* ; i.e., *f* . ``Range`` () . This must be equal to one. s = We use :ref:`abs_normal_fun@f@s` to denote the number absolute terms in *f* . level ***** This value is less that or equal 5. If *level* == 0 , no tracing of the optimization is printed. If *level* >= 1 , a trace of each iteration of ``min_nso_linear`` is printed. If *level* >= 2 , a trace of each iteration of the ``abs_min_linear`` sub-problem is printed. If *level* >= 3 , a trace of the :ref:`lp_box-name` sub-problem is printed. If *level* >= 4 , a trace of the objective and primal variables :math:`x` are printed at each :ref:`simplex_method-name` iteration. If *level* == 5 , the simplex tableau is printed at each simplex iteration. g * This is the function :ref:`abs_normal_fun@g` in the abs-normal representation of *f* . a * This is the function :ref:`abs_normal_fun@a` in the abs-normal representation of *f* . epsilon ******* This is a vector with size 2. The value *epsilon* [0] is convergence criteria in terms of the infinity norm of the difference of *x_out* between iterations. The value *epsilon* [1] is convergence criteria in terms of the derivative of :math:`f(x)`. This derivative is actually the average of the directional derivative in the direction of the sub-problem minimizer. maxitr ****** This is a vector with size 3. The value *maxitr* [0] is the maximum number of ``min_nso_linear`` iterations to try before giving up on convergence. The value *maxitr* [1] is the maximum number of iterations in the ``abs_min_linear`` sub-problem. The value *maxitr* [2] is the maximum number of iterations in the :ref:`simplex_method` sub-problems. b_in **** This the initial bound on the trust region size. To be specific, if :math:`b` is the current trust region size, at each iteration affine approximation is minimized with respect to :math:`\Delta x` and subject to .. math:: -b \leq \Delta x_j \leq b for *j* = 0 , ..., *n* ``-1`` . It must hold that *b_in* > *epsilon* [0] . x_in **** This vector *x_out* has size *n* . It is the starting point for the optimization procedure; i.e., the ``min_nso_linear`` iterations. x_out ***** This vector *x_out* has size *n* . The input value of its elements does not matter. Upon return, it is the approximate minimizer of the abs-normal approximation for :math:`f(x)` over the trust region is :math:`x = \hat{x} + \Delta x`. {xrst_toc_hidden example/abs_normal/min_nso_linear.cpp example/abs_normal/min_nso_linear.xrst } Example ******* The file :ref:`min_nso_linear.cpp-name` contains an example and test of ``min_nso_linear`` . {xrst_end min_nso_linear} ----------------------------------------------------------------------------- */ # include # include "abs_min_linear.hpp" # include "abs_eval.hpp" // BEGIN C++ namespace { CPPAD_TESTVECTOR(double) min_nso_linear_join( const CPPAD_TESTVECTOR(double)& x , const CPPAD_TESTVECTOR(double)& u ) { size_t n = x.size(); size_t s = u.size(); CPPAD_TESTVECTOR(double) xu(n + s); for(size_t j = 0; j < n; j++) xu[j] = x[j]; for(size_t j = 0; j < s; j++) xu[n + j] = u[j]; return xu; } } namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN PROTOTYPE template bool min_nso_linear( size_t level , ADFun& g , ADFun& a , const DblVector& epsilon , SizeVector maxitr , double b_in , const DblVector& x_in , DblVector& x_out ) // END PROTOTYPE { using std::fabs; // // number of absolute value terms size_t s = a.Range(); // // size of domain for f size_t n = g.Domain() - s; // // size of range space for f size_t m = g.Range() - s; // CPPAD_ASSERT_KNOWN( level <= 5, "min_nso_linear: level is not less that or equal 5" ); CPPAD_ASSERT_KNOWN( size_t(epsilon.size()) == 2, "min_nso_linear: size of epsilon not equal to 2" ); CPPAD_ASSERT_KNOWN( size_t(maxitr.size()) == 3, "min_nso_linear: size of maxitr not equal to 3" ); CPPAD_ASSERT_KNOWN( g.Domain() > s && g.Range() > s, "min_nso_linear: g, a is not an abs-normal representation" ); CPPAD_ASSERT_KNOWN( m == 1, "min_nso_linear: m is not equal to 1" ); CPPAD_ASSERT_KNOWN( size_t(x_in.size()) == n, "min_nso_linear: size of x_in not equal to n" ); CPPAD_ASSERT_KNOWN( size_t(x_out.size()) == n, "min_nso_linear: size of x_out not equal to n" ); CPPAD_ASSERT_KNOWN( epsilon[0] < b_in, "min_nso_linear: b_in <= epsilon[0]" ); if( level > 0 ) { std::cout << "start min_nso_linear\n"; std::cout << "b_in = " << b_in << "\n"; CppAD::abs_print_mat("x_in", n, 1, x_in); } // level in abs_min_linear sub-problem size_t level_tilde = 0; if( level > 0 ) level_tilde = level - 1; // // maxitr in abs_min_linear sub-problem SizeVector maxitr_tilde(2); maxitr_tilde[0] = maxitr[1]; maxitr_tilde[1] = maxitr[2]; // // epsilon in abs_min_linear sub-problem DblVector eps_tilde(2); eps_tilde[0] = epsilon[0] / 10.; eps_tilde[1] = epsilon[1] / 10.; // // current bound double b_cur = b_in; // // initialize the current x x_out = x_in; // // value of a(x) at current x DblVector a_cur = a.Forward(0, x_out); // // (x_out, a_cur) DblVector xu_cur = min_nso_linear_join(x_out, a_cur); // // value of g[ x_cur, a_cur ] DblVector g_cur = g.Forward(0, xu_cur); // for(size_t itr = 0; itr < maxitr[0]; itr++) { // Jacobian of g[ x_cur, a_cur ] DblVector g_jac = g.Jacobian(xu_cur); // // bound in abs_min_linear sub-problem DblVector bound_tilde(n); for(size_t j = 0; j < n; j++) bound_tilde[j] = b_cur; // DblVector delta_x(n); bool ok = abs_min_linear( level_tilde, n, m, s, g_cur, g_jac, bound_tilde, eps_tilde, maxitr_tilde, delta_x ); if( ! ok ) { if( level > 0 ) std::cout << "end min_nso_linear: abs_min_linear failed\n"; return false; } // // new candidate value for x DblVector x_new(n); double max_delta_x = 0.0; for(size_t j = 0; j < n; j++) { x_new[j] = x_out[j] + delta_x[j]; max_delta_x = std::max(max_delta_x, std::fabs( delta_x[j] ) ); } // if( max_delta_x < b_cur && max_delta_x < epsilon[0] ) { if( level > 0 ) std::cout << "end min_nso_linear: delta_x is near zero\n"; return true; } // value of abs-normal approximation at minimizer DblVector g_tilde = CppAD::abs_eval(n, m, s, g_cur, g_jac, delta_x); // double derivative = (g_tilde[0] - g_cur[0]) / max_delta_x; CPPAD_ASSERT_UNKNOWN( derivative <= 0.0 ) if( - epsilon[1] < derivative ) { if( level > 0 ) std::cout << "end min_nso_linear: derivative near zero\n"; return true; } // // value of a(x) at new x DblVector a_new = a.Forward(0, x_new); // // (x_new, a_new) DblVector xu_new = min_nso_linear_join(x_new, a_new); // // value of g[ x_new, a_new ] DblVector g_new = g.Forward(0, xu_new); // // // check for descent of objective double rate_new = (g_new[0] - g_cur[0]) / max_delta_x; if( - epsilon[1] < rate_new ) { // did not get sufficient descent b_cur /= 2.0; if( level > 0 ) std::cout << "itr = " << itr << ", rate_new = " << rate_new << ", b_cur = " << b_cur << "\n"; // } else { // got sufficient descent so accept candidate for x x_out = x_new; a_cur = a_new; g_cur = g_new; xu_cur = xu_new; // if( level > 0 ) { std::cout << "itr = " << itr << ", derivative = "<< derivative << ", max_delta_x = "<< max_delta_x << ", objective = " << g_cur[0] << "\n"; abs_print_mat("x_out", n, 1, x_out); } } } if( level > 0 ) std::cout << "end min_nso_linear: maximum number of iterations exceeded\n"; return false; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: example/abs_normal/min_nso_linear.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin min_nso_linear.hpp} min_nso_linear Source Code ########################## {xrst_literal example/abs_normal/min_nso_linear.hpp // BEGIN C++ // END C++ } {xrst_end min_nso_linear.hpp} ================================================ FILE: example/abs_normal/min_nso_quad.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin min_nso_quad.cpp} abs_normal min_nso_quad: Example and Test ######################################### Purpose ******* We minimize the function :math:`f : \B{R}^3 \rightarrow \B{R}` defined by .. math:: :nowrap: \begin{eqnarray} f( x_0, x_1, x_2 ) & = & x_0^2 + 2 (x_0 + x_1)^2 + | x_2 | \end{eqnarray} Discussion ********** This routine uses :ref:`abs_min_quad-name` which uses :ref:`qp_box-name` , a quadratic programming algorithm. It is mean to be compared with :ref:`min_nso_linear.cpp-name` which uses a linear programing algorithm for the same problem. To see this comparison, set *level* = 1 is both examples. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end min_nso_quad.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include # include "min_nso_quad.hpp" bool min_nso_quad(void) { bool ok = true; // using CppAD::AD; using CppAD::ADFun; // typedef CPPAD_TESTVECTOR(size_t) s_vector; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR( AD ) ad_vector; // size_t level = 0; // level of tracing size_t n = 3; // size of x size_t m = 1; // size of y size_t s = 1; // number of data points and absolute values // // start recording the function f(x) ad_vector ax(n), ay(m); for(size_t j = 0; j < n; j++) ax[j] = double(j + 1); Independent( ax ); // ay[0] = ax[0] * ax[0]; ay[0] += 2.0 * (ax[0] + ax[1]) * (ax[0] + ax[1]); ay[0] += fabs( ax[2] ); ADFun f(ax, ay); // // create its abs_normal representation in g, a ADFun g, a; f.abs_normal_fun(g, a); // check dimension of domain and range space for g ok &= g.Domain() == n + s; ok &= g.Range() == m + s; // check dimension of domain and range space for a ok &= a.Domain() == n; ok &= a.Range() == s; // epsilon d_vector epsilon(2); double eps = 1e-3; epsilon[0] = eps; epsilon[1] = eps; // maxitr s_vector maxitr(3); maxitr[0] = 100; maxitr[1] = 20; maxitr[2] = 20; // b_in double b_in = 1.0; // call min_nso_quad d_vector x_in(n), x_out(n); for(size_t j = 0; j < n; j++) x_in[j] = double(j + 1); // ok &= CppAD::min_nso_quad( level, f, g, a, epsilon, maxitr, b_in, x_in, x_out ); // for(size_t j = 0; j < n; j++) ok &= std::fabs( x_out[j] ) < eps; return ok; } // END C++ ================================================ FILE: example/abs_normal/min_nso_quad.hpp ================================================ # ifndef CPPAD_EXAMPLE_ABS_NORMAL_MIN_NSO_QUAD_HPP # define CPPAD_EXAMPLE_ABS_NORMAL_MIN_NSO_QUAD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin min_nso_quad} {xrst_spell dbl maxitr } Non-Smooth Optimization Using Abs-normal Quadratic Approximations ################################################################# Syntax ****** | *ok* = ``min_nso_quad`` ( | |tab| *level* , *f* , *g* , *a* , *epsilon* , *maxitr* , *b_in* , *x_in* , *x_out* | ) Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Source ****** This following is a link to the source code for this example: :ref:`min_nso_quad.hpp-name` . Purpose ******* Given a current that abs-normal representation :ref:`abs_normal_fun@g` , :ref:`abs_normal_fun@a` , for a function :math:`f(x)`, this routine minimizes :math:`f(x)`. DblVector ********* is a :ref:`SimpleVector-name` class with elements of type ``double`` . SizeVector ********** is a :ref:`SimpleVector-name` class with elements of type ``size_t`` . level ***** This value is less that or equal 5. If *level* == 0 , no tracing of the optimization is printed. If *level* >= 1 , a trace of each iteration of ``min_nso_quad`` is printed. If *level* >= 2 , a trace of each iteration of the ``abs_min_quad`` sub-problem is printed. If *level* >= 3 , a trace of the :ref:`qp_box-name` sub-problem is printed. If *level* >= 4 , a trace of the :ref:`qp_interior-name` sub-problem is printed. f * This is the original function for the abs-normal form; see :ref:`abs_normal_fun@f` . n = We use *n* to denote the dimension of the domain for *f* ; i.e., *f* . ``Domain`` () . m = We use *m* to denote the dimension of the range for *f* ; i.e., *f* . ``Range`` () . This must be equal to one. s = We use :ref:`abs_normal_fun@f@s` to denote the number absolute terms in *f* . g * This is the function :ref:`abs_normal_fun@g` in the abs-normal representation of *f* . a * This is the function :ref:`abs_normal_fun@a` in the abs-normal representation of *f* . epsilon ******* This is a vector with size 2. The value *epsilon* [0] is convergence criteria in terms of the infinity norm of the difference of *x_out* between iterations. The value *epsilon* [1] is convergence criteria in terms of the derivative of :math:`f(x)`. This derivative is actually the average of the directional derivative in the direction of the sub-problem minimizer. maxitr ****** This is a vector with size 3. The value *maxitr* [0] is the maximum number of ``min_nso_quad`` iterations to try before giving up on convergence. The value *maxitr* [1] is the maximum number of iterations in the ``abs_min_quad`` sub-problem. The value *maxitr* [2] is the maximum number of iterations in the :ref:`qp_interior` sub-problems. b_in **** This the initial bound on the trust region size. To be specific, if :math:`b` is the current trust region size, at each iteration affine approximation is minimized with respect to :math:`\Delta x` and subject to .. math:: -b \leq \Delta x_j \leq b for *j* = 0 , ..., *n* ``-1`` . It must hold that *b_in* > *epsilon* [0] . x_in **** This vector *x_out* has size *n* . It is the starting point for the optimization procedure; i.e., the ``min_nso_quad`` iterations. x_out ***** This vector *x_out* has size *n* . The input value of its elements does not matter. Upon return, it is the approximate minimizer of the abs-normal approximation for :math:`f(x)` over the trust region is :math:`x = \hat{x} + \Delta x`. {xrst_toc_hidden example/abs_normal/min_nso_quad.cpp example/abs_normal/min_nso_quad.xrst } Example ******* The file :ref:`min_nso_quad.cpp-name` contains an example and test of ``min_nso_quad`` . {xrst_end min_nso_quad} ----------------------------------------------------------------------------- */ # include # include "abs_min_quad.hpp" # include "abs_eval.hpp" // BEGIN C++ namespace { CPPAD_TESTVECTOR(double) min_nso_quad_join( const CPPAD_TESTVECTOR(double)& x , const CPPAD_TESTVECTOR(double)& u ) { size_t n = x.size(); size_t s = u.size(); CPPAD_TESTVECTOR(double) xu(n + s); for(size_t j = 0; j < n; j++) xu[j] = x[j]; for(size_t j = 0; j < s; j++) xu[n + j] = u[j]; return xu; } } namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN PROTOTYPE template bool min_nso_quad( size_t level , ADFun& f , ADFun& g , ADFun& a , const DblVector& epsilon , SizeVector maxitr , double b_in , const DblVector& x_in , DblVector& x_out ) // END PROTOTYPE { using std::fabs; // // number of absolute value terms size_t s = a.Range(); // // size of domain for f size_t n = f.Domain(); // // size of range space for f size_t m = f.Range(); // CPPAD_ASSERT_KNOWN( g.Domain() == n + s, "min_nso_quad: (g, a) is not an abs-normal for for f" ); CPPAD_ASSERT_KNOWN( g.Range() == m + s, "min_nso_quad: (g, a) is not an abs-normal for for f" ); CPPAD_ASSERT_KNOWN( level <= 5, "min_nso_quad: level is not less that or equal 5" ); CPPAD_ASSERT_KNOWN( size_t(epsilon.size()) == 2, "min_nso_quad: size of epsilon not equal to 2" ); CPPAD_ASSERT_KNOWN( size_t(maxitr.size()) == 3, "min_nso_quad: size of maxitr not equal to 3" ); CPPAD_ASSERT_KNOWN( g.Domain() > s && g.Range() > s, "min_nso_quad: g, a is not an abs-normal representation" ); CPPAD_ASSERT_KNOWN( m == 1, "min_nso_quad: m is not equal to 1" ); CPPAD_ASSERT_KNOWN( size_t(x_in.size()) == n, "min_nso_quad: size of x_in not equal to n" ); CPPAD_ASSERT_KNOWN( size_t(x_out.size()) == n, "min_nso_quad: size of x_out not equal to n" ); CPPAD_ASSERT_KNOWN( epsilon[0] < b_in, "min_nso_quad: b_in <= epsilon[0]" ); if( level > 0 ) { std::cout << "start min_nso_quad\n"; std::cout << "b_in = " << b_in << "\n"; CppAD::abs_print_mat("x_in", n, 1, x_in); } // level in abs_min_quad sub-problem size_t level_tilde = 0; if( level > 0 ) level_tilde = level - 1; // // maxitr in abs_min_quad sub-problem SizeVector maxitr_tilde(2); maxitr_tilde[0] = maxitr[1]; maxitr_tilde[1] = maxitr[2]; // // epsilon in abs_min_quad sub-problem DblVector eps_tilde(2); eps_tilde[0] = epsilon[0] / 10.; eps_tilde[1] = epsilon[1] / 10.; // // current bound double b_cur = b_in; // // initialize the current x x_out = x_in; // // value of a(x) at current x DblVector a_cur = a.Forward(0, x_out); // // (x_out, a_cur) DblVector xu_cur = min_nso_quad_join(x_out, a_cur); // // value of g[ x_cur, a_cur ] DblVector g_cur = g.Forward(0, xu_cur); // for(size_t itr = 0; itr < maxitr[0]; itr++) { // Jacobian of g[ x_cur, a_cur ] DblVector g_jac = g.Jacobian(xu_cur); // // Hessian at x_cur DblVector f_hes = f.Hessian(x_out, 0); // // bound in abs_min_quad sub-problem DblVector bound_tilde(n); for(size_t j = 0; j < n; j++) bound_tilde[j] = b_cur; // DblVector delta_x(n); bool ok = abs_min_quad( level_tilde, n, m, s, g_cur, g_jac, f_hes, bound_tilde, eps_tilde, maxitr_tilde, delta_x ); if( ! ok ) { if( level > 0 ) std::cout << "end min_nso_quad: abs_min_quad failed\n"; return false; } // // new candidate value for x DblVector x_new(n); double max_delta_x = 0.0; for(size_t j = 0; j < n; j++) { x_new[j] = x_out[j] + delta_x[j]; max_delta_x = std::max(max_delta_x, std::fabs( delta_x[j] ) ); } // if( max_delta_x < 0.75 * b_cur && max_delta_x < epsilon[0] ) { if( level > 0 ) std::cout << "end min_nso_quad: delta_x is near zero\n"; return true; } // value of abs-normal approximation at minimizer DblVector g_tilde = CppAD::abs_eval(n, m, s, g_cur, g_jac, delta_x); // double derivative = (g_tilde[0] - g_cur[0]) / max_delta_x; CPPAD_ASSERT_UNKNOWN( derivative <= 0.0 ) if( - epsilon[1] < derivative ) { if( level > 0 ) std::cout << "end min_nso_quad: derivative near zero\n"; return true; } // // value of a(x) at new x DblVector a_new = a.Forward(0, x_new); // // (x_new, a_new) DblVector xu_new = min_nso_quad_join(x_new, a_new); // // value of g[ x_new, a_new ] DblVector g_new = g.Forward(0, xu_new); // // check for descent of objective double rate_new = (g_new[0] - g_cur[0]) / max_delta_x; if( - epsilon[1] < rate_new ) { // did not get sufficient descent b_cur /= 2.0; if( level > 0 ) std::cout << "itr = " << itr << ", rate_new = " << rate_new << ", b_cur = " << b_cur << "\n"; // } else { // got sufficient descent so accept candidate for x x_out = x_new; a_cur = a_new; g_cur = g_new; xu_cur = xu_new; // if( level > 0 ) { std::cout << "itr = " << itr << ", derivative = "<< derivative << ", max_delta_x = "<< max_delta_x << ", objective = " << g_cur[0] << "\n"; abs_print_mat("x_out", n, 1, x_out); } } } if( level > 0 ) std::cout << "end min_nso_quad: maximum number of iterations exceeded\n"; return false; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: example/abs_normal/min_nso_quad.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin min_nso_quad.hpp} min_nso_quad Source Code ######################## {xrst_literal example/abs_normal/min_nso_quad.hpp // BEGIN C++ // END C++ } {xrst_end min_nso_quad.hpp} ================================================ FILE: example/abs_normal/qp_box.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin qp_box.cpp} {xrst_spell rl } abs_normal qp_box: Example and Test ################################### Problem ******* Our original problem is .. math:: \begin{array}{rl} \R{minimize} & x_0 - x_1 \; \R{w.r.t} \; x \in \B{R}^2 \\ \R{subject \; to} & -2 \leq x_0 \leq +2 \; \R{and} \; -2 \leq x_1 \leq +2 \end{array} Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end qp_box.cpp} */ // BEGIN C++ # include # include # include "qp_box.hpp" bool qp_box(void) { bool ok = true; typedef CppAD::vector vector; // size_t n = 2; size_t m = 0; vector a(n), b(n), c(m), C(m), g(n), G(n*n), xin(n), xout(n); a[0] = -2.0; a[1] = -2.0; b[0] = +2.0; b[1] = +2.0; g[0] = +1.0; g[1] = -1.0; for(size_t i = 0; i < n * n; i++) G[i] = 0.0; // // (0, 0) is feasible. xin[0] = 0.0; xin[1] = 0.0; // size_t level = 0; double epsilon = 99.0 * std::numeric_limits::epsilon(); size_t maxitr = 20; // ok &= CppAD::qp_box( level, a, b, c, C, g, G, epsilon, maxitr, xin, xout ); // // check optimal value for x ok &= std::fabs( xout[0] + 2.0 ) < epsilon; ok &= std::fabs( xout[1] - 2.0 ) < epsilon; // return ok; } // END C++ ================================================ FILE: example/abs_normal/qp_box.hpp ================================================ # ifndef CPPAD_EXAMPLE_ABS_NORMAL_QP_BOX_HPP # define CPPAD_EXAMPLE_ABS_NORMAL_QP_BOX_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin qp_box} {xrst_spell maxitr rl xin xout } abs_normal: Solve a Quadratic Program With Box Constraints ########################################################## Syntax ****** | *ok* = ``qp_box`` ( | |tab| *level* , *a* , *b* , *c* , *C* , *g* , *G* , *epsilon* , *maxitr* , *xin* , *xout* | ) Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Source ****** This following is a link to the source code for this example: :ref:`qp_box.hpp-name` . Purpose ******* This routine could be used to create a version of :ref:`abs_min_linear-name` that solved quadratic programs (instead of linear programs). Problem ******* We are given :math:`a \in \B{R}^n`, :math:`b \in \B{R}^n`, :math:`c \in \B{R}^m`, :math:`C \in \B{R}^{m \times n}`, :math:`g \in \B{R}^n`, :math:`G \in \B{R}^{n \times n}`, where :math:`G` is positive semi-definite. This routine solves the problem .. math:: \begin{array}{rl} \R{minimize} & \frac{1}{2} x^T G x + g^T x \; \R{w.r.t} \; x \in \B{R}^n \\ \R{subject \; to} & C x + c \leq 0 \; \R{and} \; a \leq x \leq b \end{array} The matrix :math:`G + C^T C` must be positive definite on components of the vector :math:`x` where the lower limit minus infinity and the upper limit is plus infinity; see *a* and *b* below. Vector ****** The type *Vector* is a simple vector with elements of type ``double`` . level ***** This value is less that or equal two. If *level* == 0 , no tracing is printed. If *level* >= 1 , a trace of the ``qp_box`` operations is printed. If *level* == 2 , a trace of the :ref:`qp_interior-name` sub-problem is printed. a * This is the vector of lower limits for :math:`x` in the problem. If *a* [ *j* ] is minus infinity, there is no lower limit for :math:`x_j`. b * This is the vector of upper limits for :math:`x` in the problem. If *a* [ *j* ] is plus infinity, there is no upper limit for :math:`x_j`. Lower c ******* This is the value of the inequality constraint function at :math:`x = 0`. Upper C ******* This is a :ref:`row-major` representation of thee the inequality constraint matrix :math:`C`. Lower g ******* This is the gradient of the objective function. Upper G ******* This is a row-major representation of the Hessian of the objective function. For :math:`j = 0 , \ldots , n-1`, :math:`- \infty < a_j` or :math:`b_j < + \infty` or :math:`G_{j,j} > 0.0`. epsilon ******* This argument is the convergence criteria; see :ref:`qp_box@KKT Conditions` below. It must be greater than zero. maxitr ****** This is the maximum number of :ref:`qp_interior-name` iterations to try before giving up on convergence. xin *** This argument has size *n* and is the initial point for the algorithm. It must strictly satisfy the constraints; i.e., *a* < *xin* , *xin* < *b* , *C* * *xin* ``-`` *c* < 0 xout **** This argument has size is *n* and the input value of its elements does no matter. Upon return it is the primal variables :math:`x` corresponding to the problem solution. ok ** If the return value *ok* is true, convergence is obtained; i.e., .. math:: | F ( x , y_a, s_a, y_b, s_b, y_c, s_c ) |_\infty < \varepsilon where :math:`|v|_\infty` is the infinity norm of the vector :math:`v`, :math:`\varepsilon` is *epsilon* , :math:`x` is equal to *xout* , :math:`y_a, s_a \in \B{R}_+^n`, :math:`y_b, s_b \in \B{R}_+^n` and :math:`y_c, s_c \in \B{R}_+^m`. KKT Conditions ************** Give a vector :math:`v \in \B{R}^m` we define :math:`D(v) \in \B{R}^{m \times m}` as the corresponding diagonal matrix. We also define :math:`1_m \in \B{R}^m` as the vector of ones. We define .. math:: F ( x , y_a, s_a, y_b, s_b, y_c, s_c ) = \left( \begin{array}{c} g + G x - y_a + y_b + y_c^T C \\ a + s_a - x \\ x + s_b - b \\ C x + c + s_c \\ D(s_a) D(y_a) 1_m \\ D(s_b) D(y_b) 1_m \\ D(s_c) D(y_c) 1_m \end{array} \right) where :math:`x \in \B{R}^n`, :math:`y_a, s_a \in \B{R}_+^n`, :math:`y_b, s_b \in \B{R}_+^n` and :math:`y_c, s_c \in \B{R}_+^m`. The KKT conditions for a solution of this problem is .. math:: F ( x , y_a, s_a, y_b, s_b, y_c, s_c ) = 0 {xrst_toc_hidden example/abs_normal/qp_box.cpp example/abs_normal/qp_box.xrst } Example ******* The file :ref:`qp_box.cpp-name` contains an example and test of ``qp_box`` . {xrst_end qp_box} ----------------------------------------------------------------------------- */ # include "qp_interior.hpp" // BEGIN C++ namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN PROTOTYPE template bool qp_box( size_t level , const Vector& a , const Vector& b , const Vector& c , const Vector& C , const Vector& g , const Vector& G , double epsilon , size_t maxitr , const Vector& xin , Vector& xout ) // END PROTOTYPE { double inf = std::numeric_limits::infinity(); // size_t n = a.size(); size_t m = c.size(); // CPPAD_ASSERT_KNOWN(level <= 2, "qp_interior: level is greater than 2"); CPPAD_ASSERT_KNOWN( size_t(b.size()) == n, "qp_box: size of b is not n" ); CPPAD_ASSERT_KNOWN( size_t(C.size()) == m * n, "qp_box: size of C is not m * n" ); CPPAD_ASSERT_KNOWN( size_t(g.size()) == n, "qp_box: size of g is not n" ); CPPAD_ASSERT_KNOWN( size_t(G.size()) == n * n, "qp_box: size of G is not n * n" ); if( level > 0 ) { std::cout << "start qp_box\n"; CppAD::abs_print_mat("a", n, 1, a); CppAD::abs_print_mat("b", n, 1, b); CppAD::abs_print_mat("c", m, 1, c); CppAD::abs_print_mat("C", m, n, C); CppAD::abs_print_mat("g", 1, n, g); CppAD::abs_print_mat("G", n, n, G); CppAD::abs_print_mat("xin", n, 1, xin); } // // count number of lower and upper limits size_t n_limit = 0; for(size_t j = 0; j < n; j++) { CPPAD_ASSERT_KNOWN(G[j * n + j] >= 0.0, "qp_box: G_{j,j} < 0.0"); if( -inf < a[j] ) ++n_limit; if( b[j] < inf ) ++n_limit; } // // C_int and c_int define the extended constraints Vector C_int((m + n_limit) * n ), c_int(m + n_limit); for(size_t i = 0; i < size_t(C_int.size()); i++) C_int[i] = 0.0; // // put C * x + c <= 0 in C_int, c_int for(size_t i = 0; i < m; i++) { c_int[i] = c[i]; for(size_t j = 0; j < n; j++) C_int[i * n + j] = C[i * n + j]; } // // put I * x - b <= 0 in C_int, c_int size_t i_limit = 0; for(size_t j = 0; j < n; j++) if( b[j] < inf ) { c_int[m + i_limit] = - b[j]; C_int[(m + i_limit) * n + j] = 1.0; ++i_limit; } // // put a - I * x <= 0 in C_int, c_int for(size_t j = 0; j < n; j++) if( -inf < a[j] ) { c_int[m + i_limit] = a[j]; C_int[(m + i_limit) * n + j] = -1.0; ++i_limit; } Vector yout(m + n_limit), sout(m + n_limit); size_t level_int = 0; if( level == 2 ) level_int = 1; bool ok = qp_interior( level_int, c_int, C_int, g, G, epsilon, maxitr, xin, xout, yout, sout ); if( level > 0 ) { if( level < 2 ) CppAD::abs_print_mat("xout", n, 1, xout); if( ok ) std::cout << "end q_box: ok = true\n"; else std::cout << "end q_box: ok = false\n"; } return ok; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: example/abs_normal/qp_box.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin qp_box.hpp} qp_box Source Code ################## {xrst_literal example/abs_normal/qp_box.hpp // BEGIN C++ // END C++ } {xrst_end qp_box.hpp} ================================================ FILE: example/abs_normal/qp_interior.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin qp_interior.cpp} {xrst_spell rlr } abs_normal qp_interior: Example and Test ######################################## Problem ******* Our original problem is .. math:: \R{minimize} \; | u - 1| \; \R{w.r.t} \; u \in \B{R} We reformulate this as the following problem .. math:: \begin{array}{rlr} \R{minimize} & v & \R{w.r.t} \; (u,v) \in \B{R}^2 \\ \R{subject \; to} & u - 1 \leq v \\ & 1 - u \leq v \end{array} This is equivalent to .. math:: \begin{array}{rlr} \R{minimize} & (0, 1) \cdot (u, v)^T & \R{w.r.t} \; (u,v) \in \B{R}^2 \\ \R{subject \; to} & \left( \begin{array}{cc} 1 & -1 \\ -1 & -1 \end{array} \right) \left( \begin{array}{c} u \\ v \end{array} \right) + \left( \begin{array}{c} -1 \\ 1 \end{array} \right) \leq 0 \end{array} which is in the form expected by :ref:`qp_interior-name` . Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end qp_interior.cpp} */ // BEGIN C++ # include # include # include "qp_interior.hpp" bool qp_interior(void) { bool ok = true; typedef CppAD::vector vector; // size_t n = 2; size_t m = 2; vector C(m*n), c(m), G(n*n), g(n), xin(n), xout(n), yout(m), sout(m); C[ 0 * n + 0 ] = 1.0; // C(0,0) C[ 0 * n + 1 ] = -1.0; // C(0,1) C[ 1 * n + 0 ] = -1.0; // C(1,0) C[ 1 * n + 1 ] = -1.0; // C(1,1) // c[0] = -1.0; c[1] = 1.0; // g[0] = 0.0; g[1] = 1.0; // // G = 0 for(size_t i = 0; i < n * n; i++) G[i] = 0.0; // // If (u, v) = (0,2), C * (u, v) + c = (-2,-2)^T + (1,-1)^T < 0 // Hence (0, 2) is feasible. xin[0] = 0.0; xin[1] = 2.0; // double epsilon = 99.0 * std::numeric_limits::epsilon(); size_t maxitr = 10; size_t level = 0; // ok &= CppAD::qp_interior( level, c, C, g, G, epsilon, maxitr, xin, xout, yout, sout ); // // check optimal value for u ok &= std::fabs( xout[0] - 1.0 ) < epsilon; // check optimal value for v ok &= std::fabs( xout[1] ) < epsilon; // return ok; } // END C++ ================================================ FILE: example/abs_normal/qp_interior.hpp ================================================ # ifndef CPPAD_EXAMPLE_ABS_NORMAL_QP_INTERIOR_HPP # define CPPAD_EXAMPLE_ABS_NORMAL_QP_INTERIOR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin qp_interior} {xrst_spell maxitr rl sout xin xout yout } Solve a Quadratic Program Using Interior Point Method ##################################################### Syntax ****** | *ok* = ``qp_interior`` ( | *level* , *c* , *C* , *g* , *G* , *epsilon* , *maxitr* , *xin* , *xout* , *yout* , *sout* | ) Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Source ****** This following is a link to the source code for this example: :ref:`qp_interior.hpp-name` . Purpose ******* This routine could be used to create a version of :ref:`abs_min_linear-name` that solved Quadratic programs (instead of linear programs). Problem ******* We are given :math:`C \in \B{R}^{m \times n}`, :math:`c \in \B{R}^m`, :math:`G \in \B{R}^{n \times n}`, :math:`g \in \B{R}^n`, where :math:`G` is positive semi-definite and :math:`G + C^T C` is positive definite. This routine solves the problem .. math:: \begin{array}{rl} \R{minimize} & \frac{1}{2} x^T G x + g^T x \; \R{w.r.t} \; x \in \B{R}^n \\ \R{subject \; to} & C x + c \leq 0 \end{array} Vector ****** The type *Vector* is a simple vector with elements of type ``double`` . level ***** This value is zero or one. If *level* == 0 , no tracing is printed. If *level* == 1 , a trace of the ``qp_interior`` optimization is printed. Lower c ******* This is the vector :math:`c` in the problem. Upper C ******* This is a :ref:`row-major` of the matrix :math:`C` in the problem. Lower g ******* This is the vector :math:`g` in the problem. Upper G ******* This is a :ref:`row-major` of the matrix :math:`G` in the problem. epsilon ******* This argument is the convergence criteria; see :ref:`qp_interior@KKT Conditions` below. It must be greater than zero. maxitr ****** This is the maximum number of newton iterations to try before giving up on convergence. xin *** This argument has size *n* and is the initial point for the algorithm. It must strictly satisfy the constraints; i.e., :math:`C x - c < 0` for *x* = *xin* . xout **** This argument has size is *n* and the input value of its elements does no matter. Upon return it is the primal variables corresponding to the problem solution. yout **** This argument has size is *m* and the input value of its elements does no matter. Upon return it the components of *yout* are all positive and it is the dual variables corresponding to the program solution. sout **** This argument has size is *m* and the input value of its elements does no matter. Upon return it the components of *sout* are all positive and it is the slack variables corresponding to the program solution. ok ** If the return value *ok* is true, convergence is obtained; i.e., .. math:: | F_0 (xout , yout, sout) |_\infty \leq epsilon where :math:`| v |_\infty` is the maximum absolute element for the vector :math:`v` and :math:`F_\mu (x, y, s)` is defined below. KKT Conditions ************** Give a vector :math:`v \in \B{R}^m` we define :math:`D(v) \in \B{R}^{m \times m}` as the corresponding diagonal matrix. We also define :math:`1_m \in \B{R}^m` as the vector of ones. We define :math:`F_\mu : \B{R}^{n + m + m } \rightarrow \B{R}^{n + m + m}` by .. math:: F_\mu ( x , y , s ) = \left( \begin{array}{c} g + G x + y^T C \\ C x + c + s \\ D(s) D(y) 1_m - \mu 1_m \end{array} \right) The KKT conditions for a solution of this problem is :math:`0 \leq y`, :math:`0 \leq s`, and :math:`F_0 (x , y, s) = 0`. Newton Step *********** The derivative of :math:`F_\mu` is given by .. math:: F_\mu^{(1)} (x, y, s) = \left( \begin{array}{ccc} G & C^T & 0_{n,m} \\ C & 0 & I_{m,m} \\ 0_{m,m} & D(s) && D(y) \end{array} \right) The Newton step solves the following equation for :math:`\Delta x`, :math:`\Delta y`, and :math:`\Delta z` .. math:: F_\mu^{(1)} (x, y, s) \left( \begin{array}{c} \Delta x \\ \Delta y \\ \Delta s \end{array} \right) = - F_\mu (x, y, s) To simplify notation, we define .. math:: :nowrap: \begin{eqnarray} r_x (x, y, s) & = & g + G x + y^T C \\ r_y (x, y, s) & = & C x + c + s \\ r_s (x, y, s) & = & D(s) D(y) 1_m - \mu 1_m \end{eqnarray} It follows that .. math:: \left( \begin{array}{ccc} G & C^T & 0_{n,m} \\ C & 0 & I_{m,m} \\ 0_{m,m} & D(s) && D(y) \end{array} \right) \left( \begin{array}{c} \Delta x \\ \Delta y \\ \Delta s \end{array} \right) = - \left( \begin{array}{c} r_x (x, y, s) \\ r_y (x, y, s) \\ r_s (x, y, s) \end{array} \right) Elementary Row Reduction ======================== Subtracting :math:`D(y)` times the second row from the third row we obtain: .. math:: \left( \begin{array}{ccc} G & C^T & 0_{n,m} \\ C & 0 & I_{m,m} \\ - D(y) C & D(s) & 0_{m,m} \end{array} \right) \left( \begin{array}{c} \Delta x \\ \Delta y \\ \Delta s \end{array} \right) = - \left( \begin{array}{c} r_x (x, y, s) \\ r_y (x, y, s) \\ r_s (x, y, s) - D(y) r_y(x, y, s) \end{array} \right) Multiplying the third row by :math:`D(s)^{-1}` we obtain: .. math:: \left( \begin{array}{ccc} G & C^T & 0_{n,m} \\ C & 0 & I_{m,m} \\ - D(y/s) C & I_{m,m} & 0_{m,m} \end{array} \right) \left( \begin{array}{c} \Delta x \\ \Delta y \\ \Delta s \end{array} \right) = - \left( \begin{array}{c} r_x (x, y, s) \\ r_y (x, y, s) \\ D(s)^{-1} r_s (x, y, s) - D(y/s) r_y(x, y, s) \end{array} \right) where :math:`y/s` is the vector in :math:`\B{R}^m` defined by :math:`(y/s)_i = y_i / s_i`. Subtracting :math:`C^T` times the third row from the first row we obtain: .. math:: \left( \begin{array}{ccc} G + C^T D(y/s) C & 0_{n,m} & 0_{n,m} \\ C & 0 & I_{m,m} \\ - D(y/s) C & I_{m,m} & 0_{m,m} \end{array} \right) \left( \begin{array}{c} \Delta x \\ \Delta y \\ \Delta s \end{array} \right) = - \left( \begin{array}{c} r_x (x, y, s) - C^T D(s)^{-1} \left[ r_s (x, y, s) - D(y) r_y(x, y, s) \right] \\ r_y (x, y, s) \\ D(s)^{-1} r_s (x, y, s) - D(y/s) r_y(x, y, s) \end{array} \right) Solution ******** It follows that :math:`G + C^T D(y/s) C` is invertible and we can determine :math:`\Delta x` by solving the equation .. math:: [ G + C^T D(y/s) C ] \Delta x = C^T D(s)^{-1} \left[ r_s (x, y, s) - D(y) r_y(x, y, s) \right] - r_x (x, y, s) Given :math:`\Delta x` we have that .. math:: \Delta s = - r_y (x, y, s) - C \Delta x .. math:: \Delta y = D(s)^{-1}[ D(y) r_y(x, y, s) - r_s (x, y, s) + D(y) C \Delta x ] {xrst_toc_hidden example/abs_normal/qp_interior.cpp example/abs_normal/qp_interior.xrst } Example ******* The file :ref:`qp_interior.cpp-name` contains an example and test of ``qp_interior`` . {xrst_end qp_interior} ----------------------------------------------------------------------------- */ # include # include # include "abs_print_mat.hpp" // BEGIN C++ namespace { // ------------------------------------------------------------------------ template double qp_interior_max_abs(const Vector& v) { double max_abs = 0.0; for(size_t j = 0; j < size_t(v.size()); j++) max_abs = std::max( max_abs, std::fabs(v[j]) ); return max_abs; } // ------------------------------------------------------------------------ template void qp_interior_split( const Vector& v, Vector& v_x, Vector& v_y, Vector& v_s ) { size_t n = v_x.size(); size_t m = v_y.size(); CPPAD_ASSERT_UNKNOWN( size_t(v_s.size()) == m ); CPPAD_ASSERT_UNKNOWN( size_t(v.size()) == n + m + m ); for(size_t i = 0; i < n; i++) v_x[i] = v[i]; for(size_t i = 0; i < m; i++) { v_y[i] = v[n + i]; v_s[i] = v[n + m + i]; } return; } // ------------------------------------------------------------------------ template void qp_interior_join( Vector& v, const Vector& v_x, const Vector& v_y, const Vector& v_s ) { size_t n = v_x.size(); size_t m = v_y.size(); CPPAD_ASSERT_UNKNOWN( size_t(v_s.size()) == m ); CPPAD_ASSERT_UNKNOWN( size_t(v.size()) == n + m + m ); for(size_t i = 0; i < n; i++) v[i] = v_x[i]; for(size_t i = 0; i < m; i++) v[n + i] = v_y[i]; for(size_t i = 0; i < m; i++) v[n + m + i] = v_s[i]; return; } // ------------------------------------------------------------------------ template Vector qp_interior_F_0( const Vector& c , const Vector& C , const Vector& g , const Vector& G , const Vector& x , const Vector& y , const Vector& s ) { size_t n = g.size(); size_t m = c.size(); // compute r_x(x, y, s) = g + G x + y^T C Vector r_x(n); for(size_t j = 0; j < n; j++) { r_x[j] = g[j]; for(size_t i = 0; i < n; i++) r_x[j] += G[j * n + i] * x[i]; for(size_t i = 0; i < m; i++) r_x[j] += y[i] * C[i * n + j]; } // compute r_y(x, y, s) = C x + c + s Vector r_y(m); for(size_t i = 0; i < m; i++) { r_y[i] = c[i] + s[i]; for(size_t j = 0; j < n; j++) r_y[i] += C[i * n + j] * x[j]; } // compute r_s(x, y, s) = D(s) * D(y) * 1_m - mu * 1_m // where mu = 0 Vector r_s(m); for(size_t i = 0; i < m; i++) r_s[i] = s[i] * y[i]; // // combine into one vector Vector F_0(n + m + m); qp_interior_join(F_0, r_x, r_y, r_s); // return F_0; } } // namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN PROTOTYPE template bool qp_interior( size_t level , const Vector& c , const Vector& C , const Vector& g , const Vector& G , double epsilon , size_t maxitr , const Vector& xin , Vector& xout , Vector& yout , Vector& sout ) // END PROTOTYPE { size_t m = c.size(); size_t n = g.size(); CPPAD_ASSERT_KNOWN( level <= 1, "qp_interior: level is greater than one" ); CPPAD_ASSERT_KNOWN( size_t(C.size()) == m * n, "qp_interior: size of C is not m * n" ); CPPAD_ASSERT_KNOWN( size_t(G.size()) == n * n, "qp_interior: size of G is not n * n" ); if( level > 0 ) { std::cout << "start qp_interior\n"; CppAD::abs_print_mat("c", m, 1, c); CppAD::abs_print_mat("C", m, n, C); CppAD::abs_print_mat("g", n, 1, g); CppAD::abs_print_mat("G", n, n, G); CppAD::abs_print_mat("xin", n, 1, xin); } // // compute the maximum absolute element of the problem vectors and matrices double max_element = 0.0; for(size_t i = 0; i < size_t(C.size()); i++) max_element = std::max(max_element , std::fabs(C[i]) ); for(size_t i = 0; i < size_t(c.size()); i++) max_element = std::max(max_element , std::fabs(c[i]) ); for(size_t i = 0; i < size_t(G.size()); i++) max_element = std::max(max_element , std::fabs(G[i]) ); for(size_t i = 0; i < size_t(g.size()); i++) max_element = std::max(max_element , std::fabs(g[i]) ); // double mu = 1e-1 * max_element; // if( max_element == 0.0 ) { if( level > 0 ) std::cout << "end qp_interior: line_search failed\n"; return false; } // // initialize x, y, s xout = xin; for(size_t i = 0; i < m; i++) { double sum = c[i]; for(size_t j = 0; j < n; j++) sum += C[ i * n + j ] * xout[j]; if( sum > 0.0 ) { if( level > 0 ) std::cout << "end qp_interior: xin is not in interior of feasible set\n"; return false; } // sout[i] = std::sqrt(mu); yout[i] = std::sqrt(mu); } // ---------------------------------------------------------------------- // initial F_0(xout, yout, sout) Vector F_0 = qp_interior_F_0(c, C, g, G, xout, yout, sout); double F_max_abs = qp_interior_max_abs( F_0 ); for(size_t itr = 0; itr <= maxitr; itr++) { // check for convergence if( F_max_abs <= epsilon ) { if( level > 0 ) std::cout << "end qp_interior: ok = true\n"; return true; } if( itr == maxitr ) { if( level > 0 ) std::cout << "end qp_interior: max # iterations without convergence\n"; return false; } // // compute F_mu(xout, yout, sout) Vector F_mu = F_0; for(size_t i = 0; i < m; i++) F_mu[n + m + i] -= mu; // // r_x, r_y, r_s (xout, yout, sout) Vector r_x(n), r_y(m), r_s(m); qp_interior_split(F_mu, r_x, r_y, r_s); // // tmp_m = D(s)^{-1} * [ r_s - D(y) r_y ] Vector tmp_m(m); for(size_t i = 0; i < m; i++) tmp_m[i] = ( r_s[i] - yout[i] * r_y[i] ) / sout[i]; // // right_x = C^T * D(s)^{-1} * [ r_s - D(y) r_y ] - r_x Vector right_x(n); for(size_t j = 0; j < n; j++) { right_x[j] = 0.0; for(size_t i = 0; i < m; i++) right_x[j] += C[ i * n + j ] * tmp_m[i]; right_x[j] -= r_x[j]; } // // Left_x = G + C^T * D(y / s) * C Vector Left_x = G; for(size_t i = 0; i < n; i++) { for(size_t j = 0; j < n; j++) { for(size_t k = 0; k < m; k++) { double y_s = yout[k] / sout[k]; Left_x[ i * n + j] += C[k * n + j] * y_s * C[k * n + i]; } } } // delta_x Vector delta_x(n); double logdet; LuSolve(n, 1, Left_x, right_x, delta_x, logdet); // // C_delta_x = C * delta_x Vector C_delta_x(m); for(size_t i = 0; i < m; i++) { C_delta_x[i] = 0.0; for(size_t j = 0; j < n; j++) C_delta_x[i] += C[ i * n + j ] * delta_x[j]; } // // delta_y = D(s)^-1 * [D(y) * r_y - r_s + D(y) * C * delta_x] Vector delta_y(m); for(size_t i = 0; i < m; i++) { delta_y[i] = yout[i] * r_y[i] - r_s[i] + yout[i] * C_delta_x[i]; delta_y[i] /= sout[i]; } // delta_s = - r_y - C * delta_x Vector delta_s(m); for(size_t i = 0; i < m; i++) delta_s[i] = - r_y[i] - C_delta_x[i]; // // delta_xys Vector delta_xys(n + m + m); qp_interior_join(delta_xys, delta_x, delta_y, delta_s); // ------------------------------------------------------------------- // // The initial derivative in direction Delta_xys is equal to // the negative of the norm square of F_mu // // line search parameter lam Vector x(n), y(m), s(m); double lam = 2.0; bool lam_ok = false; while( ! lam_ok && lam > 1e-5 ) { lam = lam / 2.0; for(size_t j = 0; j < n; j++) x[j] = xout[j] + lam * delta_xys[j]; lam_ok = true; for(size_t i = 0; i < m; i++) { y[i] = yout[i] + lam * delta_xys[n + i]; s[i] = sout[i] + lam * delta_xys[n + m + i]; lam_ok &= s[i] > 0.0 && y[i] > 0.0; } if( lam_ok ) { Vector F_mu_tmp = qp_interior_F_0(c, C, g, G, x, y, s); for(size_t i = 0; i < m; i++) F_mu_tmp[n + m + i] -= mu; // avoid cancellation roundoff in difference of norm squared // |v + dv|^2 = v^T * v + 2 * v^T * dv + dv^T * dv // |v + dv|^2 - |v|^2 = 2 * v^T * dv + dv^T * dv double F_norm_sq = 0.0; double diff_norm_sq = 0.0; for(size_t i = 0; i < n + m + m; i++) { double dv = F_mu_tmp[i] - F_mu[i]; F_norm_sq += F_mu[i] * F_mu[i]; diff_norm_sq += 2.0 * F_mu[i] * dv + dv * dv; } lam_ok &= diff_norm_sq < - lam * F_norm_sq / 4.0; } } if( ! lam_ok ) { if( level > 0 ) std::cout << "end qp_interior: line search failed\n"; return false; } // // update current solution xout = x; yout = y; sout = s; // // updage F_0 F_0 = qp_interior_F_0(c, C, g, G, xout, yout, sout); F_max_abs = qp_interior_max_abs( F_0 ); // // update mu if( F_max_abs <= 1e1 * mu ) mu = mu / 1e2; if( level > 0 ) { std::cout << "itr = " << itr << ", mu = " << mu << ", lam = " << lam << ", F_max_abs = " << F_max_abs << "\n"; abs_print_mat("xout", 1, n, xout); } } if( level > 0 ) std::cout << "end qp_interior: program error\n"; return false; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: example/abs_normal/qp_interior.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin qp_interior.hpp} qp_interior Source Code ####################### {xrst_literal example/abs_normal/qp_interior.hpp // BEGIN C++ // END C++ } {xrst_end qp_interior.hpp} ================================================ FILE: example/abs_normal/simplex_method.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin simplex_method.cpp} {xrst_spell rlr } abs_normal simplex_method: Example and Test ########################################### Problem ******* Our original problem is .. math:: \R{minimize} \; | u - 1| \; \R{w.r.t} \; u \in \B{R} We reformulate this as the following problem .. math:: \begin{array}{rlr} \R{minimize} & v & \R{w.r.t} \; (u,v) \in \B{R}^2 \\ \R{subject \; to} & u - 1 \leq v \\ & 1 - u \leq v \end{array} We know that the value of :math:`v` at the solution is greater than or equal zero. Hence we can reformulate this problem as .. math:: \begin{array}{rlr} \R{minimize} & v & \R{w.r.t} \; ( u_- , u_+ , v) \in \B{R}_+^3 \\ \R{subject \; to} & u_+ - u_- - 1 \leq v \\ & 1 - u_+ + u_- \leq v \end{array} This is equivalent to .. math:: \begin{array}{rlr} \R{minimize} & (0, 0, 1) \cdot ( u_+, u_- , v)^T & \R{w.r.t} \; (u,v) \in \B{R}_+^3 \\ \R{subject \; to} & \left( \begin{array}{ccc} +1 & -1 & -1 \\ -1 & +1 & +1 \end{array} \right) \left( \begin{array}{c} u_+ \\ u_- \\ v \end{array} \right) + \left( \begin{array}{c} -1 \\ 1 \end{array} \right) \leq 0 \end{array} which is in the form expected by :ref:`simplex_method-name` . Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end simplex_method.cpp} */ // BEGIN C++ # include # include # include "simplex_method.hpp" bool simplex_method(void) { bool ok = true; typedef CppAD::vector vector; double eps99 = 99.0 * std::numeric_limits::epsilon(); // size_t n = 3; size_t m = 2; vector A(m * n), b(m), c(n), xout(n); A[ 0 * n + 0 ] = 1.0; // A(0,0) A[ 0 * n + 1 ] = -1.0; // A(0,1) A[ 0 * n + 2 ] = -1.0; // A(0,2) // A[ 1 * n + 0 ] = -1.0; // A(1,0) A[ 1 * n + 1 ] = +1.0; // A(1,1) A[ 1 * n + 2 ] = -1.0; // A(1,2) // b[0] = -1.0; b[1] = 1.0; // c[0] = 0.0; c[1] = 0.0; c[2] = 1.0; // size_t maxitr = 10; size_t level = 0; // ok &= CppAD::simplex_method(level, A, b, c, maxitr, xout); // // check optimal value for u ok &= std::fabs( xout[0] - 1.0 ) < eps99; // // check optimal value for v ok &= std::fabs( xout[1] ) < eps99; // return ok; } // END C++ ================================================ FILE: example/abs_normal/simplex_method.hpp ================================================ # ifndef CPPAD_EXAMPLE_ABS_NORMAL_SIMPLEX_METHOD_HPP # define CPPAD_EXAMPLE_ABS_NORMAL_SIMPLEX_METHOD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin simplex_method} {xrst_spell maxitr rl xout } abs_normal: Solve a Linear Program Using Simplex Method ####################################################### Syntax ****** | *ok* = ``simplex_method`` ( *level* , *b* , *A* , *c* , *maxitr* , *xout* ) Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Source ****** This following is a link to the source code for this example: :ref:`simplex_method.hpp-name` . Problem ******* We are given :math:`A \in \B{R}^{m \times n}`, :math:`b \in \B{R}^m`, :math:`c \in \B{R}^n`. This routine solves the problem .. math:: \begin{array}{rl} \R{minimize} & g^T x \; \R{w.r.t} \; x \in \B{R}_+^n \\ \R{subject \; to} & A x + b \leq 0 \end{array} Vector ****** The type *Vector* is a simple vector with elements of type ``double`` . level ***** This value is less than or equal two. If *level* == 0 , no tracing is printed. If *level* >= 1 , a trace :math:`x` and the corresponding objective :math:`z` is printed at each iteration. If *level* == 2 , a trace of the simplex Tableau is printed at each iteration. A * This is a :ref:`row-major` representation of the matrix :math:`A` in the problem. b * This is the vector :math:`b` in the problem. c * This is the vector :math:`c` in the problem. maxitr ****** This is the maximum number of simplex iterations to try before giving up on convergence. xout **** This argument has size is *n* and the input value of its elements does no matter. Upon return it is the primal variables corresponding to the problem solution. ok ** If the return value *ok* is true, a solution has been found. {xrst_toc_hidden example/abs_normal/simplex_method.cpp example/abs_normal/simplex_method.xrst } Example ******* The file :ref:`simplex_method.cpp-name` contains an example and test of ``simplex_method`` . {xrst_end simplex_method} ----------------------------------------------------------------------------- */ # include # include # include "abs_print_mat.hpp" // BEGIN C++ namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN PROTOTYPE template bool simplex_method( size_t level , const Vector& A , const Vector& b , const Vector& c , size_t maxitr , Vector& xout ) // END PROTOTYPE { // number of equations size_t ne = b.size(); // number of x variables size_t nx = c.size(); CPPAD_ASSERT_UNKNOWN( size_t(A.size()) == ne * nx ); CPPAD_ASSERT_UNKNOWN( level <= 2 ); // if( level > 0 ) { std::cout << "start simplex_method\n"; CppAD::abs_print_mat("A", ne, nx, A); CppAD::abs_print_mat("b", ne, 1, b); CppAD::abs_print_mat("c", nx, 1, c); } // // variables (columns) in the Tableau: // x: the original primary variables with size n // s: slack variables, one for each equation // a: auxiliary variables, one for each negative right hand size // r: right hand size for equations // // Determine number of auxiliary variables size_t na = 0; for(size_t i = 0; i < ne; i++) { if( b[i] > 0.0 ) ++na; } // size of columns in the Tableau size_t nc = nx + ne + na + 1; // number of rows in Tableau, the equations plust two objectives size_t nr = ne + 2; // Initialize Tableau as zero Vector T(nr * nc); for(size_t i = 0; i < nr * nc; i++) T[i] = 0.0; // initialize basic variable flag as false CppAD::vector basic(nc); for(size_t j = 0; j < nc; j++) basic[j] = false; // For i = 0 , ... , m-1, place the Equations // sum_j A_{i,j} * x_j + b_i <= 0 in Tableau na = 0; // use as index of next auxiliary variable for(size_t i = 0; i < ne; i++) { if( b[i] > 0.0) { // convert to - sum_j A_{i,j} x_j - b_i >= 0 for(size_t j = 0; j < nx; j++) T[i * nc + j] = - A[i * nx + j]; // slack variable has negative coefficient T[i * nc + (nx + i)] = -1.0; // auxiliary variable is basic for this constraint T[i * nc + (nx + ne + na)] = 1.0; basic[nx + ne + na] = true; // right hand side T[i * nc + (nc - 1)] = b[i]; // ++na; } else { // sum_j A_{i,j} x_j + b_i <= 0 for(size_t j = 0; j < nx; j++) T[i * nc + j] = A[i * nx + j]; // slack variable is also basic T[ i * nc + (nx + i) ] = 1.0; basic[nx + i] = true; // right hand side for equations T[ i * nc + (nc - 1) ] = - b[i]; } } // na is back to its original value CPPAD_ASSERT_UNKNOWN( nc == nx + ne + na + 1 ); // // place the equation objective equation in Tablueau // row ne corresponds to the equation z - sum_j c_j x_j = 0 // column index for z is nx + ne + na for(size_t j = 0; j < nx; j++) T[ne * nc + j] = - c[j]; // // row ne+1 corresponds to the equation w - a_0 - ... - a_{na-1} = 0 // column index for w is nx + ne + na +1 for(size_t j = 0; j < na; j++) T[(ne + 1) * nc + (nx + ne + j)] = -1.0; // // fix auxiliary objective so coefficients in w // for auxiliary variables are zero for(size_t k = 0; k < na; k++) { size_t ja = nx + ne + k; size_t ia = ne; for(size_t i = 0; i < ne; i++) { if( T[i * nc + ja] != 0.0 ) { CPPAD_ASSERT_UNKNOWN( T[i * nc + ja] == 1.0 ); CPPAD_ASSERT_UNKNOWN( T[(ne + 1) * nc + ja] == -1.0 ) CPPAD_ASSERT_UNKNOWN( ia == ne ); ia = i; } } CPPAD_ASSERT_UNKNOWN( ia < ne ); for(size_t j = 0; j < nc; j++) T[(ne + 1) * nc + j] += T[ia * nc + j]; // The result in column ja is zero, avoid roundoff T[(ne + 1) * nc + ja] = 0.0; } // // index of current objective size_t iobj = ne; // original objective z if( na > 0 ) iobj = ne + 1; // auxiliary objective w // // simplex iterations for(size_t itr = 0; itr < maxitr; itr++) { // current value for xout for(size_t j = 0; j < nx; j++) { xout[j] = 0.0; if( basic[j] ) { // determine which row of column j is non-zero xout[j] = std::numeric_limits::quiet_NaN(); for(size_t i = 0; i < ne; i++) { double T_ij = T[i * nc + j]; CPPAD_ASSERT_UNKNOWN( T_ij == 0.0 || T_ij == 1.0 ); if( T_ij == 1.0 ) { // corresponding value in right hand side xout[j] = T[ i * nc + (nc-1) ]; } } } } if( level > 1 ) CppAD::abs_print_mat("T", nr, nc, T); if( level > 0 ) { CppAD::abs_print_mat("x", nx, 1, xout); std::cout << "itr = " << itr; if( iobj > ne ) std::cout << ", auxiliary objective w = "; else std::cout << ", objective z = "; std::cout << T[iobj * nc + (nc - 1)] << "\n"; } // // number of variables depends on objective size_t nv = nx + ne; // (x, s) if( iobj == ne + 1 ) { // check if we have solved the auxiliary problem bool done = true; for(size_t k = 0; k < na; k++) if( basic[nx + ne + k] ) done = false; if( done ) { // switch to optimizing the original objective iobj = ne; } else nv = nx + ne + na; // (x, s, a) } // // determine variable with maximum coefficient in objective row double cmax = 0.0; size_t jmax = nv; for(size_t j = 0; j < nv; j++) { if( T[iobj * nc + j] > cmax ) { CPPAD_ASSERT_UNKNOWN( ! basic[j] ); cmax = T[ iobj * nc + j]; jmax = j; } } // check for solution if( jmax == nv ) { if( iobj == ne ) { if( level > 0 ) std::cout << "end simplex_method\n"; return true; } if( level > 0 ) std::cout << "end_simples_method: no feasible solution\n"; return false; } // // We will increase the j-th variable. // Determine which row will be the pivot row. double rmin = std::numeric_limits::infinity(); size_t imin = ne; for(size_t i = 0; i < ne; i++) { if( T[i * nc + jmax] > 0.0 ) { double r = T[i * nc + (nc-1) ] / T[i * nc + jmax]; if( r < rmin ) { rmin = r; imin = i; } } } if( imin == ne ) { // not auxiliary objective CPPAD_ASSERT_UNKNOWN( iobj == ne ); if( level > 0 ) std::cout << "end simplex_method: objective is unbounded below\n"; return false; } double pivot = T[imin * nc + jmax]; // // Which variable is changing from basic to non-basic. // Initialize as not yet determined. size_t basic2not = nc; // // Divide row imin by pivot element for(size_t j = 0; j < nc; j++) { if( basic[j] && T[imin * nc + j] == 1.0 ) { CPPAD_ASSERT_UNKNOWN( basic2not == nc ); basic2not = j; } T[imin * nc + j] /= pivot; } // The result in column jmax is one, avoid roundoff T[imin * nc + jmax ] = 1.0; // // Check that we found the variable going from basic to non-basic CPPAD_ASSERT_UNKNOWN( basic2not < nv && basic2not != jmax ); // // convert variable for column jmax to basic // and for column basic2not to non-basic for(size_t i = 0; i < nr; i++) if( i != imin ) { double r = T[i * nc + jmax ] / T[imin * nc + jmax]; // row_i = row_i - r * row_imin for(size_t j = 0; j < nc; j++) T[i * nc + j] -= r * T[imin * nc + j]; // The result in column jmax is zero, avoid roundoff T[i * nc + jmax] = 0.0; } // update flag for basic variables basic[ basic2not ] = false; basic[ jmax ] = true; } if( level > 0 ) std::cout << "end simplex_method: maximum # iterations without solution\n"; return false; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: example/abs_normal/simplex_method.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin simplex_method.hpp} simplex_method Source Code ########################## {xrst_literal example/abs_normal/simplex_method.hpp // BEGIN C++ // END C++ } {xrst_end simplex_method.hpp} ================================================ FILE: example/abs_normal/talk.tex ================================================ % SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later % SPDX-FileCopyrightText: Bradley M. Bell % SPDX-FileContributor: 2003-22 Bradley M. Bell % ---------------------------------------------------------------------------- \documentclass{beamer} % needed for fonts; see %https://tex.stackexchange.com/questions/58087/ % how-to-remove-the-warnings-font-shape-ot1-cmss-m-n-in-size-4-not-available \usepackage{lmodern}% http://ctan.org/pkg/lm % macros \newcommand{\B}[1]{{\bf #1}} \title[CppAD]{CppAD's Abs-normal Representation} \institute{ \begin{tabular}{c} {\Large Bradley M. Bell} \\ \\ Applied Physics Laboratory and \\ Institute for Health Metrics and Evaluation, \\ University of Washington, \\ \texttt{bradbell@uw.edu} \\ \end{tabular} } \begin{document} \begin{frame} \titlepage \end{frame} % ---------------------------------------------------------------------------- \begin{frame}{Non-Smooth Functions} \begin{block}{$f(x)$} $ f : \B{R}^n \rightarrow \B{R}^m $ where the only non-smooth nodes in its computational graph are $| \cdot |$. \end{block} \pause \begin{block}{$a(x)$} Let $s$ be number of $| \cdot |$ in $f$. We define $a : \B{R}^n \rightarrow \B{R}^s$ where $a_i (x)$ is the result for the $i$-th absolute value. \end{block} \end{frame} % ---------------------------------------------------------------------------- \begin{frame}{Smooth Functions} \begin{block}{$z(x,u)$} There is a smooth $z : \B{R}^{n + s} \rightarrow \B{R}^s$ where $z_i (x, u)$ is argument to $i$-th absolute value when $u_j = a_j (x)$ for $j < i$. \end{block} \pause \begin{block}{$y(x,u)$} There is a smooth $y : \B{R}^{n + s} \rightarrow \B{R}^m$ where $y(x , u) = f(x)$ when $u = a (x)$ for all $i$. \end{block} \pause \begin{block}{$g(x,u)$} The function $g : \B{R}^{n + s} \rightarrow \B{R}^{m + s}$ is defined by \[ g(x, u) = \left[ \begin{array}{c} y(x, u) \\ z(x, y) \end{array} \right] \] \end{block} \end{frame} % ---------------------------------------------------------------------------- \begin{frame}{Approximating $a(x)$} \[ z[ \hat{x} ]( x , u ) = z ( \hat{x}, a( \hat{x} ) ) + \partial_x z ( \hat{x}, a( \hat{x} ) ) ( x - \hat{x} ) + \partial_u z ( \hat{x}, a( \hat{x} ) ) ( u - a( \hat{x} ) ) \] \pause Note that $z_0 ( x , u )$ does not depend on $u$: \[ a_0 [ \hat{x} ]( x ) = \left| z_0 ( \hat{x}, a( \hat{x} ) ) + \partial_x z_0 ( \hat{x}, a( \hat{x} ) ) ( x - \hat{x} ) \right| \] \pause \begin{eqnarray*} a_i [ \hat{x} ]( x ) & = & \left | \vphantom{ \sum_{j < i} } z_i ( \hat{x}, a( \hat{x} ) ) + \partial_x z_i ( \hat{x}, a( \hat{x} ) ) ( x - \hat{x} ) \right. \\ & + & \left. \sum_{j < i} \partial_{u(j)} z_i ( \hat{x}, a( \hat{x} ) ) ( a_j [ \hat{x} ] ( x ) - a_j ( \hat{x} ) ) \right| \end{eqnarray*} \pause \[ a(x) = a[ \hat{x} ]( x ) + o( x - \hat{x} ) \phantom{\rule{1in}{1pt}} \] \end{frame} % ---------------------------------------------------------------------------- \begin{frame}{Representation} \begin{block}{ \texttt{f.abs\_normal\_fun(g, a)} } Given the \texttt{ADFun} object \texttt{f} for $f(x)$, this creates the two \texttt{ADFun} objects \texttt{g}, \texttt{a} for $g(x, u)$ and $a(x)$ respectively. \end{block} \pause \begin{block}{Advantages} Any AD operation can be computed for the smooth function \texttt{g}; e.g., any order forward and reverse mode, sparsity patterns, and sparse derivatives. \end{block} \end{frame} % ---------------------------------------------------------------------------- \begin{frame}{Approximating $f(x)$} \[ y[ \hat{x} ]( x , u ) = y ( \hat{x}, a( \hat{x} ) ) + \partial_x y ( \hat{x}, a( \hat{x} ) ) ( x - \hat{x} ) + \partial_u y ( \hat{x}, a( \hat{x} ) ) ( u - a( \hat{x} ) ) \] \pause \[ f(x) = y[ \hat{x} ] ( x , a[ \hat{x} ] (x) ) + o ( x - \hat{x} ) \phantom{\rule{1.3in}{1pt}} \] \begin{block}{ \texttt{abs\_eval(n, m, s, g\_hat , g\_jac , delta\_x)} } Evaluates $y[ \hat{x} ] ( x , a[ \hat{x} ] (x) )$ \end{block} \pause \begin{itemize} \item \texttt{g\_hat} is $g[ \hat{x} , a( \hat{x} ) ]$ \pause \item \texttt{g\_jac} is $g^{(1)} [ \hat{x} , a( \hat{x} ) ]$ \pause \item \texttt{delta\_x} is $x - \hat{x}$ \end{itemize} \end{frame} % ---------------------------------------------------------------------------- \begin{frame}{ \texttt{abs\_min\_linear} } \begin{block}{Problem} minimize $\tilde{f} ( x ) = y[ \hat{x} ] ( x , a( \hat{x} ) )$ w.r.t $x$ subject to $-b \leq x \leq b$ using the assumption that $\tilde{f} (x)$ is convex. \end{block} \pause \begin{block}{Algorithm} \begin{enumerate} \item Start at with point $x = \hat{x}$ and $C$ an empty set of cutting planes. \pause \item \label{NextIterationItem} Add affine apprimation for $\tilde{f} ( x )$ at $x$ to $C$. \pause \item Minimize w.r.t $x$ the maximum of the affine functions in $C$ subject to $-b \leq x \leq b$ (this is an LP). \pause \item If change in $x$ for this this iteration is small, return $x$ as solution. Otherwise, goto step \ref{NextIterationItem}. \end{enumerate} \end{block} \end{frame} \end{document} ================================================ FILE: example/atomic_four/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- # # initialize check_example_atomic_four_depends SET(check_example_atomic_four_depends "") # ADD_SUBDIRECTORY(lin_ode) ADD_SUBDIRECTORY(mat_mul) ADD_SUBDIRECTORY(vector) # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list atomic_four.cpp bilinear.cpp dynamic.cpp forward.cpp get_started.cpp norm_sq.cpp ) # END_SORT_THIS_LINE_MINUS_2 set_compile_flags(example_atomic_four "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(example_atomic_four EXCLUDE_FROM_ALL ${source_list}) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_atomic_four ${cppad_lib} ${colpack_libs} ) # # check_example_atomic_four add_check_executable(check_example atomic_four) ================================================ FILE: example/atomic_four/atomic_four.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four.cpp} atomic_four Examples and Tests Driver ##################################### Running These Tests ******************* After executing the :ref:`cmake-name` command form the :ref:`download@Distribution Directory`, you can build and run these tests with the commands:: cd build make check_example_atomic_four Note that your choice of :ref:`cmake@generator` may require using an different version of make; e.g., ``ninja`` . {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ // CPPAD_HAS_* defines # include // for thread_alloc # include // test runner # include // BEGIN_SORT_THIS_LINE_PLUS_1 extern bool bilinear(void); extern bool dynamic(void); extern bool forward(void); extern bool get_started(void); extern bool norm_sq(void); // END_SORT_THIS_LINE_MINUS_1 // main program that runs all the tests int main(void) { std::string group = "example/atomic"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // BEGIN_SORT_THIS_LINE_PLUS_1 Run( bilinear, "bilinear" ); Run( dynamic, "dynamic" ); Run( forward, "forward" ); Run( get_started, "get_started" ); Run( norm_sq, "norm_sq" ); // END_SORT_THIS_LINE_MINUS_1 // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } // END C++ ================================================ FILE: example/atomic_four/atomic_four.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin atomic_four_example} Examples Defining Atomic Functions: Fourth Generation ##################################################### Contents ******** {xrst_toc_table example/atomic_four/get_started.cpp example/atomic_four/norm_sq.cpp example/atomic_four/bilinear.cpp example/atomic_four/forward.cpp example/atomic_four/dynamic.cpp include/cppad/example/atomic_four/vector/vector.xrst include/cppad/example/atomic_four/mat_mul/mat_mul.xrst include/cppad/example/atomic_four/lin_ode/lin_ode.xrst } {xrst_end atomic_four_example} ================================================ FILE: example/atomic_four/bilinear.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_bilinear.cpp} Bilinear Interpolation Atomic Function: Example and Test ######################################################## See Also ******** :ref:`interp_onetape.cpp-name` . Define Atomic Function ********************** {xrst_literal // BEGIN_DEFINE_ATOMIC_FUNCTION // END_DEFINE_ATOMIC_FUNCTION } Use Atomic Function ******************* {xrst_literal // BEGIN_USE_ATOMIC_FUNCTION // END_USE_ATOMIC_FUNCTION } {xrst_end atomic_four_bilinear.cpp} */ # include // CppAD include file // BEGIN_DEFINE_ATOMIC_FUNCTION // empty namespace namespace { // atomic_bilinear class atomic_bilinear : public CppAD::atomic_four { private: // u_grid_, v_grid_; y_grid_ CppAD::vector& u_grid_; CppAD::vector& v_grid_; CppAD::vector& y_grid_; // // u_index_, v_index size_t u_index_; size_t v_index_; // // set_index void set_index(double u, double v) { // // u_index_ while( u < u_grid_[u_index_] && u_index_ > 0 ) --u_index_; while( u > u_grid_[u_index_+1] && u_index_ < u_grid_.size() - 2 ) ++u_index_; // // v_index_ while( v < v_grid_[v_index_] && v_index_ > 0 ) --v_index_; while( v > v_grid_[v_index_+1] && v_index_ < v_grid_.size() - 2 ) ++v_index_; } public: // can use const char* name when calling this constructor atomic_bilinear( const std::string& name , CppAD::vector& u_grid , CppAD::vector& v_grid , CppAD::vector& y_grid ) : CppAD::atomic_four(name) , // inform base class of name u_grid_(u_grid) , v_grid_(v_grid) , y_grid_(y_grid) , u_index_(0) , v_index_(0) { assert( u_grid_.size() >= 2 ); assert( v_grid_.size() >= 2 ); assert( y_grid_.size() == u_grid_.size() * v_grid_.size() ); } private: // for_type bool for_type( size_t call_id , const CppAD::vector& type_x , CppAD::vector& type_y ) override { assert( call_id == 0 ); // default value assert( type_x.size() == 2 ); // n assert( type_y.size() == 1 ); // m // type_y[0] = std::max(type_x[0], type_x[1]); return true; } // forward bool forward( size_t call_id , const CppAD::vector& select_y , size_t order_low , size_t order_up , const CppAD::vector& taylor_x , CppAD::vector& taylor_y ) override { // ok bool ok = order_up <= 1; if( ! ok ) return ok; // // q size_t q = order_up + 1; // # ifndef NDEBUG size_t n = taylor_x.size() / q; size_t m = taylor_y.size() / q; assert( call_id == 0 ); assert( n == 2 ); assert( m == 1 ); assert( m == select_y.size() ); # endif // u, v double u = taylor_x[0 * q + 0]; double v = taylor_x[1 * q + 0]; // // u_index_, v_index_ set_index(u, v); // // u_0, u_1, v_0, v_1 double u_0 = u_grid_[ u_index_ + 0 ]; double u_1 = u_grid_[ u_index_ + 1 ]; double v_0 = v_grid_[ v_index_ + 0 ]; double v_1 = v_grid_[ v_index_ + 1 ]; // // y_00, y_01, y_10, y_11 double y_00 = y_grid_[ (u_index_+0) * v_grid_.size() + v_index_+0 ]; double y_01 = y_grid_[ (u_index_+0) * v_grid_.size() + v_index_+1 ]; double y_10 = y_grid_[ (u_index_+1) * v_grid_.size() + v_index_+0 ]; double y_11 = y_grid_[ (u_index_+1) * v_grid_.size() + v_index_+1 ]; // // taylor_y // function value if( order_low <= 0 ) { double sum = 0.0; sum += y_00 * (u_1 - u) * (v_1 - v); sum += y_01 * (u_1 - u) * (v - v_0); sum += y_10 * (u - u_0) * (v_1 - v); sum += y_11 * (u - u_0) * (v - v_0); taylor_y[0] = sum / ( (u_1 - u_0) * (v_1 - v_0) ); } // // taylor_y // first order derivatives if( order_low <= 1 && 1 <= order_up ) { // // du, dv double du = taylor_x[0 * q + 1]; double dv = taylor_x[1 * q + 1]; double dsum = 0.0; // dsum -= y_00 * du * (v_1 - v); dsum -= y_01 * du * (v - v_0); dsum += y_10 * du * (v_1 - v); dsum += y_11 * du * (v - v_0); dsum -= y_00 * (u_1 - u) * dv; dsum += y_01 * (u_1 - u) * dv; dsum -= y_10 * (u - u_0) * dv; dsum += y_11 * (u - u_0) * dv; taylor_y[1] = dsum / ( (u_1 - u_0) * (v_1 - v_0) ); } // return ok; } }; } // END_DEFINE_ATOMIC_FUNCTION // BEGIN_USE_ATOMIC_FUNCTION bool bilinear(void) { // // ok, eps bool ok = true; double eps = 10. * CppAD::numeric_limits::epsilon(); // // nu, u_grid size_t nu = 4; CppAD::vector u_grid(nu); for(size_t i = 0; i < nu; ++i) u_grid[i] = double(i) * 2.0; // // nv, v_grid size_t nv = 5; CppAD::vector v_grid(nv); for(size_t j = 0; j < nv; ++j) v_grid[j] = double(j) * 3.0; // // y_grid CppAD::vector y_grid( u_grid.size() * v_grid.size() ); for(size_t i = 0; i < nu; ++i) { for(size_t j = 0; j < nv; ++j) { double u = u_grid[i]; double v = v_grid[j]; y_grid[i * v_grid.size() + j] = u * u + v * v; } } // // afun std::string name = "atomic_bilinear"; atomic_bilinear afun(name, u_grid, v_grid, y_grid); // // n, m size_t n = 2; size_t m = 1; // // ax CPPAD_TESTVECTOR( CppAD::AD ) ax(n); ax[0] = 0.0; ax[1] = 0.0; CppAD::Independent(ax); // // ay // call atomic function and store result in ay CPPAD_TESTVECTOR( CppAD::AD ) ay(m); afun(ax, ay); // // f // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // // y CPPAD_TESTVECTOR( double ) x(n), y(m); x[0] = 1.0; x[1] = 3.5; y = f.Forward(0, x); // // u_0, u_1 double u_0 = u_grid[0]; double u_1 = u_grid[1]; assert( u_0 < x[0] && x[0] < u_1 ); // // v_0, v_1 double v_0 = v_grid[1]; double v_1 = v_grid[2]; assert( v_0 < x[1] && x[1] < v_1 ); // // y_00, y_01, y_10, y_11 double y_00 = y_grid[ 0 * v_grid.size() + 1 ]; double y_01 = y_grid[ 0 * v_grid.size() + 2 ]; double y_10 = y_grid[ 1 * v_grid.size() + 1 ]; double y_11 = y_grid[ 1 * v_grid.size() + 2 ]; // // check, ok double sum = 0.0; sum += y_00 * (u_1 - x[0]) * (v_1 - x[1]); sum += y_01 * (u_1 - x[0]) * (x[1] - v_0); sum += y_10 * (x[0] - u_0) * (v_1 - x[1]); sum += y_11 * (x[0] - u_0) * (x[1] - v_0); double check = sum / ( (u_1 - u_0) * (v_1 - v_0) ); ok &= CppAD::NearEqual(y[0] , check, eps, eps); // // dy CPPAD_TESTVECTOR( double ) dx(n), dy(m); dx[0] = 1.0; dx[1] = 0.0; dy = f.Forward(1, dx); // // check sum = 0.0; sum -= y_00 * (v_1 - x[1]); sum -= y_01 * (x[1] - v_0); sum += y_10 * (v_1 - x[1]); sum += y_11 * (x[1] - v_0); check = sum / ( (u_1 - u_0) * (v_1 - v_0) ); ok &= CppAD::NearEqual(dy[0] , check, eps, eps); // // dy dx[0] = 0.0; dx[1] = 1.0; dy = f.Forward(1, dx); // // check sum = 0.0; sum -= y_00 * (u_1 - x[0]); sum += y_01 * (u_1 - x[0]); sum -= y_10 * (x[0] - u_0); sum += y_11 * (x[0] - u_0); check = sum / ( (u_1 - u_0) * (v_1 - v_0) ); ok &= CppAD::NearEqual(dy[0] , check, eps, eps); // return ok; } // END_USE_ATOMIC_FUNCTION ================================================ FILE: example/atomic_four/dynamic.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_dynamic.cpp} Atomic Functions with Dynamic Parameters: Example and Test ########################################################## Purpose ******* This example demonstrates using dynamic parameters with an :ref:`atomic_four-name` function. Function ******** For this example, the atomic function :math:`g : \B{R}^3 \rightarrow \B{R}^3` is defined by :math:`g_0 (x) = x_0 * x_ 0`, :math:`g_1 (x) = x_0 * x_ 1`, :math:`g_2 (x) = x_1 * x_ 2`. Define Atomic Function ********************** {xrst_literal // BEGIN_DEFINE_ATOMIC_FUNCTION // END_DEFINE_ATOMIC_FUNCTION } Use Atomic Function ******************* {xrst_literal // BEGIN_USE_ATOMIC_FUNCTION // END_USE_ATOMIC_FUNCTION } {xrst_end atomic_four_dynamic.cpp} */ # include // BEGIN_DEFINE_ATOMIC_FUNCTION // empty namespace namespace { // atomic_dynamic class atomic_dynamic : public CppAD::atomic_four { public: atomic_dynamic(const std::string& name) : CppAD::atomic_four(name) { } private: // for_type bool for_type( size_t call_id , const CppAD::vector& type_x , CppAD::vector& type_y ) override { assert( call_id == 0 ); // default value assert( type_x.size() == 3 ); // n assert( type_y.size() == 3 ); // m // // type_y type_y[0] = type_x[0]; type_y[1] = std::max( type_x[0], type_x[1] ); type_y[2] = std::max( type_x[1], type_x[2] ); return true; } // forward bool forward( size_t call_id , const CppAD::vector& select_y , size_t order_low , size_t order_up , const CppAD::vector& taylor_x , CppAD::vector& taylor_y ) override { # ifndef NDEBUG size_t q = order_up + 1; size_t n = taylor_x.size() / q; size_t m = taylor_y.size() / q; assert( n == 3 ); assert( m == 3 ); # endif // ok bool ok = order_low == 0 && order_up == 0; if( ! ok ) return ok; // // taylor_y[0] = g_0 = x_0 * x_0 if( select_y[0] ) taylor_y[0] = taylor_x[0] * taylor_x[0]; // // taylor_y[1] = g_1 = x_0 * x_1 if( select_y[1] ) taylor_y[1] = taylor_x[0] * taylor_x[1]; // // taylor_y[2] = g_2 = x_1 * x_2 if( select_y[2] ) taylor_y[2] = taylor_x[1] * taylor_x[2]; // return ok; } }; } // END_DEFINE_ATOMIC_FUNCTION // BEGIN_USE_ATOMIC_FUNCTION bool dynamic(void) { // ok, eps bool ok = true; double eps = 10. * CppAD::numeric_limits::epsilon(); // // afun atomic_dynamic afun("atomic_dynamic"); // // c, p, u CPPAD_TESTVECTOR(double) c(1), p(1), u(1); c[0] = 2.0; p[0] = 3.0; u[0] = 4.0; // // // np, nu, ny size_t np = 1; size_t nu = 1; size_t nx = 3; size_t ny = 3; // // ap // independent dynamic parameter vector CPPAD_TESTVECTOR( CppAD::AD ) ap(np); ap[0] = p[0]; // // au // independent variable vector CPPAD_TESTVECTOR( CppAD::AD ) au(nu); au[0] = u[0]; // // Independent CppAD::Independent(au, ap); // // ay // y = ( c * c, c * p, p * x ) CPPAD_TESTVECTOR( CppAD::AD ) ax(nx), ay(ny); ax[0] = c[0]; // x_0 ax[1] = ap[0]; // x_1 ax[2] = au[0]; // x_2 afun(ax, ay); // // ay // check type of result ok &= Constant( ay[0] ); ok &= Dynamic( ay[1] ); ok &= Variable( ay[2] ); // // f // f(u) = (c * c, c * p, p * u) CppAD::ADFun f; f.Dependent (au, ay); // // ay[0] double check = c[0] * c[0]; ok &= CppAD::NearEqual( Value(ay[0]) , check, eps, eps); // // ay[1] check = c[0] * p[0]; ok &= CppAD::NearEqual( Value(ay[1]) , check, eps, eps); // // ay[2] check = p[0] * u[0]; ok &= CppAD::NearEqual( Value(ay[2]) , check, eps, eps); // // y = f.Forward(0, u) CPPAD_TESTVECTOR(double) y(ny); y = f.Forward(0, u); check = c[0] * c[0]; ok &= CppAD::NearEqual(y[0] , check, eps, eps); check = c[0] * p[0]; ok &= CppAD::NearEqual(y[1] , check, eps, eps); check = p[0] * u[0]; ok &= CppAD::NearEqual(y[2] , check, eps, eps); // // p p[0] = 2.0 * p[0]; f.new_dynamic(p); // // y = f.Forward(0, u) y = f.Forward(0, u); check = c[0] * c[0]; ok &= CppAD::NearEqual(y[0] , check, eps, eps); check = c[0] * p[0]; ok &= CppAD::NearEqual(y[1] , check, eps, eps); check = p[0] * u[0]; ok &= CppAD::NearEqual(y[2] , check, eps, eps); // return ok; } // END_USE_ATOMIC_FUNCTION ================================================ FILE: example/atomic_four/forward.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_forward.cpp} Atomic Functions and Forward Mode: Example and Test ################################################### Purpose ******* This example demonstrates forward mode derivative calculation using an :ref:`atomic_four-name` function. Function ******** For this example, the atomic function :math:`g : \B{R}^3 \rightarrow \B{R}^2` is defined by .. math:: g(x) = \left( \begin{array}{c} x_2 * x_2 \\ x_0 * x_1 \end{array} \right) Jacobian ******** The corresponding Jacobian is .. math:: g^{(1)} (x) = \left( \begin{array}{ccc} 0 & 0 & 2 x_2 \\ x_1 & x_0 & 0 \end{array} \right) Hessian ******* The Hessians of the component functions are .. math:: g_0^{(2)} ( x ) = \left( \begin{array}{ccc} 0 & 0 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & 2 \end{array} \right) \W{,} g_1^{(2)} ( x ) = \left( \begin{array}{ccc} 0 & 1 & 0 \\ 1 & 0 & 0 \\ 0 & 0 & 0 \end{array} \right) Define Atomic Function ********************** {xrst_literal // BEGIN_DEFINE_ATOMIC_FUNCTION // END_DEFINE_ATOMIC_FUNCTION } Use Atomic Function ******************* {xrst_literal // BEGIN_USE_ATOMIC_FUNCTION // END_USE_ATOMIC_FUNCTION } {xrst_end atomic_four_forward.cpp} */ # include // BEGIN_DEFINE_ATOMIC_FUNCTION // empty namespace namespace { // class atomic_forward : public CppAD::atomic_four { public: atomic_forward(const std::string& name) : CppAD::atomic_four(name) { } private: // for_type bool for_type( size_t call_id , const CppAD::vector& type_x , CppAD::vector& type_y ) override { bool ok = type_x.size() == 3; // n ok &= type_y.size() == 2; // m if( ! ok ) return false; type_y[0] = type_x[2]; type_y[1] = std::max(type_x[0], type_x[1]); return true; } // forward bool forward( size_t call_id , const CppAD::vector& select_y , size_t order_low , size_t order_up , const CppAD::vector& tx , CppAD::vector& ty ) override { size_t q = order_up + 1; # ifndef NDEBUG size_t n = tx.size() / q; size_t m = ty.size() / q; # endif assert( n == 3 ); assert( m == 2 ); assert( order_low <= order_up ); // this example only implements up to second order forward mode bool ok = order_up <= 2; if( ! ok ) return ok; // -------------------------------------------------------------- // Zero forward mode. // This case must always be implemented // g(x) = [ x_2 * x_2 ] // [ x_0 * x_1 ] // y^0 = f( x^0 ) if( order_low <= 0 ) { // y_0^0 = x_2^0 * x_2^0 ty[0 * q + 0] = tx[2 * q + 0] * tx[2 * q + 0]; // y_1^0 = x_0^0 * x_1^0 ty[1 * q + 0] = tx[0 * q + 0] * tx[1 * q + 0]; } if( order_up <= 0 ) return ok; // -------------------------------------------------------------- // First order forward mode. // This case is needed if first order forward mode is used. // g'(x) = [ 0, 0, 2 * x_2 ] // [ x_1, x_0, 0 ] // y^1 = f'(x^0) * x^1 if( order_low <= 1 ) { // y_0^1 = 2 * x_2^0 * x_2^1 ty[0 * q + 1] = 2.0 * tx[2 * q + 0] * tx[2 * q + 1]; // y_1^1 = x_1^0 * x_0^1 + x_0^0 * x_1^1 ty[1 * q + 1] = tx[1 * q + 0] * tx[0 * q + 1]; ty[1 * q + 1] += tx[0 * q + 0] * tx[1 * q + 1]; } if( order_up <= 1 ) return ok; // -------------------------------------------------------------- // Second order forward mode. // This case is needed if second order forward mode is used. // g'(x) = [ 0, 0, 2 x_2 ] // [ x_1, x_0, 0 ] // // [ 0 , 0 , 0 ] [ 0 , 1 , 0 ] // g_0''(x) = [ 0 , 0 , 0 ] g_1^{(2)} (x) = [ 1 , 0 , 0 ] // [ 0 , 0 , 2 ] [ 0 , 0 , 0 ] // // y_0^2 = x^1 * g_0''( x^0 ) x^1 / 2! + g_0'( x^0 ) x^2 // = ( x_2^1 * 2.0 * x_2^1 ) / 2! // + 2.0 * x_2^0 * x_2^2 ty[0 * q + 2] = tx[2 * q + 1] * tx[2 * q + 1]; ty[0 * q + 2] += 2.0 * tx[2 * q + 0] * tx[2 * q + 2]; // // y_1^2 = x^1 * g_1''( x^0 ) x^1 / 2! + g_1'( x^0 ) x^2 // = ( x_1^1 * x_0^1 + x_0^1 * x_1^1) / 2 // + x_1^0 * x_0^2 + x_0^0 + x_1^2 ty[1 * q + 2] = tx[1 * q + 1] * tx[0 * q + 1]; ty[1 * q + 2] += tx[1 * q + 0] * tx[0 * q + 2]; ty[1 * q + 2] += tx[0 * q + 0] * tx[1 * q + 2]; // -------------------------------------------------------------- return ok; } }; } // END_DEFINE_ATOMIC_FUNCTION // BEGIN_USE_ATOMIC_FUNCTION bool forward(void) { // ok, eps bool ok = true; double eps = 10. * CppAD::numeric_limits::epsilon(); // // AD, NearEqual using CppAD::AD; using CppAD::NearEqual; // // afun atomic_forward afun("atomic_forward"); // // Create the function f(u) = g(u) for this example. // // n, u, au size_t n = 3; CPPAD_TESTVECTOR(double) u(n); u[0] = 1.00; u[1] = 2.00; u[2] = 3.00; CPPAD_TESTVECTOR( AD ) au(n); for(size_t j = 0; j < n; ++j) au[j] = u[j]; CppAD::Independent(au); // // m, ay size_t m = 2; CPPAD_TESTVECTOR( AD ) ay(m); CPPAD_TESTVECTOR( AD ) ax = au; afun(ax, ay); // // f CppAD::ADFun f; f.Dependent(au, ay); // // check function value double check = u[2] * u[2]; ok &= NearEqual( Value(ay[0]) , check, eps, eps); check = u[0] * u[1]; ok &= NearEqual( Value(ay[1]) , check, eps, eps); // ---------------------------------------------------------------- // zero order forward // // u0, y0 CPPAD_TESTVECTOR(double) u0(n), y0(m); u0 = u; y0 = f.Forward(0, u0); check = u[2] * u[2]; ok &= NearEqual(y0[0] , check, eps, eps); check = u[0] * u[1]; ok &= NearEqual(y0[1] , check, eps, eps); // ---------------------------------------------------------------- // first order forward // // check_jac double check_jac[] = { 0.0, 0.0, 2.0 * u[2], u[1], u[0], 0.0 }; // // u1 CPPAD_TESTVECTOR(double) u1(n); for(size_t j = 0; j < n; j++) u1[j] = 0.0; // // y1, j CPPAD_TESTVECTOR(double) y1(m); for(size_t j = 0; j < n; j++) { // // u1, y1 // compute partial in j-th component direction u1[j] = 1.0; y1 = f.Forward(1, u1); u1[j] = 0.0; // // check this partial for(size_t i = 0; i < m; i++) ok &= NearEqual(y1[i], check_jac[i * n + j], eps, eps); } // ---------------------------------------------------------------- // second order forward // // check_hes_0 double check_hes_0[] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0 }; // // check_hes_1 double check_hes_1[] = { 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; // // u2 CPPAD_TESTVECTOR(double) u2(n); for(size_t j = 0; j < n; j++) u2[j] = 0.0; // // y2, j CPPAD_TESTVECTOR(double) y2(m); for(size_t j = 0; j < n; j++) { // // u1, y2 // first order forward in j-th direction u1[j] = 1.0; f.Forward(1, u1); y2 = f.Forward(2, u2); // // check y2 element of Hessian diagonal ok &= NearEqual(y2[0], check_hes_0[j * n + j] / 2.0, eps, eps); ok &= NearEqual(y2[1], check_hes_1[j * n + j] / 2.0, eps, eps); // // k for(size_t k = 0; k < n; k++) if( k != j ) { // // u1, y2 u1[k] = 1.0; f.Forward(1, u1); y2 = f.Forward(2, u2); // // y2 = (H_jj + H_kk + H_jk + H_kj) / 2.0 // y2 = (H_jj + H_kk) / 2.0 + H_jk // // check y2[0] double H_jj = check_hes_0[j * n + j]; double H_kk = check_hes_0[k * n + k]; double H_jk = y2[0] - (H_kk + H_jj) / 2.0; ok &= NearEqual(H_jk, check_hes_0[j * n + k], eps, eps); // // check y2[1] H_jj = check_hes_1[j * n + j]; H_kk = check_hes_1[k * n + k]; H_jk = y2[1] - (H_kk + H_jj) / 2.0; ok &= NearEqual(H_jk, check_hes_1[j * n + k], eps, eps); // // u1 u1[k] = 0.0; } // u1 u1[j] = 0.0; } // ---------------------------------------------------------------- return ok; } // END_USE_ATOMIC_FUNCTION ================================================ FILE: example/atomic_four/get_started.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_get_started.cpp} Getting Started with Atomic Functions: Example and Test ####################################################### Purpose ******* This example demonstrates the minimal amount of information necessary for a :ref:`atomic_four-name` function. Define Atomic Function ********************** {xrst_literal // BEGIN_DEFINE_ATOMIC_FUNCTION // END_DEFINE_ATOMIC_FUNCTION } Use Atomic Function ******************* {xrst_literal // BEGIN_USE_ATOMIC_FUNCTION // END_USE_ATOMIC_FUNCTION } {xrst_end atomic_four_get_started.cpp} */ # include // CppAD include file // BEGIN_DEFINE_ATOMIC_FUNCTION // empty namespace namespace { // atomic_get_started class atomic_get_started : public CppAD::atomic_four { public: // can use const char* name when calling this constructor atomic_get_started(const std::string& name) : CppAD::atomic_four(name) // inform base class of name { } private: // for_type bool for_type( size_t call_id , const CppAD::vector& type_x , CppAD::vector& type_y ) override { assert( call_id == 0 ); // default value assert( type_x.size() == 1 ); // n assert( type_y.size() == 1 ); // m // type_y[0] = type_x[0]; return true; } // forward bool forward( size_t call_id , const CppAD::vector& select_y , size_t order_low , size_t order_up , const CppAD::vector& taylor_x , CppAD::vector& taylor_y ) override { # ifndef NDEBUG size_t q = order_up + 1; size_t n = taylor_x.size() / q; size_t m = taylor_y.size() / q; assert( call_id == 0 ); assert( order_low == 0); assert( order_up == 0); assert( n == 1 ); assert( m == 1 ); assert( m == select_y.size() ); # endif // return flag bool ok = order_up == 0; if( ! ok ) return ok; // taylor_y // Order zero forward mode: y^0 = g( x^0 ) = 1 / x^0 taylor_y[0] = 1.0 / taylor_x[0]; // return ok; } }; } // END_DEFINE_ATOMIC_FUNCTION // BEGIN_USE_ATOMIC_FUNCTION bool get_started(void) { // ok, eps bool ok = true; double eps = 10. * CppAD::numeric_limits::epsilon(); // // afun atomic_get_started afun("atomic_get_started"); // // n, m size_t n = 1; size_t m = 1; // // x0 double x0 = 0.5; // // ax CPPAD_TESTVECTOR( CppAD::AD ) ax(n); ax[0] = x0; CppAD::Independent(ax); // // au // call atomic function and store result in au CPPAD_TESTVECTOR( CppAD::AD ) au(m); afun(ax, au); // // ay CPPAD_TESTVECTOR( CppAD::AD ) ay(m); ay[0] = 1.0 + au[0]; // // f // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // // check double check = 1.0 + 1.0 / x0; // // ok // check ay[0] ok &= CppAD::NearEqual( Value(ay[0]) , check, eps, eps); // // ok // check zero order forward mode CPPAD_TESTVECTOR( double ) x(n), y(m); x[0] = x0; y = f.Forward(0, x); ok &= CppAD::NearEqual(y[0] , check, eps, eps); // return ok; } // END_USE_ATOMIC_FUNCTION ================================================ FILE: example/atomic_four/lin_ode/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list forward.cpp lin_ode.cpp rev_depend.cpp reverse.cpp sparsity.cpp ) # END_SORT_THIS_LINE_MINUS_2 set_compile_flags(example_atomic_four_lin_ode "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(example_atomic_four_lin_ode EXCLUDE_FROM_ALL ${source_list}) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_atomic_four_lin_ode ${cppad_lib} ${colpack_libs} ) # # check_example_atomic_four_lin_ode add_check_executable(check_example_atomic_four lin_ode) ================================================ FILE: example/atomic_four/lin_ode/forward.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_lin_ode_forward.cpp} {xrst_spell cccc } Atomic Linear ODE Forward Mode: Example and Test ################################################ Purpose ******* This example demonstrates using forward mode with the :ref:`atomic_four_lin_ode-name` class. f(u) **** For this example, the function :math:`f(u) = z(r, u)` where :math:`z(t, u)` solves the following ODE .. math:: z_t (t, u) = \left( \begin{array}{cccc} 0 & 0 & 0 & 0 \\ u_4 & 0 & 0 & 0 \\ 0 & u_5 & 0 & 0 \\ 0 & 0 & u_6 & 0 \\ \end{array} \right) z(t, u) \W{,} z(0, u) = \left( \begin{array}{c} u_0 \\ u_1 \\ u_2 \\ u_3 \\ \end{array} \right) z(t, u) ******* The actual solution to this ODE is .. math:: z(t, u) = \left( \begin{array}{l} u_0 \\ u_1 + u_4 u_0 t \\ u_2 + u_5 u_1 t + u_5 u_4 u_0 t^2 / 2 \\ u_3 + u_6 u_2 t + u_6 u_5 u_1 t^2 / 2 + u_6 u_5 u_4 u_0 t^3 / 6 \end{array} \right) g(u) **** We define :math:`g(u) = \partial_{u0} f(u)`. It follows that .. math:: g (u) = \left( \begin{array}{l} 1 \\ u_4 r \\ u_5 u_4 r^2 / 2 \\ u_6 u_5 u_4 r^3 / 6 \end{array} \right) Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_lin_ode_forward.cpp} */ // BEGIN C++ # include # include namespace { // BEGIN_EMPTY_NAMESPACE template Vector Z(Scalar t, const Vector& u) { size_t nz = 4; Vector z(nz); // z[0] = u[0]; z[1] = u[1] + u[4]*u[0]*t; z[2] = u[2] + u[5]*u[1]*t + u[5]*u[4]*u[0]*t*t/2.0; z[3] = u[3] + u[6]*u[2]*t + u[6]*u[5]*u[1]*t*t/2.0 + u[6]*u[5]*u[4]*u[0]*t*t*t/6.0; // return z; } template Vector G(Scalar t, const Vector& u) { size_t ng = 4; Vector g(ng); // g[0] = Scalar(1.0); g[1] = u[4]*t; g[2] = u[5]*u[4]*t*t/2.0; g[3] = u[6]*u[5]*u[4]*t*t*t/6.0; // return g; } } // END_EMPTY_NAMESPACE bool forward(void) { // ok bool ok = true; // // AD, NearEqual, eps99 using CppAD::AD; using CppAD::NearEqual; double eps99 = std::numeric_limits::epsilon() * 99.0; // ----------------------------------------------------------------------- // Record f // ----------------------------------------------------------------------- // // afun CppAD::atomic_lin_ode afun("atomic_lin_ode"); // // m, r size_t m = 4; double r = 2.0; double step = 1.0; // // pattern, transpose size_t nr = m; size_t nc = m; size_t nnz = 3; CppAD::sparse_rc< CppAD::vector > pattern(nr, nc, nnz); for(size_t k = 0; k < nnz; ++k) { size_t i = k + 1; size_t j = k; pattern.set(k, i, j); } bool transpose = false; // // ny, ay size_t ny = m; CPPAD_TESTVECTOR( AD ) ay(ny); // // nu, au size_t nu = nnz + m; CPPAD_TESTVECTOR( AD ) au(nu); for(size_t j = 0; j < nu; ++j) au[j] = AD(j + 1); CppAD::Independent(au); // // ax CPPAD_TESTVECTOR( AD ) ax(nnz + m); for(size_t k = 0; k < nnz; ++k) ax[k] = au[m + k]; for(size_t i = 0; i < m; ++i) ax[nnz + i] = au[i]; // // ay size_t call_id = afun.set(r, step, pattern, transpose); afun(call_id, ax, ay); // // f CppAD::ADFun f(au, ay); // ----------------------------------------------------------------------- // ar, check_f CppAD::Independent(au); AD ar = r; ay = Z(ar, au); CppAD::ADFun check_f(au, ay); // ----------------------------------------------------------------------- // forward mode on f // ----------------------------------------------------------------------- // // u CPPAD_TESTVECTOR(double) u(nu); for(size_t j = 0; j < nu; ++j) u[j] = double( j + 2 ); // // y // zero order forward mode computation of f(u) CPPAD_TESTVECTOR(double) y(ny); y = f.Forward(0, u); // // ok CPPAD_TESTVECTOR(double) z = check_f.Forward(0, u); for(size_t i = 0; i < ny; ++i) ok &= NearEqual(y[i], z[i], eps99, eps99); // // du, ok CPPAD_TESTVECTOR(double) du(nu), dy(ny), dz(ny); for(size_t j = 0; j < nu; ++j) du[j] = 0.0; for(size_t j = 0; j < nu; ++j) { du[j] = 1.0; dy = f.Forward(1, du); dz = check_f.Forward(1, du); for(size_t i = 0; i < ny; ++i) ok &= NearEqual(dy[i], dz[i], eps99, eps99); du[j] = 0.0; } // ----------------------------------------------------------------------- // Record g // ----------------------------------------------------------------------- // // af CppAD::ADFun< AD, double> af = f.base2ad(); // // az CppAD::Independent(au); CPPAD_TESTVECTOR( AD ) dau(nu), day(ny); af.Forward(0, au); for(size_t j = 0; j < nu; ++j) dau[j] = 0.0; dau[0] = 1.0; day = af.Forward(1, dau); // g CppAD::ADFun g(au, day); // ----------------------------------------------------------------------- // check_g CppAD::Independent(au); ay = G(ar, au); CppAD::ADFun check_g(au, ay); // ----------------------------------------------------------------------- // forward mode on g // ----------------------------------------------------------------------- // // y // zero order forward mode computation of g(u) dy = g.Forward(0, u); // // ok CPPAD_TESTVECTOR(double) v = check_g.Forward(0, u); for(size_t i = 0; i < ny; ++i) ok &= NearEqual(dy[i], v[i], eps99, eps99); // // du, ok CPPAD_TESTVECTOR(double) ddy(ny), ddz(ny); for(size_t j = 0; j < nu; ++j) du[j] = 0.0; for(size_t j = 0; j < nu; ++j) { du[j] = 1.0; ddy = g.Forward(1, du); ddz = check_g.Forward(1, du); for(size_t i = 0; i < ny; ++i) ok &= NearEqual(ddy[i], ddz[i], eps99, eps99); du[j] = 0.0; } return ok; } // END C++ ================================================ FILE: example/atomic_four/lin_ode/lin_ode.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // CPPAD_HAS_* defines # include // system include files used for I/O # include // C style asserts # include // for thread_alloc # include // test runner # include // BEGIN_SORT_THIS_LINE_PLUS_1 extern bool forward(void); extern bool rev_depend(void); extern bool reverse(void); extern bool sparsity(void); // END_SORT_THIS_LINE_MINUS_1 // main program that runs all the tests int main(void) { std::string group = "example/atomic_four/lin_ode"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // BEGIN_SORT_THIS_LINE_PLUS_1 Run( forward, "forward" ); Run( rev_depend, "rev_depend" ); Run( reverse, "reverse" ); Run( sparsity, "sparsity" ); // END_SORT_THIS_LINE_MINUS_1 // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } ================================================ FILE: example/atomic_four/lin_ode/rev_depend.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_lin_ode_rev_depend.cpp} {xrst_spell cccc } Atomic Linear ODE Reverse Dependency Analysis: Example and Test ############################################################### Purpose ******* This example demonstrates calculating reverse dependency with the :ref:`atomic_four_lin_ode-name` class; see :ref:`atomic_four_lin_ode_rev_depend.hpp-name` . y(t, x) ******* We are given a positive integer m and define :math:`y : \B{R} \times \B{R}^m \rightarrow \B{R}^m` by .. math:: y(t, x) = \prod_{i=0}^m x_i t^i / i ! It follows that .. math:: \partial_t y_i (t, x) = \left \{ \begin{array}{ll} 0 & \R{if} \; i = 0 \\ x_i y_{i-1} (t, x) & \R{otherwise} \end{array} \right . \\ \partial_t y (t, x) = \left( \begin{array}{cccc} 0 & 0 & \cdots & 0 \\ x_1 & 0 & \cdots & 0 \\ 0 & x_2 & & 0 \\ \vdots & & \ddots & \vdots \\ 0 & 0 & \cdots & x_m \end{array} \right) y (t, x) \W{,} y (0, x) = \left( \begin{array}{c} x_0 \\ 0 \\ \vdots \\ 0 \\ \end{array} \right) Problem Parameters ****************** The following problem parameters can be changed: {xrst_literal // BEGIN_PROBLEM_PARAMETERS // END_PROBLEM_PARAMETERS } Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_lin_ode_rev_depend.cpp} */ // BEGIN C++ # include # include namespace { // BEGIN_EMPTY_NAMESPACE // y(t, x) template Vector Y(Scalar t, const Vector& x) { size_t m = x.size(); Vector y(m); // // y y[0] = x[0]; for(size_t i = 1; i < m; ++i) y[i] = x[i] * y[i-1] * t / Scalar(i); return y; } } // END_EMPTY_NAMESPACE bool rev_depend(void) { // ok, eps bool ok = true; // // sparse_rc, AD, eps99 typedef CppAD::sparse_rc< CppAD::vector > sparse_rc; using CppAD::AD; double eps99 = std::numeric_limits::epsilon() * 99.0; // ----------------------------------------------------------------------- // Record f // ----------------------------------------------------------------------- // // afun CppAD::atomic_lin_ode afun("atomic_lin_ode"); // // BEGIN_PROBLEM_PARAMETERS // m, r, step size_t m = 5; // number of components in x and y double r = 2.0; // final time in the ODE double step = 1.0; // step size used to approximation ODE solution // END_PROBLEM_PARAMETERS // // pattern, transpose size_t nr = m; size_t nc = m; size_t nnz = m - 1; sparse_rc pattern(nr, nc, nnz); for(size_t k = 0; k < nnz; ++k) { size_t i = k + 1; size_t j = k; pattern.set(k, i, j); } bool transpose = false; // // ax CPPAD_TESTVECTOR( AD ) ax(m); for(size_t k = 0; k < m; ++k) ax[k] = double(k + 1); CppAD::Independent(ax); // // au // au = (x[1], ..., x[nnz-1], x[0], 0, ..., 0) CPPAD_TESTVECTOR( AD ) au(nnz + m); for(size_t k = 0; k < nnz; ++k) au[k] = ax[k+1]; for(size_t i = 0; i < m; ++i) { if( i == 0 ) au[nnz + i] = ax[0]; else au[nnz + i] = 0.0; } // // ay CPPAD_TESTVECTOR( AD ) ay(m); size_t call_id = afun.set(r, step, pattern, transpose); afun(call_id, au, ay); // // z_index // Fourth order Rosen34 method is exact approximation of y[i] for i <= 4 size_t z_index = m - 1; assert(z_index <= 4); // // az CPPAD_TESTVECTOR( AD ) az(1); az[0] = ay[z_index]; // // f // optimize uses rev_depend CppAD::ADFun f(ax, az); f.optimize("val_graph no_conditional_skip"); // ----------------------------------------------------------------------- // check_f // ----------------------------------------------------------------------- CppAD::Independent(ax); AD ar = r; ay = Y(ar, ax); az[0] = ay[z_index]; CppAD::ADFun check_f(ax, az); // ----------------------------------------------------------------------- // rev_depend // use test_rev_depend to call rev_depend directly // ----------------------------------------------------------------------- // // depend_u CppAD::vector ident_zero_u(nnz + m), depend_u(nnz + m), depend_y(m); for(size_t i = 0; i < m; ++i) { depend_y[i] = i == z_index; ident_zero_u[i] = false; } for(size_t i = 1; i < m; ++i) ident_zero_u[nnz + i] = true; afun.test_rev_depend(call_id, ident_zero_u, depend_u, depend_y); // // depend_x CppAD::vector depend_x(m); depend_x[0] = depend_u[m-1]; for(size_t j = 1; j < m; ++j) depend_x[j] = depend_u[j-1]; // // x CPPAD_TESTVECTOR(double) x(m); for(size_t j = 0; j < m; ++j) x[j] = double( j + 2 ); // // dw check_f.Forward(0, x); CPPAD_TESTVECTOR(double) w(1), dw(m); w[0] = 1.0; dw = check_f.Reverse(1, w); // // ok // note that for this x, partial w.r.t x[j] is non-zero if and only if // y[z_index] depends on x[j] for(size_t j = 0; j < m; ++j) ok &= depend_x[j] == (dw[j] != 0.0); // // ----------------------------------------------------------------------- // forward mode on f // Check that the optimized version of agrees with check_f. // ----------------------------------------------------------------------- // // z // zero order forward mode computation of f(x) CPPAD_TESTVECTOR(double) z = f.Forward(0, x); // // ok CPPAD_TESTVECTOR(double) check_z = check_f.Forward(0, x); ok &= CppAD::NearEqual(z[0], check_z[0], eps99, eps99); // // du, ok CPPAD_TESTVECTOR(double) dx(m), dz(1), check_dz(1); for(size_t j = 0; j < m; ++j) dx[j] = 0.0; // for(size_t j = 0; j < m; ++j) { dx[j] = 1.0; dz = f.Forward(1, dx); check_dz = check_f.Forward(1, dx); ok &= CppAD::NearEqual(dz[0], check_dz[0], eps99, eps99); dx[j] = 0.0; } // ----------------------------------------------------------------------- return ok; } // END C++ ================================================ FILE: example/atomic_four/lin_ode/reverse.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_lin_ode_reverse.cpp} {xrst_spell cccc } Atomic Linear ODE Reverse Mode: Example and Test ################################################ Purpose ******* This example demonstrates using reverse mode with the :ref:`atomic_four_lin_ode-name` class. f(u) **** For this example, the function :math:`f(u) = z(r, u)` where :math:`z(t, u)` solves the following ODE .. math:: z_t (t, u) = \left( \begin{array}{cccc} 0 & 0 & 0 & 0 \\ u_4 & 0 & 0 & 0 \\ 0 & u_5 & 0 & 0 \\ 0 & 0 & u_6 & 0 \\ \end{array} \right) z(t, u) \W{,} z(0, u) = \left( \begin{array}{c} u_0 \\ u_1 \\ u_2 \\ u_3 \\ \end{array} \right) Solution ******** The actual solution to this ODE is .. math:: z(t, u) = \left( \begin{array}{l} u_0 \\ u_1 + u_4 u_0 t \\ u_2 + u_5 u_1 t + u_5 u_4 u_0 t^2 / 2 \\ u_3 + u_6 u_2 t + u_6 u_5 u_1 t^2 / 2 + u_6 u_5 u_4 u_0 t^3 / 6 \end{array} \right) g(u) **** .. math:: z_2 (t, u) = u_2 + u_5 u_1 t + u_5 u_4 u_0 t^2 / 2 Fix :math:`r` and define :math:`g(u) = [ \partial_u z(r, u) ]^\R{T}`. It follows that .. math:: g(u) = \left( \begin{array}{c} u_5 u_4 r^2 / 2 \\ u_5 r \\ 1 \\ 0 \\ u_5 u_0 r^2 / 2 \\ u_t r + u_4 u_0 r^2 / 2 \\ 0 \end{array} \right) Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_lin_ode_reverse.cpp} */ // BEGIN C++ # include # include namespace { // BEGIN_EMPTY_NAMESPACE template Vector Z(Scalar t, const Vector& u) { size_t nz = 4; Vector z(nz); // z[0] = u[0]; z[1] = u[1] + u[4]*u[0]*t; z[2] = u[2] + u[5]*u[1]*t + u[5]*u[4]*u[0]*t*t/2.0; z[3] = u[3] + u[6]*u[2]*t + u[6]*u[5]*u[1]*t*t/2.0 + u[6]*u[5]*u[4]*u[0]*t*t*t/6.0; // return z; } template Vector G(Scalar t, const Vector& u) { size_t nu = 7; Vector g(nu); // g[0] = u[5]*u[4]*t*t/2.0; g[1] = u[5]*t; g[2] = Scalar(1.0); g[3] = Scalar(0.0); g[4] = u[5]*u[0]*t*t/2.0; g[5] = u[1]*t + u[4]*u[0]*t*t/2.0; g[6] = Scalar(0.0); // return g; } } // END_EMPTY_NAMESPACE bool reverse(void) { // ok bool ok = true; // // AD, NearEqual, eps99 using CppAD::AD; using CppAD::NearEqual; double eps99 = std::numeric_limits::epsilon() * 99.0; // ----------------------------------------------------------------------- // Record f // ----------------------------------------------------------------------- // // afun CppAD::atomic_lin_ode afun("atomic_lin_ode"); // // m, r size_t m = 4; double r = 2.0; double step = 2.0; // // pattern, transpose size_t nr = m; size_t nc = m; size_t nnz = 3; CppAD::sparse_rc< CppAD::vector > pattern(nr, nc, nnz); for(size_t k = 0; k < nnz; ++k) { size_t i = k + 1; size_t j = k; pattern.set(k, i, j); } bool transpose = false; // // ny, ay size_t ny = m; CPPAD_TESTVECTOR( AD ) ay(ny); // // nu, au size_t nu = nnz + m; CPPAD_TESTVECTOR( AD ) au(nu); for(size_t j = 0; j < nu; ++j) au[j] = AD(j + 1); CppAD::Independent(au); // // ax CPPAD_TESTVECTOR( AD ) ax(nnz + m); for(size_t k = 0; k < nnz; ++k) ax[k] = au[m + k]; for(size_t i = 0; i < m; ++i) ax[nnz + i] = au[i]; // // ay size_t call_id = afun.set(r, step, pattern, transpose); afun(call_id, ax, ay); // // f CppAD::ADFun f(au, ay); // ----------------------------------------------------------------------- // ar, check_f CppAD::Independent(au); AD ar = r; ay = Z(ar, au); CppAD::ADFun check_f(au, ay); // ----------------------------------------------------------------------- // reverse mode on f // ----------------------------------------------------------------------- // // u CPPAD_TESTVECTOR(double) u(nu); for(size_t j = 0; j < nu; ++j) u[j] = double( j + 2 ); // // y // zero order forward mode computation of f(u) CPPAD_TESTVECTOR(double) y(ny); y = f.Forward(0, u); // // ok CPPAD_TESTVECTOR(double) check_y = check_f.Forward(0, u); for(size_t i = 0; i < ny; ++i) ok &= NearEqual(y[i], check_y[i], eps99, eps99); // // w, ok CPPAD_TESTVECTOR(double) w(ny), dw(nu), check_dw(nu); for(size_t i = 0; i < ny; ++i) w[i] = 0.0; for(size_t i = 0; i < ny; ++i) { w[i] = 1.0; dw = f.Reverse(1, w); check_dw = check_f.Reverse(1, w); for(size_t j = 0; j < nu; ++j) ok &= NearEqual(dw[j], check_dw[j], eps99, eps99); w[i] = 0.0; } // ----------------------------------------------------------------------- // Record g // ----------------------------------------------------------------------- // // af CppAD::ADFun< AD, double> af = f.base2ad(); // // au CppAD::Independent(au); CPPAD_TESTVECTOR( AD ) aw(ny), adw(nu); af.Forward(0, au); for(size_t i = 0; i < ny; ++i) aw[i] = 0.0; aw[2] = 1.0; adw = af.Reverse(1, aw); // g CppAD::ADFun g(au, adw); // ----------------------------------------------------------------------- // check_g CppAD::Independent(au); ay = G(ar, au); CppAD::ADFun check_g(au, ay); // ----------------------------------------------------------------------- // // v // zero order forward mode computation of g(u) CPPAD_TESTVECTOR(double) v(nu); v = g.Forward(0, u); // // ok CPPAD_TESTVECTOR(double) check_v = check_g.Forward(0, u); for(size_t i = 0; i < nu; ++i) ok &= NearEqual(v[i], check_v[i], eps99, eps99); // // w, ok w.resize(nu); for(size_t i = 0; i < nu; ++i) w[i] = 0.0; for(size_t i = 0; i < nu; ++i) { w[i] = 1.0; dw = g.Reverse(1, w); check_dw = check_g.Reverse(1, w); for(size_t j = 0; j < nu; ++j) ok &= NearEqual(dw[j], check_dw[j], eps99, eps99); w[i] = 0.0; } // ----------------------------------------------------------------------- return ok; } // END C++ ================================================ FILE: example/atomic_four/lin_ode/sparsity.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_lin_ode_sparsity.cpp} {xrst_spell cccc } Atomic Linear ODE Sparsity Calculations: Example and Test ######################################################### Purpose ******* This example demonstrates calculating sparsity patterns with the :ref:`atomic_four_lin_ode-name` class. f(u) **** For this example, the function :math:`f(u) = z(r, u)` where :math:`z(t, u)` solves the following ODE .. math:: z_t (t, u) = \left( \begin{array}{cccc} 0 & 0 & 0 & 0 \\ u_4 & 0 & 0 & 0 \\ 0 & u_5 & 0 & 0 \\ 0 & 0 & u_6 & 0 \\ \end{array} \right) z(t, u) \W{,} z(0, u) = \left( \begin{array}{c} u_0 \\ u_1 \\ u_2 \\ u_3 \\ \end{array} \right) Solution ******** The actual solution to this ODE is .. math:: z(t, u) = \left( \begin{array}{l} u_0 \\ u_1 + u_4 u_0 t \\ u_2 + u_5 u_1 t + u_5 u_4 u_0 t^2 / 2 \\ u_3 + u_6 u_2 t + u_6 u_5 u_1 t^2 / 2 + u_6 u_5 u_4 u_0 t^3 / 6 \end{array} \right) Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_lin_ode_sparsity.cpp} */ // BEGIN C++ # include # include namespace { // BEGIN_EMPTY_NAMESPACE template Vector Z(Scalar t, const Vector& u) { size_t nz = 4; Vector z(nz); // z[0] = u[0]; z[1] = u[1] + u[4]*u[0]*t; z[2] = u[2] + u[5]*u[1]*t + u[5]*u[4]*u[0]*t*t/2.0; z[3] = u[3] + u[6]*u[2]*t + u[6]*u[5]*u[1]*t*t/2.0 + u[6]*u[5]*u[4]*u[0]*t*t*t/6.0; // return z; } } // END_EMPTY_NAMESPACE bool sparsity(void) { // ok bool ok = true; // // sparse_rc, AD typedef CppAD::sparse_rc< CppAD::vector > sparse_rc; using CppAD::AD; // ----------------------------------------------------------------------- // Record f // ----------------------------------------------------------------------- // // afun CppAD::atomic_lin_ode afun("atomic_lin_ode"); // // m, r size_t m = 4; double r = 2.0; double step = 0.5; // // pattern, transpose size_t nr = m; size_t nc = m; size_t nnz = 3; CppAD::sparse_rc< CppAD::vector > pattern(nr, nc, nnz); for(size_t k = 0; k < nnz; ++k) { size_t i = k + 1; size_t j = k; pattern.set(k, i, j); } bool transpose = false; // // ny, ay size_t ny = m; CPPAD_TESTVECTOR( AD ) ay(ny); // // nu, au size_t nu = nnz + m; CPPAD_TESTVECTOR( AD ) au(nu); for(size_t j = 0; j < nu; ++j) au[j] = AD(j + 1); CppAD::Independent(au); // // ax CPPAD_TESTVECTOR( AD ) ax(nnz + m); for(size_t k = 0; k < nnz; ++k) ax[k] = au[m + k]; for(size_t i = 0; i < m; ++i) ax[nnz + i] = au[i]; // // ay size_t call_id = afun.set(r, step, pattern, transpose); afun(call_id, ax, ay); // // f CppAD::ADFun f(au, ay); // ----------------------------------------------------------------------- // ar, check_f CppAD::Independent(au); AD ar = r; ay = Z(ar, au); CppAD::ADFun check_f(au, ay); // ----------------------------------------------------------------------- // Jacobian Sparsity // ----------------------------------------------------------------------- // // eye_sparsity // nu by nu identitty matrix sparse_rc eye_sparsity(nu, nu, nu); for(size_t i = 0; i < nu; ++i) eye_sparsity.set(i, i, i); // // internal_bool bool internal_bool = false; // // jac_sparsity transpose = false; bool dependency = false; sparse_rc jac_sparsity; f.for_jac_sparsity( eye_sparsity, transpose, dependency, internal_bool, jac_sparsity ); // // check_jac_sparsity sparse_rc check_jac_sparsity; check_f.for_jac_sparsity( eye_sparsity, transpose, dependency, internal_bool, check_jac_sparsity ); // // ok ok &= jac_sparsity == check_jac_sparsity; // ----------------------------------------------------------------------- // Hessian Sparsity // ----------------------------------------------------------------------- // // select_domain CPPAD_TESTVECTOR(bool) select_domain(nu); for(size_t j = 0; j < nu; ++j) select_domain[j] = true; // // select_range CPPAD_TESTVECTOR(bool) select_range(ny); for(size_t i = 0; i < ny; ++i) select_range[i] = false; select_range[1] = true; // // hes_sparsity sparse_rc hes_sparsity; f.for_hes_sparsity( select_domain, select_range, internal_bool, hes_sparsity ); // // check_hes_sparsity sparse_rc check_hes_sparsity; check_f.for_hes_sparsity( select_domain, select_range, internal_bool, check_hes_sparsity ); // // ok ok &= hes_sparsity == check_hes_sparsity; // ----------------------------------------------------------------------- return ok; } // END C++ ================================================ FILE: example/atomic_four/mat_mul/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list forward.cpp identical_zero.cpp mat_mul.cpp rev_depend.cpp reverse.cpp sparsity.cpp ) # END_SORT_THIS_LINE_MINUS_2 set_compile_flags(example_atomic_four_mat_mul "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(example_atomic_four_mat_mul EXCLUDE_FROM_ALL ${source_list}) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_atomic_four_mat_mul ${cppad_lib} ${colpack_libs} ) # # check_example_atomic_four_mat_mul add_check_executable(check_example_atomic_four mat_mul) ================================================ FILE: example/atomic_four/mat_mul/forward.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_mat_mul_forward.cpp} {xrst_spell cccccccc } Atomic Matrix Multiply Forward Mode: Example and Test ##################################################### Purpose ******* This example demonstrates using forward mode with the :ref:`atomic_four_mat_mul-name` class. f(x) **** For this example, the function :math:`f(x)` is .. math:: f(x) = \left( \begin{array}{cc} x_0 & x_1 \\ x_2 & x_3 \\ x_4 & x_5 \end{array} \right) \left( \begin{array}{c} x_6 \\ x_7 \end{array} \right) = \left( \begin{array}{c} x_0 x_6 + x_1 x_7 \\ x_2 x_6 + x_3 x_7 \\ x_4 x_6 + x_5 x_7 \end{array} \right) Jacobian of f(x) **************** The Jacobian of :math:`f(x)` is .. math:: f^{(1)} (x) = \left( \begin{array}{cccccccc} x_6 & x_7 & 0 & 0 & 0 & 0 & x_0 & x_1 \\ 0 & 0 & x_6 & x_7 & 0 & 0 & x_2 & x_3 \\ 0 & 0 & 0 & 0 & x_6 & x_7 & x_4 & x_5 \end{array} \right) g(x) **** We define the function :math:`g(x) = f_1^{(1)} (x)^\R{T}`; i.e., .. math:: g(x) = ( 0, 0, x_6, x_7, 0, 0, x_2, x_3 )^\R{T} Hessian ******* The Hessian of :math:`f_1(x)` is the Jacobian of :math:`g(x)`; i.e., .. math:: f_1^{(2)} (x) = g^{(1)} (x) = \left( \begin{array}{cccccccc} 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ \end{array} \right) Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_mat_mul_forward.cpp} */ // BEGIN C++ # include # include bool forward(void) { // ok, eps bool ok = true; // // AD, NearEqual using CppAD::AD; using CppAD::NearEqual; // ----------------------------------------------------------------------- // Record f // ----------------------------------------------------------------------- // // afun CppAD::atomic_mat_mul afun("atomic_mat_mul"); // // nleft, n_middle, n_right size_t n_left = 3, n_middle = 2, n_right = 1; // // nx, ax size_t nx = n_middle * (n_left + n_right); CPPAD_TESTVECTOR( AD ) ax(nx); for(size_t j = 0; j < nx; ++j) ax[j] = AD(j + 2); CppAD::Independent(ax); // // ny, ay size_t ny = n_left * n_right; CPPAD_TESTVECTOR( AD ) ay(ny); // // ay size_t call_id = afun.set(n_left, n_middle, n_right); afun(call_id, ax, ay); // // f CppAD::ADFun f(ax, ay); // ----------------------------------------------------------------------- // Forward mode on f // ----------------------------------------------------------------------- // // x CPPAD_TESTVECTOR(double) x(nx); for(size_t j = 0; j < nx; ++j) x[j] = double(3 + nx - j); // // y // zero order forward mode computation of f(x) CPPAD_TESTVECTOR(double) y(ny); y = f.Forward(0, x); // // check_y double check_y[] = { x[0] * x[6] + x[1] * x[7], x[2] * x[6] + x[3] * x[7], x[4] * x[6] + x[5] * x[7] }; for(size_t i = 0; i < ny; ++i) ok &= y[i] == check_y[i]; // // J // first order forward mode computation of f'(x) CPPAD_TESTVECTOR(double) x1(nx), y1(ny), J(ny * nx); for(size_t j = 0; j < nx; ++j) x1[j] = 0.0; for(size_t j = 0; j < nx; ++j) { x1[j] = 1.0; y1 = f.Forward(1, x1); x1[j] = 0.0; for(size_t i = 0; i < ny; ++i) J[i * nx + j] = y1[i]; } // // check_J double check_J[] = { x[6], x[7], 0.0, 0.0, 0.0, 0.0, x[0], x[1], 0.0, 0.0, x[6], x[7], 0.0, 0.0, x[2], x[3], 0.0, 0.0, 0.0, 0.0, x[6], x[7], x[4], x[5] }; for(size_t ij = 0; ij < ny * nx; ij++) ok &= J[ij] == check_J[ij]; // // H_1 // Second order forward mode computaiton of f_1^2 (x) // (use the fact that the diagonal of this Hessian is zero). CPPAD_TESTVECTOR(double) x2(nx), y2(nx), H_1(nx * nx); for(size_t j = 0; j < nx; ++j) x2[j] = 0.0; for(size_t i = 0; i < nx; ++i) { for(size_t j = 0; j < nx; ++j) { x1[i] = 1.0; x1[j] = 1.0; f.Forward(1, x1); x1[i] = 0.0; x1[j] = 0.0; y2 = f.Forward(2, x2); H_1[i * nx + j] = y2[1]; } } // // check_H_1 double check_H_1[] = { 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0. }; for(size_t ij = 0; ij < nx * nx; ij++) ok &= H_1[ij] == check_H_1[ij]; // ----------------------------------------------------------------------- // Record g // ----------------------------------------------------------------------- // // af CppAD::ADFun< AD, double> af = f.base2ad(); // // az CppAD::Independent(ax); CPPAD_TESTVECTOR( AD ) ax1(nx), ay1(ny), az(nx); af.Forward(0, ax); for(size_t j = 0; j < nx; ++j) ax1[j] = 0.0; for(size_t j = 0; j < nx; ++j) { ax1[j] = 1.0; ay1 = af.Forward(1, ax1); ax1[j] = 0.0; az[j] = ay1[1]; } // g CppAD::ADFun g(ax, az); // ----------------------------------------------------------------------- // Forward mode on g // ----------------------------------------------------------------------- // // z // zero order forward mode computation of g(x) CPPAD_TESTVECTOR(double) z(nx); z = g.Forward(0, x); // // check z for(size_t j = 0; j < nx; ++j) ok &= z[j] == J[1 * nx + j]; // // z1 CPPAD_TESTVECTOR(double) z1(nx); for(size_t j = 0; j < nx; ++j) { x1[j] = 1.0; z1 = g.Forward(1, x1); x1[j] = 0.0; for(size_t i = 0; i < nx; ++i) ok &= z1[i] == check_H_1[i * nx + j]; } // ---------------------------------------------------------------- return ok; } // END C++ ================================================ FILE: example/atomic_four/mat_mul/identical_zero.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_mat_mul_identical_zero.cpp} Atomic Matrix Multiply Identical Zero: Example and Test ####################################################### Purpose ******* This example demonstrates how the :ref:`atomic_four_mat_mul_for_type.hpp-name` routine uses the *identical_zero_enum* type to reduce the number of variables. Zero **** The first case computes the following matrix product .. math:: \left( \begin{array}{ccc} u_0 & 0 & 0 \\ 0 & u_1 & 0 \\ 0 & 0 & u_2 \end{array} \right) \left( \begin{array}{ccc} u_3 & 0 & 0 \\ 0 & u_4 & 0 \\ 0 & 0 & u_5 \end{array} \right) = \left( \begin{array}{ccc} u_0 u_3 & 0 & 0 \\ 0 & u_1 u_4 & 0 \\ 0 & 0 & u_2 u_5 \end{array} \right) The result matrix for this case has three variables, one for each product on the diagonal. One *** The second case computes the following matrix product .. math:: \left( \begin{array}{ccc} u_0 & 1 & 1 \\ 1 & u_1 & 1 \\ 1 & 1 & u_2 \end{array} \right) \left( \begin{array}{ccc} u_3 & 1 & 1 \\ 1 & u_4 & 1 \\ 1 & 1 & u_5 \end{array} \right) = \left( \begin{array}{ccc} u_0 u_3 + 2 & u_0 + u_3 + 1 & u_0 + u_5 + 1 \\ u_1 + u_3 + 1 & u_1 u_4 + 2 & u_1 + u_5 + 1 \\ u_2 + u_3 + 1 & u_2 + u_4 + 1 & u_2 u_5 + 2 \end{array} \right) The result matrix for this case has nine variables, one for each of its elements. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_mat_mul_identical_zero.cpp} */ // BEGIN C++ # include # include bool identical_zero(void) { // ok, eps bool ok = true; // // AD, NearEqual using CppAD::AD; using CppAD::NearEqual; // ----------------------------------------------------------------------- // Record f // ----------------------------------------------------------------------- // // afun CppAD::atomic_mat_mul afun("atomic_mat_mul"); // // nleft size_t size = 3; // // size_var size_t size_var[2]; // // zero_one for(size_t zero_one = 0; zero_one < 2; ++zero_one) { // // n_right, n_middle size_t n_left = size, n_middle = size, n_right = size; // // nu, au size_t nu = 2 * size; CPPAD_TESTVECTOR( AD ) au(nu); for(size_t j = 0; j < nu; ++j) au[j] = AD(j + 2); CppAD::Independent(au); // // offset size_t offset = size * size; // // nx, ax size_t nx = size * (size + size); CPPAD_TESTVECTOR( AD ) ax(nx); for(size_t i = 0; i < size; ++i) { for(size_t j = 0; j < size; ++j) { // left size_t ij = i * size + j; if( i == j ) ax[ij] = au[j]; else ax[ij] = AD(zero_one); // right ij = offset + i * n_right + j; if( i == j ) ax[ij] = au[i]; else ax[ij] = AD(zero_one); } } // // ay size_t ny = size * size; CPPAD_TESTVECTOR( AD ) ay(ny); size_t call_id = afun.set(n_left, n_middle, n_right); afun(call_id, ax, ay); // // av size_t nv = size; CPPAD_TESTVECTOR( AD ) av(nv); for(size_t i = 0; i < nv; ++i) av[i] += ay[i * size + i]; // // f CppAD::ADFun f(au, av); // // size_var size_var[zero_one] = f.size_var(); } // ok ok = size_var[1] - size_var[0] == size * size - size; // return ok; } // END C++ ================================================ FILE: example/atomic_four/mat_mul/mat_mul.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // CPPAD_HAS_* defines # include // system include files used for I/O # include // C style asserts # include // for thread_alloc # include // test runner # include // BEGIN_SORT_THIS_LINE_PLUS_1 extern bool forward(void); extern bool identical_zero(void); extern bool rev_depend(void); extern bool reverse(void); extern bool sparsity(void); // END_SORT_THIS_LINE_MINUS_1 // main program that runs all the tests int main(void) { std::string group = "example/atomic_four/mat_mul"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // BEGIN_SORT_THIS_LINE_PLUS_1 Run( forward, "forward" ); Run( identical_zero, "identical_zero" ); Run( rev_depend, "rev_depend" ); Run( reverse, "reverse" ); Run( sparsity, "sparsity" ); // END_SORT_THIS_LINE_MINUS_1 // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } ================================================ FILE: example/atomic_four/mat_mul/rev_depend.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_mat_mul_rev_depend.cpp} Atomic Matrix Multiply Reverse Dependency: Example and Test ########################################################### Purpose ******* This example uses the atomic matrix multiply ``rev_depend`` function to reduce the number of variables in the recording of :math:`g(u)`. f(u) **** .. math:: f(u) = \left( \begin{array}{cc} 2 u_0 & 2 u_1 \\ 2 u_2 & 2 u_3 \\ \end{array} \right) \left( \begin{array}{cc} 2 u_4 & 2 u_5 \\ 2 u_6 & 2 u_7 \end{array} \right) = \left( \begin{array}{cc} 4( u_0 u_4 + u_1 u_6 ) & 4( u_0 u_5 + u_1 u_7 ) \\ 4( u_2 u_4 + u_3 u_6 ) & 4( u_2 u_5 + u_3 u_7 ) \\ \end{array} \right) .. math:: f_{0,0} (u) = 4 ( u_0 u_4 + u_1 u_6 ) Forward Analysis **************** Forward dependency analysis determines that there is a new variable for each of the 8 multiplications by 2.0. It also determines, using :ref:`for_type` that each of the 4 elements in the matrix product result is a new variable. Reverse Analysis **************** Reverse analysis detect that only 1 of the 4 elements in the matrix product is used. In addition it determines, using :ref:`rev_depend` , that only 4 of the 8 multiplications by 2.0 are used. size_var ******** The difference in :ref:`fun_property@size_var` is the difference between only using forward dependency and using both; i.e., (8 - 4) + (4 - 1) = 7. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_mat_mul_rev_depend.cpp} */ // BEGIN C++ # include # include bool rev_depend(void) { // ok, eps bool ok = true; // // AD using CppAD::AD; using CppAD::sparse_rc; // ----------------------------------------------------------------------- // Record g // ----------------------------------------------------------------------- // // afun CppAD::atomic_mat_mul afun("atomic_mat_mul"); // // nleft, n_middle, n_right size_t n_left = 2, n_middle = 2, n_right = 2; // // nu, au size_t nu = n_middle * (n_left + n_right); CPPAD_TESTVECTOR( AD ) au(nu); for(size_t j = 0; j < nu; ++j) au[j] = AD(j + 2); CppAD::Independent(au); // // nx, ax CPPAD_TESTVECTOR( AD ) ax(nu); for(size_t j = 0; j < nu; ++j) ax[j] = 2.0 * au[j]; // // ny, ay size_t ny = n_left * n_right; CPPAD_TESTVECTOR( AD ) ay(ny); size_t call_id = afun.set(n_left, n_middle, n_right); afun(call_id, ax, ay); // // az = f_{0,0} (x) CPPAD_TESTVECTOR( AD ) az(1); az[0] = ay[ 0 * n_right + 0 ]; // // g CppAD::ADFun g(au, az); // // size_var_before size_t size_var_before = g.size_var(); // // // optimize g.optimize("val_graph no_conditional_skip"); // // size_var_after size_t size_var_after = g.size_var(); // // ok ok &= size_var_before - size_var_after == 7; // return ok; } // END C++ ================================================ FILE: example/atomic_four/mat_mul/reverse.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_mat_mul_reverse.cpp} {xrst_spell ccccccccc cccccccccc } Atomic Matrix Multiply Reverse Mode: Example and Test ##################################################### Purpose ******* This example demonstrates using reverse mode with the :ref:`atomic_four_mat_mul-name` class. f(x) **** For this example, the function :math:`f(x)` is .. math:: f(x) = \left( \begin{array}{ccc} x_0 & x_1 & x_2 \\ x_3 & x_4 & x_5 \end{array} \right) \left( \begin{array}{c} x_6 \\ x_7 \\ x_8 \end{array} \right) = \left( \begin{array}{c} x_0 * x_6 + x_1 * x_7 + x_2 * x_8 \\ x_3 * x_6 + x_4 * x_7 + x_5 * x_8 \end{array} \right) Jacobian of f(x) **************** The Jacobian of :math:`f(x)` is .. math:: f^{(1)} (x) = \left( \begin{array}{cccccccccc} x_6 & x_7 & x_8 & 0 & 0 & 0 & x_0 & x_1 & x_2 \\ 0 & 0 & 0 & x_6 & x_7 & x_8 & x_3 & x_4 & x_5 \end{array} \right) g(x) **** We define the function :math:`g(x) = f_0^{(1)} (x)^\R{T}`; i.e., .. math:: g(x) = ( x_6, x_7, x_8, 0, 0, 0, x_0, x_1, x_2 )^\R{T} Hessian ******* The Hessian of :math:`f_1(x)` is the Jacobian of :math:`g(x)`; i.e., .. math:: f_1^{(2)} (x) = g^{(1)} (x) = \left( \begin{array}{ccccccccc} % 0 1 2 3 4 5 6 7 8 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\ % 0 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\ % 1 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \\ % 2 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ % 3 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ % 4 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ % 5 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ % 6 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ % 7 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\ % 8 \end{array} \right) Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_mat_mul_reverse.cpp} */ // BEGIN C++ # include # include bool reverse(void) { // ok, eps bool ok = true; // // AD, NearEqual using CppAD::AD; using CppAD::NearEqual; // ----------------------------------------------------------------------- // Record f // ----------------------------------------------------------------------- // // afun CppAD::atomic_mat_mul afun("atomic_mat_mul"); // // nleft, n_middle, n_right size_t n_left = 2, n_middle = 3, n_right = 1; // // nx, ax size_t nx = n_middle * (n_left + n_right); CPPAD_TESTVECTOR( AD ) ax(nx); for(size_t j = 0; j < nx; ++j) ax[j] = AD(j + 2); CppAD::Independent(ax); // // ny, ay size_t ny = n_left * n_right; CPPAD_TESTVECTOR( AD ) ay(ny); // // ay size_t call_id = afun.set(n_left, n_middle, n_right); afun(call_id, ax, ay); // // f CppAD::ADFun f(ax, ay); // ----------------------------------------------------------------------- // Reverse mode on f // ----------------------------------------------------------------------- // // x CPPAD_TESTVECTOR(double) x(nx); for(size_t j = 0; j < nx; ++j) x[j] = double(3 + nx - j); // // y // zero order forward mode computation of f(x) CPPAD_TESTVECTOR(double) y(nx); y = f.Forward(0, x); // // check_y double check_y[] = { x[0] * x[6] + x[1] * x[7] + x[2] * x[8], x[3] * x[6] + x[4] * x[7] + x[5] * x[8] }; for(size_t i = 0; i < ny; ++i) ok &= y[i] == check_y[i]; // // J // first order reverse mode computation of f'(x) CPPAD_TESTVECTOR(double) w1(ny), dw1(nx), J(ny * nx); for(size_t i = 0; i < ny; ++i) w1[i] = 0.0; for(size_t i = 0; i < ny; ++i) { w1[i] = 1.0; dw1 = f.Reverse(1, w1); w1[i] = 0.0; for(size_t j = 0; j < nx; ++j) J[i * nx + j] = dw1[j]; } // // check_J double check_J[] = { x[6], x[7], x[8], 0.0, 0.0, 0.0, x[0], x[1], x[2], 0.0, 0.0, 0.0, x[6], x[7], x[8], x[3], x[4], x[5] }; for(size_t ij = 0; ij < ny * nx; ij++) ok &= J[ij] == check_J[ij]; // // H_0 // Second order reverse mode computaiton of f_0^2 (x) CPPAD_TESTVECTOR(double) x1(nx), w2(ny), dw2(2 * nx), H_0(nx * nx); for(size_t i = 0; i < ny; ++i) w2[i] = 0.0; w2[0] = 1.0; for(size_t j = 0; j < nx; ++j) x1[j] = 0.0; for(size_t i = 0; i < nx; ++i) { x1[i] = 1.0; f.Forward(1, x1); x1[i] = 0.0; dw2 = f.Reverse(2, w2); for(size_t j = 0; j < nx; ++j) H_0[i * nx + j] = dw2[2 * j + 1]; } // // check_H_0 assert( nx == 9 ); double check_H_0[] = { 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0, }; for(size_t ij = 0; ij < nx * nx; ij++) ok &= H_0[ij] == check_H_0[ij]; // ----------------------------------------------------------------------- // Record g // ----------------------------------------------------------------------- // // af CppAD::ADFun< AD, double> af = f.base2ad(); // // az CppAD::Independent(ax); CPPAD_TESTVECTOR( AD ) aw(ny), az(nx); af.Forward(0, ax); for(size_t i = 0; i < ny; ++i) aw[i] = 0.0; aw[0] = 1.0; az = af.Reverse(1, aw); // g CppAD::ADFun g(ax, az); // ----------------------------------------------------------------------- // Forward mode on g // ----------------------------------------------------------------------- // // z // zero order forward mode computation of g(x) CPPAD_TESTVECTOR(double) z(nx); z = g.Forward(0, x); // // check z for(size_t j = 0; j < nx; ++j) ok &= z[j] == J[0 * nx + j]; // // z1 CPPAD_TESTVECTOR(double) w(nx), dw(nx); for(size_t i = 0; i < nx; ++i) w[i] = 0.0; for(size_t i = 0; i < nx; ++i) { w[i] = 1.0; dw = g.Reverse(1, w); w[i] = 0.0; for(size_t j = 0; j < nx; ++j) ok &= dw[j] == check_H_0[i * nx + j]; } // ---------------------------------------------------------------- return ok; } // END C++ ================================================ FILE: example/atomic_four/mat_mul/sparsity.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_mat_mul_sparsity.cpp} {xrst_spell cccccccc cccccccccc rvec } Atomic Matrix Multiply Sparsity Patterns: Example and Test ########################################################## Purpose ******* This example demonstrates computing sparsity patterns with the :ref:`atomic_four_mat_mul-name` class. f(x) **** For a matrix :math:`A` we define the function :math:`\R{rvec} ( A )` to be the elements of :math:`A` in row major order. For this example, the function :math:`f(x)` is .. math:: f(x) = \R{rvec} \left[ \left( \begin{array}{cc} x_0 & x_1 \\ x_2 & x_3 \\ \end{array} \right) \left( \begin{array}{cc} x_4 & x_5 \\ x_6 & x_7 \end{array} \right) \right] = \R{rvec} \left( \begin{array}{cc} x_0 x_4 + x_1 x_6 & x_0 x_5 + x_1 x_7 \\ x_2 x_4 + x_3 x_6 & x_2 x_5 + x_3 x_7 \\ \end{array} \right) .. math:: f(x) = \left( \begin{array}{c} x_0 x_4 + x_1 x_6 \\ x_0 x_5 + x_1 x_7 \\ x_2 x_4 + x_3 x_6 \\ x_2 x_5 + x_3 x_7 \end{array} \right) Jacobian of f(x) **************** The Jacobian of :math:`f(x)` is .. math:: f^{(1)} (x) = \left( \begin{array}{cccccccc} % 0 1 2 3 4 5 6 7 x_4 & x_6 & 0 & 0 & x_0 & 0 & x_1 & 0 \\ % 0 x_5 & x_7 & 0 & 0 & 0 & x_0 & 0 & x_1 \\ % 1 0 & 0 & x_4 & x_6 & x_2 & 0 & x_3 & 0 \\ % 2 0 & 0 & x_5 & x_7 & 0 & x_2 & 0 & x_3 \\ % 3 \end{array} \right) Hessian ******* The function :math:`f_2 (x)` is .. math:: f_2 (x) = x_2 x_4 + x_3 x_6 The Hessian of :math:`f_2(x)` is .. math:: f_2^{(2)} (x) = \left( \begin{array}{cccccccccc} & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 \\ & - & - & - & - & - & - & - & - \\ 0 \; | & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 1 \; | & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 2 \; | & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\ 3 \; | & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\ 4 \; | & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 5 \; | & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 6 \; | & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ 7 \; | & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \end{array} \right) where the first row is the column index, and the first column is the row index, for the corresponding matrix entries above. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_mat_mul_sparsity.cpp} */ // BEGIN C++ # include # include bool sparsity(void) { // ok, eps bool ok = true; // // AD using CppAD::AD; using CppAD::sparse_rc; // ----------------------------------------------------------------------- // Record f // ----------------------------------------------------------------------- // // afun CppAD::atomic_mat_mul afun("atomic_mat_mul"); // // nleft, n_middle, n_right size_t n_left = 2, n_middle = 2, n_right = 2; // // nx, ax size_t nx = n_middle * (n_left + n_right); CPPAD_TESTVECTOR( AD ) ax(nx); for(size_t j = 0; j < nx; ++j) ax[j] = AD(j + 2); CppAD::Independent(ax); // // ny, ay size_t ny = n_left * n_right; CPPAD_TESTVECTOR( AD ) ay(ny); // // ay size_t call_id = afun.set(n_left, n_middle, n_right); afun(call_id, ax, ay); // // f CppAD::ADFun f(ax, ay); // // s_vector typedef CPPAD_TESTVECTOR(size_t) s_vector; // // eye_sparsity // nx by nx identitty matrix sparse_rc eye_sparsity; eye_sparsity.resize(nx, nx, nx); for(size_t i = 0; i < nx; ++i) eye_sparsity.set(i, i, i); // // ----------------------------------------------------------------------- // jac_sparsity bool transpose = false; bool dependency = false; bool internal_bool = false; sparse_rc jac_sparsity; f.for_jac_sparsity( eye_sparsity, transpose, dependency, internal_bool, jac_sparsity ); { // check jac_sparsity // // row, col const s_vector& row = jac_sparsity.row(); const s_vector& col = jac_sparsity.col(); s_vector row_major = jac_sparsity.row_major(); // // ok ok &= jac_sparsity.nnz() == 16; for(size_t k = 0; k < jac_sparsity.nnz(); ++k) ok &= row[ row_major[k] ] == k / 4; // row 0 ok &= col[ row_major[0] ] == 0; ok &= col[ row_major[1] ] == 1; ok &= col[ row_major[2] ] == 4; ok &= col[ row_major[3] ] == 6; // row 1 ok &= col[ row_major[4] ] == 0; ok &= col[ row_major[5] ] == 1; ok &= col[ row_major[6] ] == 5; ok &= col[ row_major[7] ] == 7; // row 2 ok &= col[ row_major[8] ] == 2; ok &= col[ row_major[9] ] == 3; ok &= col[ row_major[10] ] == 4; ok &= col[ row_major[11] ] == 6; // row 3 ok &= col[ row_major[12] ] == 2; ok &= col[ row_major[13] ] == 3; ok &= col[ row_major[14] ] == 5; ok &= col[ row_major[15] ] == 7; } // ---------------------------------------------------------------- // // select_y // corresponding to f_2 CPPAD_TESTVECTOR(bool) select_y(ny); for(size_t i = 0; i < ny; ++i) select_y[i] = false; select_y[2] = true; // // hes_sparsity transpose = false; internal_bool = false; sparse_rc hes_sparsity; f.rev_hes_sparsity(select_y, transpose, internal_bool, hes_sparsity); { // check hes_sparsity // // row, col const s_vector& row = hes_sparsity.row(); const s_vector& col = hes_sparsity.col(); s_vector row_major = hes_sparsity.row_major(); // // ok ok &= hes_sparsity.nnz() == 4; // ok &= row[ row_major[0] ] == 2; ok &= col[ row_major[0] ] == 4; // ok &= row[ row_major[1] ] == 3; ok &= col[ row_major[1] ] == 6; // ok &= row[ row_major[2] ] == 4; ok &= col[ row_major[2] ] == 2; // ok &= row[ row_major[3] ] == 6; ok &= col[ row_major[3] ] == 3; } return ok; } // END C++ ================================================ FILE: example/atomic_four/norm_sq.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_norm_sq.cpp} Atomic Euclidean Norm Squared: Example and Test ############################################### Function ******** This example demonstrates using :ref:`atomic_four-name` to define the operation :math:`g : \B{R}^n \rightarrow \B{R}` where .. math:: g(x) = x_0^2 + \cdots + x_{n-1}^2 Purpose ******* This atomic function demonstrates the following cases: #. an arbitrary number of arguments *n* #. zero and first order forward mode. #. first order derivatives using reverse mode. Define Atomic Function ********************** {xrst_literal // BEGIN_DEFINE_ATOMIC_FUNCTION // END_DEFINE_ATOMIC_FUNCTION } Use Atomic Function ******************* {xrst_literal // BEGIN_USE_ATOMIC_FUNCTION // END_USE_ATOMIC_FUNCTION } {xrst_end atomic_four_norm_sq.cpp} */ # include // BEGIN_DEFINE_ATOMIC_FUNCTION // empty namespace namespace { // BEGIN CONSTRUCTOR class atomic_norm_sq : public CppAD::atomic_four { public: atomic_norm_sq(const std::string& name) : CppAD::atomic_four(name) { } // END CONSTRUCTOR private: // BEGIN FOR_TYPE bool for_type( size_t call_id , const CppAD::vector& type_x , CppAD::vector& type_y ) override { assert( call_id == 0 ); // default value assert(type_y.size() == 1 ); // m // // type_y size_t n = type_x.size(); type_y[0] = CppAD::constant_enum; for(size_t j = 0; j < n; ++j) type_y[0] = std::max(type_y[0], type_x[j]); return true; } // END FOR_TYPE // BEGIN FORWARD bool forward( size_t call_id , const CppAD::vector& select_y , size_t order_low , size_t order_up , const CppAD::vector& tx , CppAD::vector& ty ) override { size_t q = order_up + 1; size_t n = tx.size() / q; # ifndef NDEBUG size_t m = ty.size() / q; assert( call_id == 0 ); assert( m == 1 ); assert( m == select_y.size() ); # endif // ok bool ok = order_up <= 1 && order_low <= order_up; if ( ! ok ) return ok; // // sum = x_0^0 * x_0^0 + x_1^0 * x_1^0 + ... double sum = 0.0; for(size_t j = 0; j < n; ++j) { double xj0 = tx[ j * q + 0]; sum += xj0 * xj0; } // // ty[0] = sum if( order_low <= 0 ) ty[0] = sum; if( order_up < 1 ) return ok; // sum = x_0^0 * x_0^1 + x_1^0 ^ x_1^1 + ... sum = 0.0; for(size_t j = 0; j < n; ++j) { double xj0 = tx[ j * q + 0]; double xj1 = tx[ j * q + 1]; sum += xj0 * xj1; } // ty[1] = 2.0 * sum assert( order_up == 1 ); ty[1] = 2.0 * sum; return ok; } // END FORWARD // BEGIN REVERSE bool reverse( size_t call_id , const CppAD::vector& select_x , size_t order_up , const CppAD::vector& tx , const CppAD::vector& ty , CppAD::vector& px , const CppAD::vector& py ) override { size_t q = order_up + 1; size_t n = tx.size() / q; # ifndef NDEBUG size_t m = ty.size() / q; assert( call_id == 0 ); assert( m == 1 ); assert( px.size() == tx.size() ); assert( py.size() == ty.size() ); assert( n == select_x.size() ); # endif // ok bool ok = order_up == 0; if ( ! ok ) return ok; // first order reverse mode for(size_t j = 0; j < n; ++j) { // x_0^0 double xj0 = tx[ j * q + 0]; // // H( {x_j^k} ) = G[ F( {x_j^k} ), {x_j^k} ] double dF = 2.0 * xj0; // partial F w.r.t x_j^0 double dG = py[0]; // partial of G w.r.t. y[0] double dH = dG * dF; // partial of H w.r.t. x_j^0 // px[j] px[j] = dH; } return ok; } // END REVERSE // BEGIN JAC_SPARSITY // Use deprecated version of this callback to test that is still works // (missing the ident_zero_x argument). bool jac_sparsity( size_t call_id , bool dependency , // const CppAD::vector& ident_zero_x, const CppAD::vector& select_x , const CppAD::vector& select_y , CppAD::sparse_rc< CppAD::vector >& pattern_out ) override { size_t n = select_x.size(); size_t m = select_y.size(); # ifndef NDEBUG assert( call_id == 0 ); assert( m == 1 ); # endif // nnz size_t nnz = 0; if( select_y[0] ) { for(size_t j = 0; j < n; ++j) { if( select_x[j] ) ++nnz; } } // pattern_out pattern_out.resize(m, n, nnz); size_t k = 0; if( select_y[0] ) { for(size_t j = 0; j < n; ++j) { if( select_x[j] ) pattern_out.set(k++, 0, j); } } assert( k == nnz ); return true; } // END JAC_SPARSITY // BEGIN HES_SPARSITY // Use deprecated version of this callback to test that is still works // (missing the ident_zero_x argument). bool hes_sparsity( size_t call_id , // const CppAD::vector& ident_zero_x, const CppAD::vector& select_x , const CppAD::vector& select_y , CppAD::sparse_rc< CppAD::vector >& pattern_out ) override { size_t n = select_x.size(); # ifndef NDEBUG size_t m = select_y.size(); assert( call_id == 0 ); assert( m == 1 ); # endif // nnz size_t nnz = 0; if( select_y[0] ) { for(size_t j = 0; j < n; ++j) { if( select_x[j] ) ++nnz; } } // pattern_out pattern_out.resize(n, n, nnz); size_t k = 0; if( select_y[0] ) { for(size_t j = 0; j < n; ++j) { if( select_x[j] ) pattern_out.set(k++, j, j); } } return true; } // END HES_SPARSITY // BEGIN REV_DEPEND bool rev_depend( size_t call_id , CppAD::vector& depend_x , const CppAD::vector& depend_y ) override { size_t n = depend_x.size(); # ifndef NDEBUG size_t m = depend_y.size(); assert( call_id == 0 ); assert( m == 1 ); # endif for(size_t j = 0; j < n; ++j) depend_x[j] = depend_y[0]; // return true; } // END REV_DEPEND }; } // END_DEFINE_ATOMIC_FUNCTION // BEGIN_USE_ATOMIC_FUNCTION bool norm_sq(void) { // ok, eps bool ok = true; double eps = 10. * CppAD::numeric_limits::epsilon(); // // atom_norm_sq atomic_norm_sq afun("atomic_norm_sq"); // // n, m size_t n = 2; size_t m = 1; // // x CPPAD_TESTVECTOR(double) x(n); for(size_t j = 0; j < n; ++j) x[j] = 1.0 / (double(j) + 1.0); // // ax CPPAD_TESTVECTOR( CppAD::AD ) ax(n); for(size_t j = 0; j < n; ++j) ax[j] = x[j]; CppAD::Independent(ax); // // ay CPPAD_TESTVECTOR( CppAD::AD ) ay(m); afun(ax, ay); // // f CppAD::ADFun f; f.Dependent (ax, ay); // // check double check = 0.0; for(size_t j = 0; j < n; ++j) check += x[j] * x[j]; // // ok // check ay[0] ok &= CppAD::NearEqual( Value(ay[0]) , check, eps, eps); // // ok // check zero order forward mode CPPAD_TESTVECTOR(double) y(m); y = f.Forward(0, x); ok &= CppAD::NearEqual(y[0] , check, eps, eps); // // n2, check size_t n2 = n / 2; check = 2.0 * x[n2]; // // ok // check first order forward mode partial w.r.t. x[n2] CPPAD_TESTVECTOR(double) x1(n), y1(m); for(size_t j = 0; j < n; ++j) x1[j] = 0.0; x1[n2] = 1.0; y1 = f.Forward(1, x1); ok &= CppAD::NearEqual(y1[0] , check, eps, eps); // // ok // first order reverse mode size_t q = 1; CPPAD_TESTVECTOR(double) w(m), dw(n * q); w[0] = 1.; dw = f.Reverse(q, w); for(size_t j = 0; j < n; ++j) { check = 2.0 * x[j]; ok &= CppAD::NearEqual(dw[j] , check, eps, eps); } // // pattern_out // reverse mode Jacobian sparstiy pattern CppAD::sparse_rc< CPPAD_TESTVECTOR(size_t) > pattern_in, pattern_out; pattern_in.resize(m, m, m); for(size_t i = 0; i < m; ++i) pattern_in.set(i, i, i); bool transpose = false; bool dependency = false; bool internal_bool = false; f.rev_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); // // ok ok &= pattern_out.nnz() == n; CPPAD_TESTVECTOR(size_t) row_major = pattern_out.row_major(); for(size_t j = 0; j < n; ++j) { size_t r = pattern_out.row()[ row_major[j] ]; size_t c = pattern_out.col()[ row_major[j] ]; ok &= r == 0 && c == j; } // // pattern_out // forward mode Hessian sparsity pattern CPPAD_TESTVECTOR(bool) select_x(n), select_y(m); for(size_t j = 0; j < n; ++j) select_x[j] = true; for(size_t i = 0; i < m; ++i) select_y[i] = true; internal_bool = false; f.for_hes_sparsity( select_x, select_y, internal_bool, pattern_out ); // // ok ok &= pattern_out.nnz() == n; row_major = pattern_out.row_major(); for(size_t j = 0; j < n; ++j) { size_t r = pattern_out.row()[ row_major[j] ]; size_t c = pattern_out.col()[ row_major[j] ]; ok &= r == j && c == j; } // // optimize // this uses the rev_depend override above f.optimize("val_graph no_conditional_skip"); // // ok // check zero order forward mode (on optimized version of f) y = f.Forward(0, x); check = 0.0; for(size_t j = 0; j < n; ++j) check += x[j] * x[j]; ok &= CppAD::NearEqual(y[0] , check, eps, eps); // return ok; } // END_USE_ATOMIC_FUNCTION ================================================ FILE: example/atomic_four/vector/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list add.cpp div.cpp hes_sparsity.cpp jac_sparsity.cpp mul.cpp neg.cpp rev_depend.cpp sub.cpp vector.cpp ) # END_SORT_THIS_LINE_MINUS_2 set_compile_flags(example_atomic_four_vector "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(example_atomic_four_vector EXCLUDE_FROM_ALL ${source_list}) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_atomic_four_vector ${cppad_lib} ${colpack_libs} ) # # check_example_atomic_four vector add_check_executable(check_example_atomic_four vector) ================================================ FILE: example/atomic_four/vector/add.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_vector_add.cpp} Atomic Vector Addition Example ############################## f(u, v, w) ********** For this example, :math:`f : \B{R}^{3m} \rightarrow \B{R}^m` is defined by :math:`f(u, v, w) = u + v + w`. where *u* , *v* , and *w* are in :math:`\B{R}^m`. g(u, v, w) ********** For this example :math:`g : \B{R}^{3m} \rightarrow \B{R}^m` is defined by :math:`g_i (u, v, w) = \partial_{v[i]} f_i (u, v, w)` Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_vector_add.cpp} */ // BEGIN C++ # include # include bool add(void) { bool ok = true; using CppAD::NearEqual; using CppAD::AD; double eps99 = 99.0 * CppAD::numeric_limits::epsilon(); // // vec_op // atomic vector_op object CppAD::atomic_vector vec_op("atomic_vector"); // // m // size of u, v, and w size_t m = 5; // // add_op typedef CppAD::atomic_vector::op_enum_t op_enum_t; op_enum_t add_op = CppAD::atomic_vector::add_enum; // ----------------------------------------------------------------------- // Record f(u, v, w) = u + v + w // ----------------------------------------------------------------------- // Independent variable vector CPPAD_TESTVECTOR( CppAD::AD ) auvw(3 * m); for(size_t j = 0; j < 3 * m; ++j) auvw[j] = AD(1 + j); CppAD::Independent(auvw); // // au, av, aw CPPAD_TESTVECTOR( CppAD::AD ) au(m), av(m), aw(m); for(size_t i = 0; i < m; ++i) { au[i] = auvw[0 * m + i]; av[i] = auvw[1 * m + i]; aw[i] = auvw[2 * m + i]; } // // ax = (au, av) CPPAD_TESTVECTOR( CppAD::AD ) ax(2 * m); for(size_t i = 0; i < m; ++i) { ax[i] = au[i]; ax[m + i] = av[i]; } // // ay = u + v CPPAD_TESTVECTOR( CppAD::AD ) ay(m); vec_op(add_op, ax, ay); // // ax = (ay, aw) for(size_t i = 0; i < m; ++i) { ax[i] = ay[i]; ax[m + i] = aw[i]; } // // az = ay + w CPPAD_TESTVECTOR( CppAD::AD ) az(m); vec_op(add_op, ax, az); // // f CppAD::ADFun f(auvw, az); // ----------------------------------------------------------------------- // check forward mode on f // ----------------------------------------------------------------------- // // uvw, duvw CPPAD_TESTVECTOR(double) uvw(3 * m), duvw(3 * m); for(size_t j = 0; j < 3 * m; ++j) { uvw[j] = double(1 + j); duvw[j] = double(j); } // // z, dz CPPAD_TESTVECTOR(double) z(m), dz(m); z = f.Forward(0, uvw); dz = f.Forward(1, duvw); // // ok for(size_t i = 0; i < m; ++i) { double check_z = uvw[0 * m + i] + uvw[1 * m + i] + uvw[2 * m + i]; ok &= NearEqual( z[i] , check_z, eps99, eps99); double check_dz = double( (0 * m + i) + (1 * m + i) + (2 * m + i) ); ok &= NearEqual( dz[i] , check_dz, eps99, eps99); } // ----------------------------------------------------------------------- // check reverse mode on f // ----------------------------------------------------------------------- // // weight CPPAD_TESTVECTOR(double) weight(m); for(size_t i = 0; i < m; ++i) weight[i] = 1.0; // // dweight CPPAD_TESTVECTOR(double) dweight(3 * m); f.Forward(0, uvw); dweight = f.Reverse(1, weight); // // ok for(size_t j = 0; j < 3 * m; ++j) { double check = 1.0; ok &= NearEqual(dweight[j], check, eps99, eps99); } // ----------------------------------------------------------------------- // Record g_i (u, v, w) = \partial d/dv[i] f_i (u , v , w) // ----------------------------------------------------------------------- // // af CppAD::ADFun< AD, double > af = f.base2ad(); // // auvw CppAD::Independent(auvw); // // aduvw CPPAD_TESTVECTOR( AD ) aduvw(3 * m); for(size_t i = 0; i < m; ++i) { aduvw[0 * m + i] = 0.0; // du[i] aduvw[1 * m + i] = 1.0; // dv[i] aduvw[2 * m + i] = 0.0; // dw[i] } // // az // use the fact that d_v[i] f_k (u, v, w) is zero when i != k af.Forward(0, auvw); az = af.Forward(1, aduvw); CppAD::ADFun g(auvw, az); // ----------------------------------------------------------------------- // Record h (u, v, w) = sum f_i^(1) (u , v , w) // ----------------------------------------------------------------------- // // auvw CppAD::Independent(auvw); // // aweight CPPAD_TESTVECTOR( AD ) aweight(m); for(size_t i = 0; i < m; ++i) aweight[i] = 1.0; // // az CPPAD_TESTVECTOR( AD ) adweight(3 * m); af.Forward(0, auvw); az = af.Reverse(1, aweight); CppAD::ADFun h(auvw, az); // ----------------------------------------------------------------------- // check forward mode on g // ----------------------------------------------------------------------- // // z z = g.Forward(0, uvw); // // ok for(size_t i = 0; i < m; ++i) { double check_z = 1.0; ok &= NearEqual( z[i] , check_z, eps99, eps99); } // ----------------------------------------------------------------------- // check forward mode on h // ----------------------------------------------------------------------- // // z z = h.Forward(0, uvw); // // ok for(size_t j = 0; j < 3 * m; ++j) { double check_z = 1.0; ok &= NearEqual( z[j] , check_z, eps99, eps99); } return ok; } // END C++ ================================================ FILE: example/atomic_four/vector/div.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_vector_div.cpp} Atomic Vector Division Example ############################## f(u, v, w) ********** For this example, :math:`f : \B{R}^{2m} \rightarrow \B{R}^m` is defined by :math:`f(u, v) = u * u / v`. where *u* and *v* are in :math:`\B{R}^m`. g(u, v) ******* For this example :math:`g : \B{R}^{2m} \rightarrow \B{R}^m` is defined by :math:`g_i (u, v) = \partial_{v[i]} f_i (u, v)` Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_vector_div.cpp} */ // BEGIN C++ # include # include bool div(void) { bool ok = true; using CppAD::NearEqual; using CppAD::AD; double eps99 = 99.0 * CppAD::numeric_limits::epsilon(); // // vec_op // atomic vector_op object CppAD::atomic_vector vec_op("atomic_vector"); // // m // size of u and v size_t m = 4; // // mul_op, div_op typedef CppAD::atomic_vector::op_enum_t op_enum_t; op_enum_t mul_op = CppAD::atomic_vector::mul_enum; op_enum_t div_op = CppAD::atomic_vector::div_enum; // ----------------------------------------------------------------------- // Record f(u, v) = u * u / v // ----------------------------------------------------------------------- // Independent variable vector CPPAD_TESTVECTOR( CppAD::AD ) auv(2 * m); for(size_t j = 0; j < 2 * m; ++j) auv[j] = AD(1 + j); CppAD::Independent(auv); // // au, av, aw CPPAD_TESTVECTOR( CppAD::AD ) au(m), av(m); for(size_t i = 0; i < m; ++i) { au[i] = auv[0 * m + i]; av[i] = auv[1 * m + i]; } // // ax = (mul_op, au, au) CPPAD_TESTVECTOR( CppAD::AD ) ax(2 * m); for(size_t i = 0; i < m; ++i) { ax[i] = au[i]; ax[m + i] = au[i]; } // // ay = u * u CPPAD_TESTVECTOR( CppAD::AD ) ay(m); vec_op(mul_op, ax, ay); // // ax = (ay, av) for(size_t i = 0; i < m; ++i) { ax[i] = ay[i]; ax[m + i] = av[i]; } // // az = au / ay CPPAD_TESTVECTOR( CppAD::AD ) az(m); vec_op(div_op, ax, az); // // f CppAD::ADFun f(auv, az); // ----------------------------------------------------------------------- // check forward mode on f // ----------------------------------------------------------------------- // // uv, duv CPPAD_TESTVECTOR(double) uv(2 * m), duv(2 * m); for(size_t j = 0; j < 2 * m; ++j) { uv[j] = double(2 + j); duv[j] = 1.0; } // // z, dz CPPAD_TESTVECTOR(double) z(m), dz(m); z = f.Forward(0, uv); dz = f.Forward(1, duv); // // ok for(size_t i = 0; i < m; ++i) { double ui = uv[0 * m + i]; double vi = uv[1 * m + i]; double check = ui * ui / vi; ok &= NearEqual( z[i] , check, eps99, eps99); check = 2.0 * ui / vi - ui * ui / (vi * vi); ok &= NearEqual( dz[i] , check, eps99, eps99); } // ----------------------------------------------------------------------- // check reverse mode on f // ----------------------------------------------------------------------- // // weight CPPAD_TESTVECTOR(double) weight(m); for(size_t i = 0; i < m; ++i) weight[i] = 1.0; // // dweight CPPAD_TESTVECTOR(double) dweight(2 * m); f.Forward(0, uv); dweight = f.Reverse(1, weight); // // ok for(size_t i = 0; i < m; ++i) { double ui = uv[0 * m + i]; double vi = uv[1 * m + i]; double dfi_dui = 2.0 * ui / vi; ok &= NearEqual(dweight[0 * m + i], dfi_dui, eps99, eps99); double dfi_dvi = - ui * ui / (vi * vi); ok &= NearEqual(dweight[1 * m + i], dfi_dvi, eps99, eps99); } // ----------------------------------------------------------------------- // Record g_i (u, v) = \partial d/dv[i] f_i (u, v) // ----------------------------------------------------------------------- // // af CppAD::ADFun< AD, double > af = f.base2ad(); // // auv CppAD::Independent(auv); // // aduv CPPAD_TESTVECTOR( AD ) aduv(2 * m); for(size_t i = 0; i < m; ++i) { aduv[0 * m + i] = 0.0; // du[i] aduv[1 * m + i] = 1.0; // dv[i] } // // az // use the fact that d_u[i] f_k (u, v, w) is zero when i != k af.Forward(0, auv); az = af.Forward(1, aduv); CppAD::ADFun g(auv, az); // ----------------------------------------------------------------------- // Record h (u, v) = sum f_i^(1) (u , v) // ----------------------------------------------------------------------- // // auv CppAD::Independent(auv); // // aweight CPPAD_TESTVECTOR( AD ) aweight(m); for(size_t i = 0; i < m; ++i) aweight[i] = 1.0; // // az CPPAD_TESTVECTOR( AD ) adweight(3 * m); af.Forward(0, auv); az = af.Reverse(1, aweight); CppAD::ADFun h(auv, az); // ----------------------------------------------------------------------- // check forward mode on g // ----------------------------------------------------------------------- // // z z = g.Forward(0, uv); // // ok for(size_t i = 0; i < m; ++i) { double ui = uv[0 * m + i]; double vi = uv[1 * m + i]; double check = - ui * ui / (vi * vi); ok &= NearEqual( z[i] , check, eps99, eps99); } // ----------------------------------------------------------------------- // check forward mode on h // ----------------------------------------------------------------------- // // z z = h.Forward(0, uv); // // ok for(size_t i = 0; i < m; ++i) { double ui = uv[0 * m + i]; double vi = uv[1 * m + i]; // double dfi_dui = 2.0 * ui / vi; ok &= NearEqual(z[0 * m + i] , dfi_dui, eps99, eps99); // double dfi_dvi = - ui * ui / (vi * vi); ok &= NearEqual(z[1 * m + i] , dfi_dvi, eps99, eps99); } return ok; } // END C++ ================================================ FILE: example/atomic_four/vector/hes_sparsity.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_vector_hes_sparsity.cpp} Atomic Vector Sparsity Patterns Example ####################################### f(u, v) ******* For this example, :math:`f : \B{R}^{3m} \rightarrow \B{R}^m` is defined by :math:`f(u, v, w) = - u * v * w`. where *u* , *v* , and *w* are in :math:`\B{R}^m`. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_vector_hes_sparsity.cpp} */ // BEGIN C++ # include # include bool hes_sparsity(void) { bool ok = true; using CppAD::NearEqual; using CppAD::AD; // // vec_op // atomic vector_op object CppAD::atomic_vector vec_op("atomic_vector"); // // m // size of u, v, and w size_t m = 6; // // n size_t n = 3 * m; // // mul_op, neg_op typedef CppAD::atomic_vector::op_enum_t op_enum_t; op_enum_t mul_op = CppAD::atomic_vector::mul_enum; op_enum_t neg_op = CppAD::atomic_vector::neg_enum; // ----------------------------------------------------------------------- // Record f(u, v, w) = - u * v * w // ----------------------------------------------------------------------- // Independent variable vector CPPAD_TESTVECTOR( CppAD::AD ) auvw(n); for(size_t j = 0; j < n; ++j) auvw[j] = AD(1 + j); CppAD::Independent(auvw); // // au, av, aw CPPAD_TESTVECTOR( CppAD::AD ) au(m), av(m), aw(m); for(size_t i = 0; i < m; ++i) { au[i] = auvw[0 * m + i]; av[i] = auvw[1 * m + i]; aw[i] = auvw[2 * m + i]; } // // ax = (au, av) CPPAD_TESTVECTOR( CppAD::AD ) ax(2 * m); for(size_t i = 0; i < m; ++i) { ax[i] = au[i]; ax[m + i] = av[i]; } // // ay = u * v CPPAD_TESTVECTOR( CppAD::AD ) ay(m); vec_op(mul_op, ax, ay); // // ax = (ay, aw) for(size_t i = 0; i < m; ++i) { ax[i] = ay[i]; ax[m + i] = aw[i]; } // // az = ay * w CPPAD_TESTVECTOR( CppAD::AD ) az(m); vec_op(mul_op, ax, az); // // ay = - az vec_op(neg_op, az, ay); // // f CppAD::ADFun f(auvw, ay); // // size_vector, sparsity_pattern typedef CPPAD_TESTVECTOR(size_t) size_vector; typedef CppAD::sparse_rc sparsity_pattern; // ----------------------------------------------------------------------- // Hessian sparsity // ----------------------------------------------------------------------- for(size_t direction = 0; direction < 2; ++direction) { sparsity_pattern pattern_out; // // select_range CPPAD_TESTVECTOR(bool) select_range(m); for(size_t i = 0; i < m; ++i) select_range[i] = true; // if( direction == 0 ) { // Forward // // select_domain CPPAD_TESTVECTOR(bool) select_domain(n); for(size_t j = 0; j < n; ++j) select_domain[j] = true; // // pattern_out bool internal_bool = false; f.for_hes_sparsity( select_domain, select_range, internal_bool, pattern_out ); } else { // Reverse // // transpose, internal_bool bool transpose = false; bool dependency = false; bool internal_bool = false; // // pattern_in sparsity_pattern pattern_in(n, n, n); for(size_t j = 0; j < n; ++j) pattern_in.set(j, j, j); // // f stores forward Jacobian f.for_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); // // pattern_out f.rev_hes_sparsity( select_range, transpose, internal_bool, pattern_out ); } // // ok ok &= pattern_out.nnz() == 2 * n; ok &= pattern_out.nr() == n; ok &= pattern_out.nc() == n; // // row, col, row_major const size_vector& row = pattern_out.row(); const size_vector& col = pattern_out.col(); size_vector row_major = pattern_out.row_major(); // // ok size_t ell = 0; for(size_t i = 0; i < m; ++i) { // first non-zero in row i size_t k = row_major[ell++]; ok &= row[k] == i; ok &= col[k] == m + i; // second non-zero in row i k = row_major[ell++]; ok &= row[k] == i; ok &= col[k] == 2 * m + i; } for(size_t i = m; i < 2 * m; ++i) { // first non-zero in row i size_t k = row_major[ell++]; ok &= row[k] == i; ok &= col[k] == i - m; // second non-zero in row i k = row_major[ell++]; ok &= row[k] == i; ok &= col[k] == i + m; } for(size_t i = 2 * m; i < 3 * m; ++i) { // first non-zero in row i size_t k = row_major[ell++]; ok &= row[k] == i; ok &= col[k] == i - 2 * m; // second non-zero in row i k = row_major[ell++]; ok &= row[k] == i; ok &= col[k] == i - m; } } // return ok; } // END C++ ================================================ FILE: example/atomic_four/vector/jac_sparsity.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_vector_jac_sparsity.cpp} Atomic Vector Sparsity Patterns Example ####################################### f(u, v) ******* For this example, :math:`f : \B{R}^{3m} \rightarrow \B{R}^m` is defined by :math:`f(u, v, w) = - u * v * w`. where *u* , *v* , and *w* are in :math:`\B{R}^m`. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_vector_jac_sparsity.cpp} */ // BEGIN C++ # include # include bool jac_sparsity(void) { bool ok = true; using CppAD::NearEqual; using CppAD::AD; // // vec_op // atomic vector_op object CppAD::atomic_vector vec_op("atomic_vector"); // // m // size of u, v, and w size_t m = 6; // // n size_t n = 3 * m; // // mul_op, neg_op typedef CppAD::atomic_vector::op_enum_t op_enum_t; op_enum_t mul_op = CppAD::atomic_vector::mul_enum; op_enum_t neg_op = CppAD::atomic_vector::neg_enum; // ----------------------------------------------------------------------- // Record f(u, v, w) = - u * v * w // ----------------------------------------------------------------------- // Independent variable vector CPPAD_TESTVECTOR( CppAD::AD ) auvw(n); for(size_t j = 0; j < n; ++j) auvw[j] = AD(1 + j); CppAD::Independent(auvw); // // au, av, aw CPPAD_TESTVECTOR( CppAD::AD ) au(m), av(m), aw(m); for(size_t i = 0; i < m; ++i) { au[i] = auvw[0 * m + i]; av[i] = auvw[1 * m + i]; aw[i] = auvw[2 * m + i]; } // // ax = (au, av) CPPAD_TESTVECTOR( CppAD::AD ) ax(2 * m); for(size_t i = 0; i < m; ++i) { ax[i] = au[i]; ax[m + i] = av[i]; } // // ay = u * v CPPAD_TESTVECTOR( CppAD::AD ) ay(m); vec_op(mul_op, ax, ay); // // ax = (ay, aw) for(size_t i = 0; i < m; ++i) { ax[i] = ay[i]; ax[m + i] = aw[i]; } // // az = ay * w CPPAD_TESTVECTOR( CppAD::AD ) az(m); vec_op(mul_op, ax, az); // // ay = - az vec_op(neg_op, az, ay); // // f CppAD::ADFun f(auvw, ay); // // size_vector, sparsity_pattern typedef CPPAD_TESTVECTOR(size_t) size_vector; typedef CppAD::sparse_rc sparsity_pattern; // ----------------------------------------------------------------------- // Jacobian sparsity // ----------------------------------------------------------------------- for(size_t direction = 0; direction < 2; ++direction) { sparsity_pattern pattern_out; bool transpose = false; bool dependency = false; bool internal_bool = false; if( direction == 0 ) { // Forward direction // // pattern_in // sparsity pattern for identity matrix sparsity_pattern pattern_in(n, n, n); for(size_t k = 0; k < n; ++k) pattern_in.set(k, k, k); // // pattern_out f.for_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); } else { // Reverse direction // // pattern_in // sparsity pattern for identity matrix sparsity_pattern pattern_in(m, m, m); for(size_t k = 0; k < m; ++k) pattern_in.set(k, k, k); // // pattern_out f.rev_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); } // // ok ok &= pattern_out.nnz() == 3 * m; ok &= pattern_out.nr() == m; ok &= pattern_out.nc() == n; // // row, col, row_major const size_vector& row = pattern_out.row(); const size_vector& col = pattern_out.col(); size_vector row_major = pattern_out.row_major(); // // ok size_t ell = 0; for(size_t i = 0; i < m; ++i) { // first non-zero in row i size_t k = row_major[ell++]; ok &= row[k] == i; ok &= col[k] == i; // second non-zero in row i k = row_major[ell++]; ok &= row[k] == i; ok &= col[k] == m + i; // third non-zero in row i k = row_major[ell++]; ok &= row[k] == i; ok &= col[k] == 2 * m + i; } } // return ok; } // END C++ ================================================ FILE: example/atomic_four/vector/mul.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_vector_mul.cpp} Atomic Vector Multiplication Example #################################### f(u, v, w) ********** For this example, :math:`f : \B{R}^{3m} \rightarrow \B{R}^m` is defined by :math:`f(u, v, w) = u * v * w`. where *u* , *v* , and *w* are in :math:`\B{R}^m`. g(u, v, w) ********** For this example :math:`g : \B{R}^{3m} \rightarrow \B{R}^m` is defined by :math:`g_i (u, v, w) = \partial_{u[i]} f_i (u, v, w)` Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_vector_mul.cpp} */ // BEGIN C++ # include # include bool mul(void) { bool ok = true; using CppAD::NearEqual; using CppAD::AD; double eps99 = 99.0 * CppAD::numeric_limits::epsilon(); // // vec_op // atomic vector_op object CppAD::atomic_vector vec_op("atomic_vector"); // // m // size of u, v, and w size_t m = 5; // // mul_op typedef CppAD::atomic_vector::op_enum_t op_enum_t; op_enum_t mul_op = CppAD::atomic_vector::mul_enum; // ----------------------------------------------------------------------- // Record f(u, v, w) = u * v * w // ----------------------------------------------------------------------- // Independent variable vector CPPAD_TESTVECTOR( CppAD::AD ) auvw(3 * m); for(size_t j = 0; j < 3 * m; ++j) auvw[j] = AD(1 + j); CppAD::Independent(auvw); // // au, av, aw CPPAD_TESTVECTOR( CppAD::AD ) au(m), av(m), aw(m); for(size_t i = 0; i < m; ++i) { au[i] = auvw[0 * m + i]; av[i] = auvw[1 * m + i]; aw[i] = auvw[2 * m + i]; } // // ax = (mul_op, au, av) CPPAD_TESTVECTOR( CppAD::AD ) ax(2 * m); for(size_t i = 0; i < m; ++i) { ax[i] = au[i]; ax[m + i] = av[i]; } // // ay = u * v CPPAD_TESTVECTOR( CppAD::AD ) ay(m); vec_op(mul_op, ax, ay); // // ax = (ay, aw) for(size_t i = 0; i < m; ++i) { ax[i] = ay[i]; ax[m + i] = aw[i]; } // // az = ay * aw CPPAD_TESTVECTOR( CppAD::AD ) az(m); vec_op(mul_op, ax, az); // // f CppAD::ADFun f(auvw, az); // ----------------------------------------------------------------------- // check forward mode on f // ----------------------------------------------------------------------- // // uvw, duvw CPPAD_TESTVECTOR(double) uvw(3 * m), duvw(3 * m); for(size_t j = 0; j < 3 * m; ++j) { uvw[j] = double(1 + j); duvw[j] = 1.0; } // // z, dz CPPAD_TESTVECTOR(double) z(m), dz(m); z = f.Forward(0, uvw); dz = f.Forward(1, duvw); // // ok for(size_t i = 0; i < m; ++i) { double ui = uvw[0 * m + i]; double vi = uvw[1 * m + i]; double wi = uvw[2 * m + i]; // double check_z = ui * vi * wi; ok &= NearEqual( z[i] , check_z, eps99, eps99); // double check_dz = (vi * wi) + (ui * wi) + (ui * vi); ok &= NearEqual( dz[i] , check_dz, eps99, eps99); } // ----------------------------------------------------------------------- // check reverse mode on f // ----------------------------------------------------------------------- // // weight CPPAD_TESTVECTOR(double) weight(m); for(size_t i = 0; i < m; ++i) weight[i] = 1.0; // // dweight CPPAD_TESTVECTOR(double) dweight(3 * m); f.Forward(0, uvw); dweight = f.Reverse(1, weight); // // ok for(size_t i = 0; i < m; ++i) { double ui = uvw[0 * m + i]; double vi = uvw[1 * m + i]; double wi = uvw[2 * m + i]; // double dfi_dui = vi * wi; ok &= NearEqual(dweight[0 * m + i], dfi_dui, eps99, eps99); double dfi_dvi = ui * wi; ok &= NearEqual(dweight[1 * m + i], dfi_dvi, eps99, eps99); double dfi_dwi = ui * vi; ok &= NearEqual(dweight[2 * m + i], dfi_dwi, eps99, eps99); } // ----------------------------------------------------------------------- // Record g_i (u, v, w) = \partial d/dv[i] f_i (u , v , w) // ----------------------------------------------------------------------- // // af CppAD::ADFun< AD, double > af = f.base2ad(); // // auvw CppAD::Independent(auvw); // // aduvw CPPAD_TESTVECTOR( AD ) aduvw(3 * m); for(size_t i = 0; i < m; ++i) { aduvw[0 * m + i] = 1.0; // du[i] aduvw[1 * m + i] = 0.0; // dv[i] aduvw[2 * m + i] = 0.0; // dw[i] } // // az // use the fact that d_u[i] f_k (u, v, w) is zero when i != k af.Forward(0, auvw); az = af.Forward(1, aduvw); CppAD::ADFun g(auvw, az); // ----------------------------------------------------------------------- // Record h (u, v, w) = sum f_i^(1) (u , v , w) // ----------------------------------------------------------------------- // // auvw CppAD::Independent(auvw); // // aweight CPPAD_TESTVECTOR( AD ) aweight(m); for(size_t i = 0; i < m; ++i) aweight[i] = 1.0; // // az CPPAD_TESTVECTOR( AD ) adweight(3 * m); af.Forward(0, auvw); az = af.Reverse(1, aweight); CppAD::ADFun h(auvw, az); // ----------------------------------------------------------------------- // check forward mode on g // ----------------------------------------------------------------------- // // z z = g.Forward(0, uvw); // // ok for(size_t i = 0; i < m; ++i) { double vi = uvw[1 * m + i]; double wi = uvw[2 * m + i]; double check_z = vi * wi; ok &= NearEqual( z[i] , check_z, eps99, eps99); } return ok; // ----------------------------------------------------------------------- // check forward mode on h // ----------------------------------------------------------------------- // // z z = h.Forward(0, uvw); // // ok for(size_t i = 0; i < m; ++i) { double ui = uvw[0 * m + i]; double vi = uvw[1 * m + i]; double wi = uvw[2 * m + i]; // double dfi_dui = vi * wi; ok &= NearEqual(z[0 * m + i] , dfi_dui, eps99, eps99); // double dfi_dvi = ui * wi; ok &= NearEqual(z[1 * m + i] , dfi_dvi, eps99, eps99); // double dfi_dwi = ui * vi; ok &= NearEqual(z[2 * m + i] , dfi_dwi, eps99, eps99); } return ok; } // END C++ ================================================ FILE: example/atomic_four/vector/neg.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_vector_neg.cpp} Atomic Vector Negation Example ############################## f(u, w) ******* For this example, :math:`f : \B{R}^{2m} \rightarrow \B{R}^m` is defined by :math:`f(u, w) = u - w`. where *u* and *w* are in :math:`\B{R}^m`. g(u, w) ******* For this example :math:`g : \B{R}^{2m} \rightarrow \B{R}^m` is defined by :math:`g_i (u, w) = \partial_{w[i]} f_i (u, w)` Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_vector_neg.cpp} */ // BEGIN C++ # include # include bool neg(void) { bool ok = true; using CppAD::NearEqual; using CppAD::AD; double eps99 = 99.0 * CppAD::numeric_limits::epsilon(); // // vec_op // atomic vector_op object CppAD::atomic_vector vec_op("atomic_vector"); // // m // size of u, v size_t m = 5; // // add_op, neg_op typedef CppAD::atomic_vector::op_enum_t op_enum_t; op_enum_t add_op = CppAD::atomic_vector::add_enum; op_enum_t neg_op = CppAD::atomic_vector::neg_enum; // ----------------------------------------------------------------------- // Record f(u, v) = u - v // ----------------------------------------------------------------------- // Independent variable vector CPPAD_TESTVECTOR( CppAD::AD ) auv(2 * m); for(size_t j = 0; j < 2 * m; ++j) auv[j] = AD(1 + j); CppAD::Independent(auv); // // au, av CPPAD_TESTVECTOR( CppAD::AD ) au(m), av(m); for(size_t i = 0; i < m; ++i) { au[i] = auv[0 * m + i]; av[i] = auv[1 * m + i]; } // // ax = av CPPAD_TESTVECTOR( CppAD::AD ) ax(m); for(size_t i = 0; i < m; ++i) ax[i] = av[i]; // // ay = - av CPPAD_TESTVECTOR( CppAD::AD ) ay(m); vec_op(neg_op, ax, ay); // // ax = (au, ay) ax.resize(2 * m); for(size_t i = 0; i < m; ++i) { ax[i] = au[i]; ax[m + i] = ay[i]; } // // az = au + ay CPPAD_TESTVECTOR( CppAD::AD ) az(m); vec_op(add_op, ax, az); // // f CppAD::ADFun f(auv, az); // ----------------------------------------------------------------------- // check forward mode on f // ----------------------------------------------------------------------- // // uv, duv CPPAD_TESTVECTOR(double) uv(2 * m), duv(2 * m); for(size_t j = 0; j < 2 * m; ++j) { uv[j] = double(1 + j); duv[j] = double(j); } // // z, dz CPPAD_TESTVECTOR(double) z(m), dz(m); z = f.Forward(0, uv); dz = f.Forward(1, duv); // // ok for(size_t i = 0; i < m; ++i) { double check_z = uv[0 * m + i] - uv[1 * m + i]; ok &= NearEqual( z[i] , check_z, eps99, eps99); double check_dz = double(0 * m + i) - double(1 * m + i); ok &= NearEqual( dz[i] , check_dz, eps99, eps99); } // ----------------------------------------------------------------------- // Record g_i (u, v) = \partial d/dv[i] f_i (u , v) // ----------------------------------------------------------------------- // // af CppAD::ADFun< AD, double > af = f.base2ad(); // // auv CppAD::Independent(auv); // // aduv CPPAD_TESTVECTOR( AD ) aduv(2 * m); for(size_t i = 0; i < m; ++i) { aduv[0 * m + i] = 0.0; // du[i] aduv[1 * m + i] = 1.0; // dv[i] } // // az // use the fact that d_v[i] f_k (u, v) is zero when i != k af.Forward(0, auv); az = af.Forward(1, aduv); CppAD::ADFun g(auv, az); // ----------------------------------------------------------------------- // check forward mode on g // ----------------------------------------------------------------------- // // z z = g.Forward(0, uv); // // ok for(size_t i = 0; i < m; ++i) { double check_z = - 1.0; ok &= NearEqual( z[i] , check_z, eps99, eps99); } return ok; } // END C++ ================================================ FILE: example/atomic_four/vector/rev_depend.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_vector_rev_depend.cpp} Example Optimizing Atomic Vector Usage ###################################### f(u, v) ******* For this example, :math:`f : \B{R}^{3m} \rightarrow \B{R}` is defined by :math:`f(u, v, w) = - ( u_0 + v_0 ) * w_0`. where *u* , *v* , and *w* are in :math:`\B{R}^m`. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_vector_rev_depend.cpp} */ // BEGIN C++ # include # include bool rev_depend(void) { bool ok = true; using CppAD::NearEqual; using CppAD::AD; // // vec_op // atomic vector_op object CppAD::atomic_vector vec_op("atomic_vector"); // // m // size of u, v, and w size_t m = 6; // // n size_t n = 3 * m; // // add_op, mul_op, neg_op typedef CppAD::atomic_vector::op_enum_t op_enum_t; op_enum_t add_op = CppAD::atomic_vector::add_enum; op_enum_t mul_op = CppAD::atomic_vector::mul_enum; op_enum_t neg_op = CppAD::atomic_vector::neg_enum; // ----------------------------------------------------------------------- // Record f(u, v, w) = - (u + v) * w // ----------------------------------------------------------------------- // Independent variable vector CPPAD_TESTVECTOR( CppAD::AD ) a_ind(n); for(size_t j = 0; j < n; ++j) a_ind[j] = AD(1 + j); CppAD::Independent(a_ind); // // au, av, aw CPPAD_TESTVECTOR( CppAD::AD ) au(m), av(m), aw(m); for(size_t i = 0; i < m; ++i) { au[i] = a_ind[0 * m + i]; av[i] = a_ind[1 * m + i]; aw[i] = a_ind[2 * m + i]; } // // ax = (au, av) CPPAD_TESTVECTOR( CppAD::AD ) ax(2 * m); for(size_t i = 0; i < m; ++i) { ax[i] = au[i]; ax[m + i] = av[i]; } // // ay = u + v CPPAD_TESTVECTOR( CppAD::AD ) ay(m); vec_op(add_op, ax, ay); // // ax = (ay, aw) for(size_t i = 0; i < m; ++i) { ax[i] = ay[i]; ax[m + i] = aw[i]; } // // az = ay * w CPPAD_TESTVECTOR( CppAD::AD ) az(m); vec_op(mul_op, ax, az); // // ay = - az vec_op(neg_op, az, ay); // // f CPPAD_TESTVECTOR( CppAD::AD ) a_dep(1); a_dep[0] = ay[0]; CppAD::ADFun f(a_ind, a_dep); // // size_var // phantom variable, independent variables, operator results ok &= f.size_var() == 1 + n + 3 * m; // // optimize // The atomic function rev_depend routine is called by optimizer f.optimize("val_graph no_conditional_skip"); // // size_var // phantom variablem, independent variables, operator variables ok &= f.size_var() == 1 + n + 3; // // return ok; } // END C++ ================================================ FILE: example/atomic_four/vector/sub.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_vector_sub.cpp} Atomic Vector Subtraction Example ################################# f(u, v, w) ********** For this example, :math:`f : \B{R}^{3m} \rightarrow \B{R}^m` is defined by :math:`f(u, v, w) = u - (v + w)`. where *u* , *v* , and *w* are in :math:`\B{R}^m`. g(u, v, w) ********** For this example :math:`g : \B{R}^{3m} \rightarrow \B{R}^m` is defined by :math:`g_i (u, v, w) = \partial_{v[i]} f_i (u, v, w)` Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_vector_sub.cpp} */ // BEGIN C++ # include # include bool sub(void) { bool ok = true; using CppAD::NearEqual; using CppAD::AD; double eps99 = 99.0 * CppAD::numeric_limits::epsilon(); // // vec_op // atomic vector_op object CppAD::atomic_vector vec_op("atomic_vector"); // // m // size of u, v, and w size_t m = 5; // // add_op, sub_op typedef CppAD::atomic_vector::op_enum_t op_enum_t; op_enum_t add_op = CppAD::atomic_vector::add_enum; op_enum_t sub_op = CppAD::atomic_vector::sub_enum; // ----------------------------------------------------------------------- // Record f(u, v, w) = u - (v + w) // ----------------------------------------------------------------------- // Independent variable vector CPPAD_TESTVECTOR( CppAD::AD ) auvw(3 * m); for(size_t j = 0; j < 3 * m; ++j) auvw[j] = AD(1 + j); CppAD::Independent(auvw); // // au, av, aw CPPAD_TESTVECTOR( CppAD::AD ) au(m), av(m), aw(m); for(size_t i = 0; i < m; ++i) { au[i] = auvw[0 * m + i]; av[i] = auvw[1 * m + i]; aw[i] = auvw[2 * m + i]; } // // ax = (av, aw) CPPAD_TESTVECTOR( CppAD::AD ) ax(2 * m); for(size_t i = 0; i < m; ++i) { ax[i] = av[i]; ax[m + i] = aw[i]; } // // ay = v + w CPPAD_TESTVECTOR( CppAD::AD ) ay(m); vec_op(add_op, ax, ay); // // ax = (sub_op, au, ay) for(size_t i = 0; i < m; ++i) { ax[i] = au[i]; ax[m + i] = ay[i]; } // // az = au - ay CPPAD_TESTVECTOR( CppAD::AD ) az(m); vec_op(sub_op, ax, az); // // f CppAD::ADFun f(auvw, az); // ----------------------------------------------------------------------- // check forward mode on f // ----------------------------------------------------------------------- // // uvw, duvw CPPAD_TESTVECTOR(double) uvw(3 * m), duvw(3 * m); for(size_t j = 0; j < 3 * m; ++j) { uvw[j] = double(1 + j); duvw[j] = double(j); } // // z, dz CPPAD_TESTVECTOR(double) z(m), dz(m); z = f.Forward(0, uvw); dz = f.Forward(1, duvw); // // ok for(size_t i = 0; i < m; ++i) { double check_z = uvw[0 * m + i] - ( uvw[1 * m + i] + uvw[2 * m + i] ); ok &= NearEqual( z[i] , check_z, eps99, eps99); double check_dz = double(0 * m + i) - double(1 * m + i + 2 * m + i); ok &= NearEqual( dz[i] , check_dz, eps99, eps99); } // ----------------------------------------------------------------------- // Record g_i (u, v, w) = \partial d/dv[i] f_i (u , v , w) // ----------------------------------------------------------------------- // // af CppAD::ADFun< AD, double > af = f.base2ad(); // // auvw CppAD::Independent(auvw); // // aduvw CPPAD_TESTVECTOR( AD ) aduvw(3 * m); for(size_t i = 0; i < m; ++i) { aduvw[0 * m + i] = 0.0; // du[i] aduvw[1 * m + i] = 1.0; // dv[i] aduvw[2 * m + i] = 0.0; // dw[i] } // // az // use the fact that d_v[i] f_k (u, v, w) is zero when i != k af.Forward(0, auvw); az = af.Forward(1, aduvw); CppAD::ADFun g(auvw, az); // ----------------------------------------------------------------------- // Record h (u, v, w) = sum f_i^(1) (u , v , w) // ----------------------------------------------------------------------- // // auvw CppAD::Independent(auvw); // // aweight CPPAD_TESTVECTOR( AD ) aweight(m); for(size_t i = 0; i < m; ++i) aweight[i] = 1.0; // // az CPPAD_TESTVECTOR( AD ) adweight(3 * m); af.Forward(0, auvw); az = af.Reverse(1, aweight); CppAD::ADFun h(auvw, az); // ----------------------------------------------------------------------- // check forward mode on g // ----------------------------------------------------------------------- // // z z = g.Forward(0, uvw); // // ok for(size_t i = 0; i < m; ++i) { double check_z = - 1.0; ok &= NearEqual( z[i] , check_z, eps99, eps99); } return ok; // ----------------------------------------------------------------------- // check forward mode on h // ----------------------------------------------------------------------- // // z z = h.Forward(0, uvw); // // ok for(size_t i = 0; i < m; ++i) { double check_z = 1.0; ok &= NearEqual( z[0 * m + i] , check_z, eps99, eps99); check_z = - 1.0; ok &= NearEqual( z[1 * m + i] , check_z, eps99, eps99); ok &= NearEqual( z[2 * m + i] , check_z, eps99, eps99); } return ok; } // END C++ ================================================ FILE: example/atomic_four/vector/vector.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // CPPAD_HAS_* defines # include // system include files used for I/O # include // C style asserts # include // for thread_alloc # include // test runner # include // BEGIN_SORT_THIS_LINE_PLUS_1 extern bool add(void); extern bool div(void); extern bool hes_sparsity(void); extern bool jac_sparsity(void); extern bool mul(void); extern bool neg(void); extern bool rev_depend(void); extern bool sub(void); // END_SORT_THIS_LINE_MINUS_1 // main program that runs all the tests int main(void) { std::string group = "example/atomic_four/vector"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // BEGIN_SORT_THIS_LINE_PLUS_1 Run( add, "add" ); Run( div, "div" ); Run( hes_sparsity, "hes_sparsity" ); Run( jac_sparsity, "jac_sparsity" ); Run( mul, "mul" ); Run( neg, "neg" ); Run( rev_depend, "rev_depend" ); Run( sub, "sub" ); // END_SORT_THIS_LINE_MINUS_1 // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } ================================================ FILE: example/atomic_three/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list atomic_three.cpp base2ad.cpp dynamic.cpp forward.cpp get_started.cpp hes_sparsity.cpp jac_sparsity.cpp mat_mul.cpp norm_sq.cpp reciprocal.cpp rev_depend.cpp reverse.cpp tangent.cpp ) # END_SORT_THIS_LINE_MINUS_2 set_compile_flags(example_atomic_three "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(example_atomic_three EXCLUDE_FROM_ALL ${source_list}) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_atomic_three ${cppad_lib} ${colpack_libs} ) # # check_example_atomic_three add_check_executable(check_example atomic_three) ================================================ FILE: example/atomic_three/atomic_three.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three.cpp} atomic_three Examples and Tests Driver ###################################### Running These Tests ******************* After executing the :ref:`cmake-name` command form the :ref:`download@Distribution Directory`, you can build and run these tests with the commands:: cd build make check_example_atomic_three Note that your choice of :ref:`cmake@generator` may require using an different version of make; e.g., ``ninja`` . {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_three.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ // CPPAD_HAS_* defines # include // for thread_alloc # include // test runner # include // BEGIN_SORT_THIS_LINE_PLUS_1 extern bool base2ad(void); extern bool dynamic(void); extern bool forward(void); extern bool get_started(void); extern bool hes_sparsity(void); extern bool jac_sparsity(void); extern bool mat_mul(void); extern bool norm_sq(void); extern bool reciprocal(void); extern bool rev_depend(void); extern bool reverse(void); extern bool tangent(void); // END_SORT_THIS_LINE_MINUS_1 // main program that runs all the tests int main(void) { std::string group = "example/atomic"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // BEGIN_SORT_THIS_LINE_PLUS_1 Run( base2ad, "base2ad" ); Run( dynamic, "dynamic" ); Run( forward, "forward" ); Run( get_started, "get_started" ); Run( hes_sparsity, "hes_sparsity" ); Run( jac_sparsity, "jac_sparsity" ); Run( mat_mul, "mat_mul" ); Run( norm_sq, "norm_sq" ); Run( reciprocal, "reciprocal" ); Run( rev_depend, "rev_depend" ); Run( reverse, "reverse" ); Run( tangent, "tangent" ); // END_SORT_THIS_LINE_MINUS_1 // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } // END C++ ================================================ FILE: example/atomic_three/atomic_three.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin atomic_three_example} Example Defining Atomic Functions: Third Generation ################################################### Contents ******** {xrst_toc_table example/atomic_three/get_started.cpp example/atomic_three/norm_sq.cpp example/atomic_three/tangent.cpp example/atomic_three/base2ad.cpp example/atomic_three/reciprocal.cpp example/atomic_three/mat_mul.cpp } {xrst_end atomic_three_example} ================================================ FILE: example/atomic_three/base2ad.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_base2ad.cpp} base2ad with Atomic Operations: Example and Test ################################################ Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_three_base2ad.cpp} */ // BEGIN C++ # include namespace { // isolate items below to this file // // abbreviations using CppAD::AD; using CppAD::vector; // class atomic_base2ad : public CppAD::atomic_three { // public: atomic_base2ad(const std::string& name) : CppAD::atomic_three(name) { } private: // ------------------------------------------------------------------------ // type bool for_type( const vector& parameter_x , const vector& type_x , vector& type_y ) override { assert( parameter_x.size() == type_x.size() ); bool ok = type_x.size() == 1; // n ok &= type_y.size() == 1; // m if( ! ok ) return false; type_y[0] = type_x[0]; return true; } // ---------------------------------------------------------------------- // forward mode // ---------------------------------------------------------------------- template bool template_forward( size_t p , size_t q , const vector& tx , vector& ty ) { # ifndef NDEBUG size_t n = tx.size() / (q + 1); size_t m = ty.size() / (q + 1); # endif assert( n == 1 ); assert( m == 1 ); // return flag bool ok = q == 0; if( ! ok ) return ok; // Order zero forward mode. // This case must always be implemented // y^0 = g( x^0 ) = 1 / x^0 Scalar g = 1. / tx[0]; if( p <= 0 ) ty[0] = g; return ok; } // forward mode routines called by ADFun objects bool forward( const vector& parameter_x , const vector& type_x , size_t need_y , size_t p , size_t q , const vector& tx , vector& ty ) override { return template_forward(p, q, tx, ty); } // forward mode routines called by ADFun< AD , Base> objects bool forward( const vector< AD >& aparameter_x , const vector& type_x , size_t need_y , size_t p , size_t q , const vector< AD >& atx , vector< AD >& aty ) override { return template_forward(p, q, atx, aty); } // ---------------------------------------------------------------------- // reverse mode // ---------------------------------------------------------------------- template bool template_reverse( size_t q , const vector& tx , const vector& ty , vector& px , const vector& py ) { # ifndef NDEBUG size_t n = tx.size() / (q + 1); size_t m = ty.size() / (q + 1); # endif assert( n == 1 ); assert( m == 1 ); // return flag bool ok = q == 0; if( ! ok ) return ok; // Order zero reverse mode. // y^0 = g( x^0 ) = 1 / x^0 // y^1 = g'( x^0 ) * x^1 = - x^1 / (x^0 * x^0) px[0] = - py[0] / ( tx[0] * tx[0] ); return ok; } // reverse mode routines called by ADFun objects bool reverse( const vector& parameter_x , const vector& type_x , size_t q , const vector& tx , const vector& ty , vector& px , const vector& py ) override { return template_reverse(q, tx, ty, px, py); } // reverse mode routines called by ADFun objects bool reverse( const vector< AD >& aparameter_x , const vector& type_x , size_t q , const vector< AD >& atx , const vector< AD >& aty , vector< AD >& apx , const vector< AD >& apy ) override { return template_reverse(q, atx, aty, apx, apy); } }; // End of atomic_base2ad class } // End empty namespace bool base2ad(void) { bool ok = true; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); // // Create the atomic base2ad object atomic_base2ad afun("atomic_base2ad"); // // Create the function f(x) = 1 / g(x) = x // size_t n = 1; double x0 = 0.5; vector< AD > ax(n); ax[0] = x0; // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 1; vector< AD > av(m); // call atomic function and store g(x) in au[0] vector< AD > au(m); afun(ax, au); // u = 1 / x // now use AD division to invert to invert the operation av[0] = 1.0 / au[0]; // v = 1 / u = x // create f: x -> y and stop tape recording CppAD::ADFun f; f.Dependent (ax, av); // g(x) = x // check function value double check = x0; ok &= NearEqual( Value(av[0]) , check, eps, eps); // check zero order forward mode size_t q; vector x_q(n), v_q(m); q = 0; x_q[0] = x0; v_q = f.Forward(q, x_q); ok &= NearEqual(v_q[0] , check, eps, eps); // check first order reverse vector dw(n), w(m); w[0] = 1.0; dw = f.Reverse(q+1, w); check = 1.0; ok &= NearEqual(dw[0] , check, eps, eps); // create ag : x -> y CppAD::ADFun< AD , double > af ( f.base2ad() ); // check zero order forward mode vector< AD > ax_q(n), av_q(m); q = 0; ax_q[0] = x0; av_q = af.Forward(q, ax_q); check = x0; ok &= NearEqual( Value(av_q[0]) , check, eps, eps); // check first order reverse vector< AD > adw(n), aw(m); aw[0] = 1.0; adw = af.Reverse(q+1, aw); check = 1.0; ok &= NearEqual( Value(adw[0]) , check, eps, eps); return ok; } // END C++ ================================================ FILE: example/atomic_three/dynamic.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_dynamic.cpp} Atomic Functions with Dynamic Parameters: Example and Test ########################################################## Purpose ******* This example demonstrates using dynamic parameters with an :ref:`atomic_three-name` function. Function ******** For this example, the atomic function :math:`g : \B{R}^3 \rightarrow \B{R}^3` is defined by :math:`g_0 (x) = x_0 * x_ 0`, :math:`g_1 (x) = x_0 * x_ 1`, :math:`g_2 (x) = x_1 * x_ 2`. Start Class Definition ********************** {xrst_spell_off} {xrst_code cpp} */ # include // CppAD include file namespace { // start empty namespace using CppAD::vector; // abbreviate CppAD::vector using vector // start definition of atomic derived class using atomic_three interface class atomic_dynamic : public CppAD::atomic_three { /* {xrst_code} {xrst_spell_on} Constructor *********** {xrst_spell_off} {xrst_code cpp} */ public: // can use const char* name when calling this constructor atomic_dynamic(const std::string& name) : // can have more arguments CppAD::atomic_three(name) // inform base class of name { } private: /* {xrst_code} {xrst_spell_on} for_type ******** {xrst_spell_off} {xrst_code cpp} */ // calculate type_y bool for_type( const vector& parameter_x , const vector& type_x , vector& type_y ) override { assert( parameter_x.size() == type_x.size() ); bool ok = type_x.size() == 3; // n ok &= type_y.size() == 3; // m if( ! ok ) return false; type_y[0] = type_x[0]; type_y[1] = std::max( type_x[0], type_x[1] ); type_y[2] = std::max( type_x[1], type_x[2] ); return true; } /* {xrst_code} {xrst_spell_on} forward ******* {xrst_spell_off} {xrst_code cpp} */ // forward mode routine called by CppAD bool forward( const vector& parameter_x , const vector& type_x , size_t need_y , size_t order_low , size_t order_up , const vector& taylor_x , vector& taylor_y ) override { # ifndef NDEBUG size_t n = taylor_x.size() / (order_up + 1); size_t m = taylor_y.size() / (order_up + 1); # endif assert( n == 3 ); assert( m == 3 ); assert( order_low <= order_up ); // return flag bool ok = order_up == 0; if( ! ok ) return ok; // Order zero forward mode. // This case must always be implemented if( need_y > size_t(CppAD::variable_enum) ) { // g_0 = x_0 * x_0 taylor_y[0] = taylor_x[0] * taylor_x[0]; // g_1 = x_0 * x_1 taylor_y[1] = taylor_x[0] * taylor_x[1]; // g_2 = x_1 * x_2 taylor_y[2] = taylor_x[1] * taylor_x[2]; } else { // This uses need_y to reduce amount of computation. // It is probably faster, for this case, to ignore need_y. vector type_y( taylor_y.size() ); for_type(taylor_x, type_x, type_y); // g_0 = x_0 * x_0 if( size_t(type_y[0]) == need_y ) taylor_y[0] = taylor_x[0] * taylor_x[0]; // g_1 = x_0 * x_1 if( size_t(type_y[1]) == need_y ) taylor_y[1] = taylor_x[0] * taylor_x[1]; // g_2 = x_1 * x_2 if( size_t(type_y[2]) == need_y ) taylor_y[2] = taylor_x[1] * taylor_x[2]; } return ok; } /* {xrst_code} {xrst_spell_on} End Class Definition ******************** {xrst_spell_off} {xrst_code cpp} */ }; // End of atomic_dynamic class } // End empty namespace /* {xrst_code} {xrst_spell_on} Use Atomic Function ******************* {xrst_spell_off} {xrst_code cpp} */ bool dynamic(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); /* {xrst_code} {xrst_spell_on} Constructor =========== {xrst_spell_off} {xrst_code cpp} */ // Create the atomic dynamic object corresponding to g(x) atomic_dynamic afun("atomic_dynamic"); /* {xrst_code} {xrst_spell_on} Recording ========= {xrst_spell_off} {xrst_code cpp} */ // Create the function f(u) = g(c, p, u) for this example. // // constant parameter double c_0 = 2.0; // // independent dynamic parameter vector size_t np = 1; CPPAD_TESTVECTOR(double) p(np); CPPAD_TESTVECTOR( AD ) ap(np); ap[0] = p[0] = 3.0; // // independent variable vector size_t nu = 1; double u_0 = 0.5; CPPAD_TESTVECTOR( AD ) au(nu); au[0] = u_0; // declare independent variables and start tape recording CppAD::Independent(au, ap); // range space vector size_t ny = 3; CPPAD_TESTVECTOR( AD ) ay(ny); // call atomic function and store result in ay // y = ( c * c, c * p, p * x ) CPPAD_TESTVECTOR( AD ) ax(3); ax[0] = c_0; // x_0 ax[1] = ap[0]; // x_1 ax[2] = au[0]; // x_2 afun(ax, ay); // check type of result ok &= Constant( ay[0] ); ok &= Dynamic( ay[1] ); ok &= Variable( ay[2] ); // create f: x -> y and stop tape recording CppAD::ADFun f; f.Dependent (au, ay); // f(u) = (c * c, c * p, p * u) /* {xrst_code} {xrst_spell_on} forward ======= {xrst_spell_off} {xrst_code cpp} */ // check function value double check = c_0 * c_0; ok &= NearEqual( Value(ay[0]) , check, eps, eps); check = c_0 * p[0]; ok &= NearEqual( Value(ay[1]) , check, eps, eps); check = p[0] * u_0; ok &= NearEqual( Value(ay[2]) , check, eps, eps); // check zero order forward mode size_t q; CPPAD_TESTVECTOR( double ) u_q(nu), y_q(ny); q = 0; u_q[0] = u_0; y_q = f.Forward(q, u_q); check = c_0 * c_0; ok &= NearEqual(y_q[0] , check, eps, eps); check = c_0 * p[0]; ok &= NearEqual(y_q[1] , check, eps, eps); check = p[0] * u_0; ok &= NearEqual(y_q[2] , check, eps, eps); // set new value for dynamic parameters p[0] = 2.0 * p[0]; f.new_dynamic(p); y_q = f.Forward(q, u_q); check = c_0 * c_0; ok &= NearEqual(y_q[0] , check, eps, eps); check = c_0 * p[0]; ok &= NearEqual(y_q[1] , check, eps, eps); check = p[0] * u_0; ok &= NearEqual(y_q[2] , check, eps, eps); /* {xrst_code} {xrst_spell_on} Return Test Result ================== {xrst_spell_off} {xrst_code cpp} */ return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end atomic_three_dynamic.cpp} */ ================================================ FILE: example/atomic_three/forward.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_forward.cpp} Atomic Functions and Forward Mode: Example and Test ################################################### Purpose ******* This example demonstrates forward mode derivative calculation using an :ref:`atomic_three-name` function. Function ******** For this example, the atomic function :math:`g : \B{R}^3 \rightarrow \B{R}^2` is defined by .. math:: g(x) = \left( \begin{array}{c} x_2 * x_2 \\ x_0 * x_1 \end{array} \right) Jacobian ******** The corresponding Jacobian is .. math:: g^{(1)} (x) = \left( \begin{array}{ccc} 0 & 0 & 2 x_2 \\ x_1 & x_0 & 0 \end{array} \right) Hessian ******* The Hessians of the component functions are .. math:: g_0^{(2)} ( x ) = \left( \begin{array}{ccc} 0 & 0 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & 2 \end{array} \right) \W{,} g_1^{(2)} ( x ) = \left( \begin{array}{ccc} 0 & 1 & 0 \\ 1 & 0 & 0 \\ 0 & 0 & 0 \end{array} \right) Start Class Definition ********************** {xrst_spell_off} {xrst_code cpp} */ # include namespace { // begin empty namespace using CppAD::vector; // abbreviate CppAD::vector using vector // class atomic_forward : public CppAD::atomic_three { /* {xrst_code} {xrst_spell_on} Constructor *********** {xrst_spell_off} {xrst_code cpp} */ public: atomic_forward(const std::string& name) : CppAD::atomic_three(name) { } private: /* {xrst_code} {xrst_spell_on} for_type ******** {xrst_spell_off} {xrst_code cpp} */ // calculate type_y bool for_type( const vector& parameter_x , const vector& type_x , vector& type_y ) override { assert( parameter_x.size() == type_x.size() ); bool ok = type_x.size() == 3; // n ok &= type_y.size() == 2; // m if( ! ok ) return false; type_y[0] = type_x[2]; type_y[1] = std::max(type_x[0], type_x[1]); return true; } /* {xrst_code} {xrst_spell_on} forward ******* {xrst_spell_off} {xrst_code cpp} */ // forward mode routine called by CppAD bool forward( const vector& parameter_x , const vector& type_x , size_t need_y , size_t order_low , size_t order_up , const vector& taylor_x , vector& taylor_y ) override { size_t q1 = order_up + 1; # ifndef NDEBUG size_t n = taylor_x.size() / q1; size_t m = taylor_y.size() / q1; # endif assert( n == 3 ); assert( m == 2 ); assert( order_low <= order_up ); // this example only implements up to second order forward mode bool ok = order_up <= 2; if( ! ok ) return ok; // ------------------------------------------------------------------ // Zero forward mode. // This case must always be implemented // g(x) = [ x_2 * x_2 ] // [ x_0 * x_1 ] // y^0 = f( x^0 ) if( order_low <= 0 ) { // y_0^0 = x_2^0 * x_2^0 taylor_y[0*q1+0] = taylor_x[2*q1+0] * taylor_x[2*q1+0]; // y_1^0 = x_0^0 * x_1^0 taylor_y[1*q1+0] = taylor_x[0*q1+0] * taylor_x[1*q1+0]; } if( order_up <= 0 ) return ok; // ------------------------------------------------------------------ // First order forward mode. // This case is needed if first order forward mode is used. // g'(x) = [ 0, 0, 2 * x_2 ] // [ x_1, x_0, 0 ] // y^1 = f'(x^0) * x^1 if( order_low <= 1 ) { // y_0^1 = 2 * x_2^0 * x_2^1 taylor_y[0*q1+1] = 2.0 * taylor_x[2*q1+0] * taylor_x[2*q1+1]; // y_1^1 = x_1^0 * x_0^1 + x_0^0 * x_1^1 taylor_y[1*q1+1] = taylor_x[1*q1+0] * taylor_x[0*q1+1]; taylor_y[1*q1+1] += taylor_x[0*q1+0] * taylor_x[1*q1+1]; } if( order_up <= 1 ) return ok; // ------------------------------------------------------------------ // Second order forward mode. // This case is needed if second order forward mode is used. // g'(x) = [ 0, 0, 2 x_2 ] // [ x_1, x_0, 0 ] // // [ 0 , 0 , 0 ] [ 0 , 1 , 0 ] // g_0''(x) = [ 0 , 0 , 0 ] g_1^{(2)} (x) = [ 1 , 0 , 0 ] // [ 0 , 0 , 2 ] [ 0 , 0 , 0 ] // // y_0^2 = x^1 * g_0''( x^0 ) x^1 / 2! + g_0'( x^0 ) x^2 // = ( x_2^1 * 2.0 * x_2^1 ) / 2! // + 2.0 * x_2^0 * x_2^2 taylor_y[0*q1+2] = taylor_x[2*q1+1] * taylor_x[2*q1+1]; taylor_y[0*q1+2] += 2.0 * taylor_x[2*q1+0] * taylor_x[2*q1+2]; // // y_1^2 = x^1 * g_1''( x^0 ) x^1 / 2! + g_1'( x^0 ) x^2 // = ( x_1^1 * x_0^1 + x_0^1 * x_1^1) / 2 // + x_1^0 * x_0^2 + x_0^0 + x_1^2 taylor_y[1*q1+2] = taylor_x[1*q1+1] * taylor_x[0*q1+1]; taylor_y[1*q1+2] += taylor_x[1*q1+0] * taylor_x[0*q1+2]; taylor_y[1*q1+2] += taylor_x[0*q1+0] * taylor_x[1*q1+2]; // ------------------------------------------------------------------ return ok; } }; } // End empty namespace /* {xrst_code} {xrst_spell_on} Use Atomic Function ******************* {xrst_spell_off} {xrst_code cpp} */ bool forward(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); // // Create the atomic_forward object corresponding to g(x) atomic_forward afun("atomic_forward"); // // Create the function f(u) = g(u) for this example. // // domain space vector size_t n = 3; double u_0 = 1.00; double u_1 = 2.00; double u_2 = 3.00; vector< AD > au(n); au[0] = u_0; au[1] = u_1; au[2] = u_2; // declare independent variables and start tape recording CppAD::Independent(au); // range space vector size_t m = 2; vector< AD > ay(m); // call atomic function vector< AD > ax = au; afun(ax, ay); // create f: u -> y and stop tape recording CppAD::ADFun f; f.Dependent (au, ay); // y = f(u) // // check function value double check = u_2 * u_2; ok &= NearEqual( Value(ay[0]) , check, eps, eps); check = u_0 * u_1; ok &= NearEqual( Value(ay[1]) , check, eps, eps); // -------------------------------------------------------------------- // zero order forward // vector u0(n), y0(m); u0[0] = u_0; u0[1] = u_1; u0[2] = u_2; y0 = f.Forward(0, u0); check = u_2 * u_2; ok &= NearEqual(y0[0] , check, eps, eps); check = u_0 * u_1; ok &= NearEqual(y0[1] , check, eps, eps); // -------------------------------------------------------------------- // first order forward // // value of Jacobian of f double check_jac[] = { 0.0, 0.0, 2.0 * u_2, u_1, u_0, 0.0 }; vector u1(n), y1(m); // check first order forward mode for(size_t j = 0; j < n; j++) u1[j] = 0.0; for(size_t j = 0; j < n; j++) { // compute partial in j-th component direction u1[j] = 1.0; y1 = f.Forward(1, u1); u1[j] = 0.0; // check this direction for(size_t i = 0; i < m; i++) ok &= NearEqual(y1[i], check_jac[i * n + j], eps, eps); } // -------------------------------------------------------------------- // second order forward // // value of Hessian of g_0 double check_hes_0[] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0 }; // // value of Hessian of g_1 double check_hes_1[] = { 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; vector u2(n), y2(m); for(size_t j = 0; j < n; j++) u2[j] = 0.0; // compute diagonal elements of the Hessian for(size_t j = 0; j < n; j++) { // first order forward in j-th direction u1[j] = 1.0; f.Forward(1, u1); y2 = f.Forward(2, u2); // check this element of Hessian diagonal ok &= NearEqual(y2[0], check_hes_0[j * n + j] / 2.0, eps, eps); ok &= NearEqual(y2[1], check_hes_1[j * n + j] / 2.0, eps, eps); // for(size_t k = 0; k < n; k++) if( k != j ) { u1[k] = 1.0; f.Forward(1, u1); y2 = f.Forward(2, u2); // // y2 = (H_jj + H_kk + H_jk + H_kj) / 2.0 // y2 = (H_jj + H_kk) / 2.0 + H_jk // double H_jj = check_hes_0[j * n + j]; double H_kk = check_hes_0[k * n + k]; double H_jk = y2[0] - (H_kk + H_jj) / 2.0; ok &= NearEqual(H_jk, check_hes_0[j * n + k], eps, eps); // H_jj = check_hes_1[j * n + j]; H_kk = check_hes_1[k * n + k]; H_jk = y2[1] - (H_kk + H_jj) / 2.0; ok &= NearEqual(H_jk, check_hes_1[j * n + k], eps, eps); // u1[k] = 0.0; } u1[j] = 0.0; } // -------------------------------------------------------------------- return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end atomic_three_forward.cpp} */ ================================================ FILE: example/atomic_three/get_started.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_get_started.cpp} Getting Started with Atomic Functions: Example and Test ####################################################### Purpose ******* This example demonstrates the minimal amount of information necessary for a :ref:`atomic_three-name` function. Start Class Definition ********************** {xrst_spell_off} {xrst_code cpp} */ # include // CppAD include file namespace { // start empty namespace using CppAD::vector; // abbreviate CppAD::vector using vector // start definition of atomic derived class using atomic_three interface class atomic_get_started : public CppAD::atomic_three { /* {xrst_code} {xrst_spell_on} Constructor *********** {xrst_spell_off} {xrst_code cpp} */ public: // can use const char* name when calling this constructor atomic_get_started(const std::string& name) : // can have more arguments CppAD::atomic_three(name) // inform base class of name { } private: /* {xrst_code} {xrst_spell_on} for_type ******** {xrst_spell_off} {xrst_code cpp} */ // calculate type_y bool for_type( const vector& parameter_x , const vector& type_x , vector& type_y ) override { assert( parameter_x.size() == type_x.size() ); bool ok = type_x.size() == 1; // n ok &= type_y.size() == 1; // m if( ! ok ) return false; type_y[0] = type_x[0]; return true; } /* {xrst_code} {xrst_spell_on} forward ******* {xrst_spell_off} {xrst_code cpp} */ // forward mode routine called by CppAD bool forward( const vector& parameter_x , const vector& type_x , size_t need_y , size_t order_low , size_t order_up , const vector& taylor_x , vector& taylor_y ) override { # ifndef NDEBUG size_t n = taylor_x.size() / (order_up + 1); size_t m = taylor_y.size() / (order_up + 1); # endif assert( n == 1 ); assert( m == 1 ); assert( order_low <= order_up ); // return flag bool ok = order_up == 0; if( ! ok ) return ok; // Order zero forward mode. // This case must always be implemented // y^0 = g( x^0 ) = 1 / x^0 taylor_y[0] = 1. / taylor_x[0]; // return ok; } /* {xrst_code} {xrst_spell_on} End Class Definition ******************** {xrst_spell_off} {xrst_code cpp} */ }; // End of atomic_get_started class } // End empty namespace /* {xrst_code} {xrst_spell_on} Use Atomic Function ******************* {xrst_spell_off} {xrst_code cpp} */ bool get_started(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); /* {xrst_code} {xrst_spell_on} Constructor =========== {xrst_spell_off} {xrst_code cpp} */ // Create the atomic get_started object corresponding to g(x) atomic_get_started afun("atomic_get_started"); /* {xrst_code} {xrst_spell_on} Recording ========= {xrst_spell_off} {xrst_code cpp} */ // Create the function f(x) which is equal to g(x) for this example. // // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR( AD ) ax(n); ax[0] = x0; // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR( AD ) ay(m); // call atomic function and store result in au[0] // u = 1 / x CPPAD_TESTVECTOR( AD ) au(m); afun(ax, au); // now use AD division to invert to invert the operation ay[0] = 1.0 / au[0]; // y = 1 / u = x // create f: x -> y and stop tape recording CppAD::ADFun f; f.Dependent (ax, ay); // f(x) = x /* {xrst_code} {xrst_spell_on} forward ======= {xrst_spell_off} {xrst_code cpp} */ // check function value double check = x0; ok &= NearEqual( Value(ay[0]) , check, eps, eps); // check zero order forward mode size_t q; CPPAD_TESTVECTOR( double ) x_q(n), y_q(m); q = 0; x_q[0] = x0; y_q = f.Forward(q, x_q); ok &= NearEqual(y_q[0] , check, eps, eps); /* {xrst_code} {xrst_spell_on} Return Test Result ================== {xrst_spell_off} {xrst_code cpp} */ return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end atomic_three_get_started.cpp} */ ================================================ FILE: example/atomic_three/hes_sparsity.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_hes_sparsity.cpp} Atomic Forward Hessian Sparsity: Example and Test ################################################# Purpose ******* This example demonstrates calculation of the Hessian sparsity pattern for an atomic operation. Function ******** For this example, the atomic function :math:`g : \B{R}^3 \rightarrow \B{R}^2` is defined by .. math:: g( x ) = \left( \begin{array}{c} x_2 * x_2 \\ x_0 * x_1 \end{array} \right) Jacobian ******** The corresponding Jacobian is .. math:: g^{(1)} (x) = \left( \begin{array}{ccc} 0 & 0 & 2 x_2 \\ x_1 & x_0 & 0 \end{array} \right) Hessians ******** The Hessians of the component functions are .. math:: g_0^{(2)} ( x ) = \left( \begin{array}{ccc} 0 & 0 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & 2 \end{array} \right) \W{,} g_1^{(2)} ( x ) = \left( \begin{array}{ccc} 0 & 1 & 0 \\ 1 & 0 & 0 \\ 0 & 0 & 0 \end{array} \right) Start Class Definition ********************** {xrst_spell_off} {xrst_code cpp} */ # include namespace { // begin empty namespace using CppAD::vector; // abbreviate CppAD::vector as vector // class atomic_hes_sparsity : public CppAD::atomic_three { /* {xrst_code} {xrst_spell_on} Constructor *********** {xrst_spell_off} {xrst_code cpp} */ public: atomic_hes_sparsity(const std::string& name) : CppAD::atomic_three(name) { } private: /* {xrst_code} {xrst_spell_on} for_type ******** {xrst_spell_off} {xrst_code cpp} */ // calculate type_y bool for_type( const vector& parameter_x , const vector& type_x , vector& type_y ) override { assert( parameter_x.size() == type_x.size() ); bool ok = type_x.size() == 3; // n ok &= type_y.size() == 2; // m if( ! ok ) return false; type_y[0] = type_x[2]; type_y[1] = std::max(type_x[0], type_x[1]); return true; } /* {xrst_code} {xrst_spell_on} forward ******* {xrst_spell_off} {xrst_code cpp} */ // forward mode routine called by CppAD bool forward( const vector& parameter_x , const vector& type_x , size_t need_y , size_t order_low , size_t order_up , const vector& taylor_x , vector& taylor_y ) override { # ifndef NDEBUG size_t n = taylor_x.size() / (order_up + 1); size_t m = taylor_y.size() / (order_up + 1); # endif assert( n == 3 ); assert( m == 2 ); assert( order_low <= order_up ); // return flag bool ok = order_up == 0; if( ! ok ) return ok; // Order zero forward mode must always be implemented taylor_y[0] = taylor_x[2] * taylor_x[2]; taylor_y[1] = taylor_x[0] * taylor_x[1]; return ok; } /* {xrst_code} {xrst_spell_on} jac_sparsity ************ {xrst_spell_off} {xrst_code cpp} */ // Jacobian sparsity routine called by CppAD bool jac_sparsity( const vector& parameter_x , const vector& type_x , bool dependency , const vector& select_x , const vector& select_y , CppAD::sparse_rc< vector >& pattern_out ) override { size_t n = select_x.size(); size_t m = select_y.size(); assert( n == 3 ); assert( m == 2 ); assert( parameter_x.size() == n ); // count number of non-zeros in sparsity pattern size_t nnz = 0; // row 0 if( select_y[0] && select_x[2] ) ++nnz; // row 1 if( select_y[1] ) { // column 0 if( select_x[0] ) ++nnz; // column 1 if( select_x[1] ) ++nnz; } // size of pattern_out size_t nr = m; size_t nc = n; pattern_out.resize(nr, nc, nnz); // // set the values in pattern_out using index k size_t k = 0; // // y_0 depends and has possibly non-zeron partial w.r.t x_2 if( select_y[0] && select_x[2] ) pattern_out.set(k++, 0, 2); if( select_y[1] ) { // y_1 depends and has possibly non-zero partial w.r.t x_0 if( select_x[0] ) pattern_out.set(k++, 1, 0); // y_1 depends and has possibly non-zero partial w.r.t x_1 if( select_x[1] ) pattern_out.set(k++, 1, 1); } assert( k == nnz ); // return true; } /* {xrst_code} {xrst_spell_on} hes_sparsity ************ {xrst_spell_off} {xrst_code cpp} */ // Hessian sparsity routine called by CppAD bool hes_sparsity( const vector& parameter_x , const vector& type_x , const vector& select_x , const vector& select_y , CppAD::sparse_rc< vector >& pattern_out ) override { assert( parameter_x.size() == select_x.size() ); assert( select_y.size() == 2 ); size_t n = select_x.size(); assert( n == 3 ); // // [ 0 , 0 , 0 ] [ 0 , 1 , 0 ] // g_0''(x) = [ 0 , 0 , 0 ] g_1^'' (x) = [ 1 , 0 , 0 ] // [ 0 , 0 , 2 ] [ 0 , 0 , 0 ] // // // count number of non-zeros in sparsity pattern size_t nnz = 0; if( select_y[0] ) { if( select_x[2] ) ++nnz; } if( select_y[1] ) { if( select_x[0] && select_x[1] ) nnz += 2; } // // size of pattern_out size_t nr = n; size_t nc = n; pattern_out.resize(nr, nc, nnz); // // set the values in pattern_out using index k size_t k = 0; // // y[1] has possible non-zero second partial w.r.t. x[0], x[1] if( select_y[1] ) { if( select_x[0] && select_x[1] ) { pattern_out.set(k++, 0, 1); pattern_out.set(k++, 1, 0); } } // // y[0] has possibly non-zero second partial w.r.t x[2], x[2] if( select_y[0] ) { if( select_x[2] ) pattern_out.set(k++, 2, 2); } return true; } }; // End of atomic_for_sparse_hes class /* {xrst_code} {xrst_spell_on} Use Atomic Function ******************* {xrst_spell_off} {xrst_code cpp} */ bool use_hes_sparsity(bool u_1_variable, bool forward) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); // // Create the atomic_hes_sparsity object corresponding to g(x) atomic_hes_sparsity afun("atomic_hes_sparsity"); // // Create the function f(u) = g(u) for this example. // // domain space vector size_t n = 3; double u_0 = 1.00; double u_1 = 2.00; double u_2 = 3.00; vector< AD > au(n); au[0] = u_0; au[1] = u_1; au[2] = u_2; // declare independent variables and start tape recording CppAD::Independent(au); // range space vector size_t m = 2; vector< AD > ay(m); // call atomic function vector< AD > ax(n); ax[0] = au[0]; ax[2] = au[2]; if( u_1_variable ) { ok &= Variable( au[1] ); ax[1] = au[1]; } else { AD ap = u_1; ok &= Parameter(ap); ok &= ap == au[1]; ax[1] = u_1; } // u_1_variable true: y = [ u_2 * u_2 , u_0 * u_1 ]^T // u_1_variable false: y = [ u_2 * u_2 , u_0 * p ]^T afun(ax, ay); // create f: u -> y and stop tape recording CppAD::ADFun f; f.Dependent (au, ay); // f(u) = y // // check function value double check = u_2 * u_2; ok &= NearEqual( Value(ay[0]) , check, eps, eps); check = u_0 * u_1; ok &= NearEqual( Value(ay[1]) , check, eps, eps); // check zero order forward mode size_t q; vector xq(n), yq(m); q = 0; xq[0] = u_0; xq[1] = u_1; xq[2] = u_2; yq = f.Forward(q, xq); check = u_2 * u_2; ok &= NearEqual(yq[0] , check, eps, eps); check = u_0 * u_1; ok &= NearEqual(yq[1] , check, eps, eps); // select_u CPPAD_TESTVECTOR(bool) select_u(n); for(size_t j = 0; j < n; j++) select_u[j] = true; // select_y CPPAD_TESTVECTOR(bool) select_y(m); for(size_t i = 0; i < m; i++) select_y[i] = true; // for_hes_sparsity bool internal_bool = false; CppAD::sparse_rc< CPPAD_TESTVECTOR(size_t) > pattern_out; if( forward ) { f.for_hes_sparsity( select_u, select_y, internal_bool, pattern_out ); } else { // pattern for indepentity matrix CppAD::sparse_rc< CPPAD_TESTVECTOR(size_t) > pattern_in(n, n, n); bool transpose = false; bool dependency = false; for(size_t k = 0; k < n; ++k) pattern_in.set(k, k, k); // for_jac_sparsity (ignore pattern_out) f.for_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); // rev_jac_sparsity f.rev_hes_sparsity( select_y, transpose, internal_bool, pattern_out ); } const CPPAD_TESTVECTOR(size_t)& row = pattern_out.row(); const CPPAD_TESTVECTOR(size_t)& col = pattern_out.col(); CPPAD_TESTVECTOR(size_t) row_major = pattern_out.row_major(); // // in row major order first element has index (0, 1) and second has // index (1, 0). These are only included when u_1 is a variable. size_t k = 0, r, c; if( u_1_variable ) { r = row[ row_major[k] ]; c = col[ row_major[k] ]; ok &= r == 0 && c == 1; ++k; r = row[ row_major[k] ]; c = col[ row_major[k] ]; ok &= r == 1 && c == 0; ++k; } // in row major order next element, in lower triangle of Hessians, // has index (2, 2). This element is always included r = row[ row_major[k] ]; c = col[ row_major[k] ]; ok &= r == 2 && c == 2; // // k + 1 should be the number of values in sparsity pattern ok &= k + 1 == pattern_out.nnz(); // return ok; } } // End empty namespace /* {xrst_code} {xrst_spell_on} Test with u_1 Both a Variable and a Parameter ********************************************* {xrst_spell_off} {xrst_code cpp} */ bool hes_sparsity(void) { bool ok = true; // bool u_1_variable = true; bool forward = true; ok &= use_hes_sparsity(u_1_variable, forward); // u_1_variable = true; forward = false; ok &= use_hes_sparsity(u_1_variable, forward); // u_1_variable = false; forward = true; ok &= use_hes_sparsity(u_1_variable, forward); // u_1_variable = false; forward = false; ok &= use_hes_sparsity(u_1_variable, forward); // return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end atomic_three_hes_sparsity.cpp} */ ================================================ FILE: example/atomic_three/jac_sparsity.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_jac_sparsity.cpp} Atomic Function Jacobian Sparsity: Example and Test ################################################### Purpose ******* This example demonstrates calculation of a Jacobian sparsity pattern using an atomic operation. Function ******** For this example, the atomic function :math:`g : \B{R}^3 \rightarrow \B{R}^2` is defined by .. math:: g(x) = \left( \begin{array}{c} x_2 * x_2 \\ x_0 * x_1 \end{array} \right) Jacobian ******** The corresponding Jacobian is .. math:: g^{(1)} (x) = \left( \begin{array}{ccc} 0 & 0 & 2 x_2 \\ x_1 & x_0 & 0 \end{array} \right) Start Class Definition ********************** {xrst_spell_off} {xrst_code cpp} */ # include namespace { // begin empty namespace using CppAD::vector; // abbreviate CppAD::vector as vector // class atomic_jac_sparsity : public CppAD::atomic_three { /* {xrst_code} {xrst_spell_on} Constructor *********** {xrst_spell_off} {xrst_code cpp} */ public: atomic_jac_sparsity(const std::string& name) : CppAD::atomic_three(name) { } private: /* {xrst_code} {xrst_spell_on} for_type ******** {xrst_spell_off} {xrst_code cpp} */ // calculate type_y bool for_type( const vector& parameter_x , const vector& type_x , vector& type_y ) override { assert( parameter_x.size() == type_x.size() ); bool ok = type_x.size() == 3; // n ok &= type_y.size() == 2; // m if( ! ok ) return false; type_y[0] = type_x[2]; type_y[1] = std::max(type_x[0], type_x[1]); return true; } /* {xrst_code} {xrst_spell_on} forward ******* {xrst_spell_off} {xrst_code cpp} */ // forward mode routine called by CppAD bool forward( const vector& parameter_x , const vector& type_x , size_t need_y , size_t order_low , size_t order_up , const vector& taylor_x , vector& taylor_y ) override { # ifndef NDEBUG size_t n = taylor_x.size() / (order_up + 1); size_t m = taylor_y.size() / (order_up + 1); # endif assert( n == 3 ); assert( m == 2 ); assert( order_low <= order_up ); // return flag bool ok = order_up == 0; if( ! ok ) return ok; // Order zero forward mode must always be implemented taylor_y[0] = taylor_x[2] * taylor_x[2]; taylor_y[1] = taylor_x[0] * taylor_x[1]; return ok; } /* {xrst_code} {xrst_spell_on} jac_sparsity ************ {xrst_spell_off} {xrst_code cpp} */ // Jacobian sparsity routine called by CppAD bool jac_sparsity( const vector& parameter_x , const vector& type_x , bool dependency , const vector& select_x , const vector& select_y , CppAD::sparse_rc< vector >& pattern_out ) override { size_t n = select_x.size(); size_t m = select_y.size(); assert( parameter_x.size() == n ); assert( n == 3 ); assert( m == 2 ); // count number of non-zeros in sparsity pattern size_t nnz = 0; // row 0 if( select_y[0] && select_x[2] ) ++nnz; // row 1 if( select_y[1] ) { // column 0 if( select_x[0] ) ++nnz; // column 1 if( select_x[1] ) ++nnz; } // size of pattern_out size_t nr = m; size_t nc = n; pattern_out.resize(nr, nc, nnz); // set the values in pattern_out using index k size_t k = 0; // y_0 depends and has possibly non-zeron partial w.r.t x_2 if( select_y[0] && select_x[2] ) pattern_out.set(k++, 0, 2); if( select_y[1] ) { // y_1 depends and has possibly non-zero partial w.r.t x_0 if( select_x[0] ) pattern_out.set(k++, 1, 0); // y_1 depends and has possibly non-zero partial w.r.t x_1 if( select_x[1] ) pattern_out.set(k++, 1, 1); } assert( k == nnz ); // return true; } }; // End of atomic_three_jac_sparsity class /* {xrst_code} {xrst_spell_on} Use Atomic Function ******************* {xrst_spell_off} {xrst_code cpp} */ bool use_jac_sparsity(bool x_1_variable, bool forward) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); // // Create the atomic_jac_sparsity object corresponding to g(x) atomic_jac_sparsity afun("atomic_jac_sparsity"); // // Create the function f(u) = g(u) for this example. // // domain space vector size_t n = 3; double u_0 = 1.00; double u_1 = 2.00; double u_2 = 3.00; vector< AD > au(n); au[0] = u_0; au[1] = u_1; au[2] = u_2; // declare independent variables and start tape recording CppAD::Independent(au); // range space vector size_t m = 2; vector< AD > ay(m); // call atomic function vector< AD > ax(n); ax[0] = au[0]; ax[2] = au[2]; if( x_1_variable ) { ok &= Variable( au[1] ); ax[1] = au[1]; } else { AD ap = u_1; ok &= Parameter(ap); ok &= ap == au[1]; ax[1] = u_1; } // x_1_variable true: y = [ u_2 * u_2 , u_0 * u_1 ]^T // x_1_variable false: y = [ u_2 * u_2 , u_0 * p ]^T afun(ax, ay); // create f: u -> y and stop tape recording CppAD::ADFun f; f.Dependent (au, ay); // f(u) = y // // check function value double check = u_2 * u_2; ok &= NearEqual( Value(ay[0]) , check, eps, eps); check = u_0 * u_1; ok &= NearEqual( Value(ay[1]) , check, eps, eps); // check zero order forward mode size_t q; vector xq(n), yq(m); q = 0; xq[0] = u_0; xq[1] = u_1; xq[2] = u_2; yq = f.Forward(q, xq); check = u_2 * u_2; ok &= NearEqual(yq[0] , check, eps, eps); check = u_0 * u_1; ok &= NearEqual(yq[1] , check, eps, eps); // sparsity pattern for identity matrix size_t nnz; if( forward ) nnz = n; else nnz = m; CppAD::sparse_rc< CPPAD_TESTVECTOR(size_t) > pattern_in(nnz, nnz, nnz); for(size_t k = 0; k < nnz; ++k) pattern_in.set(k, k, k); // Jacobian sparsity for f(u) bool transpose = false; bool dependency = false; bool internal_bool = false; CppAD::sparse_rc< CPPAD_TESTVECTOR(size_t) > pattern_out; if( forward ) { f.for_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); } else { f.rev_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); } const CPPAD_TESTVECTOR(size_t)& row = pattern_out.row(); const CPPAD_TESTVECTOR(size_t)& col = pattern_out.col(); CPPAD_TESTVECTOR(size_t) row_major = pattern_out.row_major(); // // first element in row major order has index (0, 2) size_t k = 0; size_t r = row[ row_major[k] ]; size_t c = col[ row_major[k] ]; ok &= r == 0 && c == 2; // // second element in row major order has index (1, 0) ++k; r = row[ row_major[k] ]; c = col[ row_major[k] ]; ok &= r == 1 && c == 0; // if( x_1_variable ) { // third element in row major order has index (1, 1) ++k; r = row[ row_major[k] ]; c = col[ row_major[k] ]; ok &= r == 1 && c == 1; } // k + 1 should be the number of values in sparsity pattern ok &= k + 1 == pattern_out.nnz(); // return ok; } } // End empty namespace /* {xrst_code} {xrst_spell_on} Test with u_1 Both a Variable and a Parameter ********************************************* {xrst_spell_off} {xrst_code cpp} */ bool jac_sparsity(void) { bool ok = true; // bool u_1_variable = true; bool forward = true; ok &= use_jac_sparsity(u_1_variable, forward); // u_1_variable = true; forward = false; ok &= use_jac_sparsity(u_1_variable, forward); // u_1_variable = false; forward = true; ok &= use_jac_sparsity(u_1_variable, forward); // u_1_variable = false; forward = false; ok &= use_jac_sparsity(u_1_variable, forward); // return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end atomic_three_jac_sparsity.cpp} */ ================================================ FILE: example/atomic_three/mat_mul.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_mat_mul.cpp} User Atomic Matrix Multiply: Example and Test ############################################# See Also ******** :ref:`atomic_two_eigen_mat_mul.cpp-name` {xrst_toc_hidden include/cppad/example/atomic_three/mat_mul.hpp } Class Definition **************** This example uses the file :ref:`atomic_three_mat_mul.hpp-name` which defines matrix multiply as a :ref:`atomic_three-name` operation. Use Atomic Function ******************* {xrst_spell_off} {xrst_code cpp} */ # include # include bool mat_mul(void) { bool ok = true; using CppAD::AD; using CppAD::vector; size_t i, j; /* {xrst_code} {xrst_spell_on} Constructor =========== {xrst_spell_off} {xrst_code cpp} */ // ------------------------------------------------------------------- // object that multiplies 2 x 2 matrices atomic_mat_mul afun; /* {xrst_code} {xrst_spell_on} Recording ========= {xrst_spell_off} {xrst_code cpp} */ // start recording with four independent variables size_t n = 4; vector x(n); vector< AD > ax(n); for(j = 0; j < n; j++) ax[j] = x[j] = double(j + 1); CppAD::Independent(ax); // ------------------------------------------------------------------ size_t nr_left = 2; size_t n_middle = 2; size_t nc_right = 2; vector< AD > atom_x(3 + (nr_left + nc_right) * n_middle ); // matrix dimensions atom_x[0] = AD( nr_left ); atom_x[1] = AD( n_middle ); atom_x[2] = AD( nc_right ); // left matrix atom_x[3] = ax[0]; // left[0, 0] = x0 atom_x[4] = ax[1]; // left[0, 1] = x1 atom_x[5] = 5.; // left[1, 0] = 5 atom_x[6] = 6.; // left[1, 1] = 6 // right matrix atom_x[7] = ax[2]; // right[0, 0] = x2 atom_x[8] = 7.; // right[0, 1] = 7 atom_x[9] = ax[3]; // right[1, 0] = x3 atom_x[10] = 8.; // right[1, 1] = 8 // ------------------------------------------------------------------ /* [ x0 , x1 ] * [ x2 , 7 ] = [ x0*x2 + x1*x3 , x0*7 + x1*8 ] [ 5 , 6 ] [ x3 , 8 ] [ 5*x2 + 6*x3 , 5*7 + 6*8 ] */ vector< AD > atom_y(nr_left * nc_right); afun(atom_x, atom_y); ok &= (atom_y[0] == x[0]*x[2] + x[1]*x[3]) && Variable(atom_y[0]); ok &= (atom_y[1] == x[0]*7. + x[1]*8. ) && Variable(atom_y[1]); ok &= (atom_y[2] == 5.*x[2] + 6.*x[3]) && Variable(atom_y[2]); ok &= (atom_y[3] == 5.*7. + 6.*8. ) && Parameter(atom_y[3]); // ------------------------------------------------------------------ // define the function g : x -> atom_y // g(x) = [ x0*x2 + x1*x3 , x0*7 + x1*8 , 5*x2 + 6*x3 , 5*7 + 6*8 ]^T CppAD::ADFun g(ax, atom_y); /* {xrst_code} {xrst_spell_on} forward ======= {xrst_spell_off} {xrst_code cpp} */ // Test zero order forward mode evaluation of g(x) size_t m = atom_y.size(); vector y(m); for(j = 0; j < n; j++) x[j] = double(j + 2); y = g.Forward(0, x); ok &= y[0] == x[0] * x[2] + x[1] * x[3]; ok &= y[1] == x[0] * 7. + x[1] * 8.; ok &= y[2] == 5. * x[2] + 6. * x[3]; ok &= y[3] == 5. * 7. + 6. * 8.; //---------------------------------------------------------------------- // Test first order forward mode evaluation of g'(x) * [1, 2, 3, 4]^T // g'(x) = [ x2, x3, x0, x1 ] // [ 7 , 8, 0, 0 ] // [ 0 , 0, 5, 6 ] // [ 0 , 0, 0, 0 ] CppAD::vector dx(n), dy(m); for(j = 0; j < n; j++) dx[j] = double(j + 1); dy = g.Forward(1, dx); ok &= dy[0] == 1. * x[2] + 2. * x[3] + 3. * x[0] + 4. * x[1]; ok &= dy[1] == 1. * 7. + 2. * 8. + 3. * 0. + 4. * 0.; ok &= dy[2] == 1. * 0. + 2. * 0. + 3. * 5. + 4. * 6.; ok &= dy[3] == 1. * 0. + 2. * 0. + 3. * 0. + 4. * 0.; //---------------------------------------------------------------------- // Test second order forward mode // g_0^2 (x) = [ 0, 0, 1, 0 ], g_0^2 (x) * [1] = [3] // [ 0, 0, 0, 1 ] [2] [4] // [ 1, 0, 0, 0 ] [3] [1] // [ 0, 1, 0, 0 ] [4] [2] CppAD::vector ddx(n), ddy(m); for(j = 0; j < n; j++) ddx[j] = 0.; ddy = g.Forward(2, ddx); // [1, 2, 3, 4] * g_0^2 (x) * [1, 2, 3, 4]^T = 1*3 + 2*4 + 3*1 + 4*2 ok &= 2. * ddy[0] == 1. * 3. + 2. * 4. + 3. * 1. + 4. * 2.; // for i > 0, [1, 2, 3, 4] * g_i^2 (x) * [1, 2, 3, 4]^T = 0 ok &= ddy[1] == 0.; ok &= ddy[2] == 0.; ok &= ddy[3] == 0.; /* {xrst_code} {xrst_spell_on} reverse ======= {xrst_spell_off} {xrst_code cpp} */ // Test second order reverse mode CppAD::vector w(m), dw(2 * n); for(i = 0; i < m; i++) w[i] = 0.; w[0] = 1.; dw = g.Reverse(2, w); // g_0'(x) = [ x2, x3, x0, x1 ] ok &= dw[0*2 + 0] == x[2]; ok &= dw[1*2 + 0] == x[3]; ok &= dw[2*2 + 0] == x[0]; ok &= dw[3*2 + 0] == x[1]; // g_0'(x) * [1, 2, 3, 4] = 1 * x2 + 2 * x3 + 3 * x0 + 4 * x1 // g_0^2 (x) * [1, 2, 3, 4] = [3, 4, 1, 2] ok &= dw[0*2 + 1] == 3.; ok &= dw[1*2 + 1] == 4.; ok &= dw[2*2 + 1] == 1.; ok &= dw[3*2 + 1] == 2.; /* {xrst_code} {xrst_spell_on} jac_sparsity ============ {xrst_spell_off} {xrst_code cpp} */ // sparsity pattern for the Jacobian // g'(x) = [ x2, x3, x0, x1 ] // [ 7, 8, 0, 0 ] // [ 0, 0, 5, 6 ] // [ 0, 0, 0, 0 ] CppAD::sparse_rc< CPPAD_TESTVECTOR(size_t) > pattern_in, pattern_out; bool transpose = false; bool dependency = false; bool internal_bool = false; // test both forward and reverse mode for(size_t forward_mode = 0; forward_mode <= 1; ++forward_mode) { if( bool(forward_mode) ) { pattern_in.resize(n, n, n); for(j = 0; j < n; ++j) pattern_in.set(j, j, j); g.for_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); } else { pattern_in.resize(m, m, m); for(i = 0; i < m; ++i) pattern_in.set(i, i, i); g.rev_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); } const CPPAD_TESTVECTOR(size_t)& row = pattern_out.row(); const CPPAD_TESTVECTOR(size_t)& col = pattern_out.col(); CPPAD_TESTVECTOR(size_t) row_major = pattern_out.row_major(); size_t k = 0; for(j = 0; j < n; ++j) { ok &= row[ row_major[k] ] == 0; // (0, j) ok &= col[ row_major[k] ] == j; ++k; } ok &= row[ row_major[k] ] == 1; // (1, 0) ok &= col[ row_major[k] ] == 0; // ++k; ok &= row[ row_major[k] ] == 1; // (1, 1) ok &= col[ row_major[k] ] == 1; // ++k; ok &= row[ row_major[k] ] == 2; // (2, 2) ok &= col[ row_major[k] ] == 2; // ++k; ok &= row[ row_major[k] ] == 2; // (2, 3) ok &= col[ row_major[k] ] == 3; // ++k; ok &= pattern_out.nnz() == k; } /* {xrst_code} {xrst_spell_on} hes_sparsity ============ {xrst_spell_off} {xrst_code cpp} */ /* Hessian sparsity pattern g_0^2 (x) = [ 0, 0, 1, 0 ] and for i > 0, g_i^2 = 0 [ 0, 0, 0, 1 ] [ 1, 0, 0, 0 ] [ 0, 1, 0, 0 ] */ CPPAD_TESTVECTOR(bool) select_x(n), select_y(m); for(j = 0; j < n; ++j) select_x[j] = true; for(i = 0; i < m; ++i) select_y[i] = true; for(size_t forward_mode = 0; forward_mode <= 1; ++forward_mode) { if( bool(forward_mode) ) { g.for_hes_sparsity( select_y, select_x, internal_bool, pattern_out ); } else { // results for for_jac_sparsity are stored in g g.rev_hes_sparsity( select_y, transpose, internal_bool, pattern_out ); } const CPPAD_TESTVECTOR(size_t)& row = pattern_out.row(); const CPPAD_TESTVECTOR(size_t)& col = pattern_out.col(); CPPAD_TESTVECTOR(size_t) row_major = pattern_out.row_major(); size_t k = 0; ok &= row[ row_major[k] ] == 0; // (0, 2) ok &= col[ row_major[k] ] == 2; ++k; ok &= row[ row_major[k] ] == 1; // (1, 3) ok &= col[ row_major[k] ] == 3; ++k; ok &= row[ row_major[k] ] == 2; // (2, 0) ok &= col[ row_major[k] ] == 0; ++k; ok &= row[ row_major[k] ] == 3; // (3, 1) ok &= col[ row_major[k] ] == 1; ++k; ok &= pattern_out.nnz() == k; } return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end atomic_three_mat_mul.cpp} */ ================================================ FILE: example/atomic_three/norm_sq.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_norm_sq.cpp} Atomic Euclidean Norm Squared: Example and Test ############################################### Function ******** This example demonstrates using :ref:`atomic_three-name` to define the operation :math:`g : \B{R}^n \rightarrow \B{R}^m` where :math:`n = 2`, :math:`m = 1`, where .. math:: g(x) = x_0^2 + x_1^2 Start Class Definition ********************** {xrst_spell_off} {xrst_code cpp} */ # include namespace { // isolate items below to this file using CppAD::vector; // abbreviate CppAD::vector as vector // class atomic_norm_sq : public CppAD::atomic_three { /* {xrst_code} {xrst_spell_on} Constructor *********** {xrst_spell_off} {xrst_code cpp} */ public: atomic_norm_sq(const std::string& name) : CppAD::atomic_three(name) { } private: /* {xrst_code} {xrst_spell_on} for_type ******** {xrst_spell_off} {xrst_code cpp} */ // calculate type_y bool for_type( const vector& parameter_x , const vector& type_x , vector& type_y ) override { assert( parameter_x.size() == type_x.size() ); bool ok = type_x.size() == 2; // n ok &= type_y.size() == 1; // m if( ! ok ) return false; type_y[0] = std::max(type_x[0], type_x[1]); return true; } /* {xrst_code} {xrst_spell_on} forward ******* {xrst_spell_off} {xrst_code cpp} */ // forward mode routine called by CppAD bool forward( const vector& parameter_x , const vector& type_x , size_t need_y , size_t p , size_t q , const vector& tx , vector& ty ) override { # ifndef NDEBUG size_t n = tx.size() / (q+1); size_t m = ty.size() / (q+1); # endif assert( type_x.size() == n ); assert( n == 2 ); assert( m == 1 ); assert( p <= q ); // return flag bool ok = q <= 1; // Order zero forward mode must always be implemented. // y^0 = g( x^0 ) double x_00 = tx[ 0*(q+1) + 0]; // x_0^0 double x_10 = tx[ 1*(q+1) + 0]; // x_10 double g = x_00 * x_00 + x_10 * x_10; // g( x^0 ) if( p <= 0 ) ty[0] = g; // y_0^0 if( q <= 0 ) return ok; // Order one forward mode. // This case needed if first order forward mode is used. // y^1 = g'( x^0 ) x^1 double x_01 = tx[ 0*(q+1) + 1]; // x_0^1 double x_11 = tx[ 1*(q+1) + 1]; // x_1^1 double gp_0 = 2.0 * x_00; // partial f w.r.t x_0^0 double gp_1 = 2.0 * x_10; // partial f w.r.t x_1^0 if( p <= 1 ) ty[1] = gp_0 * x_01 + gp_1 * x_11; // g'( x^0 ) * x^1 if( q <= 1 ) return ok; // Assume we are not using forward mode with order > 1 assert( ! ok ); return ok; } /* {xrst_code} {xrst_spell_on} reverse ******* {xrst_spell_off} {xrst_code cpp} */ // reverse mode routine called by CppAD bool reverse( const vector& parameter_x , const vector& type_x , size_t q , const vector& tx , const vector& ty , vector& px , const vector& py ) override { # ifndef NDEBUG size_t n = tx.size() / (q+1); size_t m = ty.size() / (q+1); # endif assert( px.size() == tx.size() ); assert( py.size() == ty.size() ); assert( n == 2 ); assert( m == 1 ); bool ok = q <= 1; double gp_0, gp_1; switch(q) { case 0: // This case needed if first order reverse mode is used // F ( {x} ) = g( x^0 ) = y^0 gp_0 = 2.0 * tx[0]; // partial F w.r.t. x_0^0 gp_1 = 2.0 * tx[1]; // partial F w.r.t. x_0^1 px[0] = py[0] * gp_0;; // partial G w.r.t. x_0^0 px[1] = py[0] * gp_1;; // partial G w.r.t. x_0^1 assert(ok); break; default: // Assume we are not using reverse with order > 1 (q > 0) assert(!ok); } return ok; } /* {xrst_code} {xrst_spell_on} jac_sparsity ************ {xrst_spell_off} {xrst_code cpp} */ // Jacobian sparsity routine called by CppAD bool jac_sparsity( const vector& parameter_x , const vector& type_x , bool dependency , const vector& select_x , const vector& select_y , CppAD::sparse_rc< vector >& pattern_out ) override { size_t n = select_x.size(); size_t m = select_y.size(); assert( n == 2 ); assert( m == 1 ); assert( parameter_x.size() == select_x.size() ); // // count number non-zeros size_t nnz = 0; if( select_y[0] ) { if( select_x[0] ) ++nnz; if( select_x[1] ) ++nnz; } // sparsity pattern pattern_out.resize(m, n, nnz); size_t k = 0; if( select_y[0] ) { if( select_x[0] ) pattern_out.set(k++, 0, 0); if( select_x[1] ) pattern_out.set(k++, 0, 1); } return true; } /* {xrst_code} {xrst_spell_on} hes_sparsity ************ {xrst_spell_off} {xrst_code cpp} */ // Hessian sparsity routine called by CppAD bool hes_sparsity( const vector& parameter_x , const vector& type_x , const vector& select_x , const vector& select_y , CppAD::sparse_rc< vector >& pattern_out ) override { size_t n = select_x.size(); assert( n == 2 ); assert( select_y.size() == 1 ); // m assert( parameter_x.size() == select_x.size() ); // // count number non-zeros size_t nnz = 0; if( select_y[0] ) { if( select_x[0] ) ++nnz; if( select_x[1] ) ++nnz; } // sparsity pattern pattern_out.resize(n, n, nnz); size_t k = 0; if( select_y[0] ) { if( select_x[0] ) pattern_out.set(k++, 0, 0); if( select_x[1] ) pattern_out.set(k++, 1, 1); } return true; } /* {xrst_code} {xrst_spell_on} End Class Definition ******************** {xrst_spell_off} {xrst_code cpp} */ }; // End of atomic_norm_sq class } // End empty namespace /* {xrst_code} {xrst_spell_on} Use Atomic Function ******************* {xrst_spell_off} {xrst_code cpp} */ bool norm_sq(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); /* {xrst_code} {xrst_spell_on} Constructor =========== {xrst_spell_off} {xrst_code cpp} */ // -------------------------------------------------------------------- // Create the atomic reciprocal object atomic_norm_sq afun("atomic_norm_sq"); /* {xrst_code} {xrst_spell_on} Recording ========= {xrst_spell_off} {xrst_code cpp} */ // Create the function f(x) = g(x) // // domain space vector size_t n = 2; double x0 = 0.25; double x1 = 0.75; vector< AD > ax(n); ax[0] = x0; ax[1] = x1; // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 1; vector< AD > ay(m); // call atomic function and store norm_sq(x) in au[0] afun(ax, ay); // y_0 = x_0 * x_0 + x_1 * x_1 // create g: x -> y and stop tape recording CppAD::ADFun f; f.Dependent (ax, ay); /* {xrst_code} {xrst_spell_on} forward ======= {xrst_spell_off} {xrst_code cpp} */ // check function value double check = x0 * x0 + x1 * x1; ok &= NearEqual( Value(ay[0]) , check, eps, eps); // check zero order forward mode size_t q; vector x_q(n), y_q(m); q = 0; x_q[0] = x0; x_q[1] = x1; y_q = f.Forward(q, x_q); ok &= NearEqual(y_q[0] , check, eps, eps); // check first order forward mode q = 1; x_q[0] = 0.3; x_q[1] = 0.7; y_q = f.Forward(q, x_q); check = 2.0 * x0 * x_q[0] + 2.0 * x1 * x_q[1]; ok &= NearEqual(y_q[0] , check, eps, eps); /* {xrst_code} {xrst_spell_on} reverse ======= {xrst_spell_off} {xrst_code cpp} */ // first order reverse mode q = 1; vector w(m), dw(n * q); w[0] = 1.; dw = f.Reverse(q, w); check = 2.0 * x0; ok &= NearEqual(dw[0] , check, eps, eps); check = 2.0 * x1; ok &= NearEqual(dw[1] , check, eps, eps); /* {xrst_code} {xrst_spell_on} rev_jac_sparsity ================ {xrst_spell_off} {xrst_code cpp} */ // reverse mode Jacobian sparstiy pattern CppAD::sparse_rc< CPPAD_TESTVECTOR(size_t) > pattern_in, pattern_out; pattern_in.resize(m, m, m); for(size_t i = 0; i < m; ++i) pattern_in.set(i, i, i); bool transpose = false; bool dependency = false; bool internal_bool = false; f.rev_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); CPPAD_TESTVECTOR(size_t) row_major = pattern_out.row_major(); // // first element in row major order is (0, 0) size_t k = 0; size_t r = pattern_out.row()[ row_major[k] ]; size_t c = pattern_out.col()[ row_major[k] ]; ok &= r == 0 && c == 0; // // second element in row major order is (0, 1) ++k; r = pattern_out.row()[ row_major[k] ]; c = pattern_out.col()[ row_major[k] ]; ok &= r == 0 && c == 1; // // k + 1 should be number of values in sparsity pattern ok &= k + 1 == pattern_out.nnz(); /* {xrst_code} {xrst_spell_on} for_hes_sparsity ================ {xrst_spell_off} {xrst_code cpp} */ // forward mode Hessian sparsity pattern CPPAD_TESTVECTOR(bool) select_x(n), select_y(m); for(size_t j = 0; j < n; ++j) select_x[j] = true; for(size_t i = 0; i < m; ++i) select_y[i] = true; f.for_hes_sparsity( select_x, select_y, internal_bool, pattern_out ); CPPAD_TESTVECTOR(size_t) order = pattern_out.row_major(); // // first element in row major order is (0, 0) k = 0; r = pattern_out.row()[ order[k] ]; c = pattern_out.col()[ order[k] ]; ok &= r == 0 && c == 0; // // second element in row major order is (1, 1) ++k; r = pattern_out.row()[ order[k] ]; c = pattern_out.col()[ order[k] ]; ok &= r == 1 && c == 1; // // k + 1 should be number of values in sparsity pattern ok &= k + 1 == pattern_out.nnz(); // return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end atomic_three_norm_sq.cpp} */ ================================================ FILE: example/atomic_three/reciprocal.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_reciprocal.cpp} Reciprocal as an Atomic Operation: Example and Test ################################################### Function ******** This example demonstrates using :ref:`atomic_three-name` to define the operation :math:`g : \B{R}^n \rightarrow \B{R}^m` where :math:`n = 1`, :math:`m = 1`, and :math:`g(x) = 1 / x`. Start Class Definition ********************** {xrst_spell_off} {xrst_code cpp} */ # include namespace { // isolate items below to this file using CppAD::vector; // abbreviate CppAD::vector as vector // class atomic_reciprocal : public CppAD::atomic_three { /* {xrst_code} {xrst_spell_on} Constructor *********** {xrst_spell_off} {xrst_code cpp} */ public: atomic_reciprocal(const std::string& name) : CppAD::atomic_three(name) { } private: /* {xrst_code} {xrst_spell_on} for_type ******** {xrst_spell_off} {xrst_code cpp} */ // calculate type_y bool for_type( const vector& parameter_x , const vector& type_x , vector& type_y ) override { assert( parameter_x.size() == type_x.size() ); bool ok = type_x.size() == 1; // n ok &= type_y.size() == 1; // m if( ! ok ) return false; type_y[0] = type_x[0]; return true; } /* {xrst_code} {xrst_spell_on} forward ******* {xrst_spell_off} {xrst_code cpp} */ // forward mode routine called by CppAD bool forward( const vector& parameter_x , const vector& type_x , size_t need_y , size_t p , size_t q , const vector& tx , vector& ty ) override { # ifndef NDEBUG size_t n = tx.size() / (q + 1); size_t m = ty.size() / (q + 1); # endif assert( type_x.size() == n ); assert( n == 1 ); assert( m == 1 ); assert( p <= q ); // return flag bool ok = q <= 2; // Order zero forward mode. // This case must always be implemented // y^0 = g( x^0 ) = 1 / x^0 double g = 1. / tx[0]; if( p <= 0 ) ty[0] = g; if( q <= 0 ) return ok; // Order one forward mode. // This case needed if first order forward mode is used. // y^1 = g'( x^0 ) x^1 double gp = - g / tx[0]; if( p <= 1 ) ty[1] = gp * tx[1]; if( q <= 1 ) return ok; // Order two forward mode. // This case needed if second order forward mode is used. // Y''(t) = X'(t)^\R{T} g''[X(t)] X'(t) + g'[X(t)] X''(t) // 2 y^2 = x^1 * g''( x^0 ) x^1 + 2 g'( x^0 ) x^2 double gpp = - 2.0 * gp / tx[0]; ty[2] = tx[1] * gpp * tx[1] / 2.0 + gp * tx[2]; if( q <= 2 ) return ok; // Assume we are not using forward mode with order > 2 assert( ! ok ); return ok; } /* {xrst_code} {xrst_spell_on} reverse ******* {xrst_spell_off} {xrst_code cpp} */ // reverse mode routine called by CppAD bool reverse( const vector& parameter_x , const vector& type_x , size_t q , const vector& tx , const vector& ty , vector& px , const vector& py ) override { # ifndef NDEBUG size_t n = tx.size() / (q + 1); size_t m = ty.size() / (q + 1); # endif assert( px.size() == tx.size() ); assert( py.size() == ty.size() ); assert( n == 1 ); assert( m == 1 ); bool ok = q <= 2; double g, gp, gpp, gppp; switch(q) { case 0: // This case needed if first order reverse mode is used // reverse: F^0 ( tx ) = y^0 = g( x^0 ) g = ty[0]; gp = - g / tx[0]; px[0] = py[0] * gp;; assert(ok); break; case 1: // This case needed if second order reverse mode is used // reverse: F^1 ( tx ) = y^1 = g'( x^0 ) x^1 g = ty[0]; gp = - g / tx[0]; gpp = - 2.0 * gp / tx[0]; px[1] = py[1] * gp; px[0] = py[1] * gpp * tx[1]; // reverse: F^0 ( tx ) = y^0 = g( x^0 ); px[0] += py[0] * gp; assert(ok); break; case 2: // This needed if third order reverse mode is used // reverse: F^2 ( tx ) = y^2 = // = x^1 * g''( x^0 ) x^1 / 2 + g'( x^0 ) x^2 g = ty[0]; gp = - g / tx[0]; gpp = - 2.0 * gp / tx[0]; gppp = - 3.0 * gpp / tx[0]; px[2] = py[2] * gp; px[1] = py[2] * gpp * tx[1]; px[0] = py[2] * tx[1] * gppp * tx[1] / 2.0 + gpp * tx[2]; // reverse: F^1 ( tx ) = y^1 = g'( x^0 ) x^1 px[1] += py[1] * gp; px[0] += py[1] * gpp * tx[1]; // reverse: F^0 ( tx ) = y^0 = g( x^0 ); px[0] += py[0] * gp; assert(ok); break; default: assert(!ok); } return ok; } /* {xrst_code} {xrst_spell_on} jac_sparsity ************ {xrst_spell_off} {xrst_code cpp} */ // Jacobian sparsity routine called by CppAD bool jac_sparsity( const vector& parameter_x , const vector& type_x , bool dependency , const vector& select_x , const vector& select_y , CppAD::sparse_rc< vector >& pattern_out ) override { size_t n = select_x.size(); size_t m = select_y.size(); assert( parameter_x.size() == n ); assert( n == 1 ); assert( m == 1 ); // size of pattern_out size_t nr = m; size_t nc = n; size_t nnz = 0; if( select_x[0] && select_y[0] ) ++nnz; pattern_out.resize(nr, nc, nnz); // set values in pattern_out size_t k = 0; if( select_x[0] && select_y[0] ) pattern_out.set(k++, 0, 0); assert( k == nnz ); return true; } /* {xrst_code} {xrst_spell_on} hes_sparsity ************ {xrst_spell_off} {xrst_code cpp} */ // Hessian sparsity routine called by CppAD bool hes_sparsity( const vector& parameter_x , const vector& type_x , const vector& select_x , const vector& select_y , CppAD::sparse_rc< vector >& pattern_out ) override { assert( parameter_x.size() == select_x.size() ); assert( select_y.size() == 1 ); size_t n = select_x.size(); assert( n == 1 ); // size of pattern_out size_t nr = n; size_t nc = n; size_t nnz = 0; if( select_x[0] && select_y[0] ) ++nnz; pattern_out.resize(nr, nc, nnz); // set values in pattern_out size_t k = 0; if( select_x[0] && select_y[0] ) pattern_out.set(k++, 0, 0); assert( k == nnz ); return true; } /* {xrst_code} {xrst_spell_on} End Class Definition ******************** {xrst_spell_off} {xrst_code cpp} */ }; // End of atomic_reciprocal class } // End empty namespace /* {xrst_code} {xrst_spell_on} Use Atomic Function ******************* {xrst_spell_off} {xrst_code cpp} */ bool reciprocal(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); /* {xrst_code} {xrst_spell_on} Constructor =========== {xrst_spell_off} {xrst_code cpp} */ // -------------------------------------------------------------------- // Create the atomic reciprocal object atomic_reciprocal afun("atomic_reciprocal"); /* {xrst_code} {xrst_spell_on} Recording ========= {xrst_spell_off} {xrst_code cpp} */ // Create the function f(x) = 1 / g(x) = x // // domain space vector size_t n = 1; double x0 = 0.5; vector< AD > ax(n); ax[0] = x0; // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 1; vector< AD > av(m); // call atomic function and store g(x) in au[0] vector< AD > au(m); afun(ax, au); // u = 1 / x // now use AD division to invert to invert the operation av[0] = 1.0 / au[0]; // v = 1 / u = x // create f: x -> y and stop tape recording CppAD::ADFun f; f.Dependent (ax, av); // g(x) = x /* {xrst_code} {xrst_spell_on} forward ======= {xrst_spell_off} {xrst_code cpp} */ // check function value double check = x0; ok &= NearEqual( Value(av[0]) , check, eps, eps); // check zero order forward mode size_t q; vector x_q(n), v_q(m); q = 0; x_q[0] = x0; v_q = f.Forward(q, x_q); ok &= NearEqual(v_q[0] , check, eps, eps); // check first order forward mode q = 1; x_q[0] = 1; v_q = f.Forward(q, x_q); check = 1.0; ok &= NearEqual(v_q[0] , check, eps, eps); // check second order forward mode q = 2; x_q[0] = 0; v_q = f.Forward(q, x_q); check = 0.; ok &= NearEqual(v_q[0] , check, eps, eps); /* {xrst_code} {xrst_spell_on} reverse ======= {xrst_spell_off} {xrst_code cpp} */ // third order reverse mode q = 3; vector w(m), dw(n * q); w[0] = 1.; dw = f.Reverse(q, w); check = 1.; ok &= NearEqual(dw[0] , check, eps, eps); check = 0.; ok &= NearEqual(dw[1] , check, eps, eps); ok &= NearEqual(dw[2] , check, eps, eps); /* {xrst_code} {xrst_spell_on} for_jac_sparsity ================ {xrst_spell_off} {xrst_code cpp} */ // forward mode Jacobian sparstiy pattern CppAD::sparse_rc< CPPAD_TESTVECTOR(size_t) > pattern_in, pattern_out; pattern_in.resize(1, 1, 1); pattern_in.set(0, 0, 0); bool transpose = false; bool dependency = false; bool internal_bool = false; f.for_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); ok &= pattern_out.nnz() == 1; ok &= pattern_out.row()[0] == 0; ok &= pattern_out.col()[0] == 0; /* {xrst_code} {xrst_spell_on} rev_sparse_jac ============== {xrst_spell_off} {xrst_code cpp} */ // reverse mode Jacobian sparstiy pattern f.rev_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); ok &= pattern_out.nnz() == 1; ok &= pattern_out.row()[0] == 0; ok &= pattern_out.col()[0] == 0; /* {xrst_code} {xrst_spell_on} rev_sparse_hes ============== {xrst_spell_off} {xrst_code cpp} */ // Hessian sparsity (using previous for_jac_sparsity call) CPPAD_TESTVECTOR(bool) select_y(m); select_y[0] = true; f.rev_hes_sparsity( select_y, transpose, internal_bool, pattern_out ); ok &= pattern_out.nnz() == 1; ok &= pattern_out.row()[0] == 0; ok &= pattern_out.col()[0] == 0; /* {xrst_code} {xrst_spell_on} for_sparse_hes ============== {xrst_spell_off} {xrst_code cpp} */ // Hessian sparsity CPPAD_TESTVECTOR(bool) select_x(n); select_x[0] = true; f.for_hes_sparsity( select_x, select_y, internal_bool, pattern_out ); ok &= pattern_out.nnz() == 1; ok &= pattern_out.row()[0] == 0; ok &= pattern_out.col()[0] == 0; return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end atomic_three_reciprocal.cpp} */ ================================================ FILE: example/atomic_three/rev_depend.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_rev_depend.cpp} Atomic Functions Reverse Dependency Analysis: Example and Test ############################################################## Purpose ******* This example demonstrates using :ref:`atomic_three-name` function in the definition of a function that is optimized. Function ******** For this example, the atomic function :math:`g : \B{R}^3 \rightarrow \B{R}^3` is defined by :math:`g_0 (x) = x_0 * x_0`, :math:`g_1 (x) = x_0 * x_1`, :math:`g_2 (x) = x_1 * x_2`. Start Class Definition ********************** {xrst_spell_off} {xrst_code cpp} */ # include // CppAD include file namespace { // start empty namespace using CppAD::vector; // abbreviate CppAD::vector using vector // start definition of atomic derived class using atomic_three interface class atomic_optimize : public CppAD::atomic_three { /* {xrst_code} {xrst_spell_on} Constructor *********** {xrst_spell_off} {xrst_code cpp} */ public: // can use const char* name when calling this constructor atomic_optimize(const std::string& name) : // can have more arguments CppAD::atomic_three(name) // inform base class of name { } private: /* {xrst_code} {xrst_spell_on} for_type ******** {xrst_spell_off} {xrst_code cpp} */ // calculate type_y bool for_type( const vector& parameter_x , const vector& type_x , vector& type_y ) override { assert( parameter_x.size() == type_x.size() ); bool ok = type_x.size() == 3; // n ok &= type_y.size() == 3; // m if( ! ok ) return false; type_y[0] = type_x[0]; type_y[1] = std::max( type_x[0], type_x[1] ); type_y[2] = std::max( type_x[1], type_x[2] ); return true; } /* {xrst_code} {xrst_spell_on} rev_depend ********** {xrst_spell_off} {xrst_code cpp} */ // calculate depend_x bool rev_depend( const vector& parameter_x , const vector& type_x , vector& depend_x , const vector& depend_y ) override { assert( parameter_x.size() == depend_x.size() ); bool ok = depend_x.size() == 3; // n ok &= depend_y.size() == 3; // m if( ! ok ) return false; depend_x[0] = depend_y[0] || depend_y[1]; depend_x[1] = depend_y[1] || depend_y[2]; depend_x[2] = depend_y[2]; return true; } /* {xrst_code} {xrst_spell_on} forward ******* {xrst_spell_off} {xrst_code cpp} */ // forward mode routine called by CppAD bool forward( const vector& parameter_x , const vector& type_x , size_t need_y , size_t order_low , size_t order_up , const vector& taylor_x , vector& taylor_y ) override { # ifndef NDEBUG size_t n = taylor_x.size() / (order_up + 1); size_t m = taylor_y.size() / (order_up + 1); # endif assert( n == 3 ); assert( m == 3 ); assert( order_low <= order_up ); // return flag bool ok = order_up == 0; if( ! ok ) return ok; // Order zero forward mode. // This case must always be implemented if( need_y > size_t(CppAD::variable_enum) ) { // g_0 = x_0 * x_0 taylor_y[0] = taylor_x[0] * taylor_x[0]; // g_1 = x_0 * x_1 taylor_y[1] = taylor_x[0] * taylor_x[1]; // g_2 = x_1 * x_2 taylor_y[2] = taylor_x[1] * taylor_x[2]; } else { // This uses need_y to reduce amount of computation. // It is probably faster, for this case, to ignore need_y. vector type_y( taylor_y.size() ); for_type(taylor_x, type_x, type_y); // g_0 = x_0 * x_0 if( size_t(type_y[0]) == need_y ) taylor_y[0] = taylor_x[0] * taylor_x[0]; // g_1 = x_0 * x_1 if( size_t(type_y[1]) == need_y ) taylor_y[1] = taylor_x[0] * taylor_x[1]; // g_2 = x_1 * x_2 if( size_t(type_y[2]) == need_y ) taylor_y[2] = taylor_x[1] * taylor_x[2]; } return ok; } /* {xrst_code} {xrst_spell_on} End Class Definition ******************** {xrst_spell_off} {xrst_code cpp} */ }; // End of atomic_optimize class } // End empty namespace /* {xrst_code} {xrst_spell_on} Use Atomic Function ******************* {xrst_spell_off} {xrst_code cpp} */ bool rev_depend(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); /* {xrst_code} {xrst_spell_on} Constructor =========== {xrst_spell_off} {xrst_code cpp} */ // Create the atomic dynamic object corresponding to g(x) atomic_optimize afun("atomic_optimize"); /* {xrst_code} {xrst_spell_on} Recording ========= {xrst_spell_off} {xrst_code cpp} */ // Create the function f(u) = g(c, p, u) for this example. // // constant parameter double c_0 = 2.0; // // independent dynamic parameter vector size_t np = 1; CPPAD_TESTVECTOR(double) p(np); CPPAD_TESTVECTOR( AD ) ap(np); ap[0] = p[0] = 3.0; // // independent variable vector size_t nu = 1; double u_0 = 0.5; CPPAD_TESTVECTOR( AD ) au(nu); au[0] = u_0; // declare independent variables and start tape recording CppAD::Independent(au, ap); // range space vector size_t ny = 3; CPPAD_TESTVECTOR( AD ) ay(ny); // call atomic function and store result in ay // y = ( c * c, c * p, p * u ) CPPAD_TESTVECTOR( AD ) ax(3); ax[0] = c_0; // x_0 = c ax[1] = ap[0]; // x_1 = p ax[2] = au[0]; // x_2 = u afun(ax, ay); // check type of result ok &= Constant( ay[0] ); // c * c ok &= Dynamic( ay[1] ); // c * p ok &= Variable( ay[2] ); // p * u // create f: u -> y and stop tape recording CppAD::ADFun f; f.Dependent (au, ay); // f(u) = (c * c, c * p, p * u) /* {xrst_code} {xrst_spell_on} optimize ======== This operation does a callback to :ref:`atomic_three_rev_depend.cpp@rev_depend` defined above {xrst_spell_off} {xrst_code cpp} */ f.optimize(); /* {xrst_code} {xrst_spell_on} forward ======= {xrst_spell_off} {xrst_code cpp} */ // check function value double check = c_0 * c_0; ok &= NearEqual( Value(ay[0]) , check, eps, eps); check = c_0 * p[0]; ok &= NearEqual( Value(ay[1]) , check, eps, eps); check = p[0] * u_0; ok &= NearEqual( Value(ay[2]) , check, eps, eps); // check zero order forward mode size_t q; CPPAD_TESTVECTOR( double ) u_q(nu), y_q(ny); q = 0; u_q[0] = u_0; y_q = f.Forward(q, u_q); check = c_0 * c_0; ok &= NearEqual(y_q[0] , check, eps, eps); check = c_0 * p[0]; ok &= NearEqual(y_q[1] , check, eps, eps); check = p[0] * u_0; ok &= NearEqual(y_q[2] , check, eps, eps); // set new value for dynamic parameters p[0] = 2.0 * p[0]; f.new_dynamic(p); y_q = f.Forward(q, u_q); check = c_0 * c_0; ok &= NearEqual(y_q[0] , check, eps, eps); check = c_0 * p[0]; ok &= NearEqual(y_q[1] , check, eps, eps); check = p[0] * u_0; ok &= NearEqual(y_q[2] , check, eps, eps); /* {xrst_code} {xrst_spell_on} Return Test Result ================== {xrst_spell_off} {xrst_code cpp} */ return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end atomic_three_rev_depend.cpp} */ ================================================ FILE: example/atomic_three/reverse.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_reverse.cpp} Atomic Functions and Reverse Mode: Example and Test ################################################### Purpose ******* This example demonstrates reverse mode derivative calculation using an :ref:`atomic_three-name` function. Function ******** For this example, the atomic function :math:`g : \B{R}^3 \rightarrow \B{R}^2` is defined by .. math:: g(x) = \left( \begin{array}{c} x_2 * x_2 \\ x_0 * x_1 \end{array} \right) Jacobian ******** The corresponding Jacobian is .. math:: g^{(1)} (x) = \left( \begin{array}{ccc} 0 & 0 & 2 x_2 \\ x_1 & x_0 & 0 \end{array} \right) Hessian ******* The Hessians of the component functions are .. math:: g_0^{(2)} ( x ) = \left( \begin{array}{ccc} 0 & 0 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & 2 \end{array} \right) \W{,} g_1^{(2)} ( x ) = \left( \begin{array}{ccc} 0 & 1 & 0 \\ 1 & 0 & 0 \\ 0 & 0 & 0 \end{array} \right) Start Class Definition ********************** {xrst_spell_off} {xrst_code cpp} */ # include namespace { // isolate items below to this file using CppAD::vector; // abbreviate as vector // class atomic_reverse : public CppAD::atomic_three { /* {xrst_code} {xrst_spell_on} Constructor *********** {xrst_spell_off} {xrst_code cpp} */ public: atomic_reverse(const std::string& name) : CppAD::atomic_three(name) { } private: /* {xrst_code} {xrst_spell_on} for_type ******** {xrst_spell_off} {xrst_code cpp} */ // calculate type_y bool for_type( const vector& parameter_x , const vector& type_x , vector& type_y ) override { assert( parameter_x.size() == type_x.size() ); bool ok = type_x.size() == 3; // n ok &= type_y.size() == 2; // m if( ! ok ) return false; type_y[0] = type_x[2]; type_y[1] = std::max(type_x[0], type_x[1]); return true; } /* {xrst_code} {xrst_spell_on} forward ******* {xrst_spell_off} {xrst_code cpp} */ // forward mode routine called by CppAD bool forward( const vector& parameter_x , const vector& type_x , size_t need_y , size_t order_low , size_t order_up , const vector& taylor_x , vector& taylor_y ) override { size_t q1 = order_up + 1; # ifndef NDEBUG size_t n = taylor_x.size() / q1; size_t m = taylor_y.size() / q1; # endif assert( n == 3 ); assert( m == 2 ); assert( order_low <= order_up ); // this example only implements up to first order forward mode bool ok = order_up <= 1; if( ! ok ) return ok; // ------------------------------------------------------------------ // Zero forward mode. // This case must always be implemented // g(x) = [ x_2 * x_2 ] // [ x_0 * x_1 ] // y^0 = f( x^0 ) if( order_low <= 0 ) { // y_0^0 = x_2^0 * x_2^0 taylor_y[0*q1+0] = taylor_x[2*q1+0] * taylor_x[2*q1+0]; // y_1^0 = x_0^0 * x_1^0 taylor_y[1*q1+0] = taylor_x[0*q1+0] * taylor_x[1*q1+0]; } if( order_up <= 0 ) return ok; // ------------------------------------------------------------------ // First order one forward mode. // This case is needed if first order forward mode is used. // g'(x) = [ 0, 0, 2 * x_2 ] // [ x_1, x_0, 0 ] // y^1 = f'(x^0) * x^1 if( order_low <= 1 ) { // y_0^1 = 2 * x_2^0 * x_2^1 taylor_y[0*q1+1] = 2.0 * taylor_x[2*q1+0] * taylor_x[2*q1+1]; // y_1^1 = x_1^0 * x_0^1 + x_0^0 * x_1^1 taylor_y[1*q1+1] = taylor_x[1*q1+0] * taylor_x[0*q1+1]; taylor_y[1*q1+1] += taylor_x[0*q1+0] * taylor_x[1*q1+1]; } return ok; } /* {xrst_code} {xrst_spell_on} reverse ******* {xrst_spell_off} {xrst_code cpp} */ // reverse mode routine called by CppAD bool reverse( const vector& parameter_x , const vector& type_x , size_t order_up , const vector& taylor_x , const vector& taylor_y , vector& partial_x , const vector& partial_y ) override { size_t q1 = order_up + 1; size_t n = taylor_x.size() / q1; # ifndef NDEBUG size_t m = taylor_y.size() / q1; # endif assert( n == 3 ); assert( m == 2 ); // this example only implements up to second order reverse mode bool ok = q1 <= 2; if( ! ok ) return ok; // // initialize summation as zero for(size_t j = 0; j < n; j++) for(size_t k = 0; k < q1; k++) partial_x[j * q1 + k] = 0.0; // if( q1 == 2 ) { // -------------------------------------------------------------- // Second order reverse first compute partials of first order // We use the notation pg_ij^k for partial of F_i^1 w.r.t. x_j^k // // y_0^1 = 2 * x_2^0 * x_2^1 // pg_02^0 = 2 * x_2^1 // pg_02^1 = 2 * x_2^0 // // y_1^1 = x_1^0 * x_0^1 + x_0^0 * x_1^1 // pg_10^0 = x_1^1 // pg_11^0 = x_0^1 // pg_10^1 = x_1^0 // pg_11^1 = x_0^0 // // px_0^0 += py_0^1 * pg_00^0 + py_1^1 * pg_10^0 // += py_1^1 * x_1^1 partial_x[0*q1+0] += partial_y[1*q1+1] * taylor_x[1*q1+1]; // // px_0^1 += py_0^1 * pg_00^1 + py_1^1 * pg_10^1 // += py_1^1 * x_1^0 partial_x[0*q1+1] += partial_y[1*q1+1] * taylor_x[1*q1+0]; // // px_1^0 += py_0^1 * pg_01^0 + py_1^1 * pg_11^0 // += py_1^1 * x_0^1 partial_x[1*q1+0] += partial_y[1*q1+1] * taylor_x[0*q1+1]; // // px_1^1 += py_0^1 * pg_01^1 + py_1^1 * pg_11^1 // += py_1^1 * x_0^0 partial_x[1*q1+1] += partial_y[1*q1+1] * taylor_x[0*q1+0]; // // px_2^0 += py_0^1 * pg_02^0 + py_1^1 * pg_12^0 // += py_0^1 * 2 * x_2^1 partial_x[2*q1+0] += partial_y[0*q1+1] * 2.0 * taylor_x[2*q1+1]; // // px_2^1 += py_0^1 * pg_02^1 + py_1^1 * pg_12^1 // += py_0^1 * 2 * x_2^0 partial_x[2*q1+1] += partial_y[0*q1+1] * 2.0 * taylor_x[2*q1+0]; } // -------------------------------------------------------------- // First order reverse computes partials of zero order coefficients // We use the notation pg_ij for partial of F_i^0 w.r.t. x_j^0 // // y_0^0 = x_2^0 * x_2^0 // pg_00 = 0, pg_01 = 0, pg_02 = 2 * x_2^0 // // y_1^0 = x_0^0 * x_1^0 // pg_10 = x_1^0, pg_11 = x_0^0, pg_12 = 0 // // px_0^0 += py_0^0 * pg_00 + py_1^0 * pg_10 // += py_1^0 * x_1^0 partial_x[0*q1+0] += partial_y[1*q1+0] * taylor_x[1*q1+0]; // // px_1^0 += py_1^0 * pg_01 + py_1^0 * pg_11 // += py_1^0 * x_0^0 partial_x[1*q1+0] += partial_y[1*q1+0] * taylor_x[0*q1+0]; // // px_2^0 += py_1^0 * pg_02 + py_1^0 * pg_12 // += py_0^0 * 2.0 * x_2^0 partial_x[2*q1+0] += partial_y[0*q1+0] * 2.0 * taylor_x[2*q1+0]; // -------------------------------------------------------------- return ok; } }; } // End empty namespace /* {xrst_code} {xrst_spell_on} Use Atomic Function ******************* {xrst_spell_off} {xrst_code cpp} */ bool reverse(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); // // Create the atomic_reverse object corresponding to g(x) atomic_reverse afun("atomic_reverse"); // // Create the function f(u) = g(u) for this example. // // domain space vector size_t n = 3; double u_0 = 1.00; double u_1 = 2.00; double u_2 = 3.00; vector< AD > au(n); au[0] = u_0; au[1] = u_1; au[2] = u_2; // declare independent variables and start tape recording CppAD::Independent(au); // range space vector size_t m = 2; vector< AD > ay(m); // call atomic function vector< AD > ax = au; afun(ax, ay); // create f: u -> y and stop tape recording CppAD::ADFun f; f.Dependent (au, ay); // y = f(u) // // check function value double check = u_2 * u_2; ok &= NearEqual( Value(ay[0]) , check, eps, eps); check = u_0 * u_1; ok &= NearEqual( Value(ay[1]) , check, eps, eps); // -------------------------------------------------------------------- // zero order forward // vector u0(n), y0(m); u0[0] = u_0; u0[1] = u_1; u0[2] = u_2; y0 = f.Forward(0, u0); check = u_2 * u_2; ok &= NearEqual(y0[0] , check, eps, eps); check = u_0 * u_1; ok &= NearEqual(y0[1] , check, eps, eps); // -------------------------------------------------------------------- // first order reverse // // value of Jacobian of f double check_jac[] = { 0.0, 0.0, 2.0 * u_2, u_1, u_0, 0.0 }; vector w(m), dw(n); // // check derivative of f_0 (x) for(size_t i = 0; i < m; i++) { w[i] = 1.0; w[1-i] = 0.0; dw = f.Reverse(1, w); for(size_t j = 0; j < n; j++) { // compute partial in j-th component direction ok &= NearEqual(dw[j], check_jac[i * n + j], eps, eps); } } // -------------------------------------------------------------------- // second order reverse // // value of Hessian of f_0 double check_hes_0[] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0 }; // // value of Hessian of f_1 double check_hes_1[] = { 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; vector u1(n), dw2( 2 * n ); for(size_t j = 0; j < n; j++) { for(size_t j1 = 0; j1 < n; j1++) u1[j1] = 0.0; u1[j] = 1.0; // first order forward f.Forward(1, u1); w[0] = 1.0; w[1] = 0.0; dw2 = f.Reverse(2, w); for(size_t i = 0; i < n; i++) ok &= NearEqual(dw2[i * 2 + 1], check_hes_0[i * n + j], eps, eps); w[0] = 0.0; w[1] = 1.0; dw2 = f.Reverse(2, w); for(size_t i = 0; i < n; i++) ok &= NearEqual(dw2[i * 2 + 1], check_hes_1[i * n + j], eps, eps); } // -------------------------------------------------------------------- return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end atomic_three_reverse.cpp} */ ================================================ FILE: example/atomic_three/tangent.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_tangent.cpp} Tan and Tanh as User Atomic Operations: Example and Test ######################################################## Discussion ********** The code below uses the :ref:`tan_forward-name` and :ref:`tan_reverse-name` to implement the tangent and hyperbolic tangent functions as atomic function operations. It also uses ``AD`` , while most atomic examples use ``AD`` . Start Class Definition ********************** {xrst_spell_off} {xrst_code cpp} */ # include namespace { // Begin empty namespace using CppAD::vector; // class atomic_tangent : public CppAD::atomic_three { /* {xrst_code} {xrst_spell_on} Constructor *********** {xrst_spell_off} {xrst_code cpp} */ private: const bool hyperbolic_; // is this hyperbolic tangent public: // constructor atomic_tangent(const char* name, bool hyperbolic) : CppAD::atomic_three(name), hyperbolic_(hyperbolic) { } private: /* {xrst_code} {xrst_spell_on} for_type ******** {xrst_spell_off} {xrst_code cpp} */ // calculate type_y bool for_type( const vector& parameter_x , const vector& type_x , vector& type_y ) override { assert( parameter_x.size() == type_x.size() ); bool ok = type_x.size() == 1; // n ok &= type_y.size() == 2; // m if( ! ok ) return false; type_y[0] = type_x[0]; type_y[1] = type_x[0]; return true; } /* {xrst_code} {xrst_spell_on} forward ******* {xrst_spell_off} {xrst_code cpp} */ // forward mode routine called by CppAD bool forward( const vector& parameter_x , const vector& type_x , size_t need_y , size_t p , size_t q , const vector& tx , vector& tzy ) override { size_t q1 = q + 1; # ifndef NDEBUG size_t n = tx.size() / q1; size_t m = tzy.size() / q1; # endif assert( type_x.size() == n ); assert( n == 1 ); assert( m == 2 ); assert( p <= q ); size_t j, k; if( p == 0 ) { // z^{(0)} = tan( x^{(0)} ) or tanh( x^{(0)} ) if( hyperbolic_ ) tzy[0] = float( tanh( tx[0] ) ); else tzy[0] = float( tan( tx[0] ) ); // y^{(0)} = z^{(0)} * z^{(0)} tzy[q1 + 0] = tzy[0] * tzy[0]; p++; } for(j = p; j <= q; j++) { float j_inv = 1.f / float(j); if( hyperbolic_ ) j_inv = - j_inv; // z^{(j)} = x^{(j)} +- sum_{k=1}^j k x^{(k)} y^{(j-k)} / j tzy[j] = tx[j]; for(k = 1; k <= j; k++) tzy[j] += tx[k] * tzy[q1 + j-k] * float(k) * j_inv; // y^{(j)} = sum_{k=0}^j z^{(k)} z^{(j-k)} tzy[q1 + j] = 0.; for(k = 0; k <= j; k++) tzy[q1 + j] += tzy[k] * tzy[j-k]; } // All orders are implemented and there are no possible errors return true; } /* {xrst_code} {xrst_spell_on} reverse ******* {xrst_spell_off} {xrst_code cpp} */ // reverse mode routine called by CppAD bool reverse( const vector& parameter_x , const vector& type_x , size_t q , const vector& tx , const vector& tzy , vector& px , const vector& pzy ) override { size_t q1 = q + 1; # ifndef NDEBUG size_t n = tx.size() / q1; size_t m = tzy.size() / q1; # endif assert( px.size() == n * q1 ); assert( pzy.size() == m * q1 ); assert( n == 1 ); assert( m == 2 ); size_t j, k; // copy because partials w.r.t. y and z need to change vector qzy = pzy; // initialize accumultion of reverse mode partials for(k = 0; k < q1; k++) px[k] = 0.; // eliminate positive orders for(j = q; j > 0; j--) { float j_inv = 1.f / float(j); if( hyperbolic_ ) j_inv = - j_inv; // H_{x^{(k)}} += delta(j-k) +- H_{z^{(j)} y^{(j-k)} * k / j px[j] += qzy[j]; for(k = 1; k <= j; k++) px[k] += qzy[j] * tzy[q1 + j-k] * float(k) * j_inv; // H_{y^{j-k)} += +- H_{z^{(j)} x^{(k)} * k / j for(k = 1; k <= j; k++) qzy[q1 + j-k] += qzy[j] * tx[k] * float(k) * j_inv; // H_{z^{(k)}} += H_{y^{(j-1)}} * z^{(j-k-1)} * 2. for(k = 0; k < j; k++) qzy[k] += qzy[q1 + j-1] * tzy[j-k-1] * 2.f; } // eliminate order zero if( hyperbolic_ ) px[0] += qzy[0] * (1.f - tzy[q1 + 0]); else px[0] += qzy[0] * (1.f + tzy[q1 + 0]); return true; } /* {xrst_code} {xrst_spell_on} jac_sparsity ************ {xrst_spell_off} {xrst_code cpp} */ // Jacobian sparsity routine called by CppAD bool jac_sparsity( const vector& parameter_x , const vector& type_x , bool dependency , const vector& select_x , const vector& select_y , CppAD::sparse_rc< vector >& pattern_out ) override { size_t n = select_x.size(); size_t m = select_y.size(); assert( parameter_x.size() == n ); assert( n == 1 ); assert( m == 2 ); // number of non-zeros in sparsity pattern size_t nnz = 0; if( select_x[0] ) { if( select_y[0] ) ++nnz; if( select_y[1] ) ++nnz; } // sparsity pattern pattern_out.resize(m, n, nnz); size_t k = 0; if( select_x[0] ) { if( select_y[0] ) pattern_out.set(k++, 0, 0); if( select_y[1] ) pattern_out.set(k++, 1, 0); } assert( k == nnz ); return true; } /* {xrst_code} {xrst_spell_on} hes_sparsity ************ {xrst_spell_off} {xrst_code cpp} */ // Hessian sparsity routine called by CppAD bool hes_sparsity( const vector& parameter_x , const vector& type_x , const vector& select_x , const vector& select_y , CppAD::sparse_rc< vector >& pattern_out ) override { assert( parameter_x.size() == select_x.size() ); assert( select_y.size() == 2 ); size_t n = select_x.size(); assert( n == 1 ); // number of non-zeros in sparsity pattern size_t nnz = 0; if( select_x[0] && (select_y[0] || select_y[1]) ) nnz = 1; // sparsity pattern pattern_out.resize(n, n, nnz); if( select_x[0] && (select_y[0] || select_y[1]) ) pattern_out.set(0, 0, 0); return true; } /* {xrst_code} {xrst_spell_on} End Class Definition ******************** {xrst_spell_off} {xrst_code cpp} */ }; // End of atomic_tangent class } // End empty namespace /* {xrst_code} {xrst_spell_on} Use Atomic Function ******************* {xrst_spell_off} {xrst_code cpp} */ bool tangent(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; float eps = 10.f * CppAD::numeric_limits::epsilon(); /* {xrst_code} {xrst_spell_on} Constructor =========== {xrst_spell_off} {xrst_code cpp} */ // -------------------------------------------------------------------- // Create a tan and tanh object atomic_tangent my_tan("my_tan", false), my_tanh("my_tanh", true); /* {xrst_code} {xrst_spell_on} Recording ========= {xrst_spell_off} {xrst_code cpp} */ // domain space vector size_t n = 1; float x0 = 0.5; CppAD::vector< AD > ax(n); ax[0] = x0; // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 3; CppAD::vector< AD > av(m); // temporary vector for computations // (my_tan and my_tanh computes tan or tanh and its square) CppAD::vector< AD > au(2); // call atomic tan function and store tan(x) in f[0], ignore tan(x)^2 my_tan(ax, au); av[0] = au[0]; // call atomic tanh function and store tanh(x) in f[1], ignore tanh(x)^2 my_tanh(ax, au); av[1] = au[0]; // put a constant in f[2] = tanh(1.), for sparsity pattern testing CppAD::vector< AD > one(1); one[0] = 1.; my_tanh(one, au); av[2] = au[0]; // create f: x -> v and stop tape recording CppAD::ADFun f; f.Dependent(ax, av); /* {xrst_code} {xrst_spell_on} forward ======= {xrst_spell_off} {xrst_code cpp} */ // check function value float tan = std::tan(x0); ok &= NearEqual(av[0] , tan, eps, eps); float tanh = std::tanh(x0); ok &= NearEqual(av[1] , tanh, eps, eps); // check zero order forward CppAD::vector x(n), v(m); x[0] = x0; v = f.Forward(0, x); ok &= NearEqual(v[0] , tan, eps, eps); ok &= NearEqual(v[1] , tanh, eps, eps); // tan'(x) = 1 + tan(x) * tan(x) // tanh'(x) = 1 - tanh(x) * tanh(x) float tanp = 1.f + tan * tan; float tanhp = 1.f - tanh * tanh; // compute first partial of f w.r.t. x[0] using forward mode CppAD::vector dx(n), dv(m); dx[0] = 1.; dv = f.Forward(1, dx); ok &= NearEqual(dv[0] , tanp, eps, eps); ok &= NearEqual(dv[1] , tanhp, eps, eps); ok &= NearEqual(dv[2] , 0.f, eps, eps); // tan''(x) = 2 * tan(x) * tan'(x) // tanh''(x) = - 2 * tanh(x) * tanh'(x) // Note that second order Taylor coefficient for u half the // corresponding second derivative. float two = 2; float tanpp = two * tan * tanp; float tanhpp = - two * tanh * tanhp; // compute second partial of f w.r.t. x[0] using forward mode CppAD::vector ddx(n), ddv(m); ddx[0] = 0.; ddv = f.Forward(2, ddx); ok &= NearEqual(two * ddv[0], tanpp, eps, eps); ok &= NearEqual(two * ddv[1], tanhpp, eps, eps); ok &= NearEqual(two * ddv[2], 0.f, eps, eps); /* {xrst_code} {xrst_spell_on} reverse ======= {xrst_spell_off} {xrst_code cpp} */ // compute derivative of tan - tanh using reverse mode CppAD::vector w(m), dw(n); w[0] = 1.; w[1] = 1.; w[2] = 0.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], w[0]*tanp + w[1]*tanhp, eps, eps); // compute second derivative of tan - tanh using reverse mode CppAD::vector ddw(2); ddw = f.Reverse(2, w); ok &= NearEqual(ddw[0], w[0]*tanp + w[1]*tanhp , eps, eps); ok &= NearEqual(ddw[1], w[0]*tanpp + w[1]*tanhpp, eps, eps); /* {xrst_code} {xrst_spell_on} for_jac_sparsity ================ {xrst_spell_off} {xrst_code cpp} */ // forward mode Jacobian sparstiy pattern CppAD::sparse_rc< CPPAD_TESTVECTOR(size_t) > pattern_in, pattern_out; pattern_in.resize(1, 1, 1); pattern_in.set(0, 0, 0); bool transpose = false; bool dependency = false; bool internal_bool = false; f.for_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); // (0, 0) and (1, 0) are in sparsity pattern ok &= pattern_out.nnz() == 2; ok &= pattern_out.row()[0] == 0; ok &= pattern_out.col()[0] == 0; ok &= pattern_out.row()[1] == 1; ok &= pattern_out.col()[1] == 0; /* {xrst_code} {xrst_spell_on} rev_sparse_hes ============== {xrst_spell_off} {xrst_code cpp} */ // Hesian sparsity (using previous for_jac_sparsity call) CPPAD_TESTVECTOR(bool) select_y(m); select_y[0] = true; select_y[1] = false; select_y[2] = false; f.rev_hes_sparsity( select_y, transpose, internal_bool, pattern_out ); ok &= pattern_out.nnz() == 1; ok &= pattern_out.row()[0] == 0; ok &= pattern_out.col()[0] == 0; /* {xrst_code} {xrst_spell_on} Large x Values ============== {xrst_spell_off} {xrst_code cpp} */ // check tanh results for a large value of x x[0] = std::numeric_limits::max() / two; v = f.Forward(0, x); tanh = 1.; ok &= NearEqual(v[1], tanh, eps, eps); dv = f.Forward(1, dx); tanhp = 0.; ok &= NearEqual(dv[1], tanhp, eps, eps); return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end atomic_three_tangent.cpp} */ ================================================ FILE: example/atomic_two/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # # eigen_sources and CPPAD_HAS_EIGEN IF( cppad_has_eigen ) SET(eigen_sources eigen_mat_inv.cpp eigen_cholesky.cpp eigen_mat_mul.cpp) ELSE( cppad_has_eigen ) SET(eigen_sources "") ENDIF( cppad_has_eigen ) # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list ${eigen_sources} atomic_two.cpp ) # END_SORT_THIS_LINE_MINUS_2 set_compile_flags( example_atomic_two "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(example_atomic_two EXCLUDE_FROM_ALL ${source_list}) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_atomic_two ${cppad_lib} ${colpack_libs} ) # # check_example_atomic_two add_check_executable(check_example atomic_two) ================================================ FILE: example/atomic_two/atomic_two.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_two.cpp} atomic_two Examples and Tests Driver #################################### Running These Tests ******************* After executing the :ref:`cmake-name` command form the :ref:`download@Distribution Directory`, you can build and run these tests with the commands:: cd build make check_example_atomic_two Note that your choice of :ref:`cmake@generator` may require using an different version of make; e.g., ``ninja`` . {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_two.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ // CPPAD_HAS_* defines # include // for thread_alloc # include // test runner # include // external complied tests extern bool eigen_cholesky(void); extern bool eigen_mat_inv(void); extern bool eigen_mat_mul(void); // main program that runs all the tests int main(void) { std::string group = "example/atomic_two"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // external compiled tests # if CPPAD_HAS_EIGEN Run( eigen_cholesky, "eigen_cholesky" ); Run( eigen_mat_inv, "eigen_mat_inv" ); Run( eigen_mat_mul, "eigen_mat_mul" ); # endif // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } // END C++ ================================================ FILE: example/atomic_two/eigen_cholesky.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_two_eigen_cholesky.cpp app} {xrst_spell chol } Atomic Eigen Cholesky Factorization: Example and Test ##################################################### Description *********** The :ref:`ADFun-name` function object *f* for this example is .. math:: f(x) = \R{chol} \left( \begin{array}{cc} x_0 & x_1 \\ x_1 & x_2 \end{array} \right) = \frac{1}{ \sqrt{x_0} } \left( \begin{array}{cc} x_0 & 0 \\ x_1 & \sqrt{ x_0 x_2 - x_1 x_1 } \end{array} \right) where the matrix is positive definite; i.e., :math:`x_0 > 0`, :math:`x_2 > 0` and :math:`x_0 x_2 - x_1 x_1 > 0`. Contents ******** {xrst_toc_table xrst/theory/cholesky.xrst include/cppad/example/atomic_two/eigen_cholesky.hpp } Use Atomic Function ******************* {xrst_spell_off} {xrst_code cpp} */ # include # include bool eigen_cholesky(void) { typedef double scalar; typedef atomic_eigen_cholesky::ad_scalar ad_scalar; typedef atomic_eigen_cholesky::ad_matrix ad_matrix; // bool ok = true; scalar eps = 10. * std::numeric_limits::epsilon(); using CppAD::NearEqual; // /* {xrst_code} {xrst_spell_on} Constructor =========== {xrst_spell_off} {xrst_code cpp} */ // ------------------------------------------------------------------- // object that computes cholesky factor of a matrix atomic_eigen_cholesky cholesky; // ------------------------------------------------------------------- // declare independent variable vector x size_t n = 3; CPPAD_TESTVECTOR(ad_scalar) ad_x(n); ad_x[0] = 2.0; ad_x[1] = 0.5; ad_x[2] = 3.0; CppAD::Independent(ad_x); // ------------------------------------------------------------------- // A = [ x[0] x[1] ] // [ x[1] x[2] ] size_t nr = 2; ad_matrix ad_A(nr, nr); ad_A(0, 0) = ad_x[0]; ad_A(1, 0) = ad_x[1]; ad_A(0, 1) = ad_x[1]; ad_A(1, 1) = ad_x[2]; // ------------------------------------------------------------------- // use atomic operation to L such that A = L * L^T ad_matrix ad_L = cholesky.op(ad_A); // ------------------------------------------------------------------- // declare the dependent variable vector y size_t m = 3; CPPAD_TESTVECTOR(ad_scalar) ad_y(m); ad_y[0] = ad_L(0, 0); ad_y[1] = ad_L(1, 0); ad_y[2] = ad_L(1, 1); CppAD::ADFun f(ad_x, ad_y); // ------------------------------------------------------------------- // check zero order forward mode CPPAD_TESTVECTOR(scalar) x(n), y(m); x[0] = 2.0; x[1] = 0.5; x[2] = 5.0; y = f.Forward(0, x); scalar check; check = std::sqrt( x[0] ); ok &= NearEqual(y[0], check, eps, eps); check = x[1] / std::sqrt( x[0] ); ok &= NearEqual(y[1], check, eps, eps); check = std::sqrt( x[2] - x[1] * x[1] / x[0] ); ok &= NearEqual(y[2], check, eps, eps); // ------------------------------------------------------------------- // check first order forward mode CPPAD_TESTVECTOR(scalar) x1(n), y1(m); // // partial w.r.t. x[0] x1[0] = 1.0; x1[1] = 0.0; x1[2] = 0.0; // y1 = f.Forward(1, x1); check = 1.0 / (2.0 * std::sqrt( x[0] ) ); ok &= NearEqual(y1[0], check, eps, eps); // check = - x[1] / (2.0 * x[0] * std::sqrt( x[0] ) ); ok &= NearEqual(y1[1], check, eps, eps); // check = std::sqrt( x[2] - x[1] * x[1] / x[0] ); check = x[1] * x[1] / (x[0] * x[0] * 2.0 * check); ok &= NearEqual(y1[2], check, eps, eps); // // partial w.r.t. x[1] x1[0] = 0.0; x1[1] = 1.0; x1[2] = 0.0; // y1 = f.Forward(1, x1); ok &= NearEqual(y1[0], 0.0, eps, eps); // check = 1.0 / std::sqrt( x[0] ); ok &= NearEqual(y1[1], check, eps, eps); // check = std::sqrt( x[2] - x[1] * x[1] / x[0] ); check = - 2.0 * x[1] / (2.0 * check * x[0] ); ok &= NearEqual(y1[2], check, eps, eps); // // partial w.r.t. x[2] x1[0] = 0.0; x1[1] = 0.0; x1[2] = 1.0; // y1 = f.Forward(1, x1); ok &= NearEqual(y1[0], 0.0, eps, eps); ok &= NearEqual(y1[1], 0.0, eps, eps); // check = std::sqrt( x[2] - x[1] * x[1] / x[0] ); check = 1.0 / (2.0 * check); ok &= NearEqual(y1[2], check, eps, eps); // ------------------------------------------------------------------- // check second order forward mode CPPAD_TESTVECTOR(scalar) x2(n), y2(m); // // second partial w.r.t x[2] x2[0] = 0.0; x2[1] = 0.0; x2[2] = 0.0; y2 = f.Forward(2, x2); ok &= NearEqual(y2[0], 0.0, eps, eps); ok &= NearEqual(y2[1], 0.0, eps, eps); // check = std::sqrt( x[2] - x[1] * x[1] / x[0] ); // function value check = - 1.0 / ( 4.0 * check * check * check ); // second derivative check = 0.5 * check; // taylor coefficient ok &= NearEqual(y2[2], check, eps, eps); // ------------------------------------------------------------------- // check first order reverse mode CPPAD_TESTVECTOR(scalar) w(m), d1w(n); w[0] = 0.0; w[1] = 0.0; w[2] = 1.0; d1w = f.Reverse(1, w); // // partial of f[2] w.r.t x[0] scalar f2 = std::sqrt( x[2] - x[1] * x[1] / x[0] ); scalar f2_x0 = x[1] * x[1] / (2.0 * f2 * x[0] * x[0] ); ok &= NearEqual(d1w[0], f2_x0, eps, eps); // // partial of f[2] w.r.t x[1] scalar f2_x1 = - x[1] / (f2 * x[0] ); ok &= NearEqual(d1w[1], f2_x1, eps, eps); // // partial of f[2] w.r.t x[2] scalar f2_x2 = 1.0 / (2.0 * f2 ); ok &= NearEqual(d1w[2], f2_x2, eps, eps); // ------------------------------------------------------------------- // check second order reverse mode CPPAD_TESTVECTOR(scalar) d2w(2 * n); d2w = f.Reverse(2, w); // // check first order results ok &= NearEqual(d2w[0 * 2 + 0], f2_x0, eps, eps); ok &= NearEqual(d2w[1 * 2 + 0], f2_x1, eps, eps); ok &= NearEqual(d2w[2 * 2 + 0], f2_x2, eps, eps); // // check second order results scalar f2_x2_x0 = - 0.5 * f2_x0 / (f2 * f2 ); ok &= NearEqual(d2w[0 * 2 + 1], f2_x2_x0, eps, eps); scalar f2_x2_x1 = - 0.5 * f2_x1 / (f2 * f2 ); ok &= NearEqual(d2w[1 * 2 + 1], f2_x2_x1, eps, eps); scalar f2_x2_x2 = - 0.5 * f2_x2 / (f2 * f2 ); ok &= NearEqual(d2w[2 * 2 + 1], f2_x2_x2, eps, eps); // ------------------------------------------------------------------- // check third order reverse mode CPPAD_TESTVECTOR(scalar) d3w(3 * n); d3w = f.Reverse(3, w); // // check first order results ok &= NearEqual(d3w[0 * 3 + 0], f2_x0, eps, eps); ok &= NearEqual(d3w[1 * 3 + 0], f2_x1, eps, eps); ok &= NearEqual(d3w[2 * 3 + 0], f2_x2, eps, eps); // // check second order results ok &= NearEqual(d3w[0 * 3 + 1], f2_x2_x0, eps, eps); ok &= NearEqual(d3w[1 * 3 + 1], f2_x2_x1, eps, eps); ok &= NearEqual(d3w[2 * 3 + 1], f2_x2_x2, eps, eps); // ------------------------------------------------------------------- scalar f2_x2_x2_x0 = - 0.5 * f2_x2_x0 / (f2 * f2); f2_x2_x2_x0 += f2_x2 * f2_x0 / (f2 * f2 * f2); ok &= NearEqual(d3w[0 * 3 + 2], 0.5 * f2_x2_x2_x0, eps, eps); scalar f2_x2_x2_x1 = - 0.5 * f2_x2_x1 / (f2 * f2); f2_x2_x2_x1 += f2_x2 * f2_x1 / (f2 * f2 * f2); ok &= NearEqual(d3w[1 * 3 + 2], 0.5 * f2_x2_x2_x1, eps, eps); scalar f2_x2_x2_x2 = - 0.5 * f2_x2_x2 / (f2 * f2); f2_x2_x2_x2 += f2_x2 * f2_x2 / (f2 * f2 * f2); ok &= NearEqual(d3w[2 * 3 + 2], 0.5 * f2_x2_x2_x2, eps, eps); return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end atomic_two_eigen_cholesky.cpp} */ ================================================ FILE: example/atomic_two/eigen_mat_inv.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_two_eigen_mat_inv.cpp app} Atomic Eigen Matrix Inverse: Example and Test ############################################# Description *********** The :ref:`ADFun-name` function object *f* for this example is .. math:: f(x) = \left( \begin{array}{cc} x_0 & 0 \\ 0 & x_1 \end{array} \right)^{-1} \left( \begin{array}{c} 0 \\ x_2 \end{array} \right) = \left( \begin{array}{c} 0 \\ x_2 / x_1 ) \end{array} \right) {xrst_toc_hidden include/cppad/example/atomic_two/eigen_mat_inv.hpp } Class Definition **************** This example uses the file :ref:`atomic_two_eigen_mat_inv.hpp-name` which defines matrix multiply as a :ref:`atomic_two-name` operation. Use Atomic Function ******************* {xrst_spell_off} {xrst_code cpp} */ # include # include # include bool eigen_mat_inv(void) { typedef double scalar; typedef CppAD::AD ad_scalar; typedef atomic_eigen_mat_inv::ad_matrix ad_matrix; // bool ok = true; scalar eps = 10. * std::numeric_limits::epsilon(); using CppAD::NearEqual; // /* {xrst_code} {xrst_spell_on} Constructor =========== {xrst_spell_off} {xrst_code cpp} */ // ------------------------------------------------------------------- // object that multiplies matrices atomic_eigen_mat_mul mat_mul; // ------------------------------------------------------------------- // object that computes inverse of a square matrix atomic_eigen_mat_inv mat_inv; // ------------------------------------------------------------------- // declare independent variable vector x size_t n = 3; CPPAD_TESTVECTOR(ad_scalar) ad_x(n); for(size_t j = 0; j < n; j++) ad_x[j] = ad_scalar(j + 1); CppAD::Independent(ad_x); // ------------------------------------------------------------------- // left = [ x[0] 0 ] // [ 0 x[1] ] size_t nr_left = 2; ad_matrix ad_left(nr_left, nr_left); ad_left(0, 0) = ad_x[0]; ad_left(0, 1) = ad_scalar(0.0); ad_left(1, 0) = ad_scalar(0.0); ad_left(1, 1) = ad_x[1]; // ------------------------------------------------------------------- // right = [ 0 , x[2] ]^T size_t nc_right = 1; ad_matrix ad_right(nr_left, nc_right); ad_right(0, 0) = ad_scalar(0.0); ad_right(1, 0) = ad_x[2]; // ------------------------------------------------------------------- // use atomic operation to compute left^{-1} ad_matrix ad_left_inv = mat_inv.op(ad_left); // use atomic operation to multiply left^{-1} * right ad_matrix ad_result = mat_mul.op(ad_left_inv, ad_right); // ------------------------------------------------------------------- // declare the dependent variable vector y size_t m = 2; CPPAD_TESTVECTOR(ad_scalar) ad_y(2); for(size_t i = 0; i < m; i++) ad_y[i] = ad_result( long(i), 0); CppAD::ADFun f(ad_x, ad_y); // ------------------------------------------------------------------- // check zero order forward mode CPPAD_TESTVECTOR(scalar) x(n), y(m); for(size_t i = 0; i < n; i++) x[i] = scalar(i + 2); y = f.Forward(0, x); ok &= NearEqual(y[0], 0.0, eps, eps); ok &= NearEqual(y[1], x[2] / x[1], eps, eps); // ------------------------------------------------------------------- // check first order forward mode CPPAD_TESTVECTOR(scalar) x1(n), y1(m); x1[0] = 1.0; x1[1] = 0.0; x1[2] = 0.0; y1 = f.Forward(1, x1); ok &= NearEqual(y1[0], 0.0, eps, eps); ok &= NearEqual(y1[1], 0.0, eps, eps); x1[0] = 0.0; x1[1] = 0.0; x1[2] = 1.0; y1 = f.Forward(1, x1); ok &= NearEqual(y1[0], 0.0, eps, eps); ok &= NearEqual(y1[1], 1.0 / x[1], eps, eps); x1[0] = 0.0; x1[1] = 1.0; x1[2] = 0.0; y1 = f.Forward(1, x1); ok &= NearEqual(y1[0], 0.0, eps, eps); ok &= NearEqual(y1[1], - x[2] / (x[1]*x[1]), eps, eps); // ------------------------------------------------------------------- // check second order forward mode CPPAD_TESTVECTOR(scalar) x2(n), y2(m); x2[0] = 0.0; x2[1] = 0.0; x2[2] = 0.0; scalar f1_x1_x1 = 2.0 * x[2] / (x[1] * x[1] * x[1] ); y2 = f.Forward(2, x2); ok &= NearEqual(y2[0], 0.0, eps, eps); ok &= NearEqual(y2[1], f1_x1_x1 / 2.0, eps, eps); // ------------------------------------------------------------------- // check first order reverse CPPAD_TESTVECTOR(scalar) w(m), d1w(n); w[0] = 1.0; w[1] = 0.0; d1w = f.Reverse(1, w); ok &= NearEqual(d1w[0], 0.0, eps, eps); ok &= NearEqual(d1w[1], 0.0, eps, eps); ok &= NearEqual(d1w[2], 0.0, eps, eps); w[0] = 0.0; w[1] = 1.0; d1w = f.Reverse(1, w); ok &= NearEqual(d1w[0], 0.0, eps, eps); ok &= NearEqual(d1w[1], - x[2] / (x[1]*x[1]), eps, eps); ok &= NearEqual(d1w[2], 1.0 / x[1], eps, eps); // ------------------------------------------------------------------- // check second order reverse CPPAD_TESTVECTOR(scalar) d2w(2 * n); d2w = f.Reverse(2, w); // partial f_1 w.r.t x_0 ok &= NearEqual(d2w[0 * 2 + 0], 0.0, eps, eps); // partial f_1 w.r.t x_1 ok &= NearEqual(d2w[1 * 2 + 0], - x[2] / (x[1]*x[1]), eps, eps); // partial f_1 w.r.t x_2 ok &= NearEqual(d2w[2 * 2 + 0], 1.0 / x[1], eps, eps); // partial f_1 w.r.t x_1, x_0 ok &= NearEqual(d2w[0 * 2 + 1], 0.0, eps, eps); // partial f_1 w.r.t x_1, x_1 ok &= NearEqual(d2w[1 * 2 + 1], f1_x1_x1, eps, eps); // partial f_1 w.r.t x_1, x_2 ok &= NearEqual(d2w[2 * 2 + 1], - 1.0 / (x[1]*x[1]), eps, eps); // ------------------------------------------------------------------- return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end atomic_two_eigen_mat_inv.cpp} */ ================================================ FILE: example/atomic_two/eigen_mat_mul.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_two_eigen_mat_mul.cpp app} Atomic Eigen Matrix Multiply: Example and Test ############################################## Description *********** The :ref:`ADFun-name` function object *f* for this example is .. math:: f(x) = \left( \begin{array}{cc} 0 & 0 \\ 1 & 2 \\ x_0 & x_1 \end{array} \right) \left( \begin{array}{c} x_0 \\ x_1 \end{array} \right) = \left( \begin{array}{c} 0 \\ x_0 + 2 x_1 \\ x_0 x_0 + x_1 x_1 ) \end{array} \right) {xrst_toc_hidden include/cppad/example/atomic_two/eigen_mat_mul.hpp } Class Definition **************** This example uses the file :ref:`atomic_two_eigen_mat_mul.hpp-name` which defines matrix multiply as a :ref:`atomic_two-name` operation. Use Atomic Function ******************* {xrst_spell_off} {xrst_code cpp} */ # include # include bool eigen_mat_mul(void) { // typedef double scalar; typedef CppAD::AD ad_scalar; typedef atomic_eigen_mat_mul::ad_matrix ad_matrix; // bool ok = true; scalar eps = 10. * std::numeric_limits::epsilon(); using CppAD::NearEqual; // /* {xrst_code} {xrst_spell_on} Constructor =========== {xrst_spell_off} {xrst_code cpp} */ // ------------------------------------------------------------------- // object that multiplies arbitrary matrices atomic_eigen_mat_mul mat_mul; // ------------------------------------------------------------------- // declare independent variable vector x size_t n = 2; CPPAD_TESTVECTOR(ad_scalar) ad_x(n); for(size_t j = 0; j < n; j++) ad_x[j] = ad_scalar(j); CppAD::Independent(ad_x); // ------------------------------------------------------------------- // [ 0 0 ] // left = [ 1 2 ] // [ x[0] x[1] ] size_t nr_left = 3; size_t n_middle = 2; ad_matrix ad_left(nr_left, n_middle); ad_left(0, 0) = ad_scalar(0.0); ad_left(0, 1) = ad_scalar(0.0); ad_left(1, 0) = ad_scalar(1.0); ad_left(1, 1) = ad_scalar(2.0); ad_left(2, 0) = ad_x[0]; ad_left(2, 1) = ad_x[1]; // ------------------------------------------------------------------- // right = [ x[0] , x[1] ]^T size_t nc_right = 1; ad_matrix ad_right(n_middle, nc_right); ad_right(0, 0) = ad_x[0]; ad_right(1, 0) = ad_x[1]; // ------------------------------------------------------------------- // use atomic operation to multiply left * right ad_matrix ad_result = mat_mul.op(ad_left, ad_right); // ------------------------------------------------------------------- // check that first component of result is a parameter // and the other components are variables. ok &= Parameter( ad_result(0, 0) ); ok &= Variable( ad_result(1, 0) ); ok &= Variable( ad_result(2, 0) ); // ------------------------------------------------------------------- // declare the dependent variable vector y size_t m = 3; CPPAD_TESTVECTOR(ad_scalar) ad_y(m); for(size_t i = 0; i < m; i++) ad_y[i] = ad_result(long(i), 0); CppAD::ADFun f(ad_x, ad_y); // ------------------------------------------------------------------- // check zero order forward mode CPPAD_TESTVECTOR(scalar) x(n), y(m); for(size_t i = 0; i < n; i++) x[i] = scalar(i + 2); y = f.Forward(0, x); ok &= NearEqual(y[0], 0.0, eps, eps); ok &= NearEqual(y[1], x[0] + 2.0 * x[1], eps, eps); ok &= NearEqual(y[2], x[0] * x[0] + x[1] * x[1], eps, eps); // ------------------------------------------------------------------- // check first order forward mode CPPAD_TESTVECTOR(scalar) x1(n), y1(m); x1[0] = 1.0; x1[1] = 0.0; y1 = f.Forward(1, x1); ok &= NearEqual(y1[0], 0.0, eps, eps); ok &= NearEqual(y1[1], 1.0, eps, eps); ok &= NearEqual(y1[2], 2.0 * x[0], eps, eps); x1[0] = 0.0; x1[1] = 1.0; y1 = f.Forward(1, x1); ok &= NearEqual(y1[0], 0.0, eps, eps); ok &= NearEqual(y1[1], 2.0, eps, eps); ok &= NearEqual(y1[2], 2.0 * x[1], eps, eps); // ------------------------------------------------------------------- // check second order forward mode CPPAD_TESTVECTOR(scalar) x2(n), y2(m); x2[0] = 0.0; x2[1] = 0.0; y2 = f.Forward(2, x2); ok &= NearEqual(y2[0], 0.0, eps, eps); ok &= NearEqual(y2[1], 0.0, eps, eps); ok &= NearEqual(y2[2], 1.0, eps, eps); // 1/2 * f_1''(x) // ------------------------------------------------------------------- // check first order reverse mode CPPAD_TESTVECTOR(scalar) w(m), d1w(n); w[0] = 0.0; w[1] = 1.0; w[2] = 0.0; d1w = f.Reverse(1, w); ok &= NearEqual(d1w[0], 1.0, eps, eps); ok &= NearEqual(d1w[1], 2.0, eps, eps); w[0] = 0.0; w[1] = 0.0; w[2] = 1.0; d1w = f.Reverse(1, w); ok &= NearEqual(d1w[0], 2.0 * x[0], eps, eps); ok &= NearEqual(d1w[1], 2.0 * x[1], eps, eps); // ------------------------------------------------------------------- // check second order reverse mode CPPAD_TESTVECTOR(scalar) d2w(2 * n); d2w = f.Reverse(2, w); // partial f_2 w.r.t. x_0 ok &= NearEqual(d2w[0 * 2 + 0], 2.0 * x[0], eps, eps); // partial f_2 w.r.t x_1 ok &= NearEqual(d2w[1 * 2 + 0], 2.0 * x[1], eps, eps); // partial f_2 w.r.t x_1, x_0 ok &= NearEqual(d2w[0 * 2 + 1], 0.0, eps, eps); // partial f_2 w.r.t x_1, x_1 ok &= NearEqual(d2w[1 * 2 + 1], 2.0, eps, eps); // ------------------------------------------------------------------- // check forward Jacobian sparsity CPPAD_TESTVECTOR( std::set ) r(n), s(m); std::set check_set; for(size_t j = 0; j < n; j++) r[j].insert(j); s = f.ForSparseJac(n, r); check_set.clear(); ok &= s[0] == check_set; check_set.insert(0); check_set.insert(1); ok &= s[1] == check_set; ok &= s[2] == check_set; // ------------------------------------------------------------------- // check reverse Jacobian sparsity r.resize(m); for(size_t i = 0; i < m; i++) r[i].insert(i); s = f.RevSparseJac(m, r); check_set.clear(); ok &= s[0] == check_set; check_set.insert(0); check_set.insert(1); ok &= s[1] == check_set; ok &= s[2] == check_set; // ------------------------------------------------------------------- // check forward Hessian sparsity for f_2 (x) CPPAD_TESTVECTOR( std::set ) r2(1), s2(1), h(n); for(size_t j = 0; j < n; j++) r2[0].insert(j); s2[0].clear(); s2[0].insert(2); h = f.ForSparseHes(r2, s2); check_set.clear(); check_set.insert(0); ok &= h[0] == check_set; check_set.clear(); check_set.insert(1); ok &= h[1] == check_set; // ------------------------------------------------------------------- // check reverse Hessian sparsity for f_2 (x) CPPAD_TESTVECTOR( std::set ) s3(1); s3[0].clear(); s3[0].insert(2); h = f.RevSparseHes(n, s3); check_set.clear(); check_set.insert(0); ok &= h[0] == check_set; check_set.clear(); check_set.insert(1); ok &= h[1] == check_set; // ------------------------------------------------------------------- return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end atomic_two_eigen_mat_mul.cpp} */ ================================================ FILE: example/chkpoint_two/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list base2ad.cpp chkpoint_two.cpp compare.cpp dynamic.cpp get_started.cpp ode.cpp ) # END_SORT_THIS_LINE_MINUS_2 set_compile_flags( example_chkpoint_two "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(example_chkpoint_two EXCLUDE_FROM_ALL ${source_list}) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_chkpoint_two ${cppad_lib} ${colpack_libs} ) # # check_example_chkpoint_two add_check_executable(check_example chkpoint_two) ================================================ FILE: example/chkpoint_two/base2ad.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin chkpoint_two_base2ad.cpp} Checkpointing With base2ad: Example and Test ############################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end chkpoint_two_base2ad.cpp} */ // BEGIN C++ # include namespace { using CppAD::AD; typedef CPPAD_TESTVECTOR(AD) ADVector; typedef CPPAD_TESTVECTOR(size_t) size_vector; // f(y) = ( 3*y[0], 3*y[1] ) void f_algo(const ADVector& y, ADVector& z) { z[0] = 0.0; z[1] = 0.0; for(size_t k = 0; k < 3; k++) { z[0] += y[0]; z[1] += y[1]; } return; } // g(x) = ( x[0]^3, x[1]^3 ) void g_algo(const ADVector& x, ADVector& y) { y[0] = 1.0; y[1] = 1.0; for(size_t k = 0; k < 3; k++) { y[0] *= x[0]; y[1] *= x[1]; } return; } } bool base2ad(void) { bool ok = true; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // AD vectors holding x, y, and z values size_t nx = 2, ny = 2, nz = 2; ADVector ax(nx), ay(ny), az(nz); // record the function g_fun(x) for(size_t j = 0; j < nx; j++) ax[j] = double(j + 1); Independent(ax); g_algo(ax, ay); CppAD::ADFun g_fun(ax, ay); // record the function f_fun(y) Independent(ay); f_algo(ay, az); CppAD::ADFun f_fun(ay, az); // create checkpoint versions of f and g bool internal_bool = true; bool use_hes_sparsity = true; bool use_base2ad = true; bool use_in_parallel = false; CppAD::chkpoint_two f_chk(f_fun, "f_chk", internal_bool, use_hes_sparsity, use_base2ad, use_in_parallel ); CppAD::chkpoint_two g_chk(g_fun, "g_chk", internal_bool, use_hes_sparsity, use_base2ad, use_in_parallel ); // Record a version of z = f[g(x)] = h(x) with checkpointing // h(x) = [ 3*x[0]^3 , 3*x[1]^3 ] Independent(ax); g_chk(ax, ay); f_chk(ay, az); CppAD::ADFun h_fun(ax, az); // Use base2ad to create and AD version of h CppAD::ADFun< AD, double> ah_fun = h_fun.base2ad(); // start recording AD operations Independent(ax); // record evaluate derivative of h_0 (x) az = ah_fun.Forward(0, ax); ADVector aw(nz), adw(nx); aw[0] = 1.0; for(size_t i = 1; i < nz; ++i) aw[i] = 0.0; adw = ah_fun.Reverse(1, aw); // k(x) = h_0 '(x) = [ 9*x[0]^2 , 0.0 ] CppAD::ADFun k_fun(ax, adw); // Evaluate the Jacobian of k(x) CPPAD_TESTVECTOR(double) x(nx); for(size_t j = 0; j < nx; ++j) x[j] = 2.0 + double(nx - j); CPPAD_TESTVECTOR(double) J = k_fun.Jacobian(x); // check result for(size_t i = 0; i < nz; ++i) { for(size_t j = 0; j < nx; ++j) { double Jij = J[i * nx + j]; if( i == 0 && j == 0 ) { double check = 18.0 * x[0]; ok &= CppAD::NearEqual(Jij, check, eps99, eps99); } else ok &= Jij == 0.0; } } return ok; } // END C++ ================================================ FILE: example/chkpoint_two/chkpoint_two.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin chkpoint_two.cpp} chkpoint_two Examples and Tests Driver ###################################### Running These Tests ******************* After executing the :ref:`cmake-name` command form the :ref:`download@Distribution Directory`, you can build and run these tests with the commands:: cd build make check_example_chkpoint_two Note that your choice of :ref:`cmake@generator` may require using an different version of make; e.g., ``ninja`` . {xrst_literal // BEGIN C++ // END C++ } {xrst_end chkpoint_two.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ // CPPAD_HAS_* defines # include // for thread_alloc # include // test runner # include // external complied tests extern bool base2ad(void); extern bool compare(void); extern bool dynamic(void); extern bool get_started(void); extern bool ode(void); // main program that runs all the tests int main(void) { std::string group = "example/chkpoint_two"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // external compiled tests Run( base2ad, "base2ad" ); Run( compare, "compare" ); Run( dynamic, "dynamic" ); Run( get_started, "get_started" ); Run( ode, "ode" ); // // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } // END C++ ================================================ FILE: example/chkpoint_two/compare.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin chkpoint_two_compare.cpp} Compare With and Without Checkpointing: Example and Test ######################################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end chkpoint_two_compare.cpp} */ // BEGIN C++ # include namespace { using CppAD::AD; typedef CPPAD_TESTVECTOR(AD) ADVector; typedef CPPAD_TESTVECTOR(size_t) size_vector; void f_algo(const ADVector& y, ADVector& z) { z[0] = 0.0; z[1] = 0.0; for(size_t k = 0; k < 3; k++) { z[0] += y[0]; z[1] += y[1]; } return; } void g_algo(const ADVector& x, ADVector& y) { y[0] = 1.0; y[1] = 1.0; for(size_t k = 0; k < 3; k++) { y[0] *= x[0]; y[1] *= x[1]; } return; } bool equal( const CppAD::sparse_rc& pattern_left , const CppAD::sparse_rc& pattern_right ) { size_vector row_major_left = pattern_left.row_major(); size_vector row_major_right = pattern_right.row_major(); bool ok = pattern_left.nnz() == pattern_right.nnz(); if( ! ok ) return ok; for(size_t k = 0; k < pattern_left.nnz(); ++k) { size_t r_left = pattern_left.row()[ row_major_left[k] ]; size_t c_left = pattern_left.col()[ row_major_left[k] ]; size_t r_right = pattern_right.row()[ row_major_right[k] ]; size_t c_right = pattern_right.col()[ row_major_right[k] ]; ok &= (r_left == r_right) && (c_left == c_right); } return ok; } } bool compare(void) { bool ok = true; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // AD vectors holding x, y, and z values size_t nx = 2, ny = 2, nz = 2; ADVector ax(nx), ay(ny), az(nz); // record the function g_fun(x) for(size_t j = 0; j < nx; j++) ax[j] = double(j + 1); Independent(ax); g_algo(ax, ay); CppAD::ADFun g_fun(ax, ay); // record the function f_fun(y) Independent(ay); f_algo(ay, az); CppAD::ADFun f_fun(ay, az); // create checkpoint versions of f and g bool internal_bool = true; bool use_hes_sparsity = true; bool use_base2ad = false; bool use_in_parallel = false; CppAD::chkpoint_two f_chk(f_fun, "f_chk", internal_bool, use_hes_sparsity, use_base2ad, use_in_parallel ); CppAD::chkpoint_two g_chk(g_fun, "g_chk", internal_bool, use_hes_sparsity, use_base2ad, use_in_parallel ); // Record a version of z = f[g(x)] without checkpointing Independent(ax); g_algo(ax, ay); f_algo(ay, az); CppAD::ADFun check_not(ax, az); // Record a version of z = f[g(x)] with checkpointing Independent(ax); g_chk(ax, ay); f_chk(ay, az); CppAD::ADFun check_yes(ax, az); // checkpointing should use fewer operations ok &= check_not.size_var() > check_yes.size_var(); // this does not really save space because f and g are only used once ok &= check_not.size_var() <= check_yes.size_var() + f_fun.size_var() + g_fun.size_var(); // compare forward mode results for orders 0, 1, 2 size_t q1 = 3; // order_up + 1 CPPAD_TESTVECTOR(double) x_q(nx*q1), z_not(nz*q1), z_yes(nz*q1); for(size_t j = 0; j < nx; j++) { for(size_t k = 0; k < q1; k++) x_q[ j * q1 + k ] = 1.0 / double(q1 - k); } z_not = check_not.Forward(q1-1, x_q); z_yes = check_yes.Forward(q1-1, x_q); for(size_t i = 0; i < nz; i++) { for(size_t k = 0; k < q1; k++) { double zik_not = z_not[ i * q1 + k]; double zik_yes = z_yes[ i * q1 + k]; ok &= NearEqual(zik_not, zik_yes, eps99, eps99); } } // compare reverse mode results for orders 0, 1, 2 CPPAD_TESTVECTOR(double) w(nz*q1), dw_not(nx*q1), dw_yes(nx*q1); for(size_t i = 0; i < nz * q1; i++) w[i] = 1.0 / double(i + 1); dw_not = check_not.Reverse(q1, w); dw_yes = check_yes.Reverse(q1, w); for(size_t j = 0; j < nx; j++) { for(size_t k = 0; k < q1; k++) { double dwjk_not = dw_not[ j * q1 + k]; double dwjk_yes = dw_yes[ j * q1 + k]; ok &= NearEqual(dwjk_not, dwjk_yes, eps99, eps99); } } // compare Jacobian sparsity patterns CppAD::sparse_rc pattern_in, pattern_not, pattern_yes; pattern_in.resize(nx, nx, nx); for(size_t k = 0; k < nx; ++k) pattern_in.set(k, k, k); bool transpose = false; bool dependency = false; internal_bool = false; // for_jac_sparsity (not internal_bool is false) check_not.for_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_not ); pattern_in.resize(nz, nz, nz); for(size_t k = 0; k < nz; ++k) pattern_in.set(k, k, k); // forward and reverse Jacobian sparsity should give same answer check_yes.rev_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_yes ); ok &= equal(pattern_not, pattern_yes ); // compare Hessian sparsity patterns CPPAD_TESTVECTOR(bool) select_x(nx), select_z(nz); for(size_t j = 0; j < nx; ++j) select_x[j] = true; for(size_t i = 0; i < nz; ++i) select_z[i] = true; transpose = false; // Reverse should give same results as forward because // previous for_jac_sparsity used identity for pattern_in. // Note that internal_bool must be same as in call to for_sparse_jac. check_not.rev_hes_sparsity( select_z, transpose, internal_bool, pattern_yes ); // internal_bool need not be the same during a call to for_hes_sparsity internal_bool = ! internal_bool; check_yes.for_hes_sparsity( select_x, select_z, internal_bool, pattern_not ); ok &= equal(pattern_not, pattern_yes); // return ok; } // END C++ ================================================ FILE: example/chkpoint_two/dynamic.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin chkpoint_two_dynamic.cpp} Checkpoint Functions with Dynamic Parameters: Example and Test ############################################################## Purpose ******* This example demonstrates using dynamic parameters with a :ref:`chkpoint_two-name` function. g(x) **** For this example, the checkpoint function :math:`g : \B{R}^2 \rightarrow \B{R}^3` is defined by .. math:: g(x) = \left( \begin{array}{c} x_0 \cdot p_0 \\ x_0 \cdot x_ 0 \\ x_1 \cdot x_ 0 \end{array} \right) where :math:`p_0` is a dynamic parameter in the definition of :math:`g(x)` f(x) **** The function :math:`f(x) : \B{R}^2 \rightarrow \B{R}^3` is defined by :math:`f(x) = q_0 \cdot g(x)` where :math:`q_0` is a dynamic parameter in the definition of :math:`f(x)`. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end chkpoint_two_dynamic.cpp} */ // BEGIN C++ # include bool dynamic(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * CppAD::numeric_limits::epsilon(); // // // record the function g(x) size_t nx = 2, ny = 3, np = 1, nq = 1; CPPAD_TESTVECTOR( AD ) ax(nx), ay(ny), ap(np), aq(nq); for(size_t j = 0; j < nx; j++) ax[j] = double(j); ap[0] = 2.0; size_t abort_op_index = 0; bool record_compare = true; Independent(ax, abort_op_index, record_compare, ap); ay[0] = ax[0] * ap[0]; ay[1] = ax[0] * ax[0]; ay[2] = ax[1] * ax[0]; CppAD::ADFun g_fun(ax, ay); // // make a checkpoint version of g std::string name = "g(x)"; bool internal_bool = true; bool use_hes_sparsity = false; bool use_base2ad = false; bool use_in_parallel = false; CppAD::chkpoint_two g_chk(g_fun, name, internal_bool, use_hes_sparsity, use_base2ad, use_in_parallel ); // // record f(x) using checkpoint version of g(x) aq[0] = 3.0; Independent(ax, abort_op_index, record_compare, aq); g_chk(ax, ay); for(size_t i = 0; i < ny; ++i) ay[i] = aq[0] * ay[i]; CppAD::ADFun f_fun(ax, ay); // // vectors of doubles CPPAD_TESTVECTOR(double) x(nx), y(ny), p(np), q(nq); // // set dynamic parameters in g_chk p[0] = 4.0; g_chk.new_dynamic(p); // // set dynamic parameters in f_fun q[0] = 5.0; f_fun.new_dynamic(q); // // evaluate f(x) for(size_t j = 0; j < nx; ++j) x[j] = 6.0 + double(j); y = f_fun.Forward(0, x); // // check result double check; check = q[0] * x[0] * p[0]; ok &= NearEqual(check, y[0], eps99, eps99); check = q[0] * x[0] * x[0]; ok &= NearEqual(check, y[1], eps99, eps99); check = q[0] * x[1] * x[0]; ok &= NearEqual(check, y[2], eps99, eps99); // return ok; } // END C++ ================================================ FILE: example/chkpoint_two/get_started.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin chkpoint_two_get_started.cpp} Get Started Checkpointing: Example and Test ########################################### Purpose ******* Break a large computation into pieces and only store values at the interface of the pieces. In actual applications, there may many uses of each function and many more functions. f * The function :math:`f : \B{R}^2 \rightarrow \B{R}^2` is defined by .. math:: f(y) = \left( \begin{array}{c} y_0 + y_0 + y_0 \\ y_1 + y_1 + y_1 \end{array} \right) g * The function :math:`g : \B{R}^2 \rightarrow \B{R}^2` defined by .. math:: g(x) = \left( \begin{array}{c} x_0 \cdot x_0 \cdot x_0 \\ x_1 \cdot x_1 \cdot x_1 \end{array} \right) f[g(x)] ******* The function :math:`f[g(x)]` is given by .. math:: f[g(x)] = f \left[ \begin{array}{c} x_0^3 \\ x_1^3 \end{array} \right] = \left[ \begin{array}{c} 3 x_0^3 \\ 3 x_1^3 \end{array} \right] Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end chkpoint_two_get_started.cpp} */ // BEGIN C++ # include namespace { using CppAD::AD; typedef CPPAD_TESTVECTOR(AD) ADVector; void f_algo(const ADVector& y, ADVector& z) { z[0] = 0.0; z[1] = 0.0; for(size_t k = 0; k < 3; k++) { z[0] += y[0]; z[1] += y[1]; } return; } void g_algo(const ADVector& x, ADVector& y) { y[0] = 1.0; y[1] = 1.0; for(size_t k = 0; k < 3; k++) { y[0] *= x[0]; y[1] *= x[1]; } return; } } bool get_started(void) { bool ok = true; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // AD vectors holding x, y, and z values size_t nx = 2, ny = 2, nz = 2; ADVector ax(nx), ay(ny), az(nz); // record the function g_fun(x) for(size_t j = 0; j < nx; j++) ax[j] = double(j + 1); Independent(ax); g_algo(ax, ay); CppAD::ADFun g_fun(ax, ay); // record the function f_fun(y) Independent(ay); f_algo(ay, az); CppAD::ADFun f_fun(ay, az); // create checkpoint versions of f and g bool internal_bool = false; bool use_hes_sparsity = false; bool use_base2ad = false; bool use_in_parallel = false; CppAD::chkpoint_two f_chk( f_fun, "f_chk", internal_bool, use_hes_sparsity, use_base2ad, use_in_parallel ); CppAD::chkpoint_two g_chk( g_fun, "g_chk", internal_bool, use_hes_sparsity, use_base2ad, use_in_parallel ); // Record a version of z = f[g(x)] using checkpointing Independent(ax); g_chk(ax, ay); f_chk(ay, az); CppAD::ADFun fg(ax, az); // zero order forward mode CPPAD_TESTVECTOR(double) x(nx), z(nz); for(size_t j = 0; j < nx; j++) x[j] = 1.0 / double(1 + j); z = fg.Forward(0, x); for(size_t i = 0; i < nz; i++) { double check = 3.0 * x[i] * x[i] * x[i]; ok &= NearEqual(z[i], check, eps99, eps99); } // optimize fg and check that results do not change fg.optimize(); for(size_t i = 0; i < nz; i++) { double check = 3.0 * x[i] * x[i] * x[i]; ok &= NearEqual(z[i], check, eps99, eps99); } // return ok; } // END C++ ================================================ FILE: example/chkpoint_two/ode.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin chkpoint_two_ode.cpp} {xrst_spell kutta runge } Checkpointing an ODE Solver: Example and Test ############################################# Purpose ******* In this example we :ref:`checkpoint` one step of an ODE solver. Problem ******* We consider the initial value problem with parameter :math:`x` defined by, :math:`z(0, x) = z_0 (x)`, .. math:: \partial_t z(t, x ) = h [ x , z(t, x) ] Note that if :math:`t` needs to be in the equation, one can define the first component of :math:`z(t, x)` to be equal to :math:`t`. ODE Solver ********** For this example, we consider the Fourth order Runge-Kutta ODE solver. Given an approximation solution at time :math:`t_k` denoted by :math:`\tilde{z}_k (x)`, and :math:`\Delta t = t_{k+1} - t_k`, it defines the approximation solution :math:`\tilde{z}_{k+1} (x)` at time :math:`t_{k+1}` by .. math:: :nowrap: \begin{eqnarray} h_1 & = & h [ x , \tilde{z}_k (x) ] \\ h_2 & = & h [ x , \tilde{z}_k (x) + \Delta t \; h_1 / 2 ] \\ h_3 & = & h [ x , \tilde{z}_k (x) + \Delta t \; h_2 / 2 ] \\ h_4 & = & h [ x , \tilde{z}_k (x) + \Delta t \; h_3 ] \\ \tilde{z}_{k+1} (x) & = & \tilde{z}_k (x) + \Delta t \; ( h_1 + 2 h_2 + 2 h_3 + h_4 ) / 6 \end{eqnarray} If :math:`\tilde{z}_k (x) = z_k (x)`, :math:`\tilde{z}_{k+1} (x) = z_{k+1} (x) + O( \Delta t^5 )`. Other ODE solvers can use a similar method to the one used below. ODE *** For this example the ODE is defined by :math:`z(0, x) = 0` and .. math:: h[ x, z(t, x) ] = \left( \begin{array}{c} x_0 \\ x_1 z_0 (t, x) \\ \vdots \\ x_{n-1} z_{n-2} (t, x) \end{array} \right) = \left( \begin{array}{c} \partial_t z_0 (t , x) \\ \partial_t z_1 (t , x) \\ \vdots \\ \partial_t z_{n-1} (t , x) \end{array} \right) Solution ******** The solution of the ODE for this example, which is used to check the results, can be calculated by starting with the first row and then using the solution for the first row to solve the second and so on. Doing this we obtain .. math:: z(t, x) = \left( \begin{array}{c} x_0 t \\ x_1 x_0 t^2 / 2 \\ \vdots \\ x_{n-1} x_{n-2} \ldots x_0 t^n / n ! \end{array} \right) Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end chkpoint_two_ode.cpp} */ // BEGIN C++ # include namespace { using CppAD::AD; typedef AD a1double; typedef AD a2double; // typedef CPPAD_TESTVECTOR( double ) a0vector; typedef CPPAD_TESTVECTOR( a1double ) a1vector; typedef CPPAD_TESTVECTOR( a2double ) a2vector; // // set once by main and kept that way double delta_t_ = std::numeric_limits::quiet_NaN(); size_t n_ = 0; // // The function h( x , y) template FloatVector h(const FloatVector& x, const FloatVector& y) { assert( size_t( x.size() ) == n_ ); assert( size_t( y.size() ) == n_ ); FloatVector result(n_); result[0] = x[0]; for(size_t i = 1; i < n_; i++) result[i] = x[i] * y[i-1]; return result; } // The 4-th Order Runge-Kutta Step template FloatVector Runge4(const FloatVector& x, const FloatVector& z0 ) { assert( size_t( x.size() ) == n_ ); assert( size_t( z0.size() ) == n_ ); // typedef typename FloatVector::value_type Float; // Float dt = Float(delta_t_); size_t m = z0.size(); // FloatVector h1(m), h2(m), h3(m), h4(m), result(m); h1 = h( x, z0 ); // for(size_t i = 0; i < m; i++) h2[i] = z0[i] + dt * h1[i] / 2.0; h2 = h( x, h2 ); // for(size_t i = 0; i < m; i++) h3[i] = z0[i] + dt * h2[i] / 2.0; h3 = h( x, h3 ); // for(size_t i = 0; i < m; i++) h4[i] = z0[i] + dt * h3[i]; h4 = h( x, h4 ); // for(size_t i = 0; i < m; i++) { Float dz = dt * ( h1[i] + 2.0*h2[i] + 2.0*h3[i] + h4[i] ) / 6.0; result[i] = z0[i] + dz; } return result; } // pack x and z into an axz vector template void pack( FloatVector& axz , const FloatVector& x , const FloatVector& z ) { assert( size_t( axz.size() ) == n_ + n_ ); assert( size_t( x.size() ) == n_ ); assert( size_t( z.size() ) == n_ ); // size_t offset = 0; for(size_t i = 0; i < n_; i++) axz[offset + i] = x[i]; offset += n_; for(size_t i = 0; i < n_; i++) axz[offset + i] = z[i]; } // unpack an axz vector template void unpack( const FloatVector& axz , FloatVector& x , FloatVector& z ) { assert( size_t( axz.size() ) == n_ + n_ ); assert( size_t( x.size() ) == n_ ); assert( size_t( z.size() ) == n_ ); // size_t offset = 0; for(size_t i = 0; i < n_; i++) x[i] = axz[offset + i]; offset += n_; for(size_t i = 0; i < n_; i++) z[i] = axz[offset + i]; } // Algorithm that z(t, x) void ode_algo(const a1vector& axz_in, a1vector& axz_out) { assert( size_t( axz_in.size() ) == n_ + n_ ); assert( size_t( axz_out.size() ) == n_ + n_ ); // // initial ode information a1vector x(n_), z0(n_); unpack(axz_in, x, z0); // // advance z(t, x) a1vector z1 = Runge4(x, z0); // // final ode information pack(axz_out, x, z1); // return; } } // bool ode(void) { bool ok = true; using CppAD::NearEqual; double eps = std::numeric_limits::epsilon(); // // number of terms in the differential equation n_ = 6; // // step size for the differentiail equation size_t n_step = 10; double T = 1.0; delta_t_ = T / double(n_step); // // set parameter value and initial value of the ode a1vector ax(n_), az0(n_); for(size_t i = 0; i < n_; i++) { ax[i] = a1double(i + 1); az0[i] = a1double(0); } // // pack ode information input vector // // function corresponding to one step of the ode Algorithm a1vector axz_in(2 * n_), axz_out(2 * n_); pack(axz_in, ax, az0); CppAD::Independent(axz_in); ode_algo(axz_in, axz_out); CppAD::ADFun ode_fun(axz_in, axz_out); // // create checkpoint version of the algorithm bool internal_bool = false; bool use_hes_sparsity = false; bool use_base2ad = false; bool use_in_parallel = false; CppAD::chkpoint_two ode_check(ode_fun, "ode", internal_bool, use_hes_sparsity, use_base2ad, use_in_parallel ); // // set the independent variables for recording CppAD::Independent( ax ); // // repack to get dependence on ax pack(axz_in, ax, az0); // // Now run the checkpoint algorithm n_step times for(size_t k = 0; k < n_step; k++) { ode_check(axz_in, axz_out); axz_in = axz_out; } // // Unpack the results (must use ax1 so do not overwrite ax) a1vector ax1(n_), az1(n_); unpack(axz_out, ax1, az1); // // We could record a complicated function of x and z(T, x) in f, // but make this example simpler we record x -> z(T, x). CppAD::ADFun f(ax, az1); // // check function values a0vector x(n_), z1(n_); for(size_t j = 0; j < n_; j++) x[j] = double(j + 1); z1 = f.Forward(0, x); // // separate calculation of z(t, x) a0vector check_z1(n_); check_z1[0] = x[0] * T; for(size_t i = 1; i < n_; i++) check_z1[i] = x[i] * T * check_z1[i-1] / double(i+1); // // expected accuracy for each component of of z(t, x) a0vector acc(n_); for(size_t i = 0; i < n_; i++) { if( i < 4 ) { // Runge-Kutta methods is exact for this case acc[i] = 10. * eps; } else { acc[i] = 1.0; for(size_t k = 0; k < 5; k++) acc[i] *= x[k] * delta_t_; } } // check z1(T, x) for(size_t i = 0; i < n_; i++) ok &= NearEqual(z1[i] , check_z1[i], acc[i], acc[i]); // // Now use f to compute a derivative. For this 'simple' example it is // the derivative of z_{n-1} (T, x) respect to x of the a0vector w(n_), dw(n_); for(size_t i = 0; i < n_; i++) { w[i] = 0.0; if( i == n_ - 1 ) w[i] = 1.0; } dw = f.Reverse(1, w); for(size_t j = 0; j < n_; j++) { double check = z1[n_ - 1] / x[j]; ok &= NearEqual(dw[j] , check, 100.*eps, 100.*eps); } // return ok; } // END C++ ================================================ FILE: example/compare_change/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the example/compare_change directory tests # # source_list SET(source_list compare_change.cpp) # # compile_flags set_compile_flags( example_compare_change "${cppad_debug_which}" "${source_list}" ) # # example_compare_change ADD_EXECUTABLE(example_compare_change EXCLUDE_FROM_ALL ${source_list}) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_compare_change ${cppad_lib} ${colpack_libs} ) # # check_example_compare_change add_check_executable(check_example compare_change) ================================================ FILE: example/compare_change/compare_change.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin compare_change.cpp} CompareChange and Re-Tape: Example and Test ########################################### This test is run as a separate program so that it does not mix debug and release versions of the template functions it calls. {xrst_literal // BEGIN C++ // END C++ } {xrst_end compare_change.cpp} */ // BEGIN C++ # include // empty namespace namespace { // // Minimum template Type Minimum(const Type &x, const Type &y) { // Use a comparison to compute the min(x, y) // (note that CondExp would never require retaping). if( x < y ) return x; return y; } // // error_info struct error_info { bool known; int line; std::string file; std::string exp; std::string msg; }; // // error_handler void error_handler( bool known , int line , const char *file , const char *exp , const char *msg ) { // error handler must not return, so throw an exception error_info info; info.known = known; info.line = line; info.file = file; info.exp = exp; info.msg = msg; throw info; } } // // compare_change bool compare_change(void) { bool ok = true; using CppAD::AD; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 3.; ax[1] = 4.; // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = Minimum(ax[0], ax[1]); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // set count to one (not necessry because is its default value) f.compare_change_count(1); // evaluate zero mode Forward where comparison has the same result // as during taping; i.e., x[0] < x[1]. CPPAD_TESTVECTOR(double) x(n), y(m); x[0] = 2.; x[1] = 3.; y = f.Forward(0, x); ok &= (y[0] == x[0]); ok &= (y[0] == Minimum(x[0], x[1])); ok &= (f.compare_change_number() == 0); ok &= (f.compare_change_op_index() == 0); // evaluate zero mode Forward where comparison has different result // as during taping; i.e., x[0] >= x[1]. x[0] = 3.; x[1] = 2.; y = f.Forward(0, x); ok &= (y[0] == x[0]); ok &= (y[0] != Minimum(x[0], x[1])); ok &= (f.compare_change_number() == 1); ok &= (f.compare_change_op_index() > 0 ); size_t op_index = f.compare_change_op_index(); // Local block during which default CppAD error handler is replaced. // If you do not replace the default CppAD error handler, // and you run in the debugger, you will be able to inspect the // call stack and see that 'if( x < y )' is where the comparison is. bool missed_error = true; { CppAD::ErrorHandler local_error_handler(error_handler); std::string check_msg = "Operator index equals abort_op_index in Independent"; try { // determine the operation index where the change occurred CppAD::Independent(ax, op_index); ay[0] = Minimum(ax[0], ax[1]); # ifdef NDEBUG // CppAD does not spend time checking operator index when // NDEBUG is defined missed_error = false; AD::abort_recording(); # endif } catch( error_info info ) { missed_error = false; ok &= info.known; ok &= info.msg == check_msg; // Must abort the recording so we can start a new one // (and to avoid a memory leak). AD::abort_recording(); } } ok &= ! missed_error; // set count to zero to demonstrate case where comparisons are not checked f.compare_change_count(0); y = f.Forward(0, x); ok &= (y[0] == x[0]); ok &= (y[0] != Minimum(x[0], x[1])); ok &= (f.compare_change_number() == 0); ok &= (f.compare_change_op_index() == 0); // now demonstrate that compare_change_number works for an optimized // tape (note that compare_change_op_index is always zero after optimize) f.optimize(); f.compare_change_count(1); y = f.Forward(0, x); ok &= (y[0] == x[0]); ok &= (y[0] != Minimum(x[0], x[1])); ok &= (f.compare_change_number() == 1); ok &= (f.compare_change_op_index() == 0); // now retape to get the a tape that agrees with the algorithm ax[0] = x[0]; ax[1] = x[1]; Independent(ax); ay[0] = Minimum(ax[0], ax[1]); f.Dependent(ax, ay); y = f.Forward(0, x); ok &= (y[0] == x[1]); ok &= (y[0] == Minimum(x[0], x[1])); ok &= (f.compare_change_number() == 0); ok &= (f.compare_change_op_index() == 0); return ok; } // main program that runs this test int main(void) { std::string group = "example/compare_change"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh Run( compare_change, "compare_change" ); // // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } // END C++ ================================================ FILE: example/cppad_code_gen/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list cppad_code_gen.cpp file.cpp function.cpp jac_as_fun.cpp jacobian.cpp sparse_jac_as_fun.cpp sparse_jacobian.cpp ) # END_SORT_THIS_LINE_MINUS_2 set_compile_flags( example_cppad_code_gen "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(example_cppad_code_gen EXCLUDE_FROM_ALL ${source_list}) # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_cppad_code_gen ${cppad_lib} ${colpack_libs} ) # # check_example_cppad_code_gen add_check_executable(check_example cppad_code_gen) ================================================ FILE: example/cppad_code_gen/cppad_code_gen.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppad_code_gen.cpp} CppADCodeGen Examples and Tests Driver ###################################### Running These Tests ******************* After executing the :ref:`cmake-name` command form the :ref:`download@Distribution Directory`, you can build and run these tests with the commands:: cd build make check_example_cppad_code_gen Note that your choice of :ref:`cmake@generator` may require using an different version of make; e.g., ``ninja`` . {xrst_literal // BEGIN C++ // END C++ } {xrst_end cppad_code_gen.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ // CPPAD_HAS_* defines # include // for thread_alloc # include // test runner # include // BEGIN_SORT_THIS_LINE_PLUS_2 // external compiled tests extern bool file(void); extern bool function(void); extern bool jac_as_fun(void); extern bool jacobian(void); extern bool sparse_jac_as_fun(void); extern bool sparse_jacobian(void); // END_SORT_THIS_LINE_MINUS_1 // main program that runs all the tests int main(void) { std::string group = "example/cppad_code_gen"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // BEGIN_SORT_THIS_LINE_PLUS_2 // external compiled tests Run( file, "file" ); Run( function, "function" ); Run( jac_as_fun, "jac_as_fun" ); Run( jacobian, "jacobian" ); Run( sparse_jac_as_fun, "sparse_jac_as_fun" ); Run( sparse_jacobian, "sparse_jacobian" ); // END_SORT_THIS_LINE_MINUS_1 // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } // END C++ ================================================ FILE: example/cppad_code_gen/file.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin code_gen_fun_file.cpp} File Store and Retrieve a Code Gen Function: Example and Test ############################################################# {xrst_literal // BEGIN C++ // END C++ } {xrst_end code_gen_fun_file.cpp} */ // BEGIN C++ # include namespace { void store(const std::string& file_name) { // typedef CppAD::cg::CG c_double; typedef CppAD::AD ac_double; typedef CppAD::vector ac_vector; // // domain space vector size_t n = 2; ac_vector ac_x(n); for(size_t j = 0; j < n; ++j) ac_x[j] = 1.0 / double(j + 1); // declare independent variables and start tape recording CppAD::Independent(ac_x); // range space vector size_t m = 3; ac_vector ac_y(m); for(size_t i = 0; i < m; ++i) ac_y[i] = double(i + 1) * sin( ac_x[i % n] ); // create c_f: x -> y and stop tape recording CppAD::ADFun c_f(ac_x, ac_y); // create compiled version of c_f code_gen_fun f(file_name, c_f); } } bool file(void) { bool ok = true; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // Store the compiled file in a dynamic link library std::string file_name = "example_lib"; store(file_name); // // retrieve the compled function from the file // (compiling take much longer than retrieving the file) code_gen_fun f(file_name); // evaluate the compiled function size_t n = 2, m = 3; CppAD::vector x(n), y(m); for(size_t j = 0; j < n; ++j) x[j] = 1.0 / double(j + 2); y = f(x); // check function values for(size_t i = 0; i < m; ++i) { double check = double(i + 1) * std::sin( x[i % n] ); ok &= CppAD::NearEqual(y[i] , check, eps99, eps99); } return ok; } // END C++ ================================================ FILE: example/cppad_code_gen/function.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin code_gen_fun_function.cpp} Evaluate a Code Gen Function: Example and Test ############################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end code_gen_fun_function.cpp} */ // BEGIN C++ # include bool function(void) { bool ok = true; // typedef CppAD::cg::CG c_double; typedef CppAD::AD ac_double; // typedef CppAD::vector d_vector; typedef CppAD::vector ac_vector; // double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 2; ac_vector ac_x(n); for(size_t j = 0; j < n; ++j) ac_x[j] = 1.0 / double(j + 1); // declare independent variables and start tape recording CppAD::Independent(ac_x); // range space vector size_t m = 3; ac_vector ac_y(m); for(size_t i = 0; i < m; ++i) ac_y[i] = double(i + 1) * sin( ac_x[i % n] ); // create c_f: x -> y and stop tape recording CppAD::ADFun c_f(ac_x, ac_y); // create compiled version of c_f std::string file_name = "example_lib"; code_gen_fun f(file_name, c_f); // evaluate the compiled function d_vector x(n), y(m); for(size_t j = 0; j < n; ++j) x[j] = 1.0 / double(j + 2); y = f(x); // check function values for(size_t i = 0; i < m; ++i) { double check = double(i + 1) * std::sin( x[i % n] ); ok &= CppAD::NearEqual(y[i] , check, eps99, eps99); } return ok; } // END C++ ================================================ FILE: example/cppad_code_gen/jac_as_fun.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin code_gen_fun_jac_as_fun.cpp} Pass Jacobian as Code Gen Function: Example and Test #################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end code_gen_fun_jac_as_fun.cpp} */ // BEGIN C++ # include bool jac_as_fun(void) { bool ok = true; // typedef CppAD::cg::CG c_double; typedef CppAD::AD ac_double; // typedef CppAD::vector d_vector; typedef CppAD::vector ac_vector; // double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 2; ac_vector ac_x(n); for(size_t j = 0; j < n; ++j) ac_x[j] = 1.0 / double(j + 1); // declare independent variables and start tape recording CppAD::Independent(ac_x); // range space vector size_t m = 3; ac_vector ac_y(m); for(size_t i = 0; i < m; ++i) ac_y[i] = double(i + 1) * sin( ac_x[i % n] ); // create f: x -> y and stop tape recording CppAD::ADFun c_f(ac_x, ac_y); // create a version of f that evaluates using ac_double CppAD::ADFun ac_f = c_f.base2ad(); // Independent variables while evaluating Jacobian CppAD::Independent(ac_x); // Evaluate the Jacobian using any CppAD method // (for this example we just use the simplest thing) ac_vector ac_J = ac_f.Jacobian(ac_x); // create g: x -> f'(x) CppAD::ADFun c_g(ac_x, ac_J); // create compiled version of c_g std::string file_name = "example_lib"; code_gen_fun g(file_name, c_g); // evaluate the compiled jacobian d_vector x(n), J(m * n); for(size_t j = 0; j < n; ++j) x[j] = 1.0 / double(j + 2); J = g(x); // check Jaociban values for(size_t i = 0; i < m; ++i) { for(size_t j = 0; j < n; ++j) { double check = 0.0; if( j == i % n ) check = double(i + 1) * cos( x[i % n] ); ok &= CppAD::NearEqual(J[i * n + j] , check, eps99, eps99); } } return ok; } // END C++ ================================================ FILE: example/cppad_code_gen/jacobian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin code_gen_fun_jacobian.cpp} Evaluate Jacobian of a Code Gen Function: Example and Test ########################################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end code_gen_fun_jacobian.cpp} */ // BEGIN C++ # include bool jacobian(void) { bool ok = true; // typedef CppAD::cg::CG c_double; typedef CppAD::AD ac_double; // typedef CppAD::vector d_vector; typedef CppAD::vector ac_vector; // double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 2; ac_vector ac_x(n); for(size_t j = 0; j < n; ++j) ac_x[j] = 1.0 / double(j + 1); // declare independent variables and start tape recording CppAD::Independent(ac_x); // range space vector size_t m = 3; ac_vector ac_y(m); for(size_t i = 0; i < m; ++i) ac_y[i] = double(i + 1) * sin( ac_x[i % n] ); // create c_f: x -> y and stop tape recording CppAD::ADFun c_f(ac_x, ac_y); // create compiled version of c_f std::string file_name = "example_lib"; code_gen_fun::evaluation_enum eval_jac = code_gen_fun::dense_enum; code_gen_fun f(file_name, c_f, eval_jac); // evaluate the compiled jacobian d_vector x(n), J; for(size_t j = 0; j < n; ++j) x[j] = 1.0 / double(j + 2); J = f.jacobian(x); // check Jaociban values for(size_t i = 0; i < m; ++i) { for(size_t j = 0; j < n; ++j) { double check = 0.0; if( j == i % n ) check = double(i + 1) * cos( x[i % n] ); ok &= CppAD::NearEqual(J[i * n + j] , check, eps99, eps99); } } return ok; } // END C++ ================================================ FILE: example/cppad_code_gen/sparse_jac_as_fun.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin code_gen_fun_sparse_jac_as_fun.cpp} Pass Sparse Jacobian as Code Gen Function: Example and Test ########################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end code_gen_fun_sparse_jac_as_fun.cpp} */ // BEGIN C++ # include bool sparse_jac_as_fun(void) { bool ok = true; // typedef CppAD::cg::CG c_double; typedef CppAD::AD ac_double; // typedef CppAD::vector s_vector; typedef CppAD::vector d_vector; typedef CppAD::vector ac_vector; // double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 2; ac_vector ac_x(n); for(size_t j = 0; j < n; ++j) ac_x[j] = 1.0 / double(j + 1); // declare independent variables and start tape recording CppAD::Independent(ac_x); // range space vector size_t m = 3; ac_vector ac_y(m); for(size_t i = 0; i < m; ++i) ac_y[i] = double(i + 1) * sin( ac_x[i % n] ); // create f: x -> y and stop tape recording CppAD::ADFun c_f(ac_x, ac_y); // create a version of f that evaluates using ac_double CppAD::ADFun ac_f = c_f.base2ad(); // Independent variables while evaluating Jacobian CppAD::Independent(ac_x); // ---------------------------------------------------------------- // Sparse Jacobian evaluation using any CppAD method // (there are lots of choices here) // // pattern_eye (pattern for identity matrix) CppAD::sparse_rc pattern_eye(n, n, n); for(size_t k = 0; k < n; ++k) pattern_eye.set(k, k, k); // // pattern_jac bool transpose = false; bool dependency = false; bool internal_bool = true; CppAD::sparse_rc pattern_jac; // note that c_f and ac_f have the same sparsity pattern c_f.for_jac_sparsity( pattern_eye, transpose, dependency, internal_bool, pattern_jac ); // // ac_Jrcv CppAD::sparse_rcv ac_Jrcv( pattern_jac ); CppAD::sparse_jac_work work; std::string coloring = "cppad"; size_t group_max = n; ac_f.sparse_jac_for( group_max, ac_x, ac_Jrcv, pattern_jac, coloring, work ); // // create g: x -> non-zero elements of Jacobian CppAD::ADFun c_g(ac_x, ac_Jrcv.val()); // create compiled version of c_g std::string file_name = "example_lib"; code_gen_fun g(file_name, c_g); // evaluate the compiled jacobian d_vector x(n); for(size_t j = 0; j < n; ++j) x[j] = 1.0 / double(j + 2); d_vector val = g(x); // check Jaociban values size_t nnz = pattern_jac.nnz(); const s_vector& row(pattern_jac.row()); const s_vector& col(pattern_jac.col()); s_vector row_major = pattern_jac.row_major(); // size_t k = 0; ok &= val.size() == nnz; for(size_t i = 0; i < m; ++i) { for(size_t j = 0; j < n; ++j) { if( j == i % n ) { size_t ell = row_major[k]; double check = double(i + 1) * cos( x[i % n] ); ok &= i == row[ell]; ok &= j == col[ell]; ok &= CppAD::NearEqual(val[k], check, eps99, eps99); ++k; } } } ok &= k == nnz; return ok; } // END C++ ================================================ FILE: example/cppad_code_gen/sparse_jacobian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin code_gen_fun_sparse_jacobian.cpp} Evaluate Sparse Jacobian of a Code Gen Function: Example and Test ################################################################# {xrst_literal // BEGIN C++ // END C++ } {xrst_end code_gen_fun_sparse_jacobian.cpp} */ // BEGIN C++ # include bool sparse_jacobian(void) { bool ok = true; // typedef CppAD::cg::CG c_double; typedef CppAD::AD ac_double; // typedef CppAD::vector d_vector; typedef CppAD::vector ac_vector; // double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 2; ac_vector ac_x(n); for(size_t j = 0; j < n; ++j) ac_x[j] = 1.0 / double(j + 1); // declare independent variables and start tape recording CppAD::Independent(ac_x); // range space vector size_t m = 3; ac_vector ac_y(m); for(size_t i = 0; i < m; ++i) ac_y[i] = double(i + 1) * sin( ac_x[i % n] ); // create c_f: x -> y and stop tape recording CppAD::ADFun c_f(ac_x, ac_y); // create compiled version of c_f std::string file_name = "example_lib"; code_gen_fun::evaluation_enum eval_jac = code_gen_fun::sparse_enum; code_gen_fun f(file_name, c_f, eval_jac); // evaluate the compiled sparse_jacobian d_vector x(n); for(size_t j = 0; j < n; ++j) x[j] = 1.0 / double(j + 2); CppAD::sparse_rcv< CppAD::vector, CppAD::vector > Jrcv; // This assignment uses move semantics Jrcv = f.sparse_jacobian(x); // check Jaociban values ok &= Jrcv.nr() == m; ok &= Jrcv.nc() == n; const CppAD::vector& row( Jrcv.row() ); const CppAD::vector& col( Jrcv.col() ); const CppAD::vector& val( Jrcv.val() ); CppAD::vector row_major = Jrcv.row_major(); size_t k = 0; for(size_t i = 0; i < m; ++i) { for(size_t j = 0; j < n; ++j) { if( j == i % n ) { double check = double(i + 1) * cos( x[i % n] ); size_t ell = row_major[k]; ok &= row[ell] == i; ok &= col[ell] == j; ok &= CppAD::NearEqual(val[ell] , check, eps99, eps99); ++k; } } } ok &= Jrcv.nnz() == k; // return ok; } // END C++ ================================================ FILE: example/general/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-23 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the example/general directory tests # # adolc_sources IF( cppad_has_adolc ) SET(adolc_sources mul_level_adolc.cpp mul_level_adolc_ode.cpp) ELSE( cppad_has_adolc ) SET(adolc_sources "") ENDIF( cppad_has_adolc ) # # eigen_sources IF( cppad_has_eigen ) SET(eigen_sources eigen_det.cpp eigen_array.cpp) ELSE( cppad_has_eigen ) SET(eigen_sources "") ENDIF( cppad_has_eigen ) # # BEGIN_SORT_THIS_LINE_PLUS_4 SET(source_list ${adolc_sources} ${eigen_sources} abort_recording.cpp acos.cpp acosh.cpp ad_assign.cpp ad_ctor.cpp ad_fun.cpp ad_in_c.cpp ad_input.cpp ad_output.cpp add.cpp add_eq.cpp asin.cpp asinh.cpp atan.cpp atan2.cpp atanh.cpp azmul.cpp base2ad.cpp base2vec_ad.cpp base_alloc.hpp base_require.cpp bender_quad.cpp bool_fun.cpp capacity_order.cpp change_param.cpp check_for_nan.cpp compare.cpp complex_poly.cpp con_dyn_var.cpp cond_exp.cpp cos.cpp cosh.cpp div.cpp div_eq.cpp equal_op_seq.cpp erf.cpp erfc.cpp exp.cpp expm1.cpp fabs.cpp for_one.cpp for_two.cpp forward.cpp forward_dir.cpp forward_order.cpp fun_assign.cpp fun_check.cpp fun_property.cpp function_name.cpp general.cpp hes_lagrangian.cpp hes_lu_det.cpp hes_minor_det.cpp hes_times_dir.cpp hessian.cpp independent.cpp integer.cpp interface2c.cpp interp_onetape.cpp interp_retape.cpp jac_lu_det.cpp jac_minor_det.cpp jacobian.cpp log.cpp log10.cpp log1p.cpp lu_ratio.cpp lu_vec_ad.cpp lu_vec_ad.hpp lu_vec_ad_ok.cpp mul.cpp mul_eq.cpp mul_level.cpp mul_level_ode.cpp near_equal_ext.cpp new_dynamic.cpp num_limits.cpp number_skip.cpp numeric_type.cpp ode_stiff.cpp opt_val_hes.cpp pow.cpp pow_nan.cpp print_for.cpp rev_checkpoint.cpp rev_one.cpp rev_two.cpp reverse_one.cpp reverse_three.cpp reverse_two.cpp sign.cpp sin.cpp sinh.cpp sqrt.cpp stack_machine.cpp sub.cpp sub_eq.cpp tan.cpp tanh.cpp tape_index.cpp taylor_ode.cpp unary_minus.cpp unary_plus.cpp value.cpp var2par.cpp vec_ad.cpp ) # END_SORT_THIS_LINE_MINUS_2 set_compile_flags( example_general "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(example_general EXCLUDE_FROM_ALL ${source_list}) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_general ${adolc_LINK_LIBRARIES} ${cppad_lib} ${colpack_libs} ) # # check_example_general add_check_executable(check_example general) ================================================ FILE: example/general/abort_recording.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin abort_recording.cpp} Abort Current Recording: Example and Test ######################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end abort_recording.cpp} */ // BEGIN C++ # include # include bool abort_recording(void) { bool ok = true; double eps = 10. * CppAD::numeric_limits::epsilon(); using CppAD::AD; try { // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) x(n); x[0] = 0.; // declare independent variables and start tape recording CppAD::Independent(x); // simulate an error during calculation of y and the execution // stream was aborted throw 1; } catch (int e) { ok &= (e == 1); // do this in case throw occurred after the call to Independent // (for case above this is known, but in general it is unknown) AD::abort_recording(); } /* Now make sure that we can start another recording */ // declare independent variables and start tape recording size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; CppAD::Independent(x); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = 2 * x[0]; // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // forward computation of partials w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= CppAD::NearEqual(dy[0], 2., eps, eps); return ok; } // END C++ ================================================ FILE: example/general/acos.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin acos.cpp} The AD acos Function: Example and Test ###################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end acos.cpp} */ // BEGIN C++ # include bool acos(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; // 10 times machine epsilon double eps = 10. * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // a temporary value AD cos_of_x0 = CppAD::cos(x[0]); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = CppAD::acos(cos_of_x0); // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value ok &= NearEqual(y[0] , x0, eps, eps); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 1., eps, eps); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], 1., eps, eps); // use a VecAD::reference object with acos CppAD::VecAD v(1); AD zero(0); v[zero] = cos_of_x0; AD result = CppAD::acos(v[zero]); ok &= NearEqual(result, x0, eps, eps); return ok; } // END C++ ================================================ FILE: example/general/acosh.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin acosh.cpp} The AD acosh Function: Example and Test ####################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end acosh.cpp} */ // BEGIN C++ # include bool acosh(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; // 10 times machine epsilon double eps = 10. * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // a temporary value AD cosh_of_x0 = CppAD::cosh(x[0]); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = CppAD::acosh(cosh_of_x0); // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value ok &= NearEqual(y[0] , x0, eps, eps); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 1., eps, eps); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], 1., eps, eps); // use a VecAD::reference object with acosh CppAD::VecAD v(1); AD zero(0); v[zero] = cosh_of_x0; AD result = CppAD::acosh(v[zero]); ok &= NearEqual(result, x0, eps, eps); return ok; } // END C++ ================================================ FILE: example/general/ad_assign.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ad_assign.cpp} AD Assignment: Example and Test ############################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end ad_assign.cpp} */ // BEGIN C++ # include bool ad_assign(void) { bool ok = true; // initialize test result flag using CppAD::AD; // so can use AD in place of CppAD::AD // assignment to base value AD a; a = 1.; ok &= a == 1.; // assignment to a value that converts to the base type a = 2; ok &= a == 2.; // assignment to an AD AD b(3.); a = b; ok &= a == 3.; // assignment to an VecAD element CppAD::VecAD v(1); v[0] = 4.; a = v[0]; ok &= a == 4.; return ok; } // END C++ ================================================ FILE: example/general/ad_ctor.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ad_ctor.cpp} AD Constructors: Example and Test ################################# {xrst_literal // BEGIN C++ // END C++ } {xrst_end ad_ctor.cpp} */ // BEGIN C++ # include bool ad_ctor(void) { bool ok = true; // initialize test result flag using CppAD::AD; // so can use AD in place of CppAD::AD // default constructor AD a; a = 0.; ok &= a == 0.; ok &= Constant(a); // constructor from base type AD b(1.); ok &= b == 1.; ok &= Constant(b); // constructor from another type that converts to the base type AD c(2); ok &= c == 2.; ok &= Constant(c); // constructor from AD AD d(c); ok &= d == c; // constructor from a VecAD element CppAD::VecAD v(1); v[0] = 3.; AD e( v[0] ); ok &= e == 3.; return ok; } // END C++ ================================================ FILE: example/general/ad_fun.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ad_fun.cpp} Creating Your Own Interface to an ADFun Object ############################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end ad_fun.cpp} */ // BEGIN C++ # include namespace { // This class is an example of a different interface to an AD function object template class my_ad_fun { private: CppAD::ADFun f; public: // default constructor my_ad_fun(void) { } // destructor ~ my_ad_fun(void) { } // Construct an my_ad_fun object with an operation sequence. // This is the same as for ADFun except that no zero // order forward sweep is done. Note Hessian and Jacobian do // their own zero order forward mode sweep. template my_ad_fun(const ADvector& x, const ADvector& y) { f.Dependent(x, y); } // same as ADFun::Jacobian template BaseVector jacobian(const BaseVector& x) { return f.Jacobian(x); } // same as ADFun::Hessian template BaseVector hessian(const BaseVector &x, const BaseVector &w) { return f.Hessian(x, w); } }; } // End empty namespace bool ad_fun(void) { // This example is similar to example/jacobian.cpp, except that it // uses my_ad_fun instead of ADFun. bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); using CppAD::exp; using CppAD::sin; using CppAD::cos; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) X(n); X[0] = 1.; X[1] = 2.; // declare independent variables and starting recording CppAD::Independent(X); // a calculation between the domain and range values AD Square = X[0] * X[0]; // range space vector size_t m = 3; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = Square * exp( X[1] ); Y[1] = Square * sin( X[1] ); Y[2] = Square * cos( X[1] ); // create f: X -> Y and stop tape recording my_ad_fun f(X, Y); // new value for the independent variable vector CPPAD_TESTVECTOR(double) x(n); x[0] = 2.; x[1] = 1.; // compute the derivative at this x CPPAD_TESTVECTOR(double) jac( m * n ); jac = f.jacobian(x); /* F'(x) = [ 2 * x[0] * exp(x[1]) , x[0] * x[0] * exp(x[1]) ] [ 2 * x[0] * sin(x[1]) , x[0] * x[0] * cos(x[1]) ] [ 2 * x[0] * cos(x[1]) , -x[0] * x[0] * sin(x[i]) ] */ ok &= NearEqual( 2.*x[0]*exp(x[1]), jac[0*n+0], eps99, eps99); ok &= NearEqual( 2.*x[0]*sin(x[1]), jac[1*n+0], eps99, eps99); ok &= NearEqual( 2.*x[0]*cos(x[1]), jac[2*n+0], eps99, eps99); ok &= NearEqual( x[0] * x[0] *exp(x[1]), jac[0*n+1], eps99, eps99); ok &= NearEqual( x[0] * x[0] *cos(x[1]), jac[1*n+1], eps99, eps99); ok &= NearEqual(-x[0] * x[0] *sin(x[1]), jac[2*n+1], eps99, eps99); return ok; } // END C++ ================================================ FILE: example/general/ad_in_c.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ad_in_c.cpp} Example and Test Linking CppAD to Languages Other than C++ ########################################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end ad_in_c.cpp} */ // BEGIN C++ # include # include # include namespace { // Begin empty namespace ***************************************** /* void debug_print(const char *label, double d) { using std::printf; unsigned char *byte = reinterpret_cast(&d); size_t n_byte = sizeof(d); printf("%s", label); for(size_t i = 0; i < n_byte; i++) printf("%x", byte[i]); printf("\n"); } */ // type in C corresponding to an AD object typedef struct { void* p_void; } cad; // type in C corresponding to a an ADFun typedef struct { void* p_void; } cad_fun; // type in C corresponding to a C AD binary operator typedef enum { op_add, op_sub, op_mul, op_div } cad_binary_op; // type in C corresponding to a C AD unary operator typedef enum { op_abs, op_acos, op_asin, op_atan, op_cos, op_cosh, op_exp, op_log, op_sin, op_sinh, op_sqrt } cad_unary_op; // -------------------------------------------------------------------------- // helper code not intended for use by C code ------------------------------ using CppAD::AD; using CppAD::ADFun; using CppAD::vector; using CppAD::NearEqual; void cad2vector(size_t n, cad* p_cad, vector< AD >& v) { assert( n == v.size() ); for(size_t j = 0; j < n; j++) { AD* p_ad = reinterpret_cast< AD* > (p_cad[j].p_void); v[j] = *p_ad; } } void vector2cad(size_t n, vector< AD >& v, cad* p_cad) { assert( n == v.size() ); for(size_t j = 0; j < n; j++) { AD* p_ad = reinterpret_cast< AD* > (p_cad[j].p_void); *p_ad = v[j]; } } void double2vector(size_t n, double* p_dbl, vector& v) { assert( n == v.size() ); for(size_t j = 0; j < n; j++) v[j] = p_dbl[j]; } void vector2double(size_t n, vector& v, double *p_dbl) { assert( n == v.size() ); for(size_t j = 0; j < n; j++) p_dbl[j] = v[j]; } std::list allocated; # ifdef NDEBUG inline void push_allocated(void *p) { } inline void pop_allocated(void *p) { } # else inline void push_allocated(void *p) { assert( p != 0 ); allocated.push_front(p); } inline void pop_allocated(void *p) { std::list::iterator i; for(i = allocated.begin(); i != allocated.end(); ++i) { if( *i == p ) { allocated.erase(i); return; } } assert( 0 ); } # endif // -------------------------------------------------------------------------- // Here is the code that links C to CppAD. You will have to add more // functions and operators to make a complete language link. // extern "C" bool cad_near_equal(double x, double y) { double eps = 10. * std::numeric_limits::epsilon(); return NearEqual(x, y, eps, 0.); } // create a C++ AD object // value is the value that the C++ AD object will have // p_cad->p_void: on input is 0, on output points to C++ AD object extern "C" void cad_new_ad(cad *p_cad, double value) { // make sure pointer is not currently allocated assert( p_cad->p_void == 0 ); AD* p_ad = new AD(value); p_cad->p_void = reinterpret_cast(p_ad); // put in list of allocate pointers push_allocated( p_cad->p_void ); } // delete a C++ AD object // p_cad->value: not used // p_cad->p_void: on input points to C++ AD object, on output is 0 extern "C" void cad_del_ad(cad* p_cad) { // make sure that p_cad has been allocated pop_allocated( p_cad->p_void ); AD* p_ad = reinterpret_cast< AD* >( p_cad->p_void ); delete p_ad; // special value for pointers that are not allocated p_cad->p_void = 0; } // extract the value from a C++ AD object // extern "C" double cad_value(cad* p_cad) { AD* p_ad = reinterpret_cast< AD* > (p_cad->p_void); return Value( Var2Par(*p_ad) ); } // preform a C AD unary operation extern "C" void cad_unary(cad_unary_op op, cad* p_operand, cad* p_result) { AD *operand, *result; result = reinterpret_cast< AD* > (p_result->p_void); operand = reinterpret_cast< AD* > (p_operand->p_void); switch(op) { case op_abs: *result = fabs( *operand ); break; case op_acos: *result = acos( *operand ); break; case op_asin: *result = asin( *operand ); break; case op_atan: *result = atan( *operand ); break; case op_cos: *result = cos( *operand ); break; case op_cosh: *result = cosh( *operand ); break; case op_exp: *result = exp( *operand ); break; case op_log: *result = log( *operand ); break; case op_sin: *result = sin( *operand ); break; case op_sinh: *result = sinh( *operand ); break; case op_sqrt: *result = sqrt( *operand ); break; default: // not a unary operator assert(0); break; } return; } // perform a C AD binary operation extern "C" void cad_binary(cad_binary_op op, cad* p_left, cad* p_right, cad* p_result) { AD *result, *left, *right; result = reinterpret_cast< AD* > (p_result->p_void); left = reinterpret_cast< AD* > (p_left->p_void); right = reinterpret_cast< AD* > (p_right->p_void); assert( result != 0 ); assert( left != 0 ); assert( right != 0 ); switch(op) { case op_add: *result = *left + (*right); break; case op_sub: *result = *left - (*right); break; case op_mul: *result = *left * (*right); break; case op_div: *result = *left / (*right); break; default: // not a binary operator assert(0); } return; } // declare the independent variables in C++ extern "C" void cad_independent(size_t n, cad* px_cad) { vector< AD > x(n); cad2vector(n, px_cad, x); CppAD::Independent(x); vector2cad(n, x, px_cad); } // create an ADFun object in C++ extern "C" cad_fun cad_new_fun(size_t n, size_t m, cad* px_cad, cad* py_cad) { cad_fun fun; ADFun* p_adfun = new ADFun; vector< AD > x(n); vector< AD > y(m); cad2vector(n, px_cad, x); cad2vector(m, py_cad, y); p_adfun->Dependent(x, y); fun.p_void = reinterpret_cast( p_adfun ); // put in list of allocate pointers push_allocated( fun.p_void ); return fun; } // delete an AD function object in C extern "C" void cad_del_fun(cad_fun *fun) { // make sure this pointer has been allocated pop_allocated( fun->p_void ); ADFun* p_adfun = reinterpret_cast< ADFun* > (fun->p_void); delete p_adfun; // special value for pointers that are not allocated fun->p_void = 0; } // evaluate the Jacobian corresponding to a function object extern "C" void cad_jacobian(cad_fun fun, size_t n, size_t m, double* px, double* pjac ) { assert( fun.p_void != 0 ); ADFun* p_adfun = reinterpret_cast< ADFun* >(fun.p_void); vector x(n), jac(n * m); double2vector(n, px, x); jac = p_adfun->Jacobian(x); vector2double(n * m, jac, pjac); } // forward mode extern "C" void cad_forward(cad_fun fun, size_t order, size_t n, size_t m, double* px, double* py ) { assert( fun.p_void != 0 ); ADFun* p_adfun = reinterpret_cast< ADFun* >(fun.p_void); vector x(n), y(m); double2vector(n, px, x); y = p_adfun->Forward(order, x); vector2double(m, y, py); } // check that allocated list has been completely freed extern "C" bool cad_allocated_empty(void) { return allocated.empty(); } } // End empty namespace **************************************************** # include // used to check results in c code below # define N 2 // number of independent variables in example # define M 5 // number of dependent variables in example // ------------------------------------------------------------------------- // Here is the C code that uses the CppAD link above bool ad_in_c(void) { // This routine is intentionally coded as if it were written in C // as an example of how you can link C, and other languages to CppAD bool ok = true; // x vector of AD objects in C double value; size_t j, n = N; cad X[N]; for(j = 0; j < n; j++) { value = (double) (j+1) / (double) n; X[j].p_void = 0; cad_new_ad(X + j, value); } // y vector of AD objects in C size_t i, m = M; cad Y[M]; for(i = 0; i < m; i++) { value = 0.; // required, but not used Y[i].p_void = 0; cad_new_ad(Y + i, value); } // declare X as the independent variable vector cad_independent(n, X); // y[0] = x[0] + x[1] cad_binary(op_add, X+0, X+1, Y+0); ok &= cad_near_equal( cad_value(Y+0), cad_value(X+0)+cad_value(X+1) ); // y[1] = x[0] - x[1] cad_binary(op_sub, X+0, X+1, Y+1); ok &= cad_near_equal( cad_value(Y+1), cad_value(X+0)-cad_value(X+1) ); // y[2] = x[0] * x[1] cad_binary(op_mul, X+0, X+1, Y+2); ok &= cad_near_equal( cad_value(Y+2), cad_value(X+0)*cad_value(X+1) ); // y[3] = x[0] * x[1] cad_binary(op_div, X+0, X+1, Y+3); ok &= cad_near_equal( cad_value(Y+3), cad_value(X+0)/cad_value(X+1) ); // y[4] = sin(x[0]) + asin(sin(x[0])) cad sin_x0 = { 0 }; // initialize p_void as zero cad_new_ad( &sin_x0, 0.); cad_unary(op_sin, X+0, &sin_x0); ok &= cad_near_equal(cad_value(&sin_x0), sin(cad_value(X+0)) ); cad asin_sin_x0 = { 0 }; // initialize p_void as zero cad_new_ad( &asin_sin_x0, 0.); cad_unary(op_asin, &sin_x0, &asin_sin_x0); ok &= cad_near_equal( cad_value(&asin_sin_x0), asin( cad_value(&sin_x0) ) ); cad_binary(op_add, &sin_x0, &asin_sin_x0, Y+4); ok &= cad_near_equal( cad_value(Y+4), cad_value(&sin_x0) + cad_value(&asin_sin_x0) ); // declare y as the dependent variable vector and stop recording // and store function object in f cad_fun f = cad_new_fun(n, m, X, Y); // now use the function object double x[N], jac[N * M]; x[0] = 1.; x[1] = .5; // compute the Jacobian cad_jacobian(f, n, m, x, jac); // check the Jacobian values size_t k = 0; // partial y[0] w.r.t. x[0] ok &= cad_near_equal(jac[k++], 1.); // partial y[0] w.r.t. x[1] ok &= cad_near_equal(jac[k++], 1.); // partial y[1] w.r.t. x[0] ok &= cad_near_equal(jac[k++], 1.); // partial y[1] w.r.t. x[1] ok &= cad_near_equal(jac[k++], -1.); // partial y[2] w.r.t. x[0] ok &= cad_near_equal(jac[k++], x[1]); // partial y[2] w.r.t. x[1] ok &= cad_near_equal(jac[k++], x[0]); // partial y[3] w.r.t. x[0] ok &= cad_near_equal(jac[k++], 1./x[1]); // partial y[3] w.r.t. x[1] ok &= cad_near_equal(jac[k++], -x[0]/(x[1]*x[1])); // partial y[4] w.r.t x[0] ok &= cad_near_equal(jac[k++], cos(x[0]) + 1.); // partial y[4] w.r.t x[1] ok &= cad_near_equal(jac[k++], 0.); // evaluate the function f at a different x size_t order = 0; double y[M]; x[0] = .5; x[1] = 1.; cad_forward(f, order, n, m, x, y); // check the function values ok &= cad_near_equal(y[0] , x[0] + x[1] ); ok &= cad_near_equal(y[1] , x[0] - x[1] ); ok &= cad_near_equal(y[2] , x[0] * x[1] ); ok &= cad_near_equal(y[3] , x[0] / x[1] ); ok &= cad_near_equal(y[4] , sin(x[0]) + asin(sin(x[0])) ); // delete All C++ copies of the AD objects cad_del_fun( &f ); cad_del_ad( &sin_x0 ); cad_del_ad( &asin_sin_x0 ); for(j = 0; j < n; j++) cad_del_ad(X + j); for(i = 0; i < m; i++) cad_del_ad(Y + i); ok &= cad_allocated_empty(); return ok; } // END C++ ================================================ FILE: example/general/ad_input.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ad_input.cpp} AD Output Operator: Example and Test #################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end ad_input.cpp} */ // BEGIN C++ # include # include // std::istringstream # include // std::string bool ad_input(void) { bool ok = true; // create the input string stream is. std::string str ("123 456"); std::istringstream is(str); // start and AD recording CPPAD_TESTVECTOR( CppAD::AD ) x(1), y(1); x[0] = 1.0; CppAD::Independent(x); CppAD::AD z = x[0]; ok &= Variable(z); // read first number into z and second into y[0] is >> z >> y[0]; ok &= Parameter(z); ok &= (z == 123.); ok &= Parameter(y[0]); ok &= (y[0] == 456.); // // terminate recording starting by call to Independent CppAD::ADFun f(x, y); return ok; } // END C++ ================================================ FILE: example/general/ad_output.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ad_output.cpp} AD Output Operator: Example and Test #################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end ad_output.cpp} */ // BEGIN C++ # include # include // std::ostringstream # include // std::string # include // std::setprecision, setw, setfill, right namespace { template void set_ostream(S &os) { os << std::setprecision(4) // 4 digits of precision << std::setw(6) // 6 characters per field << std::setfill(' ') // fill with spaces << std::right; // adjust value to the right } } bool ad_output(void) { bool ok = true; // This output stream is an ostringstream for testing purposes. // You can use << with other types of streams; i.e., std::cout. std::ostringstream stream; // output an AD object CppAD::AD pi = 4. * atan(1.); // 3.1415926536 set_ostream(stream); stream << pi; // output a VecAD::reference object CppAD::VecAD v(1); CppAD::AD zero(0); v[zero] = exp(1.); // 2.7182818285 set_ostream(stream); stream << v[zero]; // convert output from stream to string std::string str = stream.str(); // check the output ok &= (str == " 3.142 2.718"); return ok; } // END C++ ================================================ FILE: example/general/add.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin add.cpp} AD Binary Addition: Example and Test #################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end add.cpp} */ // BEGIN C++ # include bool Add(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // some binary addition operations AD a = x[0] + 1.; // AD + double AD b = a + 2; // AD + int AD c = 3. + b; // double + AD AD d = 4 + c; // int + AD // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = d + x[0]; // AD + AD // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value ok &= NearEqual(y[0] , 2. * x0 + 10, eps99, eps99); // forward computation of partials w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 2., eps99, eps99); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], 2., eps99, eps99); // use a VecAD::reference object with addition CppAD::VecAD v(1); AD zero(0); v[zero] = a; AD result = v[zero] + 2; ok &= (result == b); return ok; } // END C++ ================================================ FILE: example/general/add_eq.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin add_eq.cpp} AD Compound Assignment Addition: Example and Test ################################################# {xrst_literal // BEGIN C++ // END C++ } {xrst_end add_eq.cpp} */ // BEGIN C++ # include bool AddEq(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = .5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // range space vector size_t m = 2; CPPAD_TESTVECTOR(AD) y(m); y[0] = x[0]; // initial value y[0] += 2; // AD += int y[0] += 4.; // AD += double y[1] = y[0] += x[0]; // use the result of a compound assignment // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value ok &= NearEqual(y[0] , x0+2.+4.+x0, eps99, eps99); ok &= NearEqual(y[1] , y[0], eps99, eps99); // forward computation of partials w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 2., eps99, eps99); ok &= NearEqual(dy[1], 2., eps99, eps99); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; w[1] = 0.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], 2., eps99, eps99); // use a VecAD::reference object with computed addition CppAD::VecAD v(1); AD zero(0); AD result = 1; v[zero] = 2; result += v[zero]; ok &= (result == 3); return ok; } // END C++ ================================================ FILE: example/general/asin.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin asin.cpp} The AD asin Function: Example and Test ###################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end asin.cpp} */ // BEGIN C++ # include bool asin(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; // 10 times machine epsilon double eps = 10. * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // a temporary value AD sin_of_x0 = CppAD::sin(x[0]); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = CppAD::asin(sin_of_x0); // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value ok &= NearEqual(y[0] , x0, eps, eps); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 1., eps, eps); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], 1., eps, eps); // use a VecAD::reference object with asin CppAD::VecAD v(1); AD zero(0); v[zero] = sin_of_x0; AD result = CppAD::asin(v[zero]); ok &= NearEqual(result, x0, eps, eps); return ok; } // END C++ ================================================ FILE: example/general/asinh.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin asinh.cpp} The AD asinh Function: Example and Test ####################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end asinh.cpp} */ // BEGIN C++ # include bool asinh(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; // 10 times machine epsilon double eps = 10. * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // a temporary value AD sinh_of_x0 = CppAD::sinh(x[0]); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = CppAD::asinh(sinh_of_x0); // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value ok &= NearEqual(y[0] , x0, eps, eps); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 1., eps, eps); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], 1., eps, eps); // use a VecAD::reference object with asinh CppAD::VecAD v(1); AD zero(0); v[zero] = sinh_of_x0; AD result = CppAD::asinh(v[zero]); ok &= NearEqual(result, x0, eps, eps); return ok; } // END C++ ================================================ FILE: example/general/atan.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atan.cpp} The AD atan Function: Example and Test ###################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end atan.cpp} */ // BEGIN C++ # include bool atan(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // a temporary value AD tan_of_x0 = CppAD::tan(x[0]); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = CppAD::atan(tan_of_x0); // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value ok &= NearEqual(y[0] , x0, eps99, eps99); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 1., eps99, eps99); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], 1., eps99, eps99); // use a VecAD::reference object with atan CppAD::VecAD v(1); AD zero(0); v[zero] = tan_of_x0; AD result = CppAD::atan(v[zero]); ok &= NearEqual(result, x0, eps99, eps99); return ok; } // END C++ ================================================ FILE: example/general/atan2.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atan2.cpp} The AD atan2 Function: Example and Test ####################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end atan2.cpp} */ // BEGIN C++ # include # define N_THETA 20 bool atan2(void) { bool ok = true; // using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); double pi = 2.0 * std::atan(1.0); // for(size_t k = 0; k < N_THETA; ++k) { // theta double theta = 2.0 * pi * double(k+1) / double(N_THETA) - pi; // // radius double radius = 1.0 + double(k) / double(N_THETA); // // x, y double x = radius * std::cos(theta); double y = radius * std::sin(theta); // // au CPPAD_TESTVECTOR(AD) au(2); au[0] = x; au[1] = y; CppAD::Independent(au); // // av CPPAD_TESTVECTOR(AD) av(1); av[0] = CppAD::atan2(au[1], au[0]); // // f(x, y) = atan2(y, x) CppAD::ADFun f(au, av); // // check value ok &= NearEqual(av[0] , theta, eps99, eps99); // // partial_x, partial_y // see https://en.wikipedia.org/wiki/Atan2#Derivative double partial_x = - y / (radius * radius); double partial_y = x / (radius * radius); // // check forward mode CPPAD_TESTVECTOR(double) du(2), dv(1); du[0] = 1.0; du[1] = 0.0; dv = f.Forward(1, du); ok &= NearEqual(dv[0], partial_x, eps99, eps99); du[0] = 0.0; du[1] = 1.0; dv = f.Forward(1, du); ok &= NearEqual(dv[0], partial_y, eps99, eps99); // // check reverse mode CPPAD_TESTVECTOR(double) w(1); CPPAD_TESTVECTOR(double) dw(2); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], partial_x, eps99, eps99); ok &= NearEqual(dw[1], partial_y, eps99, eps99); // } return ok; } // END C++ ================================================ FILE: example/general/atanh.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atanh.cpp} The AD atanh Function: Example and Test ####################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end atanh.cpp} */ // BEGIN C++ # include bool atanh(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; // 10 times machine epsilon double eps = 10. * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // a temporary value AD tanh_of_x0 = CppAD::tanh(x[0]); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = CppAD::atanh(tanh_of_x0); // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value ok &= NearEqual(y[0] , x0, eps, eps); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 1., eps, eps); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], 1., eps, eps); // use a VecAD::reference object with atanh CppAD::VecAD v(1); AD zero(0); v[zero] = tanh_of_x0; AD result = CppAD::atanh(v[zero]); ok &= NearEqual(result, x0, eps, eps); return ok; } // END C++ ================================================ FILE: example/general/azmul.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin azmul.cpp} AD Absolute Zero Multiplication: Example and Test ################################################# {xrst_literal // BEGIN C++ // END C++ } {xrst_end azmul.cpp} */ // BEGIN C++ # include # include bool azmul(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double inf = std::numeric_limits::infinity(); double eps = 10. * std::numeric_limits::epsilon(); // domain space vector size_t n = 2; double x = 0.5; double y = 2.0; CPPAD_TESTVECTOR(AD) axy(n); axy[0] = x; axy[1] = y; // declare independent variables and start tape recording CppAD::Independent(axy); // range space vector size_t m = 5; CPPAD_TESTVECTOR(AD) az(m); az[0] = CppAD::azmul(axy[0], axy[1]); // azmul(variable, variable) az[1] = CppAD::azmul(axy[0], inf); // azmul(variable, parameter=inf) az[2] = CppAD::azmul(axy[0], 3.0); // azmul(variable, parameter=3.0) az[3] = CppAD::azmul(0.0, axy[1]); // azmul(parameter=0.0, variable) az[4] = CppAD::azmul(4.0, axy[1]); // azmul(parameter=4.0, variable) // create f: axy -> az and stop tape recording CppAD::ADFun f(axy, az); // check value when x is not zero ok &= NearEqual(az[0] , x * y, eps, eps); ok &= az[1] == inf; ok &= NearEqual(az[2] , x * 3.0, eps, eps); ok &= az[3] == 0.0; ok &= NearEqual(az[4] , 4.0 * y, eps, eps); // check value x is zero and y is infinity CPPAD_TESTVECTOR(double) xy(n), z(m); xy[0] = 0.0; xy[1] = inf; z = f.Forward(0, xy); ok &= z[0] == 0.0; ok &= z[1] == 0.0; ok &= z[2] == 0.0; ok &= z[3] == 0.0; ok &= z[4] == inf; return ok; } // END C++ ================================================ FILE: example/general/base2ad.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin base2ad.cpp} {xrst_spell cccc } Taylor's Ode Solver: base2ad Example and Test ############################################# See Also ******** :ref:`taylor_ode.cpp-name` , :ref:`mul_level_ode.cpp-name` Purpose ******* This is a realistic example using :ref:`base2ad-name` to create an ``AD`` < *Base* > function from an *Base* function. The function represents an ordinary differential equation. It is differentiated with respect to its :ref:`variables` . These derivatives are used by the :ref:`taylor_ode-name` method. This solution is then differentiated with respect to the functions :ref:`dynamic parameters` . ODE *** For this example the function :math:`y : \B{R} \times \B{R}^n \rightarrow \B{R}^n` is defined by :math:`y(0, x) = 0` and :math:`\partial_t y(t, x) = g(y, x)` where :math:`g : \B{R}^n \times \B{R}^n \rightarrow \B{R}^n` is defined by .. math:: g(y, x) = \left( \begin{array}{c} x_0 \\ x_1 y_0 \\ \vdots \\ x_{n-1} y_{n-2} \end{array} \right) ODE Solution ************ The solution for this example can be calculated by starting with the first row and then using the solution for the first row to solve the second and so on. Doing this we obtain .. math:: y(t, x ) = \left( \begin{array}{c} x_0 t \\ x_1 x_0 t^2 / 2 \\ \vdots \\ x_{n-1} x_{n-2} \ldots x_0 t^n / n ! \end{array} \right) Derivative of ODE Solution ************************** Differentiating the solution above, with respect to the parameter vector :math:`x`, we notice that .. math:: \partial_x y(t, x ) = \left( \begin{array}{cccc} y_0 (t,x) / x_0 & 0 & \cdots & 0 \\ y_1 (t,x) / x_0 & y_1 (t,x) / x_1 & 0 & \vdots \\ \vdots & \vdots & \ddots & 0 \\ y_{n-1} (t,x) / x_0 & y_{n-1} (t,x) / x_1 & \cdots & y_{n-1} (t,x) / x_{n-1} \end{array} \right) Taylor's Method Using AD ************************ We define the function :math:`z(t, x)` by the equation .. math:: z ( t , x ) = g[ y ( t , x ), x ] see :ref:`taylor_ode-name` for the method used to compute the Taylor coefficients w.r.t :math:`t` of :math:`y(t, x)`. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end base2ad.cpp} -------------------------------------------------------------------------- */ // BEGIN C++ # include // ========================================================================= namespace { // BEGIN empty namespace typedef CppAD::AD a_double; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR(a_double) a_vector; typedef CppAD::ADFun fun_double; typedef CppAD::ADFun afun_double; // ------------------------------------------------------------------------- // class definition for C++ function object that defines ODE class Ode { private: // copy of x that is set by constructor and used by g(y) a_vector x_; public: // constructor Ode(const a_vector& x) : x_(x) { } // the function g(y) given the parameter vector x a_vector operator() (const a_vector& y) const { size_t n = y.size(); a_vector g(n); g[0] = x_[0]; for(size_t i = 1; i < n; i++) g[i] = x_[i] * y[i-1]; // return g; } }; // ------------------------------------------------------------------------- // Routine that uses Taylor's method to solve ordinary differential equaitons a_vector taylor_ode( afun_double& fun_g , // function that defines the ODE size_t order , // order of Taylor's method used size_t nstep , // number of steps to take const a_double& dt , // Delta t for each step const a_vector& y_ini) // y(t) at the initial time { // number of variables in the ODE size_t n = y_ini.size(); // initialize y a_vector y = y_ini; // loop with respect to each step of Taylors method for(size_t s = 0; s < nstep; s++) { // initialize a_vector y_k = y; a_double dt_k = a_double(1.0); a_vector next = y; for(size_t k = 0; k < order; k++) { // evaluate k-th order Taylor coefficient z^{(k)} (t) a_vector z_k = fun_g.Forward(k, y_k); // dt^{k+1} dt_k *= dt; // y^{(k+1)} for(size_t i = 0; i < n; i++) { // y^{(k+1)} y_k[i] = z_k[i] / a_double(k + 1); // add term for k+1 Taylor coefficient // to solution for next y next[i] += y_k[i] * dt_k; } } // take step y = next; } return y; } } // END empty namespace // ========================================================================== // Routine that tests algorithmic differentiation of solutions computed // by the routine taylor_ode. bool base2ad(void) { bool ok = true; double eps = 100. * std::numeric_limits::epsilon(); // number of components in differential equation size_t n = 4; // record function g(y, x) // with y as the independent variables and x as dynamic parameters a_vector ay(n), ax(n); for(size_t i = 0; i < n; i++) ay[i] = ax[i] = double(i + 1); CppAD::Independent(ay, ax); // fun_g Ode G(ax); a_vector ag = G(ay); fun_double fun_g(ay, ag); // afun_g afun_double afun_g( fun_g.base2ad() ); // differential equation // other arguments to taylor_ode size_t order = n; // order of Taylor's method used size_t nstep = 2; // number of steps to take a_double adt = 1.; // Delta t for each step a_vector ay_ini(n); // initial value of y for(size_t i = 0; i < n; i++) ay_ini[i] = 0.; // declare x as independent variables CppAD::Independent(ax); // the independent variables if this function are // the dynamic parameters in afun_g afun_g.new_dynamic(ax); // integrate the differential equation a_vector ay_final; ay_final = taylor_ode(afun_g, order, nstep, adt, ay_ini); // define differentiable function object f(x) = y_final(x) // that computes its derivatives in double CppAD::ADFun fun_f(ax, ay_final); // double version of ax d_vector x(n); for(size_t i = 0; i < n; i++) x[i] = Value( ax[i] ); // check function values double check = 1.; double t = double(nstep) * Value(adt); for(size_t i = 0; i < n; i++) { check *= x[i] * t / double(i + 1); ok &= CppAD::NearEqual(Value(ay_final[i]), check, eps, eps); } // There appears to be a bug in g++ version 4.4.2 because it generates // a warning for the equivalent form // d_vector jac = fun_f.Jacobian(x); d_vector jac ( fun_f.Jacobian(x) ); // check Jacobian for(size_t i = 0; i < n; i++) { for(size_t j = 0; j < n; j++) { double jac_ij = jac[i * n + j]; if( i < j ) check = 0.; else check = Value( ay_final[i] ) / x[j]; ok &= CppAD::NearEqual(jac_ij, check, eps, eps); } } return ok; } // END C++ ================================================ FILE: example/general/base2vec_ad.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin base2vec_ad.cpp} Using base2ad and VecAD together: Example and Test ################################################## Purpose ******* This example demonstrate that :ref:`base2ad-name` does not convert :ref:`VecAD-name` operations as might be expected. To be specific, this indexing into a VecAD object gets fixed when zero order forward mode is run for a ``base2ad`` result; e.g. *af* below. {xrst_literal // BEGIN C++ // END C++ } {xrst_end base2vec_ad.cpp} */ // BEGIN C++ # include bool base2vec_ad(void) { using CppAD::vector; using CppAD::AD; // bool ok = true; // // f(x) = x[0] if 0 <= x[2] < 1 // = x[1] if 1 <= x[2] < 2 vector< AD > ax(3), ay(1); ax[0] = 3.0; ax[1] = 2.0; ax[2] = 1.0; Independent(ax); CppAD::VecAD av(2); av[ AD(0) ] = ax[0]; av[ AD(1) ] = ax[1]; ay[0] = av[ ax[2] ]; CppAD::ADFun f(ax, ay); // // ok // value during recording of f ok &= ay[0] == ax[1]; // // ok // f zero order forward mode, the VecAD commands give expected result vector x(3), y(1); x[0] = 2.0; x[1] = 1.0; x[2] = 0.0; y = f.Forward(0, x); ok &= y[0] == x[0]; // // af CppAD::ADFun< AD, double > af = f.base2ad(); // // g ax[0] = 3.0; ax[1] = 2.0; ax[2] = 1.0; Independent(ax); ay = af.Forward(0, ax); CppAD::ADFun g(ax, ay); // // ok // value during recording of g ok &= ay[0] == ax[1]; // // ok // g zero order forward mode // Note that this index does not change, but the value does x[0] = 5.0; x[1] = 4.0; x[2] = 0.0; y = g.Forward(0, x); ok &= y[0] == x[1]; // return ok; } // END C++ ================================================ FILE: example/general/base_alloc.hpp ================================================ # ifndef CPPAD_EXAMPLE_GENERAL_BASE_ALLOC_HPP # define CPPAD_EXAMPLE_GENERAL_BASE_ALLOC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin base_alloc.hpp} {xrst_spell azmul isnan } Example AD Where Base Constructor Allocates Memory ######################################################## Purpose ******* Demonstrate use of ``AD`` < *Base* > where memory is allocated for each element of the type *Base* . In addition, this is a complete example where all the :ref:`required Base` type operations are defined (as apposed to other examples where some of the operations for the Base type are already defined). Include File ************ This file uses some of the definitions in :ref:`base_require-name` and :ref:`thread_alloc-name` . {xrst_spell_off} {xrst_code cpp} */ # include # include /* {xrst_code} {xrst_spell_on} Compound Assignment Macro ************************* This macro is used for the ``base_alloc`` compound assignment operators; to be specific, used with *op* equal to ``+=`` , ``-=`` , ``*=`` , ``/=`` . {xrst_spell_off} {xrst_code cpp} */ # define BASE_ALLOC_ASSIGN_OPERATOR(op) \ void operator op (const base_alloc& x) \ { *ptrdbl_ op *x.ptrdbl_; } /* {xrst_code} {xrst_spell_on} Binary Operator Macro ********************* This macro is used for the ``base_alloc`` binary operators (as member functions); to be specific, used with *op* equal to ``+`` , ``-`` , ``*`` , ``/`` . {xrst_spell_off} {xrst_code cpp} */ # define BASE_ALLOC_BINARY_OPERATOR(op) const \ base_alloc operator op (const base_alloc& x) const \ { base_alloc result; \ double dbl = *ptrdbl_; \ double x_dbl = *x.ptrdbl_; \ *result.ptrdbl_ = dbl op x_dbl; \ return result; \ } /* {xrst_code} {xrst_spell_on} Boolean Operator Macro ********************** This macro can be used for the ``base_alloc`` binary operators that have a ``bool`` result; to be specific, used with *op* equal to ``==`` , ``!=`` , ``<`` , ``<=`` , ``>=`` , and ``>`` , {xrst_spell_off} {xrst_code cpp} */ # define BASE_ALLOC_BOOL_OPERATOR(op) const \ bool operator op (const base_alloc& x) const \ { double dbl = *ptrdbl_; \ double x_dbl = *x.ptrdbl_; \ return dbl op x_dbl; \ } /* {xrst_code} {xrst_spell_on} Class Definition **************** The following example class defines the necessary :ref:`base_member-name` functions. It is made more complicated by storing a pointer to a ``double`` instead of the ``double`` value itself. {xrst_spell_off} {xrst_code cpp} */ class base_alloc { public: double* ptrdbl_; base_alloc(void) { size_t cap; void* v = CppAD::thread_alloc::get_memory(sizeof(double), cap); ptrdbl_ = static_cast(v); // // Avoid warning about possible use of uninitialized value *ptrdbl_ = CppAD::numeric_limits::quiet_NaN(); } base_alloc(double dbl) { size_t cap; void *v = CppAD::thread_alloc::get_memory(sizeof(double), cap); ptrdbl_ = static_cast(v); *ptrdbl_ = dbl; } base_alloc(const base_alloc& x) { size_t cap; void *v = CppAD::thread_alloc::get_memory(sizeof(double), cap); ptrdbl_ = static_cast(v); *ptrdbl_ = *x.ptrdbl_; } ~base_alloc(void) { void* v = static_cast(ptrdbl_); CppAD::thread_alloc::return_memory(v); } base_alloc operator-(void) const { base_alloc result; *result.ptrdbl_ = - *ptrdbl_; return result; } base_alloc operator+(void) const { return *this; } void operator=(const base_alloc& x) { *ptrdbl_ = *x.ptrdbl_; } BASE_ALLOC_ASSIGN_OPERATOR(+=) BASE_ALLOC_ASSIGN_OPERATOR(-=) BASE_ALLOC_ASSIGN_OPERATOR(*=) BASE_ALLOC_ASSIGN_OPERATOR(/=) BASE_ALLOC_BINARY_OPERATOR(+) BASE_ALLOC_BINARY_OPERATOR(-) BASE_ALLOC_BINARY_OPERATOR(*) BASE_ALLOC_BINARY_OPERATOR(/) BASE_ALLOC_BOOL_OPERATOR(==) BASE_ALLOC_BOOL_OPERATOR(!=) // The <= operator is not necessary for the base type requirements // (needed so we can use NearEqual with base_alloc arguments). BASE_ALLOC_BOOL_OPERATOR(<=) }; /* {xrst_code} {xrst_spell_on} CondExpOp ********* The type ``base_alloc`` does not use :ref:`CondExp-name` operations. Hence its ``CondExpOp`` function is defined by {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline base_alloc CondExpOp( enum CompareOp cop , const base_alloc& left , const base_alloc& right , const base_alloc& exp_if_true , const base_alloc& exp_if_false ) { // not used assert(false); // to void compiler error return base_alloc(); } } /* {xrst_code} {xrst_spell_on} CondExpRel ********** The :ref:`CPPAD_COND_EXP_REL` macro invocation {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { CPPAD_COND_EXP_REL(base_alloc) } /* {xrst_code} {xrst_spell_on} uses ``CondExpOp`` above to define ``CondExp`` *Rel* for ``base_alloc`` arguments and *Rel* equal to ``Lt`` , ``Le`` , ``Eq`` , ``Ge`` , and ``Gt`` . EqualOpSeq ********** The type ``base_alloc`` is simple (in this respect) and so we define {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline bool EqualOpSeq(const base_alloc& x, const base_alloc& y) { return *x.ptrdbl_ == *y.ptrdbl_; } } /* {xrst_code} {xrst_spell_on} Identical ********* The type ``base_alloc`` is simple (in this respect) and so we define {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline bool IdenticalCon(const base_alloc& x) { return true; } inline bool IdenticalZero(const base_alloc& x) { return (*x.ptrdbl_ == 0.0); } inline bool IdenticalOne(const base_alloc& x) { return (*x.ptrdbl_ == 1.0); } inline bool IdenticalEqualCon(const base_alloc& x, const base_alloc& y) { return (*x.ptrdbl_ == *y.ptrdbl_); } } /* {xrst_code} {xrst_spell_on} Output Operator *************** {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline std::ostream& operator << (std::ostream &os, const base_alloc& x) { os << *x.ptrdbl_; return os; } } /* {xrst_code} {xrst_spell_on} Integer ******* {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline int Integer(const base_alloc& x) { return static_cast(*x.ptrdbl_); } } /* {xrst_code} {xrst_spell_on} azmul ***** {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { CPPAD_AZMUL( base_alloc ) } /* {xrst_code} {xrst_spell_on} Ordered ******* The ``base_alloc`` type supports ordered comparisons {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline bool GreaterThanZero(const base_alloc& x) { return *x.ptrdbl_ > 0.0; } inline bool GreaterThanOrZero(const base_alloc& x) { return *x.ptrdbl_ >= 0.0; } inline bool LessThanZero(const base_alloc& x) { return *x.ptrdbl_ < 0.0; } inline bool LessThanOrZero(const base_alloc& x) { return *x.ptrdbl_ <= 0.f; } inline bool abs_geq(const base_alloc& x, const base_alloc& y) { return std::fabs(*x.ptrdbl_) >= std::fabs(*y.ptrdbl_); } } /* {xrst_code} {xrst_spell_on} Unary Standard Math ******************* The macro :ref:`base_std_math@CPPAD_STANDARD_MATH_UNARY` would not work with the type ``base_alloc`` so we define a special macro for this type: {xrst_spell_off} {xrst_code cpp} */ # define BASE_ALLOC_STD_MATH(fun) \ inline base_alloc fun (const base_alloc& x) \ { return std::fun(*x.ptrdbl_); } /* {xrst_code} {xrst_spell_on} The following invocations of the macro above define the :ref:`base_std_math@Unary Standard Math` functions (except for ``abs`` ): {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { BASE_ALLOC_STD_MATH(acos) BASE_ALLOC_STD_MATH(acosh) BASE_ALLOC_STD_MATH(asin) BASE_ALLOC_STD_MATH(asinh) BASE_ALLOC_STD_MATH(atan) BASE_ALLOC_STD_MATH(atanh) BASE_ALLOC_STD_MATH(cos) BASE_ALLOC_STD_MATH(cosh) BASE_ALLOC_STD_MATH(erf) BASE_ALLOC_STD_MATH(erfc) BASE_ALLOC_STD_MATH(exp) BASE_ALLOC_STD_MATH(expm1) BASE_ALLOC_STD_MATH(fabs) BASE_ALLOC_STD_MATH(log) BASE_ALLOC_STD_MATH(log1p) BASE_ALLOC_STD_MATH(log10) BASE_ALLOC_STD_MATH(sin) BASE_ALLOC_STD_MATH(sinh) BASE_ALLOC_STD_MATH(sqrt) BASE_ALLOC_STD_MATH(tan) BASE_ALLOC_STD_MATH(tanh) } /* {xrst_code} {xrst_spell_on} The absolute value function is special because it ``std`` name is ``fabs`` {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline base_alloc abs(const base_alloc& x) { return fabs(*x.ptrdbl_); } } /* {xrst_code} {xrst_spell_on} The isnan function is special because it returns a bool {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline bool isnan(const base_alloc& x) { return *x.ptrdbl_ != *x.ptrdbl_; } } /* {xrst_code} {xrst_spell_on} sign **** The following defines the ``CppAD::sign`` function that is required to use ``AD`` : {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline base_alloc sign(const base_alloc& x) { if( *x.ptrdbl_ > 0.0 ) return 1.0; if( *x.ptrdbl_ == 0.0 ) return 0.0; return -1.0; } } /* {xrst_code} {xrst_spell_on} pow *** The following defines a ``CppAD::pow`` function that is required to use ``AD`` : {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline base_alloc pow(const base_alloc& x, const base_alloc& y) { return std::pow(*x.ptrdbl_, *y.ptrdbl_); } } /* {xrst_code} {xrst_spell_on} numeric_limits ************** The following defines the CppAD :ref:`numeric_limits-name` for the type ``base_alloc`` : {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { CPPAD_NUMERIC_LIMITS(double, base_alloc) } /* {xrst_code} {xrst_spell_on} to_string ********* The following defines the CppAD :ref:`to_string-name` function for the type ``base_alloc`` : {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { CPPAD_TO_STRING(base_alloc) } /* {xrst_code} {xrst_spell_on} hash_code ********* The :ref:`base_hash@Default` hashing function does not work well for this case because two different pointers can have the same value. {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline unsigned short hash_code(const base_alloc& x) { unsigned short code = 0; if( *x.ptrdbl_ == 0.0 ) return code; double log_x = log( std::fabs( *x.ptrdbl_ ) ); // assume log( std::numeric_limits::max() ) is near 700 code = static_cast( (CPPAD_HASH_TABLE_SIZE / 700 + 1) * log_x ); code = code % CPPAD_HASH_TABLE_SIZE; return code; } } /* {xrst_code} {xrst_spell_on} {xrst_end base_alloc.hpp} */ # endif ================================================ FILE: example/general/base_require.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin base_require.cpp} Using a User Defined AD Base Type: Example and Test ################################################### {xrst_literal // BEGIN C++ // END C++ } Purpose ******* The type ``base_alloc`` , defined in :ref:`base_alloc.hpp-name` , meets the requirements specified by :ref:`base_require-name` for *Base* in ``AD`` < *Base* > . The program below is an example use of ``AD`` . {xrst_end base_require.cpp} */ // BEGIN C++ // suppress conversion warnings before other includes # include // # include "base_alloc.hpp" # include bool base_require(void) { bool ok = true; using CppAD::thread_alloc; typedef CppAD::AD ad_base_alloc; // check the amount of memory inuse by this thread (thread zero) size_t thread = thread_alloc::thread_num(); ok &= thread == 0; // y = x^2 size_t n = 1, m = 1; CPPAD_TESTVECTOR(ad_base_alloc) a_x(n), a_y(m); a_x[0] = ad_base_alloc(1.); CppAD::Independent(a_x); a_y[0] = a_x[0] * a_x[0]; CppAD::ADFun f(a_x, a_y); // check function value f(x) = x^2 CPPAD_TESTVECTOR(base_alloc) x(n), y(m); base_alloc eps = base_alloc(100.) * CppAD::numeric_limits::epsilon(); x[0] = base_alloc(3.); y = f.Forward(0, x); ok &= CppAD::NearEqual(y[0], x[0] * x[0], eps, eps); // check derivative value f'(x) = 2 * x CPPAD_TESTVECTOR(base_alloc) dy(m * n); dy = f.Jacobian(x); ok &= CppAD::NearEqual(dy[0], base_alloc(2.) * x[0], eps, eps); // check the abs function ok &= abs( - a_x[0] ) == abs( a_x[0] ); return ok; } // END C++ ================================================ FILE: example/general/bender_quad.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin bender_quad.cpp app} {xrst_spell argmin } BenderQuad: Example and Test ############################ Define :math:`F : \B{R} \times \B{R} \rightarrow \B{R}` by .. math:: F(x, y) = \frac{1}{2} \sum_{i=1}^N [ y * \sin ( x * t_i ) - z_i ]^2 where :math:`z \in \B{R}^N` is a fixed vector. It follows that .. math:: :nowrap: \begin{eqnarray} \partial_y F(x, y) & = & \sum_{i=1}^N [ y * \sin ( x * t_i ) - z_i ] \sin( x * t_i ) \\ \partial_y \partial_y F(x, y) & = & \sum_{i=1}^N \sin ( x t_i )^2 \end{eqnarray} Furthermore if we define :math:`Y(x)` as the argmin of :math:`F(x, y)` with respect to :math:`y`, .. math:: :nowrap: \begin{eqnarray} Y(x) & = & y - [ \partial_y \partial_y F(x, y) ]^{-1} \partial_y F[x, y] \\ & = & \left. \sum_{i=1}^N z_i \sin ( x t_i ) \right/ \sum_{i=1}^N z_i \sin ( x * t_i )^2 \end{eqnarray} {xrst_literal // BEGIN C++ // END C++ } {xrst_end bender_quad.cpp} */ // BEGIN C++ # include namespace { using CppAD::AD; typedef CPPAD_TESTVECTOR(double) BAvector; typedef CPPAD_TESTVECTOR(AD) ADvector; class Fun { private: BAvector t_; // measurement times BAvector z_; // measurement values public: // constructor Fun(const BAvector &t, const BAvector &z) : t_(t), z_(z) { } // Fun.f(x, y) = F(x, y) ADvector f(const ADvector &x, const ADvector &y) { size_t i; size_t N = size_t(z_.size()); ADvector F(1); F[0] = 0.; AD residual; for(i = 0; i < N; i++) { residual = y[0] * sin( x[0] * t_[i] ) - z_[i]; F[0] += .5 * residual * residual; } return F; } // Fun.h(x, y) = H(x, y) = F_y (x, y) ADvector h(const ADvector &x, const BAvector &y) { size_t i; size_t N = size_t(z_.size()); ADvector fy(1); fy[0] = 0.; AD residual; for(i = 0; i < N; i++) { residual = y[0] * sin( x[0] * t_[i] ) - z_[i]; fy[0] += residual * sin( x[0] * t_[i] ); } return fy; } // Fun.dy(x, y, h) = - H_y (x,y)^{-1} * h // = - F_yy (x, y)^{-1} * h ADvector dy( const BAvector &x , const BAvector &y , const ADvector &H ) { size_t i; size_t N = size_t(z_.size()); ADvector Dy(1); AD fyy = 0.; for(i = 0; i < N; i++) { fyy += sin( x[0] * t_[i] ) * sin( x[0] * t_[i] ); } Dy[0] = - H[0] / fyy; return Dy; } }; // Used to test calculation of Hessian of G AD G(const ADvector& x, const BAvector& t, const BAvector& z) { // compute Y(x) AD numerator = 0.; AD denominator = 0.; size_t k; for(k = 0; k < size_t(t.size()); k++) { numerator += sin( x[0] * t[k] ) * z[k]; denominator += sin( x[0] * t[k] ) * sin( x[0] * t[k] ); } AD y = numerator / denominator; // V(x) = F[x, Y(x)] AD sum = 0; for(k = 0; k < size_t(t.size()); k++) { AD residual = y * sin( x[0] * t[k] ) - z[k]; sum += .5 * residual * residual; } return sum; } } bool BenderQuad(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; // temporary indices size_t i, j; // x space vector size_t n = 1; BAvector x(n); x[0] = 2. * 3.141592653; // y space vector size_t m = 1; BAvector y(m); y[0] = 1.; // t and z vectors size_t N = 10; BAvector t(N); BAvector z(N); for(i = 0; i < N; i++) { t[i] = double(i) / double(N); // time of measurement z[i] = y[0] * sin( x[0] * t[i] ); // data without noise } // construct the function object Fun fun(t, z); // evaluate the G(x), G'(x) and G''(x) BAvector g(1), gx(n), gxx(n * n); CppAD::BenderQuad(x, y, fun, g, gx, gxx); // create ADFun object Gfun corresponding to G(x) ADvector a_x(n), a_g(1); for(j = 0; j < n; j++) a_x[j] = x[j]; Independent(a_x); a_g[0] = G(a_x, t, z); CppAD::ADFun Gfun(a_x, a_g); // accuracy for checks double eps = 10. * CppAD::numeric_limits::epsilon(); // check Jacobian BAvector check_gx = Gfun.Jacobian(x); for(j = 0; j < n; j++) ok &= NearEqual(gx[j], check_gx[j], eps, eps); // check Hessian BAvector check_gxx = Gfun.Hessian(x, 0); for(j = 0; j < n*n; j++) ok &= NearEqual(gxx[j], check_gxx[j], eps, eps); return ok; } // END C++ ================================================ FILE: example/general/bool_fun.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin bool_fun.cpp} AD Boolean Functions: Example and Test ###################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end bool_fun.cpp} */ // BEGIN C++ # include # include // define abbreviation for double precision complex typedef std::complex Complex; namespace { // a unary bool function with Complex argument static bool IsReal(const Complex &x) { return x.imag() == 0.; } // a binary bool function with Complex arguments static bool AbsGeq(const Complex &x, const Complex &y) { double axsq = x.real() * x.real() + x.imag() * x.imag(); double aysq = y.real() * y.real() + y.imag() * y.imag(); return axsq >= aysq; } // Create version of IsReal with AD argument // inside of namespace and outside of any other function. CPPAD_BOOL_UNARY(Complex, IsReal) // Create version of AbsGeq with AD arguments // inside of namespace and outside of any other function. CPPAD_BOOL_BINARY(Complex, AbsGeq) } bool BoolFun(void) { bool ok = true; CppAD::AD x = Complex(1., 0.); CppAD::AD y = Complex(1., 1.); ok &= IsReal(x); ok &= ! AbsGeq(x, y); return ok; } // END C++ ================================================ FILE: example/general/capacity_order.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin capacity_order.cpp} Controlling Taylor Coefficient Memory Allocation: Example and Test ################################################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end capacity_order.cpp} */ // BEGIN C++ # include namespace { bool test(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; using CppAD::thread_alloc; // domain space vector size_t n(1), m(1); CPPAD_TESTVECTOR(AD) ax(n), ay(n); // declare independent variables and start tape recording ax[0] = 1.0; CppAD::Independent(ax); // Set y = x^3, use enough variables so more that the minimal amount // of memory is allocated for Taylor coefficients ay[0] = 0.; for( size_t i = 0; i < 10; i++) ay[0] += ax[0] * ax[0] * ax[0]; ay[0] = ay[0] / 10.; // create f: x -> y and stop tape recording // (without running zero order forward mode). CppAD::ADFun f; f.Dependent(ax, ay); // check that this is master thread size_t thread = thread_alloc::thread_num(); ok &= thread == 0; // this should be master thread // The highest order forward mode calculation below is first order. // This corresponds to two Taylor coefficient per variable,direction // (orders zero and one). Preallocate memory for speed. size_t inuse = thread_alloc::inuse(thread); f.capacity_order(2); ok &= thread_alloc::inuse(thread) > inuse; // zero order forward mode CPPAD_TESTVECTOR(double) x(n), y(m); x[0] = 0.5; y = f.Forward(0, x); double eps = 10. * CppAD::numeric_limits::epsilon(); ok &= NearEqual(y[0], x[0] * x[0] * x[0], eps, eps); // forward computation of partials w.r.t. x CPPAD_TESTVECTOR(double) dx(n), dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 3. * x[0] * x[0], eps, eps); // Suppose we no longer need the first order Taylor coefficients. inuse = thread_alloc::inuse(thread); f.capacity_order(1); // just keep zero order coefficients ok &= thread_alloc::inuse(thread) < inuse; // Suppose we no longer need the zero order Taylor coefficients // (could have done this first and not used f.capacity_order(1)). inuse = thread_alloc::inuse(thread); f.capacity_order(0); ok &= thread_alloc::inuse(thread) < inuse; // turn off memory holding thread_alloc::hold_memory(false); return ok; } } bool capacity_order(void) { bool ok = true; using CppAD::thread_alloc; // original amount of memory inuse size_t thread = thread_alloc::thread_num(); ok &= thread == 0; // this should be master thread size_t inuse = thread_alloc::inuse(thread); // do test in separate routine so all objects are destroyed ok &= test(); // check that the amount of memory inuse has not changed ok &= thread_alloc::inuse(thread) == inuse; // Test above uses hold_memory, so return available memory thread_alloc::free_available(thread); return ok; } // END C++ ================================================ FILE: example/general/change_param.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include /* {xrst_begin change_param.cpp} Computing a Jacobian With Constants that Change ############################################### Purpose ******* In this example we use two levels of taping so that a derivative can have constant parameters that can be changed. To be specific, we consider the function :math:`f : \B{R}^2 \rightarrow \B{R}^2` .. math:: f(x) = p \left( \begin{array}{c} \sin( x_0 ) \\ \sin( x_1 ) \end{array} \right) were :math:`p \in \B{R}` is a parameter. The Jacobian of this function is .. math:: g(x,p) = p \left( \begin{array}{cc} \cos( x_0 ) & 0 \\ 0 & \cos( x_1 ) \end{array} \right) In this example we use two levels of AD to avoid computing the partial of :math:`f(x)` with respect to :math:`p`, but still allow for the evaluation of :math:`g(x, p)` at different values of :math:`p`. {xrst_end change_param.cpp} */ bool change_param(void) { bool ok = true; // initialize test result typedef CppAD::AD a1type; // for first level of taping typedef CppAD::AD a2type; // for second level of taping size_t nu = 3; // number components in u size_t nx = 2; // number components in x size_t ny = 2; // num components in f(x) size_t nJ = ny * nx; // number components in Jacobian of f(x) // temporary indices size_t j; // declare first level of independent variables // (Start taping now so can record dependency of a1f on a1p.) CPPAD_TESTVECTOR(a1type) a1u(nu); for(j = 0; j < nu; j++) a1u[j] = 0.; CppAD::Independent(a1u); // parameter in computation of Jacobian a1type a1p = a1u[2]; // declare second level of independent variables CPPAD_TESTVECTOR(a2type) a2x(nx); for(j = 0; j < nx; j++) a2x[j] = 0.; CppAD::Independent(a2x); // compute dependent variables at second level CPPAD_TESTVECTOR(a2type) a2y(ny); a2y[0] = sin( a2x[0] ) * a1p; a2y[1] = sin( a2x[1] ) * a1p; // declare function object that computes values at the first level // (make sure we do not run zero order forward during constructor) CppAD::ADFun a1f; a1f.Dependent(a2x, a2y); // compute the Jacobian of a1f at a1u[0], a1u[1] CPPAD_TESTVECTOR(a1type) a1x(nx); a1x[0] = a1u[0]; a1x[1] = a1u[1]; CPPAD_TESTVECTOR(a1type) a1J(nJ); a1J = a1f.Jacobian( a1x ); // declare function object that maps u = (x, p) to Jacobian of f // (make sure we do not run zero order forward during constructor) CppAD::ADFun g; g.Dependent(a1u, a1J); // remove extra variables used during the reconding of a1f, // but not needed any more. g.optimize(); // compute the Jacobian of f using zero order forward // sweep with double values CPPAD_TESTVECTOR(double) J(nJ), u(nu); for(j = 0; j < nu; j++) u[j] = double(j+1); J = g.Forward(0, u); // accuracy for tests double eps = 100. * CppAD::numeric_limits::epsilon(); // y[0] = sin( x[0] ) * p // y[1] = sin( x[1] ) * p CPPAD_TESTVECTOR(double) x(nx); x[0] = u[0]; x[1] = u[1]; double p = u[2]; // J[0] = partial y[0] w.r.t x[0] = cos( x[0] ) * p double check = cos( x[0] ) * p; ok &= fabs( check - J[0] ) <= eps; // J[1] = partial y[0] w.r.t x[1] = 0.; check = 0.; ok &= fabs( check - J[1] ) <= eps; // J[2] = partial y[1] w.r.t. x[0] = 0. check = 0.; ok &= fabs( check - J[2] ) <= eps; // J[3] = partial y[1] w.r.t x[1] = cos( x[1] ) * p check = cos( x[1] ) * p; ok &= fabs( check - J[3] ) <= eps; return ok; } // END PROGRAM ================================================ FILE: example/general/check_for_nan.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin check_for_nan.cpp} ADFun Checking For Nan: Example and Test ######################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end check_for_nan.cpp} */ // BEGIN C++ # include # include namespace { void myhandler( bool known , int line , const char *file , const char *exp , const char *msg ) { // error handler must not return, so throw an exception std::string message = msg; throw message; } } bool check_for_nan(void) { bool ok = true; using CppAD::AD; using std::string; double eps = 10. * std::numeric_limits::epsilon(); // replace the default CppAD error handler CppAD::ErrorHandler info(myhandler); CPPAD_TESTVECTOR(AD) ax(2), ay(2); ax[0] = 2.0; ax[1] = 1.0; CppAD::Independent(ax); ay[0] = sqrt( ax[0] ); ay[1] = sqrt( ax[1] ); CppAD::ADFun f(ax, ay); CPPAD_TESTVECTOR(double) x(2), y(2); x[0] = 2.0; x[1] = -1.0; // use try / catch because this causes an exception // (assuming that NDEBUG is not defined) f.check_for_nan(true); try { y = f.Forward(0, x); # ifndef NDEBUG // When compiled with NDEBUG defined, // CppAD does not spend time checking for nan. ok = false; # endif } catch(std::string msg) { // get and check size of the independent variable vector string pattern = "vector_size = "; size_t start = msg.find(pattern) + pattern.size(); string number; for(size_t i = start; msg[i] != '\n'; i++) number += msg[i]; size_t vector_size = size_t( std::atoi(number.c_str()) ); ok &= vector_size == 2; // get and check first dependent variable index that is nan pattern = "index = "; start = msg.find(pattern) + pattern.size(); number = ""; for(size_t i = start; msg[i] != '\n'; i++) number += msg[i]; size_t index = size_t( std::atoi(number.c_str()) ); ok &= index == 1; // get the name of the file pattern = "file_name = "; start = msg.find(pattern) + pattern.size(); string file_name; for(size_t i = start; msg[i] != '\n'; i++) file_name += msg[i]; // get and check independent variable vector that resulted in the nan CppAD::vector vec(vector_size); CppAD::get_check_for_nan(vec, file_name); for(size_t i = 0; i < vector_size; i++) ok &= vec[i] == x[i]; } // now do calculation without an exception f.check_for_nan(false); y = f.Forward(0, x); ok &= CppAD::NearEqual(y[0], std::sqrt(x[0]), eps, eps); ok &= CppAD::isnan( y[1] ); return ok; } // END C++ ================================================ FILE: example/general/compare.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin compare.cpp} AD Binary Comparison Operators: Example and Test ################################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end compare.cpp} */ // BEGIN C++ # include bool Compare(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // declare independent variables and start tape recording size_t n = 2; double x0 = 0.5; double x1 = 1.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; x[1] = x1; CppAD::Independent(x); // some binary comparison operations AD p; if( x[0] < x[1] ) p = x[0]; // values in x choose this case else p = x[1]; if( x[0] <= x[1] ) p *= x[0]; // values in x choose this case else p *= x[1]; if( x[0] > x[1] ) p *= x[0]; else p *= x[1]; // values in x choose this case if( x[0] >= x[1] ) p *= x[0]; else p *= x[1]; // values in x choose this case if( x[0] == x[1] ) p *= x[0]; else p *= x[1]; // values in x choose this case if( x[0] != x[1] ) p *= x[0]; // values in x choose this case else p *= x[1]; // dependent variable vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = p; // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value ok &= NearEqual(y[0] , x0*x0*x1*x1*x1*x0, eps99, eps99); // forward computation of partials w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dx[1] = 0.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 3.*x0*x0*x1*x1*x1, eps99, eps99); // forward computation of partials w.r.t. x[1] dx[0] = 0.; dx[1] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 3.*x0*x0*x1*x1*x0, eps99, eps99); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], 3.*x0*x0*x1*x1*x1, eps99, eps99); ok &= NearEqual(dw[1], 3.*x0*x0*x1*x1*x0, eps99, eps99); return ok; } // END C++ ================================================ FILE: example/general/complex_poly.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin complex_poly.cpp} Complex Polynomial: Example and Test #################################### Poly **** Select this link to view specifications for :ref:`Poly-name` : {xrst_literal // BEGIN C++ // END C++ } {xrst_end complex_poly.cpp} */ // BEGIN C++ // Complex examples should suppress conversion warnings # include # include # include bool complex_poly(void) { bool ok = true; size_t deg = 4; using CppAD::AD; using CppAD::Poly; typedef std::complex Complex; // polynomial coefficients CPPAD_TESTVECTOR( Complex ) a (deg + 1); // coefficients for p(z) CPPAD_TESTVECTOR(AD) A (deg + 1); size_t i; for(i = 0; i <= deg; i++) A[i] = a[i] = Complex(double(i), double(i)); // independent variable vector CPPAD_TESTVECTOR(AD) Z(1); Complex z = Complex(1., 2.); Z[0] = z; Independent(Z); // dependent variable vector and indices CPPAD_TESTVECTOR(AD) P(1); // dependent variable values P[0] = Poly(0, A, Z[0]); // create f: Z -> P and vectors used for derivative calculations CppAD::ADFun f(Z, P); CPPAD_TESTVECTOR(Complex) v( f.Domain() ); CPPAD_TESTVECTOR(Complex) w( f.Range() ); // check first derivative w.r.t z v[0] = 1.; w = f.Forward(1, v); Complex p = Poly(1, a, z); ok &= ( w[0] == p ); // second derivative w.r.t z is 2 times its second order Taylor coeff v[0] = 0.; w = f.Forward(2, v); p = Poly(2, a, z); ok &= ( 2. * w[0] == p ); return ok; } // END C++ ================================================ FILE: example/general/con_dyn_var.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin con_dyn_var.cpp} AD Parameter and Variable Functions: Example and Test ##################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end con_dyn_var.cpp} */ // BEGIN C++ # include bool con_dyn_var(void) { bool ok = true; using CppAD::AD; using CppAD::VecAD; using CppAD::Parameter; using CppAD::Variable; // No recording active CPPAD_TESTVECTOR(AD) x(1), y(1), dynamic(1); x[0] = 0.0; y[0] = 1.0; dynamic[0] = 2.0; // ok &= Constant(x[0]); ok &= Parameter(x[0]); ok &= ! Dynamic(x[0]); ok &= ! Variable(x[0]); // ok &= Constant(y[0]); ok &= Parameter(y[0]); ok &= ! Dynamic(y[0]); ok &= ! Variable(y[0]); // ok &= Constant(dynamic[0]); ok &= Parameter(dynamic[0]); ok &= ! Dynamic(dynamic[0]); ok &= ! Variable(dynamic[0]); // declare independent variables and start recording CppAD::Independent(x, dynamic); // ok &= Variable(x[0]); ok &= ! Constant(x[0]); ok &= ! Dynamic(x[0]); ok &= ! Parameter(x[0]); // ok &= Constant(y[0]); ok &= Parameter(y[0]); ok &= ! Dynamic(y[0]); ok &= ! Variable(y[0]); // ok &= Dynamic(dynamic[0]); ok &= Parameter(dynamic[0]); ok &= ! Constant(dynamic[0]); ok &= ! Variable(dynamic[0]); // a dependent variable y[0] = fabs(x[0]) * dynamic[0]; ok &= Variable(y[0]); ok &= ! Constant(y[0]); ok &= ! Dynamic(y[0]); ok &= ! Parameter(y[0]); // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // ok &= Constant(x[0]); ok &= Parameter(x[0]); ok &= ! Dynamic(x[0]); ok &= ! Variable(x[0]); // ok &= Constant(y[0]); ok &= Parameter(y[0]); ok &= ! Dynamic(y[0]); ok &= ! Variable(y[0]); // ok &= Constant(dynamic[0]); ok &= Parameter(dynamic[0]); ok &= ! Dynamic(dynamic[0]); ok &= ! Variable(dynamic[0]); return ok; } // END C++ ================================================ FILE: example/general/cond_exp.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cond_exp.cpp} Conditional Expressions: Example and Test ######################################### See Also ******** :ref:`optimize_conditional_skip.cpp-name` Description *********** Use ``CondExp`` to compute .. math:: f(x) = \sum_{j=0}^{m-1} x_j \log( x_j ) and its derivative at various argument values ( where :math:`x_j \geq 0` ) with out having to re-tape; i.e., using only one :ref:`ADFun-name` object. Note that :math:`x_j \log ( x_j ) \rightarrow 0` as :math:`x_j \downarrow 0` and we need to handle the case :math:`x_j = 0` in a special way to avoid returning zero times minus infinity. {xrst_literal // BEGIN C++ // END C++ } {xrst_end cond_exp.cpp} */ // BEGIN C++ # include # include bool CondExp(void) { bool ok = true; using CppAD::isnan; using CppAD::AD; using CppAD::NearEqual; using CppAD::log; double eps = 100. * CppAD::numeric_limits::epsilon(); // domain space vector size_t n = 5; CPPAD_TESTVECTOR(AD) ax(n); size_t j; for(j = 0; j < n; j++) ax[j] = 1.; // declare independent variables and start tape recording CppAD::Independent(ax); AD asum = 0.; AD azero = 0.; for(j = 0; j < n; j++) { // if x_j > 0, add x_j * log( x_j ) to the sum asum += CppAD::CondExpGt(ax[j], azero, ax[j] * log(ax[j]), azero); } // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = asum; // create f: x -> ay and stop tape recording CppAD::ADFun f(ax, ay); // vectors for arguments to the function object f CPPAD_TESTVECTOR(double) x(n); // argument values CPPAD_TESTVECTOR(double) y(m); // function values CPPAD_TESTVECTOR(double) w(m); // function weights CPPAD_TESTVECTOR(double) dw(n); // derivative of weighted function // a case where x[j] > 0 for all j double check = 0.; for(j = 0; j < n; j++) { x[j] = double(j + 1); check += x[j] * log( x[j] ); } // function value y = f.Forward(0, x); ok &= NearEqual(y[0], check, eps, eps); // compute derivative of y[0] w[0] = 1.; dw = f.Reverse(1, w); for(j = 0; j < n; j++) ok &= NearEqual(dw[j], log(x[j]) + 1., eps, eps); // a case where x[3] is equal to zero check -= x[3] * log( x[3] ); x[3] = 0.; ok &= std::isnan( x[3] * log( x[3] ) ); // function value y = f.Forward(0, x); ok &= NearEqual(y[0], check, eps, eps); // check derivative of y[0] w[0] = 1.; dw = f.Reverse(1, w); for(j = 0; j < n; j++) { if( x[j] > 0 ) ok &= NearEqual(dw[j], log(x[j]) + 1., eps, eps); else ok &= NearEqual(dw[j], 0.0, eps, eps); } return ok; } // END C++ ================================================ FILE: example/general/cos.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cos.cpp} The AD cos Function: Example and Test ##################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end cos.cpp} */ // BEGIN C++ # include # include bool Cos(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = CppAD::cos(x[0]); // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value double check = std::cos(x0); ok &= NearEqual(y[0] , check, eps99, eps99); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); check = - std::sin(x0); ok &= NearEqual(dy[0], check, eps99, eps99); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], check, eps99, eps99); // use a VecAD::reference object with cos CppAD::VecAD v(1); AD zero(0); v[zero] = x0; AD result = CppAD::cos(v[zero]); check = std::cos(x0); ok &= NearEqual(result, check, eps99, eps99); return ok; } // END C++ ================================================ FILE: example/general/cosh.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cosh.cpp} The AD cosh Function: Example and Test ###################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end cosh.cpp} */ // BEGIN C++ # include # include bool Cosh(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = CppAD::cosh(x[0]); // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value double check = std::cosh(x0); ok &= NearEqual(y[0] , check, eps99, eps99); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); check = std::sinh(x0); ok &= NearEqual(dy[0], check, eps99, eps99); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], check, eps99, eps99); // use a VecAD::reference object with cosh CppAD::VecAD v(1); AD zero(0); v[zero] = x0; AD result = CppAD::cosh(v[zero]); check = std::cosh(x0); ok &= NearEqual(result, check, eps99, eps99); return ok; } // END C++ ================================================ FILE: example/general/div.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin div.cpp} AD Binary Division: Example and Test #################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end div.cpp} */ // BEGIN C++ # include bool Div(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // some binary division operations AD a = x[0] / 1.; // AD / double AD b = a / 2; // AD / int AD c = 3. / b; // double / AD AD d = 4 / c; // int / AD // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = (x[0] * x[0]) / d; // AD / AD // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value ok &= NearEqual(y[0], x0*x0*3.*2.*1./(4.*x0), eps99, eps99); // forward computation of partials w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 3.*2.*1./4., eps99, eps99); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], 3.*2.*1./4., eps99, eps99); // use a VecAD::reference object with division CppAD::VecAD v(1); AD zero(0); v[zero] = d; AD result = (x[0] * x[0]) / v[zero]; ok &= (result == y[0]); return ok; } // END C++ ================================================ FILE: example/general/div_eq.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin div_eq.cpp} AD Compound Assignment Division: Example and Test ################################################# {xrst_literal // BEGIN C++ // END C++ } {xrst_end div_eq.cpp} */ // BEGIN C++ # include bool DivEq(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = .5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // range space vector size_t m = 2; CPPAD_TESTVECTOR(AD) y(m); y[0] = x[0] * x[0]; // initial value y[0] /= 2; // AD /= int y[0] /= 4.; // AD /= double y[1] = y[0] /= x[0]; // use the result of a compound assignment // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value ok &= NearEqual(y[0] , x0*x0/(2.*4.*x0), eps99, eps99); ok &= NearEqual(y[1] , y[0], eps99, eps99); // forward computation of partials w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 1./8., eps99, eps99); ok &= NearEqual(dy[1], 1./8., eps99, eps99); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; w[1] = 0.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], 1./8., eps99, eps99); // use a VecAD::reference object with computed division CppAD::VecAD v(1); AD zero(0); AD result = 2; v[zero] = 1; result /= v[zero]; ok &= (result == 2); return ok; } // END C++ ================================================ FILE: example/general/eigen_array.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin eigen_array.cpp} Using Eigen Arrays: Example and Test #################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end eigen_array.cpp} */ // BEGIN C++ # include # include bool eigen_array(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; // typedef CppAD::eigen_vector< AD > a_vector; // // domain and range space vectors size_t n = 10, m = n; a_vector a_x(n), a_y(m); // set and declare independent variables and start tape recording for(size_t j = 0; j < n; j++) a_x[j] = double(1 + j); CppAD::Independent(a_x); // evaluate a component wise function for(size_t j = 0; j < n; j++) a_y[j] = a_x[j] + sin( a_x[j] ); // create f: x -> y and stop tape recording CppAD::ADFun f(a_x, a_y); // compute the derivative of y w.r.t x using CppAD CPPAD_TESTVECTOR(double) x(n); for(size_t j = 0; j < n; j++) x[j] = double(j) + 1.0 / double(j+1); CPPAD_TESTVECTOR(double) jac = f.Jacobian(x); // check Jacobian double eps = 100. * CppAD::numeric_limits::epsilon(); for(size_t i = 0; i < m; i++) { for(size_t j = 0; j < n; j++) { double check = 1.0 + cos(x[i]); if( i != j ) check = 0.0; ok &= NearEqual(jac[i * n + j], check, eps, eps); } } return ok; } // END C++ ================================================ FILE: example/general/eigen_det.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin eigen_det.cpp} Using Eigen To Compute Determinant: Example and Test #################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end eigen_det.cpp} */ // BEGIN C++ # include # include # include bool eigen_det(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; using Eigen::Matrix; using Eigen::Dynamic; using Eigen::Index; // typedef Matrix< double , Dynamic, Dynamic > matrix; typedef Matrix< AD , Dynamic, Dynamic > a_matrix; // typedef CppAD::eigen_vector vector; typedef CppAD::eigen_vector< AD > a_vector; // // domain and range space vectors size_t size = 3, n = size * size, m = 1; a_vector a_x(n), a_y(m); vector x(n); // set and declare independent variables and start tape recording for(size_t i = 0; i < size; i++) { for(size_t j = 0; j < size; j++) { // lower triangular matrix a_x[i * size + j] = x[i * size + j] = 0.0; if( j <= i ) a_x[i * size + j] = x[i * size + j] = double(1 + i + j); } } CppAD::Independent(a_x); // copy independent variable vector to a matrix Index Size = Index(size); a_matrix a_X(Size, Size); matrix X(Size, Size); for(size_t i = 0; i < size; i++) { for(size_t j = 0; j < size; j++) { Index I = Index(i); Index J = Index(j); X(I ,J) = x[i * size + j]; // If we used a_X(i, j) = X(i, j), a_X would not depend on a_x. a_X(I, J) = a_x[i * size + j]; } } // Compute the log of determinant of X a_y[0] = log( a_X.determinant() ); // create f: x -> y and stop tape recording CppAD::ADFun f(a_x, a_y); // check function value double eps = 100. * CppAD::numeric_limits::epsilon(); CppAD::det_by_minor det(size); ok &= NearEqual(Value(a_y[0]) , log(det(x)), eps, eps); // compute the derivative of y w.r.t x using CppAD vector jac = f.Jacobian(x); // check the derivative using the formula // d/dX log(det(X)) = transpose( inv(X) ) matrix inv_X = X.inverse(); for(size_t i = 0; i < size; i++) { for(size_t j = 0; j < size; j++) { Index I = Index(i); Index J = Index(j); ok &= NearEqual(jac[i * size + j], inv_X(J, I), eps, eps); } } return ok; } // END C++ ================================================ FILE: example/general/equal_op_seq.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin equal_op_seq.cpp} EqualOpSeq: Example and Test ############################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end equal_op_seq.cpp} */ // BEGIN C++ # include bool EqualOpSeq(void) { bool ok = true; using CppAD::AD; using CppAD::EqualOpSeq; // domain space vector size_t n = 1; double x0 = 1.; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); AD a = 1. + x[0]; // this variable is 1 + x0 AD b = 2. * x[0]; // this variable is 2 * x0 // both a and b are variables ok &= (a == b); // 1 + 1 == 2 * 1 ok &= ! EqualOpSeq(a, b); // 1 + x[0] != 2 * x[0] // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = a; // both y[0] and a are variables EqualOpSeq(y[0], a); // 2 * x[0] == 2 * x[0] // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // both a and b are parameters (after the creation of f above) ok &= EqualOpSeq(a, b); // 1 + 1 == 2 * 1 return ok; } // END C++ ================================================ FILE: example/general/erf.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin erf.cpp} The AD erf Function: Example and Test ##################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end erf.cpp} */ // BEGIN C++ # include # include # include bool erf(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = x0; // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = CppAD::erf(ax[0]); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // check relative error double erf_x0 = 0.5204998778130465; ok &= NearEqual(ay[0] , erf_x0, 0., 4e-4); double tmp = std::max(1e-15, eps); ok &= NearEqual(ay[0] , erf_x0, 0., tmp); // value of derivative of erf at x0 double pi = 4. * std::atan(1.); double factor = 2. / sqrt(pi); double check = factor * std::exp(-x0 * x0); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], check, 0., 1e-3); ok &= NearEqual(dy[0], check, 0., eps); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], check, 0., 1e-1); ok &= NearEqual(dw[0], check, 0., eps); // use a VecAD::reference object with erf CppAD::VecAD v(1); AD zero(0); v[zero] = x0; AD result = CppAD::erf(v[zero]); ok &= NearEqual(result, ay[0], eps, eps); // use a double with erf ok &= NearEqual(CppAD::erf(x0), ay[0], eps, eps); return ok; } // END C++ ================================================ FILE: example/general/erfc.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin erfc.cpp} The AD erfc Function: Example and Test ###################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end erfc.cpp} */ // BEGIN C++ # include # include # include bool erfc(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = x0; // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = CppAD::erfc(ax[0]); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // check relative error double erfc_x0 = 0.4795001221869534; ok &= NearEqual(ay[0] , erfc_x0, 0., 4e-4); double tmp = std::max(1e-15, eps); ok &= NearEqual(ay[0] , erfc_x0, 0., tmp); // value of derivative of erfc at x0 double pi = 4. * std::atan(1.); double factor = 2. / sqrt(pi); double check = - factor * std::exp(-x0 * x0); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], check, 0., 1e-3); ok &= NearEqual(dy[0], check, 0., eps); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], check, 0., 1e-1); ok &= NearEqual(dw[0], check, 0., eps); // use a VecAD::reference object with erfc CppAD::VecAD v(1); AD zero(0); v[zero] = x0; AD result = CppAD::erfc(v[zero]); ok &= NearEqual(result, ay[0], eps, eps); // use a double with erfc ok &= NearEqual(CppAD::erfc(x0), ay[0], eps, eps); return ok; } // END C++ ================================================ FILE: example/general/exp.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin exp.cpp} The AD exp Function: Example and Test ##################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end exp.cpp} */ // BEGIN C++ # include # include bool exp(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = x0; // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = CppAD::exp(ax[0]); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // check value double check = std::exp(x0); ok &= NearEqual(ay[0], check, eps, eps); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], check, eps, eps); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], check, eps, eps); // use a VecAD::reference object with exp CppAD::VecAD v(1); AD zero(0); v[zero] = x0; AD result = CppAD::exp(v[zero]); ok &= NearEqual(result, check, eps, eps); return ok; } // END C++ ================================================ FILE: example/general/expm1.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin expm1.cpp} The AD exp Function: Example and Test ##################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end expm1.cpp} */ // BEGIN C++ # include # include bool expm1(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = x0; // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = CppAD::expm1(ax[0]); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // expx0 value double expx0 = std::exp(x0); ok &= NearEqual(ay[0], expx0-1.0, eps, eps); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], expx0, eps, eps); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], expx0, eps, eps); // use a VecAD::reference object with exp CppAD::VecAD v(1); AD zero(0); v[zero] = x0; AD result = CppAD::expm1(v[zero]); ok &= NearEqual(result, expx0-1.0, eps, eps); return ok; } // END C++ ================================================ FILE: example/general/fabs.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin fabs.cpp} AD Absolute Value Function: Example and Test ############################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end fabs.cpp} */ // BEGIN C++ # include bool fabs(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.; // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 3; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = fabs(ax[0] - 1.); ay[1] = fabs(ax[0]); ay[2] = fabs(ax[0] + 1.); // // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // check values ok &= (ay[0] == 1.); ok &= (ay[1] == 0.); ok &= (ay[2] == 1.); // // forward computation of partials w.r.t. a positive x[0] direction size_t p = 1; CPPAD_TESTVECTOR(double) dx(n), dy(m); dx[0] = 1.; dy = f.Forward(p, dx); ok &= (dy[0] == - dx[0]); ok &= (dy[1] == 0. ); ok &= (dy[2] == + dx[0]); // // forward computation of partials w.r.t. a negative x[0] direction dx[0] = -1.; dy = f.Forward(p, dx); ok &= (dy[0] == - dx[0]); ok &= (dy[1] == 0. ); ok &= (dy[2] == + dx[0]); // // reverse computation of derivative of y[0] p = 1; CPPAD_TESTVECTOR(double) w(m), dw(n); w[0] = 1.; w[1] = 0.; w[2] = 0; dw = f.Reverse(p, w); ok &= (dw[0] == -1.); // reverse computation of derivative of y[1] w[0] = 0.; w[1] = 1.; w[2] = 0; dw = f.Reverse(p, w); ok &= (dw[0] == 0.); // reverse computation of derivative of y[3] w[0] = 0.; w[1] = 0.; w[2] = 1; dw = f.Reverse(p, w); ok &= (dw[0] == 1.); // use a VecAD::reference object with fabs CppAD::VecAD av(1); AD az(0); av[az] = -1; AD a_result = fabs(av[az]); ok &= a_result == 1.0; return ok; } // END C++ ================================================ FILE: example/general/for_one.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin for_one.cpp} First Order Partial Driver: Example and Test ############################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end for_one.cpp} */ // BEGIN C++ # include namespace { // ------------------------------------------------------- // define the template function ForOneCases in empty namespace template bool ForOneCases() { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); using CppAD::exp; using CppAD::sin; using CppAD::cos; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) X(n); X[0] = 1.; X[1] = 2.; // declare independent variables and starting recording CppAD::Independent(X); // range space vector size_t m = 3; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = X[0] * exp( X[1] ); Y[1] = X[0] * sin( X[1] ); Y[2] = X[0] * cos( X[1] ); // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // new value for the independent variable vector Vector x(n); x[0] = 2.; x[1] = 1.; // compute partial of y w.r.t x[0] Vector dy(m); dy = f.ForOne(x, 0); ok &= NearEqual( dy[0], exp(x[1]), eps99, eps99); // for y[0] ok &= NearEqual( dy[1], sin(x[1]), eps99, eps99); // for y[1] ok &= NearEqual( dy[2], cos(x[1]), eps99, eps99); // for y[2] // compute partial of F w.r.t x[1] dy = f.ForOne(x, 1); ok &= NearEqual( dy[0], x[0]*exp(x[1]), eps99, eps99); ok &= NearEqual( dy[1], x[0]*cos(x[1]), eps99, eps99); ok &= NearEqual( dy[2], -x[0]*sin(x[1]), eps99, eps99); return ok; } } // End empty namespace # include # include bool ForOne(void) { bool ok = true; // Run with Vector equal to three different cases // all of which are Simple Vectors with elements of type double. ok &= ForOneCases< CppAD::vector >(); ok &= ForOneCases< std::vector >(); ok &= ForOneCases< std::valarray >(); return ok; } // END C++ ================================================ FILE: example/general/for_two.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin for_two.cpp} Subset of Second Order Partials: Example and Test ################################################# {xrst_literal // BEGIN C++ // END C++ } {xrst_end for_two.cpp} */ // BEGIN C++ # include namespace { // ----------------------------------------------------- // define the template function in empty namespace // bool ForTwoCases(void) template bool ForTwoCases() { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); using CppAD::exp; using CppAD::sin; using CppAD::cos; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) X(n); X[0] = 1.; X[1] = 2.; // declare independent variables and starting recording CppAD::Independent(X); // a calculation between the domain and range values AD Square = X[0] * X[0]; // range space vector size_t m = 3; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = Square * exp( X[1] ); Y[1] = Square * sin( X[1] ); Y[2] = Square * cos( X[1] ); // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // new value for the independent variable vector BaseVector x(n); x[0] = 2.; x[1] = 1.; // set j and k to compute specific second partials of y size_t p = 2; SizeVector_t j(p); SizeVector_t k(p); j[0] = 0; k[0] = 0; // for second partial w.r.t. x[0] and x[0] j[1] = 0; k[1] = 1; // for second partial w.r.t x[0] and x[1] // compute the second partials BaseVector ddy(m * p); ddy = f.ForTwo(x, j, k); /* partial of y w.r.t x[0] is [ 2 * x[0] * exp(x[1]) ] [ 2 * x[0] * sin(x[1]) ] [ 2 * x[0] * cos(x[1]) ] */ // second partial of y w.r.t x[0] and x[1] ok &= NearEqual( 2.*exp(x[1]), ddy[0*p+0], eps99, eps99); ok &= NearEqual( 2.*sin(x[1]), ddy[1*p+0], eps99, eps99); ok &= NearEqual( 2.*cos(x[1]), ddy[2*p+0], eps99, eps99); // second partial of F w.r.t x[0] and x[1] ok &= NearEqual( 2.*x[0]*exp(x[1]), ddy[0*p+1], eps99, eps99); ok &= NearEqual( 2.*x[0]*cos(x[1]), ddy[1*p+1], eps99, eps99); ok &= NearEqual(-2.*x[0]*sin(x[1]), ddy[2*p+1], eps99, eps99); return ok; } } // End empty namespace # include # include bool ForTwo(void) { bool ok = true; // Run with BaseVector equal to three different cases // all of which are Simple Vectors with elements of type double. ok &= ForTwoCases< CppAD::vector , std::vector >(); ok &= ForTwoCases< std::vector , std::vector >(); ok &= ForTwoCases< std::valarray , std::vector >(); // Run with SizeVector_t equal to two other cases // which are Simple Vectors with elements of type size_t. ok &= ForTwoCases< std::vector , CppAD::vector >(); ok &= ForTwoCases< std::vector , std::valarray >(); return ok; } // END C++ ================================================ FILE: example/general/forward.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin forward.cpp} Forward Mode: Example and Test ############################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end forward.cpp} */ // BEGIN C++ # include # include namespace { // -------------------------------------------------------- // define the template function ForwardCases in empty namespace template bool ForwardCases(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * std::numeric_limits::epsilon(); // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.; ax[1] = 1.; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = ax[0] * ax[0] * ax[1]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // initially, the variable values during taping are stored in f ok &= f.size_order() == 1; // zero order forward mode using notation in forward_zero // use the template parameter Vector for the vector type Vector x0(n), y0(m); x0[0] = 3.; x0[1] = 4.; y0 = f.Forward(0, x0); ok &= NearEqual(y0[0] , x0[0]*x0[0]*x0[1], eps, eps); ok &= f.size_order() == 1; // first order forward mode using notation in forward_one // X(t) = x0 + x1 * t // Y(t) = F[X(t)] = y0 + y1 * t + o(t) Vector x1(n), y1(m); x1[0] = 1.; x1[1] = 0.; y1 = f.Forward(1, x1); // partial F w.r.t. x_0 ok &= NearEqual(y1[0] , 2.*x0[0]*x0[1], eps, eps); ok &= f.size_order() == 2; // second order forward mode using notation in forward_order // X(t) = x0 + x1 * t + x2 * t^2 // Y(t) = F[X(t)] = y0 + y1 * t + y2 * t^2 + o(t^3) Vector x2(n), y2(m); x2[0] = 0.; x2[1] = 0.; y2 = f.Forward(2, x2); double F_00 = 2. * y2[0]; // second partial F w.r.t. x_0, x_0 ok &= NearEqual(F_00, 2.*x0[1], eps, eps); ok &= f.size_order() == 3; return ok; } } // End empty namespace # include # include bool Forward(void) { bool ok = true; // Run with Vector equal to three different cases // all of which are Simple Vectors with elements of type double. ok &= ForwardCases< CppAD::vector >(); ok &= ForwardCases< std::vector >(); ok &= ForwardCases< std::valarray >(); return ok; } // END C++ ================================================ FILE: example/general/forward_dir.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin forward_dir.cpp} Forward Mode: Example and Test of Multiple Directions ##################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end forward_dir.cpp} */ // BEGIN C++ # include # include bool forward_dir(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 3; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.; ax[1] = 1.; ax[2] = 2.; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = ax[0] * ax[1] * ax[2]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // initially, the variable values during taping are stored in f ok &= f.size_order() == 1; // zero order Taylor coefficients CPPAD_TESTVECTOR(double) x0(n), y0; for(j = 0; j < n; j++) x0[j] = double(j+1); y0 = f.Forward(0, x0); ok &= size_t( y0.size() ) == m; double y_0 = 1.*2.*3.; ok &= NearEqual(y0[0], y_0, eps, eps); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + 1 + ell); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = 0.0; } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // Y_0 (t) = F[X_0(t)] // = (1 + 1t)(2 + 2t)(3 + 3t) double y_1_0 = 1.*2.*3. + 2.*1.*3. + 3.*1.*2.; double y_2_0 = 1.*2.*3. + 2.*1.*3. + 3.*1.*2.; // // Y_1 (t) = F[X_1(t)] // = (1 + 2t)(2 + 3t)(3 + 4t) double y_1_1 = 2.*2.*3. + 3.*1.*3. + 4.*1.*2.; double y_2_1 = 1.*3.*4. + 2.*2.*4. + 3.*2.*3.; // ok &= NearEqual(y1[0] , y_1_0, eps, eps); ok &= NearEqual(y1[1] , y_1_1, eps, eps); ok &= NearEqual(y2[0] , y_2_0, eps, eps); ok &= NearEqual(y2[1] , y_2_1, eps, eps); // // check number of orders ok &= f.size_order() == 3; // // check number of directions ok &= f.size_direction() == 2; // return ok; } // END C++ ================================================ FILE: example/general/forward_order.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin forward_order.cpp} Forward Mode: Example and Test of Multiple Orders ################################################# {xrst_literal // BEGIN C++ // END C++ } {xrst_end forward_order.cpp} */ // BEGIN C++ # include # include bool forward_order(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * std::numeric_limits::epsilon(); // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.; ax[1] = 1.; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = ax[0] * ax[0] * ax[1]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // initially, the variable values during taping are stored in f ok &= f.size_order() == 1; // Compute three forward orders at once size_t q = 2, q1 = q+1; CPPAD_TESTVECTOR(double) xq(n*q1), yq; xq[q1*0 + 0] = 3.; xq[q1*1 + 0] = 4.; // x^0 (order zero) xq[q1*0 + 1] = 1.; xq[q1*1 + 1] = 0.; // x^1 (order one) xq[q1*0 + 2] = 0.; xq[q1*1 + 2] = 0.; // x^2 (order two) // X(t) = x^0 + x^1 * t + x^2 * t^2 // = [ 3 + t, 4 ] yq = f.Forward(q, xq); ok &= size_t( yq.size() ) == m*q1; // Y(t) = F[X(t)] // = (3 + t) * (3 + t) * 4 // = y^0 + y^1 * t + y^2 * t^2 + o(t^3) // // check y^0 (order zero) CPPAD_TESTVECTOR(double) x0(n); x0[0] = xq[q1*0 + 0]; x0[1] = xq[q1*1 + 0]; ok &= NearEqual(yq[q1*0 + 0] , x0[0]*x0[0]*x0[1], eps, eps); // // check y^1 (order one) ok &= NearEqual(yq[q1*0 + 1] , 2.*x0[0]*x0[1], eps, eps); // // check y^2 (order two) double F_00 = 2. * yq[q1*0 + 2]; // second partial F w.r.t. x_0, x_0 ok &= NearEqual(F_00, 2.*x0[1], eps, eps); // // check number of orders per variable ok &= f.size_order() == 3; // return ok; } // END C++ ================================================ FILE: example/general/fun_assign.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin fun_assign.cpp} ADFun Assignment: Example and Test ################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end fun_assign.cpp} */ // BEGIN C++ # include # include bool fun_assign(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; size_t i, j; // ten times machine precision double eps = 10. * CppAD::numeric_limits::epsilon(); // an empty ADFun object CppAD::ADFun g; // domain space vector size_t n = 3; CPPAD_TESTVECTOR(AD) x(n); for(j = 0; j < n; j++) x[j] = AD(j + 2); // declare independent variables and start tape recording CppAD::Independent(x); // range space vector size_t m = 2; CPPAD_TESTVECTOR(AD) y(m); y[0] = x[0] + x[0] * x[1]; y[1] = x[1] * x[2] + x[2]; // Store operation sequence, and order zero forward results, in f. // This assignment will use move semantics CppAD::ADFun f; f = CppAD::ADFun(x, y); // sparsity pattern for the identity matrix CPPAD_TESTVECTOR(std::set) r(n); for(j = 0; j < n; j++) r[j].insert(j); // Store forward mode sparsity pattern in f f.ForSparseJac(n, r); // make a copy of f in g g = f; // check values that should be equal ok &= ( g.size_order() == f.size_order() ); ok &= ( (g.size_forward_bool() > 0) == (f.size_forward_bool() > 0) ); ok &= ( (g.size_forward_set() > 0) == (f.size_forward_set() > 0) ); // Use zero order Taylor coefficient from f for first order // calculation using g. CPPAD_TESTVECTOR(double) dx(n), dy(m); for(i = 0; i < n; i++) dx[i] = 0.; dx[1] = 1; dy = g.Forward(1, dx); ok &= NearEqual(dy[0], x[0], eps, eps); // partial y[0] w.r.t x[1] ok &= NearEqual(dy[1], x[2], eps, eps); // partial y[1] w.r.t x[1] // Use forward Jacobian sparsity pattern from f to calculate // Hessian sparsity pattern using g. CPPAD_TESTVECTOR(std::set) s(1), h(n); s[0].insert(0); // Compute sparsity pattern for Hessian of y[0] h = f.RevSparseHes(n, s); // check sparsity pattern for Hessian of y[0] = x[0] + x[0] * x[1] ok &= ( h[0].find(0) == h[0].end() ); // zero w.r.t x[0], x[0] ok &= ( h[0].find(1) != h[0].end() ); // non-zero w.r.t x[0], x[1] ok &= ( h[0].find(2) == h[0].end() ); // zero w.r.t x[0], x[2] ok &= ( h[1].find(0) != h[1].end() ); // non-zero w.r.t x[1], x[0] ok &= ( h[1].find(1) == h[1].end() ); // zero w.r.t x[1], x[1] ok &= ( h[1].find(2) == h[1].end() ); // zero w.r.t x[1], x[2] ok &= ( h[2].find(0) == h[2].end() ); // zero w.r.t x[2], x[0] ok &= ( h[2].find(1) == h[2].end() ); // zero w.r.t x[2], x[1] ok &= ( h[2].find(2) == h[2].end() ); // zero w.r.t x[2], x[2] return ok; } // END C++ ================================================ FILE: example/general/fun_check.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* ! WARNING: This file is used as an example by fun_construct and Dependent {xrst_begin fun_check.cpp} ADFun Check and Re-Tape: Example and Test ######################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end fun_check.cpp} */ // BEGIN C++ # include namespace { // ----------------------------------------------------------- // define the template function object Fun in empty namespace template class Fun { private: size_t n; public: // function constructor Fun(size_t n_) : n(n_) { } // function evaluator Vector operator() (const Vector &x) { Vector y(n); size_t i; for(i = 0; i < n; i++) { // This operation sequence depends on x if( x[i] >= 0 ) y[i] = exp(x[i]); else y[i] = exp(-x[i]); } return y; } }; // template function FunCheckCases in empty namespace template bool FunCheckCases(void) { bool ok = true; using CppAD::AD; using CppAD::ADFun; using CppAD::Independent; double eps99 = 99.0 * std::numeric_limits::epsilon(); // use the ADFun default constructor ADFun f; // domain space vector size_t n = 2; ADVector X(n); X[0] = -1.; X[1] = 1.; // declare independent variables and starting recording Independent(X); // create function object to use with AD Fun< AD, ADVector > G(n); // range space vector size_t m = n; ADVector Y(m); Y = G(X); // stop tape and store operation sequence in f : X -> Y f.Dependent(X, Y); ok &= (f.size_order() == 0); // no implicit forward operation // create function object to use with double Fun g(n); // function values should agree when the independent variable // values are the same as during recording Vector x(n); size_t j; for(j = 0; j < n; j++) x[j] = Value(X[j]); double r = eps99; double a = eps99; ok &= FunCheck(f, g, x, a, r); // function values should not agree when the independent variable // values are the negative of values during recording for(j = 0; j < n; j++) x[j] = - Value(X[j]); ok &= ! FunCheck(f, g, x, a, r); // re-tape to obtain the new AD of double operation sequence for(j = 0; j < n; j++) X[j] = x[j]; Independent(X); Y = G(X); // stop tape and store operation sequence in f : X -> Y f.Dependent(X, Y); ok &= (f.size_order() == 0); // no implicit forward with this x // function values should agree now ok &= FunCheck(f, g, x, a, r); return ok; } } // End empty namespace # include # include bool FunCheck(void) { bool ok = true; typedef CppAD::vector Vector1; typedef CppAD::vector< CppAD::AD > ADVector1; typedef std::vector Vector2; typedef std::vector< CppAD::AD > ADVector2; typedef std::valarray Vector3; typedef std::valarray< CppAD::AD > ADVector3; // Run with Vector and ADVector equal to three different cases // all of which are Simple Vectors with elements of type // double and AD respectively. ok &= FunCheckCases< Vector1, ADVector2 >(); ok &= FunCheckCases< Vector2, ADVector3 >(); ok &= FunCheckCases< Vector3, ADVector1 >(); return ok; } // END C++ ================================================ FILE: example/general/fun_property.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin fun_property.cpp} ADFun Function Properties: Example and Test ########################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end fun_property.cpp} */ // BEGIN C++ # include // Note that CPPAD_VEC_ENUM_TYPE is not part of CppAD API and may change # define CPPAD_VEC_ENUM_TYPE unsigned char bool fun_property(void) { bool ok = true; using CppAD::AD; // Use nvar to track the number of variables in the operation sequence. // Start with one for the phantom variable at tape address zero. size_t nvar = 1; // Use npar to track the number of parameters in the operation sequence. // Start with one for the phantom parameter at index zero. size_t npar = 1; // Use ndyn to track the number of dynamic parameters. size_t ndyn = 0; // Use ndyn to track number of arguments to dynamic parameter operators. size_t ndyn_arg = 0; // Start with one for operator corresponding to phantom variable size_t nop = 1; // Start with one for operator corresponding to phantom argument size_t narg = 1; // Use ntext to track the number of characters used to label // output generated using PrintFor commands. size_t ntext = 0; // Use nvecad to track the number of VecAD vectors, plus the number // of VecAD vector elements, in the operation sequence. size_t nvecad = 0; // a VecAD vector CppAD::VecAD v(2); v[0] = 0; // requires the parameter 0, when becomes a variable v[1] = 1; // requires the parameter 1, when becomes a variable // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) x(n); x[0] = 0.; x[1] = 1.; // dynamic parameter vector CPPAD_TESTVECTOR(AD) dynamic(1); dynamic[0] = 1.; // declare independent variables and start tape recording size_t abort_op_index = 0; bool record_compare = true; CppAD::Independent(x, abort_op_index, record_compare, dynamic); nvar += n; nop += n; ndyn += dynamic.size(); npar += ndyn; // a computation that adds to the operation sequence AD I = 0; v[I] = x[0]; nvecad += 3; // one for vector, two for its elements npar += 2; // need parameters 0 and 1 for initial v nop += 1; // operator for storing in a VecAD object narg += 3; // the three arguments are v, I, and x[0] // some operations that do not add to the operation sequence AD u = x[0]; // use same variable as x[0] AD w = x[1]; // use same variable as x[1] // a computation that adds to the operation sequence w = w * (u + w); nop += 2; // requires two new operators, an add and a multiply nvar += 2; // each operator results in its own variable narg += 4; // each operator has two arguments // range space vector size_t m = 3; CPPAD_TESTVECTOR(AD) y(m); // operations that do not add to the operation sequence y[0] = 1.; // re-use the parameter 1 y[1] = u; // use same variable as u // a computation that adds to the operation sequence y[2] = w + 2.; nop += 1; // requires a new add operator npar += 1; // new parameter 2 is new, so it must be included nvar += 1; // variable corresponding to the result narg += 2; // operator has two arguments // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); nop += 1; // special operator for y[0] because it is a parameter nvar += 1; // special variable for y[0] because it is a parameter narg += 1; // identifies which parameter corresponds to y[0] nop += 1; // special operator at the end of operation sequence // check the sequence property functions ok &= f.Domain() == n; ok &= f.Range() == m; ok &= f.Parameter(0) == true; ok &= f.Parameter(1) == false; ok &= f.Parameter(2) == false; ok &= f.size_var() == nvar; ok &= f.size_op() == nop; ok &= f.size_op_arg() == narg; ok &= f.size_par() == npar; ok &= f.size_text() == ntext; ok &= f.size_VecAD() == nvecad; ok &= f.size_dyn_ind() == ndyn; ok &= f.size_dyn_par() == ndyn; ok &= f.size_dyn_arg() == ndyn_arg; // size_t sum = 0; sum += nop * sizeof(CPPAD_VEC_ENUM_TYPE); sum += narg * sizeof(CPPAD_TAPE_ADDR_TYPE); sum += npar * sizeof(double); sum += npar * sizeof(bool); sum += ndyn * sizeof(CPPAD_VEC_ENUM_TYPE); sum += ndyn * sizeof(CPPAD_TAPE_ADDR_TYPE); sum += ndyn_arg * sizeof(CPPAD_TAPE_ADDR_TYPE); sum += ntext * sizeof(char); sum += nvecad * sizeof(CPPAD_TAPE_ADDR_TYPE); ok &= f.size_op_seq() == sum; return ok; } // END C++ ================================================ FILE: example/general/function_name.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin function_name.cpp} ADFun Function Name: Example and Test ##################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end function_name.cpp} */ // BEGIN C++ # include bool function_name(void) { bool ok = true; using CppAD::AD; // create empty function CppAD::ADFun f; // check its name ok &= f.function_name_get() == ""; // set and check a new name f.function_name_set("empty_function"); ok &= f.function_name_get() == "empty_function"; // store an operation sequence in f size_t nx = 1; size_t ny = 1; CPPAD_TESTVECTOR( AD ) ax(nx), ay(ny); ax[0] = 1.0; CppAD::Independent(ax); ay[0] = sin(ax[0]); f.Dependent(ax, ay); // check function name has not changed ok &= f.function_name_get() == "empty_function"; // now set a better name for this function f.function_name_set("sin"); ok &= f.function_name_get() == "sin"; return ok; } // END C++ ================================================ FILE: example/general/general.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin general.cpp} General Examples and Tests Driver ################################# Running These Tests ******************* After executing the :ref:`cmake-name` command form the :ref:`download@Distribution Directory`, you can build and run these tests with the commands:: cd build make check_example_general Note that your choice of :ref:`cmake@generator` may require using an different version of make; e.g., ``ninja`` . {xrst_literal // BEGIN C++ // END C++ } {xrst_end general.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ // CPPAD_HAS_* defines # include // standard string # include // memory utility # include // test runner # include // BEGIN_SORT_THIS_LINE_PLUS_1 extern bool Add(void); extern bool AddEq(void); extern bool BenderQuad(void); extern bool BoolFun(void); extern bool Compare(void); extern bool CondExp(void); extern bool Cos(void); extern bool Cosh(void); extern bool Div(void); extern bool DivEq(void); extern bool EqualOpSeq(void); extern bool ForOne(void); extern bool ForTwo(void); extern bool Forward(void); extern bool FunCheck(void); extern bool HesLagrangian(void); extern bool HesLuDet(void); extern bool HesMinorDet(void); extern bool HesTimesDir(void); extern bool Hessian(void); extern bool Independent(void); extern bool Integer(void); extern bool Interface2C(void); extern bool JacLuDet(void); extern bool JacMinorDet(void); extern bool Jacobian(void); extern bool LuRatio(void); extern bool Mul(void); extern bool MulEq(void); extern bool NumericType(void); extern bool OdeStiff(void); extern bool RevOne(void); extern bool RevTwo(void); extern bool Sin(void); extern bool Sinh(void); extern bool Sqrt(void); extern bool StackMachine(void); extern bool Sub(void); extern bool SubEq(void); extern bool Tan(void); extern bool Tanh(void); extern bool TapeIndex(void); extern bool Value(void); extern bool Var2Par(void); extern bool abort_recording(void); extern bool acos(void); extern bool acosh(void); extern bool ad_assign(void); extern bool ad_ctor(void); extern bool ad_fun(void); extern bool ad_in_c(void); extern bool ad_input(void); extern bool ad_output(void); extern bool asin(void); extern bool asinh(void); extern bool atan(void); extern bool atan2(void); extern bool atanh(void); extern bool azmul(void); extern bool base2ad(void); extern bool base2vec_ad(void); extern bool base_require(void); extern bool capacity_order(void); extern bool change_param(void); extern bool check_for_nan(void); extern bool complex_poly(void); extern bool con_dyn_var(void); extern bool eigen_array(void); extern bool eigen_det(void); extern bool erf(void); extern bool erfc(void); extern bool exp(void); extern bool expm1(void); extern bool fabs(void); extern bool forward_dir(void); extern bool forward_order(void); extern bool fun_assign(void); extern bool fun_property(void); extern bool function_name(void); extern bool interp_onetape(void); extern bool interp_retape(void); extern bool log(void); extern bool log10(void); extern bool log1p(void); extern bool lu_vec_ad_ok(void); extern bool mul_level(void); extern bool mul_level_adolc(void); extern bool mul_level_adolc_ode(void); extern bool mul_level_ode(void); extern bool near_equal_ext(void); extern bool new_dynamic(void); extern bool num_limits(void); extern bool number_skip(void); extern bool opt_val_hes(void); extern bool pow(void); extern bool pow_nan(void); extern bool print_for(void); extern bool rev_checkpoint(void); extern bool reverse_one(void); extern bool reverse_three(void); extern bool reverse_two(void); extern bool sign(void); extern bool taylor_ode(void); extern bool unary_minus(void); extern bool unary_plus(void); extern bool vec_ad(void); // END_SORT_THIS_LINE_MINUS_1 // main program that runs all the tests int main(void) { std::string group = "example/general"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // BEGIN_SORT_THIS_LINE_PLUS_1 Run( Add, "Add" ); Run( AddEq, "AddEq" ); Run( BenderQuad, "BenderQuad" ); Run( BoolFun, "BoolFun" ); Run( Compare, "Compare" ); Run( CondExp, "CondExp" ); Run( Cos, "Cos" ); Run( Cosh, "Cosh" ); Run( Div, "Div" ); Run( DivEq, "DivEq" ); Run( EqualOpSeq, "EqualOpSeq" ); Run( ForOne, "ForOne" ); Run( ForTwo, "ForTwo" ); Run( Forward, "Forward" ); Run( FunCheck, "FunCheck" ); Run( HesLagrangian, "HesLagrangian" ); Run( HesLuDet, "HesLuDet" ); Run( HesMinorDet, "HesMinorDet" ); Run( HesTimesDir, "HesTimesDir" ); Run( Hessian, "Hessian" ); Run( Independent, "Independent" ); Run( Integer, "Integer" ); Run( Interface2C, "Interface2C" ); Run( JacLuDet, "JacLuDet" ); Run( JacMinorDet, "JacMinorDet" ); Run( Jacobian, "Jacobian" ); Run( LuRatio, "LuRatio" ); Run( Mul, "Mul" ); Run( MulEq, "MulEq" ); Run( NumericType, "NumericType" ); Run( OdeStiff, "OdeStiff" ); Run( RevOne, "RevOne" ); Run( RevTwo, "RevTwo" ); Run( Sin, "Sin" ); Run( Sinh, "Sinh" ); Run( Sqrt, "Sqrt" ); Run( StackMachine, "StackMachine" ); Run( Sub, "Sub" ); Run( SubEq, "SubEq" ); Run( Tan, "Tan" ); Run( Tanh, "Tanh" ); Run( TapeIndex, "TapeIndex" ); Run( Value, "Value" ); Run( Var2Par, "Var2Par" ); Run( abort_recording, "abort_recording" ); Run( acos, "acos" ); Run( acosh, "acosh" ); Run( ad_assign, "ad_assign" ); Run( ad_ctor, "ad_ctor" ); Run( ad_fun, "ad_fun" ); Run( ad_in_c, "ad_in_c" ); Run( ad_input, "ad_input" ); Run( ad_output, "ad_output" ); Run( asin, "asin" ); Run( asinh, "asinh" ); Run( atan, "atan" ); Run( atan2, "atan2" ); Run( atanh, "atanh" ); Run( azmul, "azmul" ); Run( base2ad, "base2ad" ); Run( base2vec_ad, "base2vec_ad" ); Run( base_require, "base_require" ); Run( capacity_order, "capacity_order" ); Run( change_param, "change_param" ); Run( complex_poly, "complex_poly" ); Run( con_dyn_var, "con_dyn_var" ); Run( erf, "erf" ); Run( erfc, "erfc" ); Run( exp, "exp" ); Run( expm1, "expm1" ); Run( fabs, "fabs" ); Run( forward_dir, "forward_dir" ); Run( forward_order, "forward_order" ); Run( fun_assign, "fun_assign" ); Run( fun_property, "fun_property" ); Run( function_name, "function_name" ); Run( interp_onetape, "interp_onetape" ); Run( interp_retape, "interp_retape" ); Run( log, "log" ); Run( log10, "log10" ); Run( log1p, "log1p" ); Run( lu_vec_ad_ok, "lu_vec_ad_ok" ); Run( mul_level, "mul_level" ); Run( mul_level_ode, "mul_level_ode" ); Run( near_equal_ext, "near_equal_ext" ); Run( new_dynamic, "new_dynamic" ); Run( num_limits, "num_limits" ); Run( number_skip, "number_skip" ); Run( opt_val_hes, "opt_val_hes" ); Run( pow, "pow" ); Run( pow_nan, "pow_nan" ); Run( rev_checkpoint, "rev_checkpoint" ); Run( reverse_one, "reverse_one" ); Run( reverse_three, "reverse_three" ); Run( reverse_two, "reverse_two" ); Run( sign, "sign" ); Run( taylor_ode, "ode_taylor" ); Run( unary_minus, "unary_minus" ); Run( unary_plus, "unary_plus" ); Run( vec_ad, "vec_ad" ); // END_SORT_THIS_LINE_MINUS_1 # if ! CPPAD_DEBUG_AND_RELEASE Run( check_for_nan, "check_for_nan" ); # endif # if CPPAD_HAS_ADOLC Run( mul_level_adolc, "mul_level_adolc" ); Run( mul_level_adolc_ode, "mul_level_adolc_ode" ); # endif # if CPPAD_HAS_EIGEN Run( eigen_array, "eigen_array" ); Run( eigen_det, "eigen_det" ); # endif // // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } // END C++ ================================================ FILE: example/general/hes_lagrangian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin hes_lagrangian.cpp} {xrst_comment ! NOTE the title states that this example is used two places !} Hessian of Lagrangian and ADFun Default Constructor: Example and Test ##################################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end hes_lagrangian.cpp} */ // BEGIN C++ # include # include namespace { CppAD::AD Lagragian( const CppAD::vector< CppAD::AD > &xyz ) { using CppAD::AD; assert( xyz.size() == 6 ); AD x0 = xyz[0]; AD x1 = xyz[1]; AD x2 = xyz[2]; AD y0 = xyz[3]; AD y1 = xyz[4]; AD z = xyz[5]; // compute objective function AD f = x0 * x0; // compute constraint functions AD g0 = 1. + 2.*x1 + 3.*x2; AD g1 = log( x0 * x2 ); // compute the Lagragian AD L = y0 * g0 + y1 * g1 + z * f; return L; } CppAD::vector< CppAD::AD > fg( const CppAD::vector< CppAD::AD > &x ) { using CppAD::AD; using CppAD::vector; assert( x.size() == 3 ); vector< AD > fg(3); fg[0] = x[0] * x[0]; fg[1] = 1. + 2. * x[1] + 3. * x[2]; fg[2] = log( x[0] * x[2] ); return fg; } bool CheckHessian( CppAD::vector H , double x0, double x1, double x2, double y0, double y1, double z ) { using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); bool ok = true; size_t n = 3; assert( H.size() == n * n ); /* L = z*x0*x0 + y0*(1 + 2*x1 + 3*x2) + y1*log(x0*x2) L_0 = 2 * z * x0 + y1 / x0 L_1 = y0 * 2 L_2 = y0 * 3 + y1 / x2 */ // L_00 = 2 * z - y1 / ( x0 * x0 ) double check = 2. * z - y1 / (x0 * x0); ok &= NearEqual(H[0 * n + 0], check, eps99, eps99); // L_01 = L_10 = 0 ok &= NearEqual(H[0 * n + 1], 0., eps99, eps99); ok &= NearEqual(H[1 * n + 0], 0., eps99, eps99); // L_02 = L_20 = 0 ok &= NearEqual(H[0 * n + 2], 0., eps99, eps99); ok &= NearEqual(H[2 * n + 0], 0., eps99, eps99); // L_11 = 0 ok &= NearEqual(H[1 * n + 1], 0., eps99, eps99); // L_12 = L_21 = 0 ok &= NearEqual(H[1 * n + 2], 0., eps99, eps99); ok &= NearEqual(H[2 * n + 1], 0., eps99, eps99); // L_22 = - y1 / (x2 * x2) check = - y1 / (x2 * x2); ok &= NearEqual(H[2 * n + 2], check, eps99, eps99); return ok; } bool UseL() { using CppAD::AD; using CppAD::vector; // double values corresponding to x, y, and z vectors double x0(.5), x1(1e3), x2(1), y0(2.), y1(3.), z(4.); // domain space vector size_t n = 3; vector< AD > a_x(n); a_x[0] = x0; a_x[1] = x1; a_x[2] = x2; // declare a_x as independent variable vector and start recording CppAD::Independent(a_x); // vector including x, y, and z vector< AD > a_xyz(n + 2 + 1); a_xyz[0] = a_x[0]; a_xyz[1] = a_x[1]; a_xyz[2] = a_x[2]; a_xyz[3] = y0; a_xyz[4] = y1; a_xyz[5] = z; // range space vector size_t m = 1; vector< AD > a_L(m); a_L[0] = Lagragian(a_xyz); // create K: x -> L and stop tape recording. // Use default ADFun construction for example purposes. CppAD::ADFun K; K.Dependent(a_x, a_L); // Operation sequence corresponding to K depends on // value of y0, y1, and z. Must redo calculations above when // y0, y1, or z changes. // declare independent variable vector and Hessian vector x(n); vector H( n * n ); // point at which we are computing the Hessian // (must redo calculations below each time x changes) x[0] = x0; x[1] = x1; x[2] = x2; H = K.Hessian(x, 0); // check this Hessian calculation return CheckHessian(H, x0, x1, x2, y0, y1, z); } bool Usefg() { using CppAD::AD; using CppAD::vector; // parameters defining problem double x0(.5), x1(1e3), x2(1), y0(2.), y1(3.), z(4.); // domain space vector size_t n = 3; vector< AD > a_x(n); a_x[0] = x0; a_x[1] = x1; a_x[2] = x2; // declare a_x as independent variable vector and start recording CppAD::Independent(a_x); // range space vector size_t m = 3; vector< AD > a_fg(m); a_fg = fg(a_x); // create K: x -> fg and stop tape recording CppAD::ADFun K; K.Dependent(a_x, a_fg); // Operation sequence corresponding to K does not depend on // value of x0, x1, x2, y0, y1, or z. // forward and reverse mode arguments and results vector x(n); vector H( n * n ); vector dx(n); vector w(m); vector dw(2*n); // compute Hessian at this value of x // (must redo calculations below each time x changes) x[0] = x0; x[1] = x1; x[2] = x2; K.Forward(0, x); // set weights to Lagrange multiplier values // (must redo calculations below each time y0, y1, or z changes) w[0] = z; w[1] = y0; w[2] = y1; // initialize dx as zero size_t i, j; for(i = 0; i < n; i++) dx[i] = 0.; // loop over components of x for(i = 0; i < n; i++) { dx[i] = 1.; // dx is i-th elementary vector K.Forward(1, dx); // partial w.r.t dx dw = K.Reverse(2, w); // deritavtive of partial for(j = 0; j < n; j++) H[ i * n + j ] = dw[ j * 2 + 1 ]; dx[i] = 0.; // dx is zero vector } // check this Hessian calculation return CheckHessian(H, x0, x1, x2, y0, y1, z); } } bool HesLagrangian(void) { bool ok = true; // UseL is simpler, but must retape every time that y of z changes ok &= UseL(); // Usefg does not need to retape unless operation sequence changes ok &= Usefg(); return ok; } // END C++ ================================================ FILE: example/general/hes_lu_det.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin hes_lu_det.cpp} Gradient of Determinant Using LU Factorization: Example and Test ################################################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end hes_lu_det.cpp} */ // BEGIN C++ // Complex examples should suppress conversion warnings # include # include # include // The AD complex case is used by this example so must // define a specializatgion of LeqZero,AbsGeq for the AD case namespace CppAD { CPPAD_BOOL_BINARY( std::complex , AbsGeq ) CPPAD_BOOL_UNARY( std::complex , LeqZero ) } bool HesLuDet(void) { bool ok = true; using namespace CppAD; typedef std::complex Complex; double eps99 = 99.0 * std::numeric_limits::epsilon(); size_t n = 2; // object for computing determinants det_by_lu< AD > Det(n); // independent and dependent variable vectors CPPAD_TESTVECTOR(AD) X(n * n); CPPAD_TESTVECTOR(AD) D(1); // value of the independent variable size_t i; for(i = 0; i < n * n; i++) X[i] = Complex( double(i), -double(i) ); // set the independent variables Independent(X); D[0] = Det(X); // create the function object ADFun f(X, D); // argument value CPPAD_TESTVECTOR(Complex) x( n * n ); for(i = 0; i < n * n; i++) x[i] = Complex( double(2 * i) , double(i) ); // first derivative of the determinant CPPAD_TESTVECTOR(Complex) H( n * n * n * n ); H = f.Hessian(x, 0); /* f(x) = x[0] * x[3] - x[1] * x[2] f'(x) = ( x[3], -x[2], -x[1], x[0] ) */ Complex zero(0., 0.); Complex one(1., 0.); Complex Htrue[] = { zero, zero, zero, one, zero, zero, -one, zero, zero, -one, zero, zero, one, zero, zero, zero }; for( i = 0; i < n*n*n*n; i++) ok &= NearEqual( Htrue[i], H[i], eps99 , eps99 ); return ok; } // END C++ ================================================ FILE: example/general/hes_minor_det.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin hes_minor_det.cpp} Gradient of Determinant Using Expansion by Minors: Example and Test ################################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end hes_minor_det.cpp} */ // BEGIN C++ // Complex examples should suppress conversion warnings # include # include # include # include typedef std::complex Complex; typedef CppAD::AD ADComplex; typedef CPPAD_TESTVECTOR(ADComplex) ADVector; // ---------------------------------------------------------------------------- bool HesMinorDet(void) { bool ok = true; using namespace CppAD; size_t n = 2; // object for computing determinants det_by_minor Det(n); // independent and dependent variable vectors CPPAD_TESTVECTOR(ADComplex) X(n * n); CPPAD_TESTVECTOR(ADComplex) D(1); // value of the independent variable size_t i; for(i = 0; i < n * n; i++) X[i] = Complex( double(i), -double(i) ); // set the independent variables Independent(X); // comupute the determinant D[0] = Det(X); // create the function object ADFun f(X, D); // argument value CPPAD_TESTVECTOR(Complex) x( n * n ); for(i = 0; i < n * n; i++) x[i] = Complex( double(2 * i) , double(i) ); // first derivative of the determinant CPPAD_TESTVECTOR(Complex) H( n * n * n * n); H = f.Hessian(x, 0); /* f(x) = x[0] * x[3] - x[1] * x[2] f'(x) = ( x[3], -x[2], -x[1], x[0] ) */ Complex zero(0., 0.); Complex one(1., 0.); Complex Htrue[] = { zero, zero, zero, one, zero, zero, -one, zero, zero, -one, zero, zero, one, zero, zero, zero }; for( i = 0; i < n*n*n*n; i++) ok &= Htrue[i] == H[i]; return ok; } // END C++ ================================================ FILE: example/general/hes_times_dir.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin hes_times_dir.cpp} Hessian Times Direction: Example and Test ######################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end hes_times_dir.cpp} */ // BEGIN C++ // Example and test of computing the Hessian times a direction; i.e., // given F : R^n -> R and a direction dx in R^n, we compute F''(x) * dx # include namespace { // put this function in the empty namespace // F(x) = |x|^2 = x[0]^2 + ... + x[n-1]^2 template Type F(CPPAD_TESTVECTOR(Type) &x) { Type sum = 0; size_t i = x.size(); while(i--) sum += x[i] * x[i]; return sum; } } bool HesTimesDir(void) { bool ok = true; // initialize test result size_t j; // a domain variable variable using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 5; CPPAD_TESTVECTOR(AD) X(n); for(j = 0; j < n; j++) X[j] = AD(j); // declare independent variables and start recording CppAD::Independent(X); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = F(X); // create f : X -> Y and stop recording CppAD::ADFun f(X, Y); // choose a direction dx and compute dy(x) = F'(x) * dx CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); for(j = 0; j < n; j++) dx[j] = double(n - j); dy = f.Forward(1, dx); // compute ddw = F''(x) * dx CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) ddw(2 * n); w[0] = 1.; ddw = f.Reverse(2, w); // F(x) = x[0]^2 + x[1]^2 + ... + x[n-1]^2 // F''(x) = 2 * Identity_Matrix // F''(x) * dx = 2 * dx for(j = 0; j < n; j++) ok &= NearEqual(ddw[j * 2 + 1], 2.*dx[j], eps99, eps99); return ok; } // END C++ ================================================ FILE: example/general/hessian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin hessian.cpp} Hessian: Example and Test ######################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end hessian.cpp} */ // BEGIN C++ # include namespace { // --------------------------------------------------------- // define the template function HessianCases in empty namespace template bool HessianCases() { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); using CppAD::exp; using CppAD::sin; using CppAD::cos; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) X(n); X[0] = 1.; X[1] = 2.; // declare independent variables and starting recording CppAD::Independent(X); // a calculation between the domain and range values AD Square = X[0] * X[0]; // range space vector size_t m = 3; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = Square * exp( X[1] ); Y[1] = Square * sin( X[1] ); Y[2] = Square * cos( X[1] ); // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // new value for the independent variable vector Vector x(n); x[0] = 2.; x[1] = 1.; // second derivative of y[1] Vector hes( n * n ); hes = f.Hessian(x, 1); /* F_1 = x[0] * x[0] * sin(x[1]) F_1^{(1)} = [ 2 * x[0] * sin(x[1]) , x[0] * x[0] * cos(x[1]) ] F_1^{(2)} = [ 2 * sin(x[1]) , 2 * x[0] * cos(x[1]) ] [ 2 * x[0] * cos(x[1]) , - x[0] * x[0] * sin(x[1]) ] */ ok &= NearEqual( 2.*sin(x[1]), hes[0*n+0], eps99, eps99); ok &= NearEqual( 2.*x[0]*cos(x[1]), hes[0*n+1], eps99, eps99); ok &= NearEqual( 2.*x[0]*cos(x[1]), hes[1*n+0], eps99, eps99); ok &= NearEqual( - x[0]*x[0]*sin(x[1]), hes[1*n+1], eps99, eps99); return ok; } } // End empty namespace # include # include bool Hessian(void) { bool ok = true; // Run with Vector equal to three different cases // all of which are Simple Vectors with elements of type double. ok &= HessianCases< CppAD::vector >(); ok &= HessianCases< std::vector >(); ok &= HessianCases< std::valarray >(); return ok; } // END C++ ================================================ FILE: example/general/independent.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* WARNING: This file is used an an example by fun_construct. {xrst_begin independent.cpp} {xrst_comment ! NOTE the title states that this example is used two places !} Independent and ADFun Constructor: Example and Test ################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end independent.cpp} */ // BEGIN C++ # include namespace { // -------------------------------------------------------- // define the template function Test(void) in empty namespace template bool Test(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 2; ADVector X(n); // ADVector is the template parameter in call to Test X[0] = 0.; X[1] = 1.; // declare independent variables and start recording // use the template parameter ADVector for the vector type CppAD::Independent(X); AD a = X[0] + X[1]; // first AD operation AD b = X[0] * X[1]; // second AD operation // range space vector size_t m = 2; ADVector Y(m); // ADVector is the template paraemter in call to Test Y[0] = a; Y[1] = b; // create f: X -> Y and stop tape recording // use the template parameter ADVector for the vector type CppAD::ADFun f(X, Y); // check value ok &= NearEqual(Y[0] , 1., eps99 , eps99); ok &= NearEqual(Y[1] , 0., eps99 , eps99); // compute f(1, 2) CPPAD_TESTVECTOR(double) x(n); CPPAD_TESTVECTOR(double) y(m); x[0] = 1.; x[1] = 2.; y = f.Forward(0, x); ok &= NearEqual(y[0] , 3., eps99 , eps99); ok &= NearEqual(y[1] , 2., eps99 , eps99); // compute partial of f w.r.t x[0] at (1, 2) CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dx[1] = 0.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0] , 1., eps99 , eps99); ok &= NearEqual(dy[1] , x[1], eps99 , eps99); // compute partial of f w.r.t x[1] at (1, 2) dx[0] = 0.; dx[1] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0] , 1., eps99 , eps99); ok &= NearEqual(dy[1] , x[0], eps99 , eps99); return ok; } } // End of empty namespace ------------------------------------------- # include # include bool Independent(void) { bool ok = true; typedef CppAD::AD ADdouble; // Run with ADVector equal to three different cases // all of which are Simple Vectors with elements of type AD. ok &= Test< CppAD::vector >(); ok &= Test< std::vector >(); ok &= Test< std::valarray >(); return ok; } // END C++ ================================================ FILE: example/general/integer.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin integer.cpp} Convert From AD to Integer: Example and Test ############################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end integer.cpp} */ // BEGIN C++ # include bool Integer(void) { bool ok = true; using CppAD::AD; using CppAD::Integer; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) x(n); x[0] = 3.5; x[1] = 4.5; // check integer before recording ok &= (Integer(x[0]) == 3); ok &= (Integer(x[1]) == 4); // start recording // declare independent variables and start tape recording CppAD::Independent(x); // check integer during recording ok &= (Integer(x[0]) == 3); ok &= (Integer(x[1]) == 4); // check integer for VecAD element CppAD::VecAD v(1); AD zero(0); v[zero] = 2; ok &= (Integer(v[zero]) == 2); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = - x[1]; // create f: x -> y and stop recording CppAD::ADFun f(x, y); // check integer after recording ok &= (Integer(x[0]) == 3.); ok &= (Integer(x[1]) == 4.); ok &= (Integer(y[0]) == -4.); return ok; } // END C++ ================================================ FILE: example/general/interface2c.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin interface2c.cpp} Interfacing to C: Example and Test ################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end interface2c.cpp} */ // BEGIN C++ # include // CppAD utilities # include // assert macro namespace { // Begin empty namespace /* Compute the value of a sum of Gaussians defined by a and evaluated at x y = sum_{i=1}^n a[3*i] exp( (x - a[3*i+1])^2 / a[3*i+2])^2 ) where the floating point type is a template parameter */ template Float sumGauss(const Float &x, const CppAD::vector &a) { // number of components in a size_t na = a.size(); // number of Gaussians size_t n = na / 3; // check the restrictions on na assert( na == n * 3 ); // declare temporaries used inside of loop Float ex, arg; // initialize sum Float y = 0.; // loop with respect to Gaussians size_t i; for(i = 0; i < n; i++) { arg = (x - a[3*i+1]) / a[3*i+2]; ex = exp(-arg * arg); y += a[3*i] * ex; } return y; } /* Create a C function interface that computes both y = sum_{i=1}^n a[3*i] exp( (x - a[3*i+1])^2 / a[3*i+2])^2 ) and its derivative with respect to the parameter vector a. */ extern "C" void sumGauss(float x, float a[], float *y, float dyda[], size_t na) { // Note that any simple vector could replace CppAD::vector; // for example, std::vector, std::valarray // check the restrictions on na assert( na % 3 == 0 ); // mod(na, 3) = 0 // use the shorthand ADfloat for the type CppAD::AD typedef CppAD::AD ADfloat; // vector for independent variables CppAD::vector A(na); // used with template function above CppAD::vector acopy(na); // used for derivative calculations // vector for the dependent variables (there is only one) CppAD::vector Y(1); // copy the independent variables from C vector to CppAD vectors size_t i; for(i = 0; i < na; i++) A[i] = acopy[i] = a[i]; // declare that A is the independent variable vector CppAD::Independent(A); // value of x as an ADfloat object ADfloat X = x; // Evaluate template version of sumGauss with ADfloat as the template // parameter. Set the independent variable to the resulting value Y[0] = sumGauss(X, A); // create the AD function object F : A -> Y CppAD::ADFun F(A, Y); // use Value to convert Y[0] to float and return y = F(a) *y = CppAD::Value(Y[0]); // evaluate the derivative F'(a) CppAD::vector J(na); J = F.Jacobian(acopy); // return the value of dyda = F'(a) as a C vector for(i = 0; i < na; i++) dyda[i] = J[i]; return; } /* Link CppAD::NearEqual so do not have to use namespace notation in Interface2C */ bool NearEqual(float x, float y, float r, float a) { return CppAD::NearEqual(x, y, r, a); } } // End empty namespace bool Interface2C(void) { // This routine is intentionally coded as if it were a C routine // except for the fact that it uses the predefined type bool. bool ok = true; // declare variables float x, a[6], y, dyda[6], tmp[6]; size_t na, i; // number of parameters (3 for each Gaussian) na = 6; // number of Gaussians: n = na / 3; // value of x x = 1.; // value of the parameter vector a for(i = 0; i < na; i++) a[i] = (float) (i+1); // evaluate function and derivative sumGauss(x, a, &y, dyda, na); // compare dyda to central difference approximation for deriative for(i = 0; i < na; i++) { // local variables float eps, ai, yp, ym, dy_da; // We assume that the type float has at least 7 digits of // precision, so we choose eps to be about pow(10., -7./2.). eps = (float) 3e-4; // value of this component of a ai = a[i]; // evaluate F( a + eps * ei ) a[i] = ai + eps; sumGauss(x, a, &yp, tmp, na); // evaluate F( a - eps * ei ) a[i] = ai - eps; sumGauss(x, a, &ym, tmp, na); // evaluate central difference approximates for partial dy_da = (yp - ym) / (2 * eps); // restore this component of a a[i] = ai; ok &= NearEqual(dyda[i], dy_da, eps, eps); } return ok; } // END C++ ================================================ FILE: example/general/interp_onetape.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin interp_onetape.cpp} {xrst_spell retaping } Interpolation With Out Retaping: Example and Test ################################################# See Also ******** :ref:`interp_retape.cpp-name` , :ref:`atomic_four_bilinear.cpp-name` {xrst_literal // BEGIN C++ // END C++ } {xrst_end interp_onetape.cpp} */ // BEGIN C++ # include # include # include namespace { double ArgumentValue[] = { .0 , .2 , .4 , .8 , 1. }; double FunctionValue[] = { std::sin( ArgumentValue[0] ) , std::sin( ArgumentValue[1] ) , std::sin( ArgumentValue[2] ) , std::sin( ArgumentValue[3] ) , std::sin( ArgumentValue[4] ) }; size_t TableLength = 5; size_t Index(const double &x) { // determine the index j such that x is between // ArgumentValue[j] and ArgumentValue[j+1] static size_t j = 0; while ( x < ArgumentValue[j] && j > 0 ) j--; while ( x > ArgumentValue[j+1] && j < TableLength - 2) j++; // assert conditions that must be true given logic above assert( j >= 0 && j < TableLength - 1 ); return j; } double Argument(const double &x) { size_t j = Index(x); return ArgumentValue[j]; } double Function(const double &x) { size_t j = Index(x); return FunctionValue[j]; } double Slope(const double &x) { size_t j = Index(x); double dx = ArgumentValue[j+1] - ArgumentValue[j]; double dy = FunctionValue[j+1] - FunctionValue[j]; return dy / dx; } CPPAD_DISCRETE_FUNCTION(double, Argument) CPPAD_DISCRETE_FUNCTION(double, Function) CPPAD_DISCRETE_FUNCTION(double, Slope) } bool interp_onetape(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = .4 * ArgumentValue[1] + .6 * ArgumentValue[2]; // declare independent variables and start tape recording CppAD::Independent(ax); // evaluate piecewise linear interpolant at ax[0] AD ax_grid = Argument(ax[0]); AD af_grid = Function(ax[0]); AD as_grid = Slope(ax[0]); AD ay_linear = af_grid + (ax[0] - ax_grid) * as_grid; // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = ay_linear; // create f: x -> ay and stop tape recording CppAD::ADFun f(ax, ay); // vectors for arguments to the function object f CPPAD_TESTVECTOR(double) x(n); // argument values CPPAD_TESTVECTOR(double) y(m); // function values CPPAD_TESTVECTOR(double) dx(n); // differentials in x space CPPAD_TESTVECTOR(double) dy(m); // differentials in y space // to check function value we use the fact that ax[0] is between // ArgumentValue[1] and ArgumentValue[2] x[0] = Value(ax[0]); double delta = ArgumentValue[2] - ArgumentValue[1]; double check = FunctionValue[2] * (x[0] - ArgumentValue[1]) / delta + FunctionValue[1] * (ArgumentValue[2] - x[0]) / delta; ok &= NearEqual(ay[0], check, eps99, eps99); // evaluate f where x has different value x[0] = .7 * ArgumentValue[2] + .3 * ArgumentValue[3]; y = f.Forward(0, x); // check function value delta = ArgumentValue[3] - ArgumentValue[2]; check = FunctionValue[3] * (x[0] - ArgumentValue[2]) / delta + FunctionValue[2] * (ArgumentValue[3] - x[0]) / delta; ok &= NearEqual(y[0], check, eps99, eps99); // evaluate partials w.r.t. x[0] dx[0] = 1.; dy = f.Forward(1, dx); // check that the derivative is the slope check = (FunctionValue[3] - FunctionValue[2]) / (ArgumentValue[3] - ArgumentValue[2]); ok &= NearEqual(dy[0], check, eps99, eps99); return ok; } // END C++ ================================================ FILE: example/general/interp_retape.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin interp_retape.cpp} {xrst_spell retaping } Interpolation With Retaping: Example and Test ############################################# See Also ******** :ref:`interp_onetape.cpp-name` {xrst_literal // BEGIN C++ // END C++ } {xrst_end interp_retape.cpp} */ // BEGIN C++ # include # include # include namespace { double ArgumentValue[] = { .0 , .2 , .4 , .8 , 1. }; double FunctionValue[] = { std::sin( ArgumentValue[0] ) , std::sin( ArgumentValue[1] ) , std::sin( ArgumentValue[2] ) , std::sin( ArgumentValue[3] ) , std::sin( ArgumentValue[4] ) }; size_t TableLength = 5; size_t Index(const CppAD::AD &x) { // determine the index j such that x is between // ArgumentValue[j] and ArgumentValue[j+1] static size_t j = 0; while ( x < ArgumentValue[j] && j > 0 ) j--; while ( x > ArgumentValue[j+1] && j < TableLength - 2) j++; // assert conditions that must be true given logic above assert( j >= 0 && j < TableLength - 1 ); return j; } double Argument(const CppAD::AD &x) { size_t j = Index(x); return ArgumentValue[j]; } double Function(const CppAD::AD &x) { size_t j = Index(x); return FunctionValue[j]; } double Slope(const CppAD::AD &x) { size_t j = Index(x); double dx = ArgumentValue[j+1] - ArgumentValue[j]; double dy = FunctionValue[j+1] - FunctionValue[j]; return dy / dx; } } bool interp_retape(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) X(n); // loop over argument values size_t k; for(k = 0; k < TableLength - 1; k++) { X[0] = .4 * ArgumentValue[k] + .6 * ArgumentValue[k+1]; // declare independent variables and start tape recording // (use a different tape for each argument value) CppAD::Independent(X); // evaluate piecewise linear interpolant at X[0] AD A = Argument(X[0]); AD F = Function(X[0]); AD S = Slope(X[0]); AD I = F + (X[0] - A) * S; // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = I; // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // vectors for arguments to the function object f CPPAD_TESTVECTOR(double) x(n); // argument values CPPAD_TESTVECTOR(double) y(m); // function values CPPAD_TESTVECTOR(double) dx(n); // differentials in x space CPPAD_TESTVECTOR(double) dy(m); // differentials in y space // to check function value we use the fact that X[0] is between // ArgumentValue[k] and ArgumentValue[k+1] double delta, check; x[0] = Value(X[0]); delta = ArgumentValue[k+1] - ArgumentValue[k]; check = FunctionValue[k+1] * (x[0]-ArgumentValue[k]) / delta + FunctionValue[k] * (ArgumentValue[k+1]-x[0]) / delta; ok &= NearEqual(Y[0], check, eps99, eps99); // evaluate partials w.r.t. x[0] dx[0] = 1.; dy = f.Forward(1, dx); // check that the derivative is the slope check = (FunctionValue[k+1] - FunctionValue[k]) / (ArgumentValue[k+1] - ArgumentValue[k]); ok &= NearEqual(dy[0], check, eps99, eps99); } return ok; } // END C++ ================================================ FILE: example/general/jac_lu_det.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin jac_lu_det.cpp} Gradient of Determinant Using Lu Factorization: Example and Test ################################################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end jac_lu_det.cpp} */ // BEGIN C++ // Complex examples should suppress conversion warnings # include # include # include // The AD complex case is used by this example so must // define a specializatgion of LeqZero,AbsGeq for the AD case namespace CppAD { CPPAD_BOOL_BINARY( std::complex , AbsGeq ) CPPAD_BOOL_UNARY( std::complex , LeqZero ) } bool JacLuDet(void) { bool ok = true; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); typedef std::complex Complex; typedef AD ADComplex; size_t n = 2; // object for computing determinants det_by_lu Det(n); // independent and dependent variable vectors CPPAD_TESTVECTOR(ADComplex) X(n * n); CPPAD_TESTVECTOR(ADComplex) D(1); // value of the independent variable size_t i; for(i = 0; i < n * n; i++) X[i] = Complex( double(i), -double(i) ); // set the independent variables Independent(X); // compute the determinant D[0] = Det(X); // create the function object ADFun f(X, D); // argument value CPPAD_TESTVECTOR(Complex) x( n * n ); for(i = 0; i < n * n; i++) x[i] = Complex( double(2 * i) , double(i) ); // first derivative of the determinant CPPAD_TESTVECTOR(Complex) J( n * n ); J = f.Jacobian(x); /* f(x) = x[0] * x[3] - x[1] * x[2] */ Complex Jtrue[] = { x[3], -x[2], -x[1], x[0] }; for( i = 0; i < n*n; i++) ok &= NearEqual( Jtrue[i], J[i], eps99 , eps99 ); return ok; } // END C++ ================================================ FILE: example/general/jac_minor_det.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin jac_minor_det.cpp} Gradient of Determinant Using Expansion by Minors: Example and Test ################################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end jac_minor_det.cpp} */ // BEGIN C++ // Complex examples should suppress conversion warnings # include # include # include # include typedef std::complex Complex; typedef CppAD::AD ADComplex; typedef CPPAD_TESTVECTOR(ADComplex) ADVector; // ---------------------------------------------------------------------------- bool JacMinorDet(void) { bool ok = true; using namespace CppAD; size_t n = 2; // object for computing determinant det_by_minor Det(n); // independent and dependent variable vectors CPPAD_TESTVECTOR(ADComplex) X(n * n); CPPAD_TESTVECTOR(ADComplex) D(1); // value of the independent variable size_t i; for(i = 0; i < n * n; i++) X[i] = Complex( double(i), -double(i) ); // set the independent variables Independent(X); // comupute the determinant D[0] = Det(X); // create the function object ADFun f(X, D); // argument value CPPAD_TESTVECTOR(Complex) x( n * n ); for(i = 0; i < n * n; i++) x[i] = Complex( double(2 * i) , double(i) ); // first derivative of the determinant CPPAD_TESTVECTOR(Complex) J( n * n ); J = f.Jacobian(x); /* f(x) = x[0] * x[3] - x[1] * x[2] f'(x) = ( x[3], -x[2], -x[1], x[0] ) */ Complex Jtrue[] = { x[3], -x[2], -x[1], x[0] }; for(i = 0; i < n * n; i++) ok &= Jtrue[i] == J[i]; return ok; } // END C++ ================================================ FILE: example/general/jacobian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin jacobian.cpp} Jacobian: Example and Test ########################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end jacobian.cpp} */ // BEGIN C++ # include namespace { // --------------------------------------------------------- // define the template function JacobianCases in empty namespace template bool JacobianCases() { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); using CppAD::exp; using CppAD::sin; using CppAD::cos; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) X(n); X[0] = 1.; X[1] = 2.; // declare independent variables and starting recording CppAD::Independent(X); // a calculation between the domain and range values AD Square = X[0] * X[0]; // range space vector size_t m = 3; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = Square * exp( X[1] ); Y[1] = Square * sin( X[1] ); Y[2] = Square * cos( X[1] ); // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // new value for the independent variable vector Vector x(n); x[0] = 2.; x[1] = 1.; // compute the derivative at this x Vector jac( m * n ); jac = f.Jacobian(x); /* F'(x) = [ 2 * x[0] * exp(x[1]) , x[0] * x[0] * exp(x[1]) ] [ 2 * x[0] * sin(x[1]) , x[0] * x[0] * cos(x[1]) ] [ 2 * x[0] * cos(x[1]) , -x[0] * x[0] * sin(x[i]) ] */ ok &= NearEqual( 2.*x[0]*exp(x[1]), jac[0*n+0], eps99, eps99); ok &= NearEqual( 2.*x[0]*sin(x[1]), jac[1*n+0], eps99, eps99); ok &= NearEqual( 2.*x[0]*cos(x[1]), jac[2*n+0], eps99, eps99); ok &= NearEqual( x[0] * x[0] *exp(x[1]), jac[0*n+1], eps99, eps99); ok &= NearEqual( x[0] * x[0] *cos(x[1]), jac[1*n+1], eps99, eps99); ok &= NearEqual(-x[0] * x[0] *sin(x[1]), jac[2*n+1], eps99, eps99); return ok; } } // End empty namespace # include # include bool Jacobian(void) { bool ok = true; // Run with Vector equal to three different cases // all of which are Simple Vectors with elements of type double. ok &= JacobianCases< CppAD::vector >(); ok &= JacobianCases< std::vector >(); ok &= JacobianCases< std::valarray >(); return ok; } // END C++ ================================================ FILE: example/general/log.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin log.cpp} The AD log Function: Example and Test ##################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end log.cpp} */ // BEGIN C++ # include bool log(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // a temporary value AD exp_of_x0 = CppAD::exp(x[0]); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = CppAD::log(exp_of_x0); // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value ok &= NearEqual(y[0] , x0, eps99, eps99); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 1., eps99, eps99); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], 1., eps99, eps99); // use a VecAD::reference object with log CppAD::VecAD v(1); AD zero(0); v[zero] = exp_of_x0; AD result = CppAD::log(v[zero]); ok &= NearEqual(result, x0, eps99, eps99); return ok; } // END C++ ================================================ FILE: example/general/log10.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin log10.cpp} The AD log10 Function: Example and Test ####################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end log10.cpp} */ // BEGIN C++ # include bool log10(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // ten raised to the x0 power AD ten = 10.; AD pow_10_x0 = CppAD::pow(ten, x[0]); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = CppAD::log10(pow_10_x0); // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value ok &= NearEqual(y[0] , x0, eps99, eps99); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 1., eps99, eps99); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], 1., eps99, eps99); // use a VecAD::reference object with log10 CppAD::VecAD v(1); AD zero(0); v[zero] = pow_10_x0; AD result = CppAD::log10(v[zero]); ok &= NearEqual(result, x0, eps99, eps99); return ok; } // END C++ ================================================ FILE: example/general/log1p.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin log1p.cpp} The AD log1p Function: Example and Test ####################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end log1p.cpp} */ // BEGIN C++ # include bool log1p(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; // 10 times machine epsilon double eps = 10. * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // a temporary value AD expm1_of_x0 = CppAD::expm1(x[0]); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = CppAD::log1p(expm1_of_x0); // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value ok &= NearEqual(y[0] , x0, eps, eps); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 1., eps, eps); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], 1., eps, eps); // use a VecAD::reference object with log1p CppAD::VecAD v(1); AD zero(0); v[zero] = expm1_of_x0; AD result = CppAD::log1p(v[zero]); ok &= NearEqual(result, x0, eps, eps); return ok; } // END C++ ================================================ FILE: example/general/lu_ratio.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin lu_ratio.cpp app} LuRatio: Example and Test ######################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end lu_ratio.cpp} */ // BEGIN C++ # include // for rand function # include # include namespace { // Begin empty namespace CppAD::ADFun *NewFactor( size_t n , const CPPAD_TESTVECTOR(double) &x , bool &ok , CPPAD_TESTVECTOR(size_t) &ip , CPPAD_TESTVECTOR(size_t) &jp ) { using CppAD::AD; using CppAD::ADFun; size_t i, j, k; // values for independent and dependent variables CPPAD_TESTVECTOR(AD) Y(n*n+1), X(n*n); // values for the LU factor CPPAD_TESTVECTOR(AD) LU(n*n); // record the LU factorization corresponding to this value of x AD Ratio; for(k = 0; k < n*n; k++) X[k] = x[k]; Independent(X); for(k = 0; k < n*n; k++) LU[k] = X[k]; CppAD::LuRatio(ip, jp, LU, Ratio); for(k = 0; k < n*n; k++) Y[k] = LU[k]; Y[n*n] = Ratio; // use a function pointer so can return ADFun object ADFun *FunPtr = new ADFun(X, Y); // check value of ratio during recording ok &= (Ratio == 1.); // check that ip and jp are permutations of the indices 0, ... , n-1 for(i = 0; i < n; i++) { ok &= (ip[i] < n); ok &= (jp[i] < n); for(j = 0; j < n; j++) { if( i != j ) { ok &= (ip[i] != ip[j]); ok &= (jp[i] != jp[j]); } } } return FunPtr; } bool CheckLuFactor( size_t n , const CPPAD_TESTVECTOR(double) &x , const CPPAD_TESTVECTOR(double) &y , const CPPAD_TESTVECTOR(size_t) &ip , const CPPAD_TESTVECTOR(size_t) &jp ) { bool ok = true; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); double sum; // element of L * U double pij; // element of permuted x size_t i, j, k; // temporary indices // L and U factors CPPAD_TESTVECTOR(double) L(n*n), U(n*n); // Extract L from LU factorization for(i = 0; i < n; i++) { // elements along and below the diagonal for(j = 0; j <= i; j++) L[i * n + j] = y[ ip[i] * n + jp[j] ]; // elements above the diagonal for(j = i+1; j < n; j++) L[i * n + j] = 0.; } // Extract U from LU factorization for(i = 0; i < n; i++) { // elements below the diagonal for(j = 0; j < i; j++) U[i * n + j] = 0.; // elements along the diagonal U[i * n + i] = 1.; // elements above the diagonal for(j = i+1; j < n; j++) U[i * n + j] = y[ ip[i] * n + jp[j] ]; } // Compute L * U for(i = 0; i < n; i++) { for(j = 0; j < n; j++) { // compute element (i,j) entry in L * U sum = 0.; for(k = 0; k < n; k++) sum += L[i * n + k] * U[k * n + j]; // element (i,j) in permuted version of A pij = x[ ip[i] * n + jp[j] ]; // compare ok &= NearEqual(pij, sum, eps99, eps99); } } return ok; } } // end Empty namespace bool LuRatio(void) { bool ok = true; size_t n = 2; // number rows in A double ratio; // values for independent and dependent variables CPPAD_TESTVECTOR(double) x(n*n), y(n*n+1); // pivot vectors CPPAD_TESTVECTOR(size_t) ip(n), jp(n); // set x equal to the identity matrix x[0] = 1.; x[1] = 0; x[2] = 0.; x[3] = 1.; // create a function object corresponding to this value of x CppAD::ADFun *FunPtr = NewFactor(n, x, ok, ip, jp); // use function object to factor matrix y = FunPtr->Forward(0, x); ratio = y[n*n]; ok &= (ratio == 1.); ok &= CheckLuFactor(n, x, y, ip, jp); // set x so that the pivot ratio will be infinite x[0] = 0. ; x[1] = 1.; x[2] = 1. ; x[3] = 0.; // try to use old function pointer to factor matrix y = FunPtr->Forward(0, x); ratio = y[n*n]; // check to see if we need to refactor matrix ok &= (ratio > 10.); if( ratio > 10. ) { delete FunPtr; // to avoid a memory leak FunPtr = NewFactor(n, x, ok, ip, jp); } // now we can use the function object to factor matrix y = FunPtr->Forward(0, x); ratio = y[n*n]; ok &= (ratio == 1.); ok &= CheckLuFactor(n, x, y, ip, jp); delete FunPtr; // avoid memory leak return ok; } // END C++ ================================================ FILE: example/general/lu_vec_ad.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin lu_vec_ad.cpp} {xrst_spell logdet rhs signdet } Lu Factor and Solve with Recorded Pivoting ########################################## Syntax ****** | ``int lu_vec_ad`` ( | |tab| ``size_t`` *n* , | |tab| ``size_t`` *m* , | |tab| ``VecAD`` < *double* > & *Matrix* , | |tab| ``VecAD`` < *double* > & *Rhs* , | |tab| ``VecAD`` < *double* > & *Result* , | |tab| *AD* < ``double`` > & ``logdet`` ) Purpose ******* Solves the linear equation .. math:: Matrix * Result = Rhs where *Matrix* is an :math:`n \times n` matrix, *Rhs* is an :math:`n x m` matrix, and *Result* is an :math:`n x m` matrix. The routine :ref:`LuSolve-name` uses an arbitrary vector type, instead of :ref:`VecAD-name` , to hold its elements. The pivoting operations for a ``ADFun`` object corresponding to an ``lu_vec_ad`` solution will change to be optimal for the matrix being factored. It is often the case that ``LuSolve`` is faster than ``lu_vec_ad`` when ``LuSolve`` uses a simple vector class with :ref:`elements of type double` , but the corresponding :ref:`ADFun-name` objects have a fixed set of pivoting operations. Storage Convention ****************** The matrices stored in row major order. To be specific, if :math:`A` contains the vector storage for an :math:`n x m` matrix, :math:`i` is between zero and :math:`n-1`, and :math:`j` is between zero and :math:`m-1`, .. math:: A_{i,j} = A[ i * m + j ] (The length of :math:`A` must be equal to :math:`n * m`.) n * is the number of rows in *Matrix* , *Rhs* , and *Result* . m * is the number of columns in *Rhs* and *Result* . It is ok for *m* to be zero which is reasonable when you are only interested in the determinant of *Matrix* . Matrix ****** On input, this is an :math:`n \times n` matrix containing the variable coefficients for the equation we wish to solve. On output, the elements of *Matrix* have been overwritten and are not specified. Rhs *** On input, this is an :math:`n \times m` matrix containing the right hand side for the equation we wish to solve. On output, the elements of *Rhs* have been overwritten and are not specified. If *m* is zero, *Rhs* is not used. Result ****** On input, this is an :math:`n \times m` matrix and the value of its elements do not matter. On output, the elements of *Rhs* contain the solution of the equation we wish to solve (unless the value returned by ``lu_vec_ad`` is equal to zero). If *m* is zero, *Result* is not used. logdet ****** On input, the value of *logdet* does not matter. On output, it has been set to the log of the determinant of *Matrix* (but not quite). To be more specific, if *signdet* is the value returned by ``lu_vec_ad`` , the determinant of *Matrix* is given by the formula .. math:: det = signdet \exp( logdet ) This enables ``lu_vec_ad`` to use logs of absolute values. Example ******* {xrst_toc_hidden example/general/lu_vec_ad_ok.cpp } The file :ref:`lu_vec_ad_ok.cpp-name` contains an example and test of ``lu_vec_ad`` . {xrst_end lu_vec_ad.cpp} ------------------------------------------------------------------------------ */ # include "lu_vec_ad.hpp" # include // BEGIN CppAD namespace namespace CppAD { AD lu_vec_ad( size_t n, size_t m, CppAD::VecAD &Matrix, CppAD::VecAD &Rhs, CppAD::VecAD &Result, CppAD::AD &logdet) { using namespace CppAD; typedef AD Type; // temporary index Type index; Type jndex; // index and maximum element value Type imax; Type jmax; Type itmp; Type jtmp; Type emax; // some temporary indices Type i; Type j; Type k; // count pivots Type p; // sign of the determinant Type signdet; // temporary values Type etmp; Type diff; // pivot element Type pivot; // singular matrix Type singular = 0.; // some constants Type M(m); Type N(n); Type One(1); Type Zero(0); // pivot row and column order in the matrix VecAD ip(n); VecAD jp(n); // ------------------------------------------------------- // initialize row and column order in matrix not yet pivoted for(i = 0; i < N; i += 1.) { ip[i] = i; jp[i] = i; } // initialize the log determinant logdet = 0.; signdet = 1; for(p = 0; p < N; p += 1.) { // determine row and column corresponding to element of // maximum absolute value in remaining part of Matrix imax = N; jmax = N; emax = 0.; for(i = p; i < N; i += 1.) { itmp = ip[i] * N; for(j = p; j < N; j += 1.) { assert( (ip[i] < N) && (jp[j] < N) ); index = itmp + jp[j]; etmp = Matrix[ index ]; // compute absolute value of element etmp = fabs(etmp); // update maximum absolute value so far emax = CondExpGe(etmp, emax, etmp, emax); imax = CondExpGe(etmp, emax, i, imax); jmax = CondExpGe(etmp, emax, j, jmax); } } assert( (imax < N) && (jmax < N) ); // switch rows so max absolute element is in row p index = ip[p]; ip[p] = ip[imax]; ip[imax] = index; // if imax != p, switch sign of determinant signdet = CondExpEq(imax, p, signdet, -signdet); // switch columns so max absolute element is in column p jndex = jp[p]; jp[p] = jp[jmax]; jp[jmax] = jndex; // if imax != p, switch sign of determinant signdet = CondExpEq(jmax, p, signdet, -signdet); // pivot using the max absolute element itmp = ip[p] * N; index = itmp + jp[p]; pivot = Matrix[ index ]; // update the singular matrix flag singular = CondExpEq(pivot, Zero, One, singular); // update the log of absolute determinant and its sign etmp = fabs(pivot); logdet = logdet + log( etmp ); signdet = CondExpGe(pivot, Zero, signdet, - signdet); /* Reduce by the elementary transformations that maps Matrix( ip[p], jp[p] ) to one and Matrix( ip[i], jp[p] ) to zero for i = p + 1., ... , n-1 */ // divide row number ip[p] by pivot element for(j = p + 1.; j < N; j += 1.) { index = itmp + jp[j]; Matrix[ index ] = Matrix[ index ] / pivot; } // not used anymore so no need to set to 1 // Matrix[ ip[p] * N + jp[p] ] = Type(1); // divide corresponding row of right hand side by pivot element itmp = ip[p] * M; for(k = 0; k < M; k += 1.) { index = itmp + k; Rhs[ index ] = Rhs[ index ] / pivot; } for(i = p + 1.; i < N; i += 1. ) { itmp = ip[i] * N; jtmp = ip[p] * N; index = itmp + jp[p]; etmp = Matrix[ index ]; for(j = p + 1.; j < N; j += 1.) { index = itmp + jp[j]; jndex = jtmp + jp[j]; Matrix[ index ] = Matrix[ index ] - etmp * Matrix[ jndex ]; } itmp = ip[i] * M; jtmp = ip[p] * M; for(k = 0; k < M; k += 1.) { index = itmp + k; jndex = jtmp + k; Rhs[ index ] = Rhs[ index ] - etmp * Rhs[ jndex ]; } // not used any more so no need to set to zero // Matrix[ ip[i] * N + jp[p] ] = 0.; } } // loop over equations for(k = 0; k < M; k += 1.) { // loop over variables p = N; while( p > 0. ) { p -= 1.; index = ip[p] * M + k; jndex = jp[p] * M + k; etmp = Rhs[ index ]; Result[ jndex ] = etmp; for(i = 0; i < p; i += 1. ) { index = ip[i] * M + k; jndex = ip[i] * N + jp[p]; Rhs[ index ] = Rhs[ index ] - etmp * Matrix[ jndex ]; } } } // make sure return zero in the singular case return (1. - singular) * signdet; } } // END CppAD namespace ================================================ FILE: example/general/lu_vec_ad.hpp ================================================ # ifndef CPPAD_EXAMPLE_GENERAL_LU_VEC_AD_HPP # define CPPAD_EXAMPLE_GENERAL_LU_VEC_AD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace CppAD { extern CppAD::AD lu_vec_ad( size_t n, size_t m, CppAD::VecAD &Matrix, CppAD::VecAD &Rhs, CppAD::VecAD &Result, CppAD::AD &logdet ); } # endif ================================================ FILE: example/general/lu_vec_ad_ok.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin lu_vec_ad_ok.cpp} Lu Factor and Solve With Recorded Pivoting: Example and Test ############################################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end lu_vec_ad_ok.cpp} */ // BEGIN C++ # include # include "lu_vec_ad.hpp" # include bool lu_vec_ad_ok(void) { bool ok = true; using namespace CppAD; typedef AD ADdouble; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); size_t n = 3; size_t m = 2; double a1[] = { 3., 0., 0., // (1,1) is first pivot 1., 2., 1., // (2,2) is second pivot 1., 0., .5 // (3,3) is third pivot }; double a2[] = { 1., 2., 1., // (1,2) is second pivot 3., 0., 0., // (2,1) is first pivot 1., 0., .5 // (3,3) is third pivot }; double rhs[] = { 1., 3., 2., 2., 3., 1. }; VecAD Copy (n * n); VecAD Rhs (n * m); VecAD Result (n * m); ADdouble logdet; ADdouble signdet; // routine for checking determinants using expansion by minors det_by_minor Det(n); // matrix we are computing the determinant of CPPAD_TESTVECTOR(ADdouble) A(n * n); // dependent variable values CPPAD_TESTVECTOR(ADdouble) Y(1 + n * m); size_t i; size_t j; size_t k; // Original matrix for(i = 0; i < n * n; i++) A[i] = a1[i]; // right hand side for(j = 0; j < n; j++) for(k = 0; k < m; k++) Rhs[ j * m + k ] = rhs[ j * m + k ]; // Declare independent variables Independent(A); // Copy the matrix ADdouble index(0); for(i = 0; i < n*n; i++) { Copy[index] = A[i]; index += 1.; } // Solve the equation signdet = lu_vec_ad(n, m, Copy, Rhs, Result, logdet); // Result is the first n * m dependent variables index = 0.; for(i = 0; i < n * m; i++) { Y[i] = Result[index]; index += 1.; } // Determinant is last component of the solution Y[ n * m ] = signdet * exp( logdet ); // construct f: A -> Y ADFun f(A, Y); // check determinant using minors routine ADdouble determinant = Det( A ); ok &= NearEqual(Y[n * m], determinant, eps99, eps99); // Check solution of Rhs = A * Result double sum; for(k = 0; k < m; k++) { for(i = 0; i < n; i++) { sum = 0.; for(j = 0; j < n; j++) sum += a1[i * n + j] * Value( Y[j * m + k] ); ok &= NearEqual( rhs[i * m + k], sum, eps99, eps99); } } CPPAD_TESTVECTOR(double) y2(1 + n * m); CPPAD_TESTVECTOR(double) A2(n * n); for(i = 0; i < n * n; i++) A[i] = A2[i] = a2[i]; y2 = f.Forward(0, A2); determinant = Det(A); ok &= NearEqual(y2[ n * m], Value(determinant), eps99, eps99); // Check solution of Rhs = A2 * Result for(k = 0; k < m; k++) { for(i = 0; i < n; i++) { sum = 0.; for(j = 0; j < n; j++) sum += a2[i * n + j] * y2[j * m + k]; ok &= NearEqual( rhs[i * m + k], sum, eps99, eps99); } } return ok; } // END C++ ================================================ FILE: example/general/mul.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin mul.cpp} AD Binary Multiplication: Example and Test ########################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end mul.cpp} */ // BEGIN C++ # include bool Mul(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = .5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // some binary multiplication operations AD a = x[0] * 1.; // AD * double AD b = a * 2; // AD * int AD c = 3. * b; // double * AD AD d = 4 * c; // int * AD // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = x[0] * d; // AD * AD // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value ok &= NearEqual(y[0] , x0*(4.*3.*2.*1.)*x0, eps99 , eps99); // forward computation of partials w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], (4.*3.*2.*1.)*2.*x0, eps99 , eps99); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], (4.*3.*2.*1.)*2.*x0, eps99 , eps99); // use a VecAD::reference object with multiplication CppAD::VecAD v(1); AD zero(0); v[zero] = c; AD result = 4 * v[zero]; ok &= (result == d); return ok; } // END C++ ================================================ FILE: example/general/mul_eq.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin mul_eq.cpp} AD Compound Assignment Multiplication: Example and Test ####################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end mul_eq.cpp} */ // BEGIN C++ # include bool MulEq(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = .5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // range space vector size_t m = 2; CPPAD_TESTVECTOR(AD) y(m); y[0] = x[0]; // initial value y[0] *= 2; // AD *= int y[0] *= 4.; // AD *= double y[1] = y[0] *= x[0]; // use the result of a compound assignment // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value ok &= NearEqual(y[0] , x0*2.*4.*x0, eps99, eps99); ok &= NearEqual(y[1] , y[0], eps99, eps99); // forward computation of partials w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 8.*2.*x0, eps99, eps99); ok &= NearEqual(dy[1], 8.*2.*x0, eps99, eps99); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; w[1] = 0.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], 8.*2.*x0, eps99, eps99); // use a VecAD::reference object with computed multiplication CppAD::VecAD v(1); AD zero(0); AD result = 1; v[zero] = 2; result *= v[zero]; ok &= (result == 2); return ok; } // END C++ ================================================ FILE: example/general/mul_level.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin mul_level.cpp} {xrst_spell adouble dx } Multiple Level of AD: Example and Test ###################################### See Also ******** :ref:`base2ad.cpp-name` Purpose ******* In this example, we use ``AD< AD >`` (level two taping), the compute values of the function :math:`f : \B{R}^n \rightarrow \B{R}` where .. math:: f(x) = \frac{1}{2} \left( x_0^2 + \cdots + x_{n-1}^2 \right) We then use ``AD`` (level one taping) to compute the directional derivative .. math:: f^{(1)} (x) * v = x_0 v_0 + \cdots + x_{n-1} v_{n-1} where :math:`v \in \B{R}^n`. We then use ``double`` (no taping) to compute .. math:: \frac{d}{dx} \left[ f^{(1)} (x) * v \right] = v This is only meant as an example of multiple levels of taping. The example :ref:`hes_times_dir.cpp-name` computes the same value more efficiently by using the identity: .. math:: \frac{d}{dx} \left[ f^{(1)} (x) * v \right] = f^{(2)} (x) * v The example :ref:`mul_level_adolc.cpp-name` computes the same values using Adolc's type ``adouble`` and CppAD's type ``AD`` . Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end mul_level.cpp} */ // BEGIN C++ # include namespace { // f(x) = |x|^2 / 2 = .5 * ( x[0]^2 + ... + x[n-1]^2 ) template Type f(const CPPAD_TESTVECTOR(Type)& x) { Type sum; sum = 0.; size_t i = size_t(x.size()); for(i = 0; i < size_t(x.size()); i++) sum += x[i] * x[i]; return .5 * sum; } } bool mul_level(void) { bool ok = true; // initialize test result typedef CppAD::AD a1type; // for one level of taping typedef CppAD::AD a2type; // for two levels of taping size_t n = 5; // dimension for example size_t j; // a temporary index variable // 10 times machine epsilon double eps = 10. * std::numeric_limits::epsilon(); CPPAD_TESTVECTOR(double) x(n); CPPAD_TESTVECTOR(a1type) a1x(n), a1v(n), a1dy(1) ; CPPAD_TESTVECTOR(a2type) a2x(n), a2y(1); // Values for the independent variables while taping the function f(x) for(j = 0; j < n; j++) a2x[j] = a1x[j] = x[j] = double(j); // Declare the independent variable for taping f(x) CppAD::Independent(a2x); // Use AD< AD > to tape the evaluation of f(x) a2y[0] = f(a2x); // Declare a1f as the corresponding ADFun< AD > // (make sure we do not run zero order forward during constructor) CppAD::ADFun a1f; a1f.Dependent(a2x, a2y); // Values for the independent variables while taping f'(x) * v // Declare the independent variable for taping f'(x) * v // (Note we did not have to tape the creation of a1f.) CppAD::Independent(a1x); // set the argument value x for computing f'(x) * v a1f.Forward(0, a1x); // compute f'(x) * v for(j = 0; j < n; j++) a1v[j] = double(n - j); a1dy = a1f.Forward(1, a1v); // declare g as ADFun function corresponding to f'(x) * v CppAD::ADFun g; g.Dependent(a1x, a1dy); // optimize out operations not necessary for function f'(x) * v g.optimize(); // Evaluate f'(x) * v g.Forward(0, x); // compute the d/dx of f'(x) * v = f''(x) * v = v CPPAD_TESTVECTOR(double) w(1); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = g.Reverse(1, w); for(j = 0; j < n; j++) ok &= CppAD::NearEqual(dw[j], a1v[j], eps, eps); return ok; } // END C++ ================================================ FILE: example/general/mul_level_adolc.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin mul_level_adolc.cpp} {xrst_spell adouble dx } Using Adolc with Multiple Levels of Taping: Example and Test ############################################################ Purpose ******* In this example, we use ``AD< adouble> >`` (level two taping), the compute values of the function :math:`f : \B{R}^n \rightarrow \B{R}` where .. math:: f(x) = \frac{1}{2} \left( x_0^2 + \cdots + x_{n-1}^2 \right) We then use Adolc's ``adouble`` (level one taping) to compute the directional derivative .. math:: f^{(1)} (x) * v = x_0 v_0 + \cdots + x_{n-1} v_{n-1} where :math:`v \in \B{R}^n`. We then use ``double`` (no taping) to compute .. math:: \frac{d}{dx} \left[ f^{(1)} (x) * v \right] = v This is only meant as an example of multiple levels of taping. The example :ref:`hes_times_dir.cpp-name` computes the same value more efficiently by using the identity: .. math:: \frac{d}{dx} \left[ f^{(1)} (x) * v \right] = f^{(2)} (x) * v The example :ref:`mul_level.cpp-name` computes the same values using ``AD< AD >`` and ``AD`` . Memory Management ***************** Adolc uses raw memory arrays that depend on the number of dependent and independent variables. The memory management utility :ref:`thread_alloc-name` is used to manage this memory allocation. Configuration Requirement ************************* This example will be compiled and tested provided :ref:`cmake@include_adolc` is true on the cmake command line. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end mul_level_adolc.cpp} */ // BEGIN C++ // suppress conversion warnings before other includes # include // # include # include # include // adouble definitions not in Adolc distribution and // required in order to use CppAD::AD # include # include namespace { // f(x) = |x|^2 / 2 = .5 * ( x[0]^2 + ... + x[n-1]^2 ) template Type f(const CPPAD_TESTVECTOR(Type)& x) { Type sum; sum = 0.; size_t i = size_t(x.size()); for(i = 0; i < size_t(x.size()); i++) sum += x[i] * x[i]; return .5 * sum; } } bool mul_level_adolc(void) { bool ok = true; // initialize test result using CppAD::thread_alloc; // The CppAD memory allocator typedef adouble a1type; // for first level of taping typedef CppAD::AD a2type; // for second level of taping size_t n = 5; // number independent variables size_t j; // 10 times machine epsilon double eps = 10. * std::numeric_limits::epsilon(); CPPAD_TESTVECTOR(double) x(n); CPPAD_TESTVECTOR(a1type) a1x(n); CPPAD_TESTVECTOR(a2type) a2x(n); // Values for the independent variables while taping the function f(x) for(j = 0; j < n; j++) a2x[j] = double(j); // Declare the independent variable for taping f(x) CppAD::Independent(a2x); // Use AD to tape the evaluation of f(x) CPPAD_TESTVECTOR(a2type) a2y(1); a2y[0] = f(a2x); // Declare a1f as the corresponding ADFun function f(x) // (make sure we do not run zero order forward during constructor) CppAD::ADFun a1f; a1f.Dependent(a2x, a2y); // Value of the independent variables whitle taping f'(x) * v short tag = 0; int keep = 1; trace_on(tag, keep); for(j = 0; j < n; j++) a1x[j] <<= double(j); // set the argument value x for computing f'(x) * v a1f.Forward(0, a1x); // compute f'(x) * v CPPAD_TESTVECTOR(a1type) a1v(n); CPPAD_TESTVECTOR(a1type) a1df(1); for(j = 0; j < n; j++) a1v[j] = double(n - j); a1df = a1f.Forward(1, a1v); // declare Adolc function corresponding to f'(x) * v double df; a1df[0] >>= df; trace_off(); // compute the d/dx of f'(x) * v = f''(x) * v size_t m = 1; // # dependent in f'(x) * v // w = new double[capacity] where capacity >= m size_t capacity; double* w = thread_alloc::create_array(m, capacity); // dw = new double[capacity] where capacity >= n double* dw = thread_alloc::create_array(n, capacity); w[0] = 1.; fos_reverse(tag, int(m), int(n), w, dw); for(j = 0; j < n; j++) { double vj = a1v[j].value(); ok &= CppAD::NearEqual(dw[j], vj, eps, eps); } // make memory available for other use by this thread thread_alloc::delete_array(w); thread_alloc::delete_array(dw); return ok; } // END C++ ================================================ FILE: example/general/mul_level_adolc_ode.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin mul_level_adolc_ode.cpp} {xrst_spell adouble cccc } Taylor's Ode Solver: A Multi-Level Adolc Example and Test ######################################################### See Also ******** :ref:`taylor_ode.cpp-name` , :ref:`mul_level_ode.cpp-name` Purpose ******* This is a realistic example using two levels of AD; see :ref:`mul_level-name` . The first level uses Adolc's ``adouble`` type to tape the solution of an ordinary differential equation. This solution is then differentiated with respect to a parameter vector. The second level uses CppAD's type ``AD`` to take derivatives during the solution of the differential equation. These derivatives are used in the application of Taylor's method to the solution of the ODE. ODE *** For this example the function :math:`y : \B{R} \times \B{R}^n \rightarrow \B{R}^n` is defined by :math:`y(0, x) = 0` and :math:`\partial_t y(t, x) = g(y, x)` where :math:`g : \B{R}^n \times \B{R}^n \rightarrow \B{R}^n` is defined by .. math:: g(y, x) = \left( \begin{array}{c} x_0 \\ x_1 y_0 \\ \vdots \\ x_{n-1} y_{n-2} \end{array} \right) ODE Solution ************ The solution for this example can be calculated by starting with the first row and then using the solution for the first row to solve the second and so on. Doing this we obtain .. math:: y(t, x ) = \left( \begin{array}{c} x_0 t \\ x_1 x_0 t^2 / 2 \\ \vdots \\ x_{n-1} x_{n-2} \ldots x_0 t^n / n ! \end{array} \right) Derivative of ODE Solution ************************** Differentiating the solution above, with respect to the parameter vector :math:`x`, we notice that .. math:: \partial_x y(t, x ) = \left( \begin{array}{cccc} y_0 (t,x) / x_0 & 0 & \cdots & 0 \\ y_1 (t,x) / x_0 & y_1 (t,x) / x_1 & 0 & \vdots \\ \vdots & \vdots & \ddots & 0 \\ y_{n-1} (t,x) / x_0 & y_{n-1} (t,x) / x_1 & \cdots & y_{n-1} (t,x) / x_{n-1} \end{array} \right) Taylor's Method Using AD ************************ We define the function :math:`z(t, x)` by the equation .. math:: z ( t , x ) = g[ y ( t , x ) ] = h [ x , y( t , x ) ] see :ref:`taylor_ode-name` for the method used to compute the Taylor coefficients w.r.t :math:`t` of :math:`y(t, x)`. base_adolc.hpp ************** The file :ref:`base_adolc.hpp-name` is implements the :ref:`Base type requirements` where *Base* is ``adolc`` . Memory Management ***************** Adolc uses raw memory arrays that depend on the number of dependent and independent variables. The :ref:`thread_alloc-name` memory management utilities :ref:`create_array` and :ref:`delete_array` are used to manage this memory allocation. Configuration Requirement ************************* This example will be compiled and tested provided :ref:`cmake@include_ipopt` is on the cmake command line. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end mul_level_adolc_ode.cpp} -------------------------------------------------------------------------- */ // BEGIN C++ // suppress conversion warnings before other includes # include // # include # include # include // definitions not in Adolc distribution and required to use CppAD::AD # include # include // ========================================================================== namespace { // BEGIN empty namespace // define types for each level typedef adouble a1type; typedef CppAD::AD a2type; // ------------------------------------------------------------------------- // class definition for C++ function object that defines ODE class Ode { private: // copy of a that is set by constructor and used by g(y) CPPAD_TESTVECTOR(a1type) a1x_; public: // constructor Ode(const CPPAD_TESTVECTOR(a1type)& a1x) : a1x_(a1x) { } // the function g(y) is evaluated with two levels of taping CPPAD_TESTVECTOR(a2type) operator() ( const CPPAD_TESTVECTOR(a2type)& a2y) const { size_t n = a2y.size(); CPPAD_TESTVECTOR(a2type) a2g(n); size_t i; a2g[0] = a1x_[0]; for(i = 1; i < n; i++) a2g[i] = a1x_[i] * a2y[i-1]; return a2g; } }; // ------------------------------------------------------------------------- // Routine that uses Taylor's method to solve ordinary differential equaitons // and allows for algorithmic differentiation of the solution. CPPAD_TESTVECTOR(a1type) taylor_ode_adolc( Ode G , // function that defines the ODE size_t order , // order of Taylor's method used size_t nstep , // number of steps to take const a1type &a1dt , // Delta t for each step const CPPAD_TESTVECTOR(a1type) &a1y_ini) // y(t) at the initial time { // some temporary indices size_t i, k, ell; // number of variables in the ODE size_t n = a1y_ini.size(); // copies of x and g(y) with two levels of taping CPPAD_TESTVECTOR(a2type) a2y(n), Z(n); // y, y^{(k)} , z^{(k)}, and y^{(k+1)} CPPAD_TESTVECTOR(a1type) a1y(n), a1y_k(n), a1z_k(n), a1y_kp(n); // initialize x for(i = 0; i < n; i++) a1y[i] = a1y_ini[i]; // loop with respect to each step of Taylors method for(ell = 0; ell < nstep; ell++) { // prepare to compute derivatives using a1type for(i = 0; i < n; i++) a2y[i] = a1y[i]; CppAD::Independent(a2y); // evaluate ODE using a2type Z = G(a2y); // define differentiable version of g: X -> Y // that computes its derivatives using a1type CppAD::ADFun a1g(a2y, Z); // Use Taylor's method to take a step a1y_k = a1y; // initialize y^{(k)} a1type dt_kp = a1dt; // initialize dt^(k+1) for(k = 0; k <= order; k++) { // evaluate k-th order Taylor coefficient of y a1z_k = a1g.Forward(k, a1y_k); for(i = 0; i < n; i++) { // convert to (k+1)-Taylor coefficient for x a1y_kp[i] = a1z_k[i] / a1type(k + 1); // add term for to this Taylor coefficient // to solution for y(t, x) a1y[i] += a1y_kp[i] * dt_kp; } // next power of t dt_kp *= a1dt; // next Taylor coefficient a1y_k = a1y_kp; } } return a1y; } } // END empty namespace // ========================================================================== // Routine that tests algorithmic differentiation of solutions computed // by the routine taylor_ode. bool mul_level_adolc_ode(void) { bool ok = true; double eps = 100. * std::numeric_limits::epsilon(); // number of components in differential equation size_t n = 4; // some temporary indices size_t i, j; // set up for thread_alloc memory allocator using CppAD::thread_alloc; // the allocator size_t capacity; // capacity of an allocation // the vector x with length n (or greater) in double double* x = thread_alloc::create_array(n, capacity); // the vector x with length n in a1type CPPAD_TESTVECTOR(a1type) a1x(n); for(i = 0; i < n; i++) a1x[i] = x[i] = double(i + 1); // declare the parameters as the independent variable short tag = 0; // Adolc setup int keep = 1; trace_on(tag, keep); for(i = 0; i < n; i++) a1x[i] <<= double(i + 1); // a1x is independent for adouble type // arguments to taylor_ode_adolc Ode G(a1x); // function that defines the ODE size_t order = n; // order of Taylor's method used size_t nstep = 2; // number of steps to take a1type a1dt = 1.; // Delta t for each step // value of y(t, x) at the initial time CPPAD_TESTVECTOR(a1type) a1y_ini(n); for(i = 0; i < n; i++) a1y_ini[i] = 0.; // integrate the differential equation CPPAD_TESTVECTOR(a1type) a1y_final(n); a1y_final = taylor_ode_adolc(G, order, nstep, a1dt, a1y_ini); // declare the differentiable function f : x -> y_final // (corresponding to the tape of adouble operations) double* y_final = thread_alloc::create_array(n, capacity); for(i = 0; i < n; i++) a1y_final[i] >>= y_final[i]; trace_off(); // check function values double check = 1.; double t = nstep * a1dt.value(); for(i = 0; i < n; i++) { check *= x[i] * t / double(i + 1); ok &= CppAD::NearEqual(y_final[i], check, eps, eps); } // memory where Jacobian will be returned double* jac_ = thread_alloc::create_array(n * n, capacity); double** jac = thread_alloc::create_array(n, capacity); for(i = 0; i < n; i++) jac[i] = jac_ + i * n; // evaluate Jacobian of h at a size_t m = n; // # dependent variables jacobian(tag, int(m), int(n), x, jac); // check Jacobian for(i = 0; i < n; i++) { for(j = 0; j < n; j++) { if( i < j ) check = 0.; else check = y_final[i] / x[j]; ok &= CppAD::NearEqual(jac[i][j], check, eps, eps); } } // make memory available for other use by this thread thread_alloc::delete_array(x); thread_alloc::delete_array(y_final); thread_alloc::delete_array(jac_); thread_alloc::delete_array(jac); return ok; } // END C++ ================================================ FILE: example/general/mul_level_ode.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin mul_level_ode.cpp} {xrst_spell cccc } Taylor's Ode Solver: A Multi-Level AD Example and Test ###################################################### See Also ******** :ref:`taylor_ode.cpp-name` , :ref:`base2ad.cpp-name` , :ref:`mul_level_adolc_ode.cpp-name` Purpose ******* This is a realistic example using two levels of AD; see :ref:`mul_level-name` . The first level uses ``AD`` to tape the solution of an ordinary differential equation. This solution is then differentiated with respect to a parameter vector. The second level uses ``AD< AD >`` to take derivatives during the solution of the differential equation. These derivatives are used in the application of Taylor's method to the solution of the ODE. ODE *** For this example the function :math:`y : \B{R} \times \B{R}^n \rightarrow \B{R}^n` is defined by :math:`y(0, x) = 0` and :math:`\partial_t y(t, x) = g(y, x)` where :math:`g : \B{R}^n \times \B{R}^n \rightarrow \B{R}^n` is defined by .. math:: g(y, x) = \left( \begin{array}{c} x_0 \\ x_1 y_0 \\ \vdots \\ x_{n-1} y_{n-2} \end{array} \right) ODE Solution ************ The solution for this example can be calculated by starting with the first row and then using the solution for the first row to solve the second and so on. Doing this we obtain .. math:: y(t, x ) = \left( \begin{array}{c} x_0 t \\ x_1 x_0 t^2 / 2 \\ \vdots \\ x_{n-1} x_{n-2} \ldots x_0 t^n / n ! \end{array} \right) Derivative of ODE Solution ************************** Differentiating the solution above, with respect to the parameter vector :math:`x`, we notice that .. math:: \partial_x y(t, x ) = \left( \begin{array}{cccc} y_0 (t,x) / x_0 & 0 & \cdots & 0 \\ y_1 (t,x) / x_0 & y_1 (t,x) / x_1 & 0 & \vdots \\ \vdots & \vdots & \ddots & 0 \\ y_{n-1} (t,x) / x_0 & y_{n-1} (t,x) / x_1 & \cdots & y_{n-1} (t,x) / x_{n-1} \end{array} \right) Taylor's Method Using AD ************************ We define the function :math:`z(t, x)` by the equation .. math:: z ( t , x ) = g[ y ( t , x ) ] = h [ x , y( t , x ) ] see :ref:`taylor_ode-name` for the method used to compute the Taylor coefficients w.r.t :math:`t` of :math:`y(t, x)`. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end mul_level_ode.cpp} -------------------------------------------------------------------------- */ // BEGIN C++ # include // ========================================================================= // define types for each level namespace { // BEGIN empty namespace typedef CppAD::AD a1double; typedef CppAD::AD a2double; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR(a1double) a1vector; typedef CPPAD_TESTVECTOR(a2double) a2vector; // ------------------------------------------------------------------------- // class definition for C++ function object that defines ODE class Ode { private: // copy of a that is set by constructor and used by g(y) a1vector a1x_; public: // constructor Ode(const a1vector& a1x) : a1x_(a1x) { } // the function g(y) is evaluated with two levels of taping a2vector operator() ( const a2vector& a2y) const { size_t n = a2y.size(); a2vector a2g(n); size_t i; a2g[0] = a1x_[0]; for(i = 1; i < n; i++) a2g[i] = a1x_[i] * a2y[i-1]; return a2g; } }; // ------------------------------------------------------------------------- // Routine that uses Taylor's method to solve ordinary differential equaitons // and allows for algorithmic differentiation of the solution. a1vector taylor_ode( Ode G , // function that defines the ODE size_t order , // order of Taylor's method used size_t nstep , // number of steps to take const a1double& a1dt , // Delta t for each step const a1vector& a1y_ini) // y(t) at the initial time { // some temporary indices size_t i, k, ell; // number of variables in the ODE size_t n = a1y_ini.size(); // copies of x and g(y) with two levels of taping a2vector a2y(n), a2z(n); // y, y^{(k)} , z^{(k)}, and y^{(k+1)} a1vector a1y(n), a1y_k(n), a1z_k(n), a1y_kp(n); // initialize x for(i = 0; i < n; i++) a1y[i] = a1y_ini[i]; // loop with respect to each step of Taylors method for(ell = 0; ell < nstep; ell++) { // prepare to compute derivatives using a1double for(i = 0; i < n; i++) a2y[i] = a1y[i]; CppAD::Independent(a2y); // evaluate ODE in a2double a2z = G(a2y); // define differentiable version of a1g: y -> z // that computes its derivatives using a1double objects CppAD::ADFun a1g(a2y, a2z); // Use Taylor's method to take a step a1y_k = a1y; // initialize y^{(k)} a1double a1dt_kp = a1dt; // initialize dt^(k+1) for(k = 0; k <= order; k++) { // evaluate k-th order Taylor coefficient of y a1z_k = a1g.Forward(k, a1y_k); for(i = 0; i < n; i++) { // convert to (k+1)-Taylor coefficient for x a1y_kp[i] = a1z_k[i] / a1double(k + 1); // add term for to this Taylor coefficient // to solution for y(t, x) a1y[i] += a1y_kp[i] * a1dt_kp; } // next power of t a1dt_kp *= a1dt; // next Taylor coefficient a1y_k = a1y_kp; } } return a1y; } } // END empty namespace // ========================================================================== // Routine that tests algorithmic differentiation of solutions computed // by the routine taylor_ode. bool mul_level_ode(void) { bool ok = true; double eps = 100. * std::numeric_limits::epsilon(); // number of components in differential equation size_t n = 4; // some temporary indices size_t i, j; // parameter vector in both double and a1double d_vector x(n); a1vector a1x(n); for(i = 0; i < n; i++) a1x[i] = x[i] = double(i + 1); // declare the parameters as the independent variable CppAD::Independent(a1x); // arguments to taylor_ode Ode G(a1x); // function that defines the ODE size_t order = n; // order of Taylor's method used size_t nstep = 2; // number of steps to take a1double a1dt = double(1.); // Delta t for each step // value of y(t, x) at the initial time a1vector a1y_ini(n); for(i = 0; i < n; i++) a1y_ini[i] = 0.; // integrate the differential equation a1vector a1y_final(n); a1y_final = taylor_ode(G, order, nstep, a1dt, a1y_ini); // define differentiable function object f : x -> y_final // that computes its derivatives in double CppAD::ADFun f(a1x, a1y_final); // check function values double check = 1.; double t = double(nstep) * Value(a1dt); for(i = 0; i < n; i++) { check *= x[i] * t / double(i + 1); ok &= CppAD::NearEqual(Value(a1y_final[i]), check, eps, eps); } // evaluate the Jacobian of h at a d_vector jac ( f.Jacobian(x) ); // There appears to be a bug in g++ version 4.4.2 because it generates // a warning for the equivalent form // d_vector jac = f.Jacobian(x); // check Jacobian for(i = 0; i < n; i++) { for(j = 0; j < n; j++) { double jac_ij = jac[i * n + j]; if( i < j ) check = 0.; else check = Value( a1y_final[i] ) / x[j]; ok &= CppAD::NearEqual(jac_ij, check, eps, eps); } } return ok; } // END C++ ================================================ FILE: example/general/near_equal_ext.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin near_equal_ext.cpp} Compare AD with Base Objects: Example and Test ############################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end near_equal_ext.cpp} */ // BEGIN C++ # include # include bool near_equal_ext(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; // double double x = 1.00000; double y = 1.00001; double a = .00005; double r = .00005; double zero = 0.; // AD AD ax(x); AD ay(y); ok &= NearEqual(ax, ay, zero, a); ok &= NearEqual(ax, y, r, zero); ok &= NearEqual(x, ay, r, a); // std::complex AD cx(x); AD cy(y); // AD< std::complex > AD acx(x); AD acy(y); ok &= NearEqual(acx, acy, zero, a); ok &= NearEqual(acx, cy, r, zero); ok &= NearEqual(acx, y, r, a); ok &= NearEqual( cx, acy, r, a); ok &= NearEqual( x, acy, r, a); return ok; } // END C++ ================================================ FILE: example/general/new_dynamic.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin new_dynamic.cpp} Dynamic Parameters: Example and Test #################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end new_dynamic.cpp} */ // BEGIN C++ # include # include bool new_dynamic(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * std::numeric_limits::epsilon(); // independent dynamic parameter vector size_t nd = 3; CPPAD_TESTVECTOR(AD) adynamic(nd); adynamic[0] = 1.0; adynamic[1] = 1.0; adynamic[2] = 1.0; // domain space vector size_t nx = 2; CPPAD_TESTVECTOR(AD) ax(nx); ax[0] = 0.; ax[1] = 1.; // declare independent variables, dynamic parameters, starting recording CppAD::Independent(ax, adynamic); // create a dependent dynamic parameter AD adependent_dyn = adynamic[0] + 2.0; // check that elements of adynamic are currently dynamic parameters for(size_t j = 0; j < nd; ++j) ok &= Dynamic( adynamic[j] ); ok &= Dynamic( adependent_dyn ); // range space vector size_t ny = 1; CPPAD_TESTVECTOR(AD) ay(ny); ay[0] = adependent_dyn + ax[0]; ay[0] *= adynamic[1] + ax[0]; ay[0] *= adynamic[2] + ax[1]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // check the number of independent dynamic parameters in f ok &= f.size_dyn_ind() == nd; // total number of dynamic parameters in f ok &= f.size_dyn_par() == nd + 1; // check that these are no longer dynamic parameters for(size_t j = 0; j < nd; ++j) ok &= ! Dynamic( adynamic[j] ); ok &= ! Dynamic( adependent_dyn ); // zero order forward mode CPPAD_TESTVECTOR(double) x(nx), y(ny); x[0] = 3.; x[1] = 4.; y = f.Forward(0, x); ok &= f.size_order() == 1; double check; check = Value( adynamic[0] ) + 2.0 + x[0]; check *= Value( adynamic[1] ) + x[0]; check *= Value( adynamic[2] ) + x[1]; ok &= NearEqual(y[0] , check, eps, eps); // change the dynamic parameter values CPPAD_TESTVECTOR(double) dynamic(nd); dynamic[0] = 2.0; dynamic[1] = 3.0; dynamic[2] = 4.0; f.new_dynamic(dynamic); ok &= f.size_order() == 0; // y = f.Forward(0, x); ok &= f.size_order() == 1; check = dynamic[0] + 2.0 + x[0]; check *= dynamic[1] + x[0]; check *= dynamic[2] + x[1]; ok &= NearEqual(y[0] , check, eps, eps); // use first order forward mode to compute partial of f w.r.t x[0] CPPAD_TESTVECTOR(double) dx(nx), dy(ny); dx[0] = 1.0; dx[1] = 0.0; dy = f.Forward(1, dx); ok &= f.size_order() == 2; check = (dynamic[2] + x[1])*(dynamic[0] + 2.0 + x[0] + dynamic[1] + x[0]); ok &= NearEqual(dy[0] , check, eps, eps); // return ok; } // END C++ ================================================ FILE: example/general/num_limits.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin num_limits.cpp} Numeric Limits: Example and Test ################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end num_limits.cpp} */ // BEGIN C++ # ifdef _MSC_VER // Suppress Microsoft compiler warning about possible loss of precision, // in the constructors (when converting to std::complex) // Float one = 1 // Float two = 2 // 1 and 2 are small enough so no loss of precision when converting to float. # pragma warning(disable:4244) # endif # include # include namespace { typedef CppAD::AD Float; // // ----------------------------------------------------------------- bool check_epsilon(void) { bool ok = true; Float eps = CppAD::numeric_limits::epsilon(); Float eps2 = eps / 2.0; Float check = 1.0 + eps; ok &= 1.0 != check; check = 1.0 + eps2; ok &= 1.0 == check; return ok; } // ----------------------------------------------------------------- bool check_min(void) { bool ok = true; Float min = CppAD::numeric_limits::min(); Float eps = CppAD::numeric_limits::epsilon(); // Float match = (min / 100.) * 100.; ok &= fabs(match / min - 1.0) > 3.0 * eps; // match = (min * 100.) / 100.; ok &= fabs(match / min - 1.0) < 3.0 * eps; return ok; } // ----------------------------------------------------------------- bool check_max(void) { bool ok = true; Float max = CppAD::numeric_limits::max(); Float eps = CppAD::numeric_limits::epsilon(); // Float match = (max * 100.) / 100.; ok &= fabs(match / max - 1.0) > 3.0 * eps; // match = (max / 100.) * 100.; ok &= fabs(match / max - 1.0) < 3.0 * eps; return ok; } // ----------------------------------------------------------------- bool check_nan(void) { bool ok = true; Float nan = CppAD::numeric_limits::quiet_NaN(); ok &= nan != nan; return ok; } // ----------------------------------------------------------------- bool check_infinity(void) { bool ok = true; Float inf = CppAD::numeric_limits::infinity(); Float hun = Float(100); Float tmp = Float(0); tmp = inf + hun; ok &= inf == tmp; tmp = inf - inf; ok &= CppAD::isnan( tmp ); return ok; } // ----------------------------------------------------------------- bool check_digits10(void) { bool ok = true; Float neg_log_eps = - log10( CppAD::numeric_limits::epsilon() ); int ceil_neg_log_eps = Integer( neg_log_eps ); ok &= ceil_neg_log_eps == CppAD::numeric_limits::digits10; return ok; } // ----------------------------------------------------------------- bool check_max_digits10(void) { bool ok = true; int max_digits10 = CppAD::numeric_limits::max_digits10; Float pi = 4.0 * atan( Float(1.0) ); // std::stringstream ss; ss << std::setprecision( max_digits10 ) << pi; // Float check = Float( std::atof( ss.str().c_str() ) ); ok &= pi == check; return ok; } } bool num_limits(void) { bool ok = true; ok &= check_epsilon(); ok &= check_min(); ok &= check_max(); ok &= check_nan(); ok &= check_infinity(); ok &= check_digits10(); ok &= check_max_digits10(); return ok; } // END C++ ================================================ FILE: example/general/number_skip.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin number_skip.cpp} Number of Variables That Can be Skipped: Example and Test ######################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end number_skip.cpp} */ // BEGIN C++ # include bool number_skip(void) { bool ok = true; using CppAD::AD; // independent variable vector CppAD::vector< AD > ax(2); ax[0] = 0.; ax[1] = 1.; Independent(ax); // Use a conditional expression CppAD::vector< AD > ay(1); // variable that gets optimized out AD az = ax[0] * ax[0]; // conditional expression ay[0] = CondExpLt(ax[0], ax[1], ax[0] + ax[1], ax[0] - ax[1]); // create function object F : x -> ay CppAD::ADFun f; f.Dependent(ax, ay); // use zero order to evaluate F[ (3, 4) ] CppAD::vector x( f.Domain() ); CppAD::vector y( f.Range() ); x[0] = 3.; x[1] = 4.; y = f.Forward(0, x); ok &= (y[0] == x[0] + x[1]); // before call to optimize ok &= f.number_skip() == 0; size_t n_var = f.size_var(); // now optimize the operation sequence f.optimize(); // after optimize, check forward mode result x[0] = 4.; x[1] = 3.; y = f.Forward(0, x); ok &= (y[0] == x[0] - x[1]); // after optimize, check amount of optimization ok &= f.size_var() == n_var - 1; ok &= f.number_skip() == 1; return ok; } // END C++ ================================================ FILE: example/general/numeric_type.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin numeric_type.cpp} The NumericType: Example and Test ################################# {xrst_literal // BEGIN C++ // END C++ } {xrst_end numeric_type.cpp} */ // BEGIN C++ # include namespace { // Empty namespace // ------------------------------------------------------------------- class MyType { private: double d; public: // constructor from void MyType(void) : d(0.) { } // constructor from an int MyType(int d_) : d(d_) { } // copy constructor MyType(const MyType &x) { d = x.d; } // assignment operator void operator = (const MyType &x) { d = x.d; } // member function that converts to double double Double(void) const { return d; } // unary plus MyType operator + (void) const { MyType x; x.d = d; return x; } // unary plus MyType operator - (void) const { MyType x; x.d = - d; return x; } // binary addition MyType operator + (const MyType &x) const { MyType y; y.d = d + x.d ; return y; } // binary subtraction MyType operator - (const MyType &x) const { MyType y; y.d = d - x.d ; return y; } // binary multiplication MyType operator * (const MyType &x) const { MyType y; y.d = d * x.d ; return y; } // binary division MyType operator / (const MyType &x) const { MyType y; y.d = d / x.d ; return y; } // compound assignment addition void operator += (const MyType &x) { d += x.d; } // compound assignment subtraction void operator -= (const MyType &x) { d -= x.d; } // compound assignment multiplication void operator *= (const MyType &x) { d *= x.d; } // compound assignment division void operator /= (const MyType &x) { d /= x.d; } }; } bool NumericType(void) { bool ok = true; using CppAD::AD; using CppAD::CheckNumericType; CheckNumericType (); CheckNumericType (); CheckNumericType (); CheckNumericType< AD > (); CheckNumericType< AD< AD > >(); return ok; } // END C++ ================================================ FILE: example/general/ode_stiff.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ode_stiff.cpp} {xrst_spell rosen } A Stiff Ode: Example and Test ############################# Define :math:`x : \B{R} \rightarrow \B{R}^2` by .. math:: :nowrap: \begin{eqnarray} x_0 (0) & = & 1 \\ x_1 (0) & = & 0 \\ x_0^\prime (t) & = & - a_0 x_0 (t) \\ x_1^\prime (t) & = & + a_0 x_0 (t) - a_1 x_1 (t) \end{eqnarray} If :math:`a_0 \gg a_1 > 0`, this is a stiff Ode and the analytic solution is .. math:: :nowrap: \begin{eqnarray} x_0 (t) & = & \exp( - a_0 t ) \\ x_1 (t) & = & a_0 [ \exp( - a_1 t ) - \exp( - a_0 t ) ] / ( a_0 - a_1 ) \end{eqnarray} The example tests Rosen34 using the relations above: {xrst_literal // BEGIN C++ // END C++ } {xrst_end ode_stiff.cpp} */ // BEGIN C++ # include // To print the comparison, change the 0 to 1 on the next line. # define CPPAD_ODE_STIFF_PRINT 0 namespace { // -------------------------------------------------------------- class Fun { private: CPPAD_TESTVECTOR(double) a; public: // constructor Fun(const CPPAD_TESTVECTOR(double)& a_) : a(a_) { } // compute f(t, x) void Ode( const double &t, const CPPAD_TESTVECTOR(double) &x, CPPAD_TESTVECTOR(double) &f) { f[0] = - a[0] * x[0]; f[1] = + a[0] * x[0] - a[1] * x[1]; } // compute partial of f(t, x) w.r.t. t void Ode_ind( const double &t, const CPPAD_TESTVECTOR(double) &x, CPPAD_TESTVECTOR(double) &f_t) { f_t[0] = 0.; f_t[1] = 0.; } // compute partial of f(t, x) w.r.t. x void Ode_dep( const double &t, const CPPAD_TESTVECTOR(double) &x, CPPAD_TESTVECTOR(double) &f_x) { f_x[0] = -a[0]; f_x[1] = 0.; f_x[2] = +a[0]; f_x[3] = -a[1]; } }; // -------------------------------------------------------------- class RungeMethod { private: Fun F; public: // constructor RungeMethod(const CPPAD_TESTVECTOR(double) &a_) : F(a_) { } void step( double ta , double tb , CPPAD_TESTVECTOR(double) &xa , CPPAD_TESTVECTOR(double) &xb , CPPAD_TESTVECTOR(double) &eb ) { xb = CppAD::Runge45(F, 1, ta, tb, xa, eb); } size_t order(void) { return 5; } }; class RosenMethod { private: Fun F; public: // constructor RosenMethod(const CPPAD_TESTVECTOR(double) &a_) : F(a_) { } void step( double ta , double tb , CPPAD_TESTVECTOR(double) &xa , CPPAD_TESTVECTOR(double) &xb , CPPAD_TESTVECTOR(double) &eb ) { xb = CppAD::Rosen34(F, 1, ta, tb, xa, eb); } size_t order(void) { return 4; } }; } bool OdeStiff(void) { bool ok = true; // initial return value CPPAD_TESTVECTOR(double) a(2); a[0] = 1e3; a[1] = 1.; RosenMethod rosen(a); RungeMethod runge(a); Fun gear(a); CPPAD_TESTVECTOR(double) xi(2); xi[0] = 1.; xi[1] = 0.; CPPAD_TESTVECTOR(double) eabs(2); eabs[0] = 1e-6; eabs[1] = 1e-6; CPPAD_TESTVECTOR(double) ef(2); CPPAD_TESTVECTOR(double) xf(2); CPPAD_TESTVECTOR(double) maxabs(2); size_t nstep; size_t k; for(k = 0; k < 3; k++) { size_t M = 5; double ti = 0.; double tf = 1.; double smin = 1e-7; double sini = 1e-7; double smax = 1.; double scur = .5; double erel = 0.; if( k == 0 ) { xf = CppAD::OdeErrControl(rosen, ti, tf, xi, smin, smax, scur, eabs, erel, ef, maxabs, nstep); } else if( k == 1 ) { xf = CppAD::OdeErrControl(runge, ti, tf, xi, smin, smax, scur, eabs, erel, ef, maxabs, nstep); } else if( k == 2 ) { xf = CppAD::OdeGearControl(gear, M, ti, tf, xi, smin, smax, sini, eabs, erel, ef, maxabs, nstep); } double x0 = exp(-a[0]*tf); ok &= CppAD::NearEqual(x0, xf[0], 0., eabs[0]); ok &= CppAD::NearEqual(0., ef[0], 0., eabs[0]); double x1 = a[0] * (exp(-a[1]*tf) - exp(-a[0]*tf))/(a[0] - a[1]); ok &= CppAD::NearEqual(x1, xf[1], 0., eabs[1]); ok &= CppAD::NearEqual(0., ef[1], 0., eabs[0]); # if CPPAD_ODE_STIFF_PRINT const char* method[]={ "Rosen34", "Runge45", "Gear5" }; std::cout << std::endl; std::cout << "method = " << method[k] << std::endl; std::cout << "nstep = " << nstep << std::endl; std::cout << "x0 = " << x0 << std::endl; std::cout << "xf[0] = " << xf[0] << std::endl; std::cout << "x0 - xf[0] = " << x0 - xf[0] << std::endl; std::cout << "ef[0] = " << ef[0] << std::endl; std::cout << "x1 = " << x1 << std::endl; std::cout << "xf[1] = " << xf[1] << std::endl; std::cout << "x1 - xf[1] = " << x1 - xf[1] << std::endl; std::cout << "ef[1] = " << ef[1] << std::endl; # endif } return ok; } // END C++ ================================================ FILE: example/general/opt_val_hes.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin opt_val_hes.cpp app} opt_val_hes: Example and Test ############################# Fix :math:`z \in \B{R}^\ell` and define the functions :math:`S_k : \B{R} \times \B{R} \rightarrow \B{R}^\ell` by and :math:`F : \B{R} \times \B{R} \rightarrow \B{R}` by .. math:: :nowrap: \begin{eqnarray} S_k (x, y) & = & \frac{1}{2} [ y * \sin ( x * t_k ) - z_k ]^2 \\ F(x, y) & = & \sum_{k=0}^{\ell-1} S_k (x, y) \end{eqnarray} It follows that .. math:: :nowrap: \begin{eqnarray} \partial_y F(x, y) & = & \sum_{k=0}^{\ell-1} [ y * \sin ( x * t_k ) - z_k ] \sin( x * t_k ) \\ \partial_y \partial_y F(x, y) & = & \sum_{k=0}^{\ell-1} \sin ( x t_k )^2 \end{eqnarray} Furthermore if we define :math:`Y(x)` as solving the equation :math:`\partial F[ x, Y(x) ] = 0` we have .. math:: :nowrap: \begin{eqnarray} 0 & = & \sum_{k=0}^{\ell-1} [ Y(x) * \sin ( x * t_k ) - z_k ] \sin( x * t_k ) \\ Y(x) \sum_{k=0}^{\ell-1} \sin ( x * t_k )^2 - \sum_{k=0}^{\ell-1} \sin ( x * t_k ) z_k \\ Y(x) & = & \frac{ \sum_{k=0}^{\ell-1} \sin( x * t_k ) z_k }{ \sum_{k=0}^{\ell-1} \sin ( x * t_k )^2 } \end{eqnarray} {xrst_literal // BEGIN C++ // END C++ } {xrst_end opt_val_hes.cpp} */ // BEGIN C++ # include # include namespace { using CppAD::AD; typedef CPPAD_TESTVECTOR(double) BaseVector; typedef CPPAD_TESTVECTOR(AD) ADVector; class Fun { private: const BaseVector t_; // measurement times const BaseVector z_; // measurement values public: typedef ADVector ad_vector; // constructor Fun(const BaseVector &t, const BaseVector &z) : t_(t) , z_(z) { assert( t.size() == z.size() ); } // ell size_t ell(void) const { return t_.size(); } // Fun.s AD s(size_t k, const ad_vector& x, const ad_vector& y) const { AD residual = y[0] * sin( x[0] * t_[k] ) - z_[k]; AD s_k = .5 * residual * residual; return s_k; } // Fun.sy ad_vector sy(size_t k, const ad_vector& x, const ad_vector& y) const { assert( y.size() == 1); ad_vector sy_k(1); AD residual = y[0] * sin( x[0] * t_[k] ) - z_[k]; sy_k[0] = residual * sin( x[0] * t_[k] ); return sy_k; } }; // Used to test calculation of Hessian of V AD V(const ADVector& x, const BaseVector& t, const BaseVector& z) { // compute Y(x) AD numerator = 0.; AD denominator = 0.; size_t k; for(k = 0; k < size_t(t.size()); k++) { numerator += sin( x[0] * t[k] ) * z[k]; denominator += sin( x[0] * t[k] ) * sin( x[0] * t[k] ); } AD y = numerator / denominator; // V(x) = F[x, Y(x)] AD sum = 0; for(k = 0; k < size_t(t.size()); k++) { AD residual = y * sin( x[0] * t[k] ) - z[k]; sum += .5 * residual * residual; } return sum; } } bool opt_val_hes(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; // temporary indices size_t j, k; // x space vector size_t n = 1; BaseVector x(n); x[0] = 2. * 3.141592653; // y space vector size_t m = 1; BaseVector y(m); y[0] = 1.; // t and z vectors size_t ell = 10; BaseVector t(ell); BaseVector z(ell); for(k = 0; k < ell; k++) { t[k] = double(k) / double(ell); // time of measurement z[k] = y[0] * sin( x[0] * t[k] ); // data without noise } // construct the function object Fun fun(t, z); // evaluate the Jacobian and Hessian BaseVector jac(n), hes(n * n); # ifndef NDEBUG int signdet = # endif CppAD::opt_val_hes(x, y, fun, jac, hes); // we know that F_yy is positive definite for this case assert( signdet == 1 ); // create ADFun object g corresponding to V(x) ADVector a_x(n), a_v(1); for(j = 0; j < n; j++) a_x[j] = x[j]; Independent(a_x); a_v[0] = V(a_x, t, z); CppAD::ADFun g(a_x, a_v); // accuracy for checks double eps = 10. * CppAD::numeric_limits::epsilon(); // check Jacobian BaseVector check_jac = g.Jacobian(x); for(j = 0; j < n; j++) ok &= NearEqual(jac[j], check_jac[j], eps, eps); // check Hessian BaseVector check_hes = g.Hessian(x, 0); for(j = 0; j < n*n; j++) ok &= NearEqual(hes[j], check_hes[j], eps, eps); return ok; } // END C++ ================================================ FILE: example/general/pow.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin pow.cpp} The AD Power Function: Example and Test ####################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end pow.cpp} */ // BEGIN C++ # include # include bool pow(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * std::numeric_limits::epsilon(); // domain space vector size_t n = 2; double x = 0.5; double y = 2.; CPPAD_TESTVECTOR(AD) axy(n); axy[0] = x; axy[1] = y; // declare independent variables and start tape recording CppAD::Independent(axy); // range space vector size_t m = 3; CPPAD_TESTVECTOR(AD) az(m); az[0] = CppAD::pow(axy[0], axy[1]); // pow(variable, variable) az[1] = CppAD::pow(axy[0], y); // pow(variable, parameter) az[2] = CppAD::pow(x, axy[1]); // pow(parameter, variable) // create f: axy -> az and stop tape recording CppAD::ADFun f(axy, az); // check value double check = std::pow(x, y); size_t i; for(i = 0; i < m; i++) ok &= NearEqual(az[i] , check, eps, eps); // forward computation of first partial w.r.t. x CPPAD_TESTVECTOR(double) dxy(n); CPPAD_TESTVECTOR(double) dz(m); dxy[0] = 1.; dxy[1] = 0.; dz = f.Forward(1, dxy); check = y * std::pow(x, y-1.); ok &= NearEqual(dz[0], check, eps, eps); ok &= NearEqual(dz[1], check, eps, eps); ok &= NearEqual(dz[2], 0., eps, eps); // forward computation of first partial w.r.t. y dxy[0] = 0.; dxy[1] = 1.; dz = f.Forward(1, dxy); check = std::log(x) * std::pow(x, y); ok &= NearEqual(dz[0], check, eps, eps); ok &= NearEqual(dz[1], 0., eps, eps); ok &= NearEqual(dz[2], check, eps, eps); // reverse computation of derivative of z[0] + z[1] + z[2] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; w[1] = 1.; w[2] = 1.; dw = f.Reverse(1, w); check = y * std::pow(x, y-1.); ok &= NearEqual(dw[0], 2. * check, eps, eps); check = std::log(x) * std::pow(x, y); ok &= NearEqual(dw[1], 2. * check, eps, eps); // use a VecAD::reference object with pow CppAD::VecAD v(2); AD zero(0); AD one(1); v[zero] = axy[0]; v[one] = axy[1]; AD result = CppAD::pow(v[zero], v[one]); ok &= NearEqual(result, az[0], eps, eps); return ok; } // END C++ ================================================ FILE: example/general/pow_nan.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin pow_nan.cpp} pow: Nan in Result of Pow Function: Example and Test #################################################### Purpose ******* The :ref:`pow(x, y)` function will work when :math:`x < 0` and :math:`y` is a parameter. It will often generate nan or infinity when :math:`x < 0` and one tries to compute a derivatives (even if :math:`y` is a positive integer). This is because the derivative of the log is :math:`1 / x` and the power function uses the representation .. math:: \R{pow}(x, y) = \exp [ y \cdot \log(x) ] Problem ******* There is a problem with this representation when :math:`y` is a parameter and :math:`x = 0`. For example, when :math:`x = 0` and :math:`y = 1`, it returns zero for the derivative, but the actual derivative w.r.t :math:`x` is one. {xrst_literal // BEGIN C++ // END C++ } {xrst_end pow_nan.cpp} */ // BEGIN C++ # include # include bool pow_nan(void) { bool ok = true; using std::cout; using CppAD::AD; using CppAD::vector; // vector x(2), y(2), dx(2), dy(2), ddx(2), ddy(2); vector< AD > ax(2), ay(2); // // variable vector ax[0] = x[0] = -1.0; ax[1] = x[1] = 2.0; // CppAD::Independent(ax); // ay[0] = pow( ax[0], ax[1] ); ay[1] = pow( ax[0], 2.0 ); // CppAD::ADFun f(ax, ay); // // check_for_nan is set false so it does not generate an assert // when compiling with debugging f.check_for_nan(false); // // Zero order forward does not generate nan y = f.Forward(0, x); ok &= y[0] == 1.0; ok &= y[1] == 1.0; // // First order forward generates a nan dx[0] = 1.0; dx[1] = 1.0; dy = f.Forward(1, dx); ok &= std::isnan( dy[0] ); ok &= dy[1] == -2.0; // // Second order Taylor coefficient is 1/2 times second derivative ddx[0] = 0.0; ddx[1] = 0.0; ddy = f.Forward(2, ddx); ok &= std::isnan( ddy[0] ); ok &= ddy[1] == 1.0; // return ok; } // END C++ ================================================ FILE: example/general/print_for.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin print_for_string.cpp} Print During Zero Order Forward Mode: Example and Test ###################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end print_for_string.cpp} */ // BEGIN C++ # include namespace { using std::endl; using CppAD::AD; // use of PrintFor to check for invalid function arguments AD check_log(const AD& u, std::ostream& s_out) { // check AD value during recording if( u <= 0 ) s_out << "check_log: u = " << u << " which is <= 0\n"; // check double value during zero order forward calculation PrintFor(u, "check_log: u = ", u , " which is <= 0\n"); return log(u); } } bool print_for(void) { bool ok = true; using CppAD::PrintFor; std::stringstream stream_out; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector size_t np = 1; size_t nx = 1; size_t ny = 1; CPPAD_TESTVECTOR(AD) ap(np), ax(nx), ay(ny); ap[0] = 1.0; ax[0] = 2.0; Independent(ax, ap); // // define f(x, p) = log(p) + log(x) ay[0] = check_log(ap[0], stream_out) + check_log(ax[0], stream_out); CppAD::ADFun f(ax, ay); // // zero order forward // both x and p are positive so no output generated CPPAD_TESTVECTOR(double) p(np), x(nx), y(ny); p[0] = 1.0; x[0] = 2.0; f.new_dynamic(p); f.check_for_nan(false); y = f.Forward(0, x, stream_out); ok &= stream_out.str() == ""; ok &= CppAD::NearEqual(y[0], std::log(p[0]) + std::log(x[0]), eps99, eps99); // // zero order forward // p is negative so output generated p[0] = -1.0; x[0] = 2.0; f.new_dynamic(p); f.check_for_nan(false); y = f.Forward(0, x, stream_out); ok &= stream_out.str() == "check_log: u = -1 which is <= 0\n"; ok &= std::isnan(y[0]); // return ok; } // END C++ ================================================ FILE: example/general/rev_checkpoint.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin rev_checkpoint.cpp} Reverse Mode General Case (Checkpointing): Example and Test ########################################################### See Also ******** :ref:`checkpoint` Purpose ******* Break a large computation into pieces and only store values at the interface of the pieces (this is much easier to do using :ref:`checkpoint` ). In actual applications, there may be many functions, but for this example there are only two. The functions :math:`F : \B{R}^2 \rightarrow \B{R}^2` and :math:`G : \B{R}^2 \rightarrow \B{R}^2` defined by .. math:: F(x) = \left( \begin{array}{c} x_0 x_1 \\ x_1 - x_0 \end{array} \right) \; , \; G(y) = \left( \begin{array}{c} y_0 - y_1 \\ y_1 y_0 \end{array} \right) Processing Steps **************** We apply reverse mode to compute the derivative of :math:`H : \B{R}^2 \rightarrow \B{R}` is defined by .. math:: :nowrap: \begin{eqnarray} H(x) & = & G_0 [ F(x) ] + G_1 [ F(x) ] \\ & = & x_0 x_1 - ( x_1 - x_0 ) + x_0 x_1 ( x_1 - x_0 ) \\ & = & x_0 x_1 ( 1 - x_0 + x_1 ) - x_1 + x_0 \end{eqnarray} Given the zero and first order Taylor coefficients :math:`x^{(0)}` and :math:`x^{(1)}`, we use :math:`X(t)`, :math:`Y(t)` and :math:`Z(t)` for the corresponding functions; i.e., .. math:: :nowrap: \begin{eqnarray} X(t) & = & x^{(0)} + x^{(1)} t \\ Y(t) & = & F[X(t)] = y^{(0)} + y^{(1)} t + O(t^2) \\ Z(t) & = & G \{ F [ X(t) ] \} = z^{(0)} + z^{(1)} t + O(t^2) \\ h^{(0)} & = & z^{(0)}_0 + z^{(0)}_1 \\ h^{(1)} & = & z^{(1)}_0 + z^{(1)}_1 \end{eqnarray} Here are the processing steps: #. Use forward mode on :math:`F(x)` to compute :math:`y^{(0)}` and :math:`y^{(1)}`. #. Free some, or all, of the memory corresponding to :math:`F(x)`. #. Use forward mode on :math:`G(y)` to compute :math:`z^{(0)}` and :math:`z^{(1)}` #. Use reverse mode on :math:`G(y)` to compute the derivative of :math:`h^{(1)}` with respect to :math:`y^{(0)}` and :math:`y^{(1)}`. #. Free all the memory corresponding to :math:`G(y)`. #. Use reverse mode on :math:`F(x)` to compute the derivative of :math:`h^{(1)}` with respect to :math:`x^{(0)}` and :math:`x^{(1)}`. This uses the following relations: .. math:: :nowrap: \begin{eqnarray} \partial_{x(0)} h^{(1)} [ x^{(0)} , x^{(1)} ] & = & \partial_{y(0)} h^{(1)} [ y^{(0)} , y^{(1)} ] \partial_{x(0)} y^{(0)} [ x^{(0)} , x^{(1)} ] \\ & + & \partial_{y(1)} h^{(1)} [ y^{(0)} , y^{(1)} ] \partial_{x(0)} y^{(1)} [ x^{(0)} , x^{(1)} ] \\ \partial_{x(1)} h^{(1)} [ x^{(0)} , x^{(1)} ] & = & \partial_{y(0)} h^{(1)} [ y^{(0)} , y^{(1)} ] \partial_{x(1)} y^{(0)} [ x^{(0)} , x^{(1)} ] \\ & + & \partial_{y(1)} h^{(1)} [ y^{(0)} , y^{(1)} ] \partial_{x(1)} y^{(1)} [ x^{(0)} , x^{(1)} ] \end{eqnarray} where :math:`\partial_{x(0)}` denotes the partial with respect to :math:`x^{(0)}`. {xrst_literal // BEGIN C++ // END C++ } {xrst_end rev_checkpoint.cpp} */ // BEGIN C++ # include namespace { template Vector F(const Vector& x) { Vector y(2); y[0] = x[0] * x[1]; y[1] = x[1] - x[0]; return y; } template Vector G(const Vector& y) { Vector z(2); z[0] = y[0] - y[1]; z[1] = y[1] * y[0]; return z; } } namespace { bool rev_checkpoint_case(bool free_all) { bool ok = true; double eps = 10. * CppAD::numeric_limits::epsilon(); using CppAD::AD; using CppAD::NearEqual; CppAD::ADFun f, g, empty; // specify the Taylor coefficients for X(t) size_t n = 2; CPPAD_TESTVECTOR(double) x0(n), x1(n); x0[0] = 1.; x0[1] = 2.; x1[0] = 3.; x1[1] = 4.; // record the function F(x) CPPAD_TESTVECTOR(AD) X(n), Y(n); size_t i; for(i = 0; i < n; i++) X[i] = x0[i]; CppAD::Independent(X); Y = F(X); f.Dependent(X, Y); // a function object with an almost empty operation sequence CppAD::Independent(X); empty.Dependent(X, X); // compute the Taylor coefficients for Y(t) CPPAD_TESTVECTOR(double) y0(n), y1(n); y0 = f.Forward(0, x0); y1 = f.Forward(1, x1); if( free_all ) f = empty; else { // free all the Taylor coefficients stored in f f.capacity_order(0); } // record the function G(x) CPPAD_TESTVECTOR(AD) Z(n); CppAD::Independent(Y); Z = G(Y); g.Dependent(Y, Z); // compute the Taylor coefficients for Z(t) CPPAD_TESTVECTOR(double) z0(n), z1(n); z0 = g.Forward(0, y0); z1 = g.Forward(1, y1); // check zero order Taylor coefficient for h^0 = z_0^0 + z_1^0 double check = x0[0] * x0[1] * (1. - x0[0] + x0[1]) - x0[1] + x0[0]; double h0 = z0[0] + z0[1]; ok &= NearEqual(h0, check, eps, eps); // check first order Taylor coefficient h^1 check = x0[0] * x0[1] * (- x1[0] + x1[1]) - x1[1] + x1[0]; check += x1[0] * x0[1] * (1. - x0[0] + x0[1]); check += x0[0] * x1[1] * (1. - x0[0] + x0[1]); double h1 = z1[0] + z1[1]; ok &= NearEqual(h1, check, eps, eps); // compute the derivative with respect to y^0 and y^0 of h^1 size_t p = 2; CPPAD_TESTVECTOR(double) w(n*p), dw(n*p); w[0*p+0] = 0.; // coefficient for z_0^0 w[0*p+1] = 1.; // coefficient for z_0^1 w[1*p+0] = 0.; // coefficient for z_1^0 w[1*p+1] = 1.; // coefficient for z_1^1 dw = g.Reverse(p, w); // We are done using g, so we can free its memory. g = empty; // We need to use f next. if( free_all ) { // we must again record the operation sequence for F(x) CppAD::Independent(X); Y = F(X); f.Dependent(X, Y); } // now recompute the Taylor coefficients corresponding to F(x) // (we already know the result; i.e., y0 and y1). f.Forward(0, x0); f.Forward(1, x1); // compute the derivative with respect to x^0 and x^0 of // h^1 = z_0^1 + z_1^1 CPPAD_TESTVECTOR(double) dv(n*p); dv = f.Reverse(p, dw); // check partial of h^1 w.r.t x^0_0 check = x0[1] * (- x1[0] + x1[1]); check -= x1[0] * x0[1]; check += x1[1] * (1. - x0[0] + x0[1]) - x0[0] * x1[1]; ok &= NearEqual(dv[0*p+0], check, eps, eps); // check partial of h^1 w.r.t x^0_1 check = x0[0] * (- x1[0] + x1[1]); check += x1[0] * (1. - x0[0] + x0[1]) + x1[0] * x0[1]; check += x0[0] * x1[1]; ok &= NearEqual(dv[1*p+0], check, eps, eps); // check partial of h^1 w.r.t x^1_0 check = 1. - x0[0] * x0[1]; check += x0[1] * (1. - x0[0] + x0[1]); ok &= NearEqual(dv[0*p+1], check, eps, eps); // check partial of h^1 w.r.t x^1_1 check = x0[0] * x0[1] - 1.; check += x0[0] * (1. - x0[0] + x0[1]); ok &= NearEqual(dv[1*p+1], check, eps, eps); return ok; } } bool rev_checkpoint(void) { bool ok = true; ok &= rev_checkpoint_case(true); ok &= rev_checkpoint_case(false); return ok; } // END C++ ================================================ FILE: example/general/rev_one.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin rev_one.cpp} First Order Derivative Driver: Example and Test ############################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end rev_one.cpp} */ // BEGIN C++ # include namespace { // ------------------------------------------------------- // define the template function RevOneCases in empty namespace template bool RevOneCases() { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); using CppAD::exp; using CppAD::sin; using CppAD::cos; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) X(n); X[0] = 1.; X[1] = 2.; // declare independent variables and starting recording CppAD::Independent(X); // range space vector size_t m = 3; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = X[0] * exp( X[1] ); Y[1] = X[0] * sin( X[1] ); Y[2] = X[0] * cos( X[1] ); // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // new value for the independent variable vector Vector x(n); x[0] = 2.; x[1] = 1.; // compute and check derivative of y[0] Vector dw(n); dw = f.RevOne(x, 0); ok &= NearEqual(dw[0], exp(x[1]), eps99, eps99); // w.r.t x[0] ok &= NearEqual(dw[1], x[0]*exp(x[1]), eps99, eps99); // w.r.t x[1] // compute and check derivative of y[1] dw = f.RevOne(x, 1); ok &= NearEqual(dw[0], sin(x[1]), eps99, eps99); ok &= NearEqual(dw[1], x[0]*cos(x[1]), eps99, eps99); // compute and check derivative of y[2] dw = f.RevOne(x, 2); ok &= NearEqual(dw[0], cos(x[1]), eps99, eps99); ok &= NearEqual(dw[1], - x[0]*sin(x[1]), eps99, eps99); return ok; } } // End empty namespace # include # include bool RevOne(void) { bool ok = true; // Run with Vector equal to three different cases // all of which are Simple Vectors with elements of type double. ok &= RevOneCases< CppAD::vector >(); ok &= RevOneCases< std::vector >(); ok &= RevOneCases< std::valarray >(); return ok; } // END C++ ================================================ FILE: example/general/rev_two.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin rev_two.cpp} Second Partials Reverse Driver: Example and Test ################################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end rev_two.cpp} */ // BEGIN C++ # include namespace { // ----------------------------------------------------- // define the template function in empty namespace // bool RevTwoCases(void) template bool RevTwoCases() { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); using CppAD::exp; using CppAD::sin; using CppAD::cos; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) X(n); X[0] = 1.; X[1] = 2.; // declare independent variables and starting recording CppAD::Independent(X); // a calculation between the domain and range values AD Square = X[0] * X[0]; // range space vector size_t m = 3; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = Square * exp( X[1] ); Y[1] = Square * sin( X[1] ); Y[2] = Square * cos( X[1] ); // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // new value for the independent variable vector BaseVector x(n); x[0] = 2.; x[1] = 1.; // set i and j to compute specific second partials of y size_t p = 2; SizeVector_t i(p); SizeVector_t j(p); i[0] = 0; j[0] = 0; // for partials y[0] w.r.t x[0] and x[k] i[1] = 1; j[1] = 1; // for partials y[1] w.r.t x[1] and x[k] // compute the second partials BaseVector ddw(n * p); ddw = f.RevTwo(x, i, j); // partials of y[0] w.r.t x[0] is 2 * x[0] * exp(x[1]) // check partials of y[0] w.r.t x[0] and x[k] for k = 0, 1 ok &= NearEqual( 2.*exp(x[1]), ddw[0*p+0], eps99, eps99); ok &= NearEqual( 2.*x[0]*exp(x[1]), ddw[1*p+0], eps99, eps99); // partials of y[1] w.r.t x[1] is x[0] * x[0] * cos(x[1]) // check partials of F_1 w.r.t x[1] and x[k] for k = 0, 1 ok &= NearEqual( 2.*x[0]*cos(x[1]), ddw[0*p+1], eps99, eps99); ok &= NearEqual( -x[0]*x[0]*sin(x[1]), ddw[1*p+1], eps99, eps99); return ok; } } // End empty namespace # include # include bool RevTwo(void) { bool ok = true; // Run with BaseVector equal to three different cases // all of which are Simple Vectors with elements of type double. ok &= RevTwoCases< CppAD::vector , std::vector >(); ok &= RevTwoCases< std::vector , std::vector >(); ok &= RevTwoCases< std::valarray , std::vector >(); // Run with SizeVector_t equal to two other cases // which are Simple Vectors with elements of type size_t. ok &= RevTwoCases< std::vector , CppAD::vector >(); ok &= RevTwoCases< std::vector , std::valarray >(); return ok; } // END C++ ================================================ FILE: example/general/reverse_one.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin reverse_one.cpp} First Order Reverse Mode: Example and Test ########################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end reverse_one.cpp} */ // BEGIN C++ # include namespace { // ---------------------------------------------------------- // define the template function reverse_one_cases in empty namespace template bool reverse_one_cases(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.; ax[1] = 1.; // declare independent variables and start recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = ax[0] * ax[0] * ax[1]; // create f : x -> y and stop recording CppAD::ADFun f(ax, ay); // use first order reverse mode to evaluate derivative of y[0] // and use the values in x for the independent variables. CPPAD_TESTVECTOR(double) w(m), dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0] , 2.*ax[0]*ax[1], eps99, eps99); ok &= NearEqual(dw[1] , ax[0]*ax[0], eps99, eps99); // use zero order forward mode to evaluate y at x = (3, 4) // and use the template parameter Vector for the vector type Vector x(n), y(m); x[0] = 3.; x[1] = 4.; y = f.Forward(0, x); ok &= NearEqual(y[0] , x[0]*x[0]*x[1], eps99, eps99); // use first order reverse mode to evaluate derivative of y[0] // and using the values in x for the independent variables. w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0] , 2.*x[0]*x[1], eps99, eps99); ok &= NearEqual(dw[1] , x[0]*x[0], eps99, eps99); return ok; } } // End empty namespace # include # include bool reverse_one(void) { bool ok = true; // Run with Vector equal to three different cases // all of which are Simple Vectors with elements of type double. ok &= reverse_one_cases< CppAD::vector >(); ok &= reverse_one_cases< std::vector >(); ok &= reverse_one_cases< std::valarray >(); return ok; } // END C++ ================================================ FILE: example/general/reverse_three.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin reverse_three.cpp} Third Order Reverse Mode: Example and Test ########################################## Taylor Coefficients ******************* .. math:: :nowrap: \begin{eqnarray} X(t) & = & x^{(0)} + x^{(1)} t + x^{(2)} t^2 \\ X^{(1)} (t) & = & x^{(1)} + 2 x^{(2)} t \\ X^{(2)} (t) & = & 2 x^{(2)} \end{eqnarray} Thus, we need to be careful to properly account for the fact that :math:`X^{(2)} (0) = 2 x^{(2)}` (and similarly :math:`Y^{(2)} (0) = 2 y^{(2)}`). {xrst_literal // BEGIN C++ // END C++ } {xrst_end reverse_three.cpp} */ // BEGIN C++ # include namespace { // ---------------------------------------------------------- // define the template function cases in empty namespace template bool cases(void) { bool ok = true; double eps = 10. * CppAD::numeric_limits::epsilon(); using CppAD::AD; using CppAD::NearEqual; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) X(n); X[0] = 0.; X[1] = 1.; // declare independent variables and start recording CppAD::Independent(X); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = X[0] * X[1]; // create f : X -> Y and stop recording CppAD::ADFun f(X, Y); // define x^0 and compute y^0 using user zero order forward Vector x0(n), y0(m); x0[0] = 2.; x0[1] = 3.; y0 = f.Forward(0, x0); // y^0 = F(x^0) double check; check = x0[0] * x0[1]; ok &= NearEqual(y0[0] , check, eps, eps); // define x^1 and compute y^1 using first order forward mode Vector x1(n), y1(m); x1[0] = 4.; x1[1] = 5.; y1 = f.Forward(1, x1); // Y^1 (x) = partial_t F( x^0 + x^1 * t ) // y^1 = Y^1 (0) check = x1[0] * x0[1] + x0[0] * x1[1]; ok &= NearEqual(y1[0], check, eps, eps); // define x^2 and compute y^2 using second order forward mode Vector x2(n), y2(m); x2[0] = 6.; x2[1] = 7.; y2 = f.Forward(2, x2); // Y^2 (x) = partial_tt F( x^0 + x^1 * t + x^2 * t^2 ) // y^2 = (1/2) * Y^2 (0) check = x2[0] * x0[1] + x1[0] * x1[1] + x0[0] * x2[1]; ok &= NearEqual(y2[0], check, eps, eps); // W(x) = Y^0 (x) + 2 * Y^1 (x) + 3 * (1/2) * Y^2 (x) size_t p = 3; Vector dw(n*p), w(m*p); w[0] = 1.; w[1] = 2.; w[2] = 3.; dw = f.Reverse(p, w); // check partial w.r.t x^0_0 of W(x) check = x0[1] + 2. * x1[1] + 3. * x2[1]; ok &= NearEqual(dw[0*p+0], check, eps, eps); // check partial w.r.t x^0_1 of W(x) check = x0[0] + 2. * x1[0] + 3. * x2[0]; ok &= NearEqual(dw[1*p+0], check, eps, eps); // check partial w.r.t x^1_0 of W(x) check = 2. * x0[1] + 3. * x1[1]; ok &= NearEqual(dw[0*p+1], check, eps, eps); // check partial w.r.t x^1_1 of W(x) check = 2. * x0[0] + 3. * x1[0]; ok &= NearEqual(dw[1*p+1], check, eps, eps); // check partial w.r.t x^2_0 of W(x) check = 3. * x0[1]; ok &= NearEqual(dw[0*p+2], check, eps, eps); // check partial w.r.t x^2_1 of W(x) check = 3. * x0[0]; ok &= NearEqual(dw[1*p+2], check, eps, eps); return ok; } } // End empty namespace # include # include bool reverse_three(void) { bool ok = true; ok &= cases< CppAD::vector >(); ok &= cases< std::vector >(); ok &= cases< std::valarray >(); return ok; } // END C++ ================================================ FILE: example/general/reverse_two.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin reverse_two.cpp} Second Order Reverse ModeExample and Test ######################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end reverse_two.cpp} */ // BEGIN C++ # include namespace { // ---------------------------------------------------------- // define the template function reverse_two_cases in empty namespace template bool reverse_two_cases(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) X(n); X[0] = 0.; X[1] = 1.; // declare independent variables and start recording CppAD::Independent(X); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = X[0] * X[0] * X[1]; // create f : X -> Y and stop recording CppAD::ADFun f(X, Y); // use zero order forward mode to evaluate y at x = (3, 4) // use the template parameter Vector for the vector type Vector x(n), y(m); x[0] = 3.; x[1] = 4.; y = f.Forward(0, x); ok &= NearEqual(y[0] , x[0]*x[0]*x[1], eps99, eps99); // use first order forward mode in x[0] direction // (all second order partials below involve x[0]) Vector dx(n), dy(m); dx[0] = 1.; dx[1] = 1.; dy = f.Forward(1, dx); double check = 2.*x[0]*x[1]*dx[0] + x[0]*x[0]*dx[1]; ok &= NearEqual(dy[0], check, eps99, eps99); // use second order reverse mode to evalaute second partials of y[0] // with respect to (x[0], x[0]) and with respect to (x[0], x[1]) Vector w(m), dw( n * 2 ); w[0] = 1.; dw = f.Reverse(2, w); // check derivative of f ok &= NearEqual(dw[0*2+0] , 2.*x[0]*x[1], eps99, eps99); ok &= NearEqual(dw[1*2+0] , x[0]*x[0], eps99, eps99); // check derivative of f^{(1)} (x) * dx check = 2.*x[1]*dx[1] + 2.*x[0]*dx[1]; ok &= NearEqual(dw[0*2+1] , check, eps99, eps99); check = 2.*x[0]*dx[1]; ok &= NearEqual(dw[1*2+1] , check, eps99, eps99); return ok; } } // End empty namespace # include # include bool reverse_two(void) { bool ok = true; ok &= reverse_two_cases< CppAD::vector >(); ok &= reverse_two_cases< std::vector >(); ok &= reverse_two_cases< std::valarray >(); return ok; } // END C++ ================================================ FILE: example/general/sign.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sign.cpp} Sign Function: Example and Test ############################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end sign.cpp} */ // BEGIN C++ # include bool sign(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; // create f: x -> y where f(x) = sign(x) size_t n = 1; size_t m = 1; CPPAD_TESTVECTOR(AD) ax(n), ay(m); ax[0] = 0.; CppAD::Independent(ax); ay[0] = sign(ax[0]); CppAD::ADFun f(ax, ay); // check value during recording ok &= (ay[0] == 0.); // use f(x) to evaluate the sign function and its derivatives CPPAD_TESTVECTOR(double) x(n), y(m), dx(n), dy(m), w(m), dw(n); dx[0] = 1.; w[0] = 1.; // x[0] = 2.; y = f.Forward(0, x); ok &= (y[0] == 1.); dy = f.Forward(1, dx); ok &= (dy[0] == 0.); dw = f.Reverse(1, w); ok &= (dw[0] == 0.); // x[0] = 0.; y = f.Forward(0, x); ok &= (y[0] == 0.); dy = f.Forward(1, dx); ok &= (dy[0] == 0.); dw = f.Reverse(1, w); ok &= (dw[0] == 0.); // x[0] = -2.; y = f.Forward(0, x); ok &= (y[0] == -1.); dy = f.Forward(1, dx); ok &= (dy[0] == 0.); dw = f.Reverse(1, w); ok &= (dw[0] == 0.); // use a VecAD::reference object with sign CppAD::VecAD v(1); AD zero(0); v[zero] = 2.; AD result = sign(v[zero]); ok &= (result == 1.); return ok; } // END C++ ================================================ FILE: example/general/sin.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sin.cpp} The AD sin Function: Example and Test ##################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end sin.cpp} */ // BEGIN C++ # include # include bool Sin(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = CppAD::sin(x[0]); // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value double check = std::sin(x0); ok &= NearEqual(y[0] , check, eps99, eps99); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); check = std::cos(x0); ok &= NearEqual(dy[0], check, eps99, eps99); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], check, eps99, eps99); // use a VecAD::reference object with sin CppAD::VecAD v(1); AD zero(0); v[zero] = x0; AD result = CppAD::sin(v[zero]); check = std::sin(x0); ok &= NearEqual(result, check, eps99, eps99); return ok; } // END C++ ================================================ FILE: example/general/sinh.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sinh.cpp} The AD sinh Function: Example and Test ###################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end sinh.cpp} */ // BEGIN C++ # include # include bool Sinh(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = CppAD::sinh(x[0]); // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value double check = std::sinh(x0); ok &= NearEqual(y[0] , check, eps99, eps99); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); check = std::cosh(x0); ok &= NearEqual(dy[0], check, eps99, eps99); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], check, eps99, eps99); // use a VecAD::reference object with sinh CppAD::VecAD v(1); AD zero(0); v[zero] = x0; AD result = CppAD::sinh(v[zero]); check = std::sinh(x0); ok &= NearEqual(result, check, eps99, eps99); return ok; } // END C++ ================================================ FILE: example/general/sqrt.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sqrt.cpp} The AD sqrt Function: Example and Test ###################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end sqrt.cpp} */ // BEGIN C++ # include # include bool Sqrt(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = CppAD::sqrt(x[0]); // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value double check = std::sqrt(x0); ok &= NearEqual(y[0] , check, eps99, eps99); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); check = 1. / (2. * std::sqrt(x0) ); ok &= NearEqual(dy[0], check, eps99, eps99); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], check, eps99, eps99); // use a VecAD::reference object with sqrt CppAD::VecAD v(1); AD zero(0); v[zero] = x0; AD result = CppAD::sqrt(v[zero]); check = std::sqrt(x0); ok &= NearEqual(result, check, eps99, eps99); return ok; } // END C++ ================================================ FILE: example/general/stack_machine.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin stack_machine.cpp} Example Differentiating a Stack Machine Interpreter ################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end stack_machine.cpp} */ // BEGIN C++ # include # include # include # include # include # include # include namespace { // Begin empty namespace ------------------------------------------------ bool is_number( const std::string &s ) { char ch = s[0]; bool number = (std::strchr("0123456789.", ch) != 0); return number; } bool is_binary( const std::string &s ) { char ch = s[0]; bool binary = (strchr("+-*/.", ch) != 0); return binary; } bool is_variable( const std::string &s ) { char ch = s[0]; bool variable = ('a' <= ch) && (ch <= 'z'); return variable; } void StackMachine( std::stack< std::string > &token_stack , CppAD::vector< CppAD::AD > &variable ) { using std::string; using std::stack; using CppAD::AD; stack< AD > value_stack; string token; AD value_one; AD value_two; while( ! token_stack.empty() ) { string s = token_stack.top(); token_stack.pop(); if( is_number(s) ) { value_one = std::atof( s.c_str() ); value_stack.push( value_one ); } else if( is_variable(s) ) { value_one = variable[ size_t(s[0]) - size_t('a') ]; value_stack.push( value_one ); } else if( is_binary(s) ) { assert( value_stack.size() >= 2 ); value_one = value_stack.top(); value_stack.pop(); value_two = value_stack.top(); value_stack.pop(); switch( s[0] ) { case '+': value_stack.push(value_one + value_two); break; case '-': value_stack.push(value_one - value_two); break; case '*': value_stack.push(value_one * value_two); break; case '/': value_stack.push(value_one / value_two); break; default: assert(0); } } else if( s[0] == '=' ) { assert( value_stack.size() >= 1 ); assert( token_stack.size() >= 1 ); // s = token_stack.top(); token_stack.pop(); // assert( is_variable( s ) ); value_one = value_stack.top(); value_stack.pop(); // variable[ size_t(s[0]) - size_t('a') ] = value_one; } else assert(0); } return; } // End empty namespace ------------------------------------------------------- } bool StackMachine(void) { bool ok = true; using std::string; using std::stack; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); using CppAD::vector; // The users program in that stack machine language const char *program[] = { "1.0", "a", "+", "=", "b", // b = a + 1 "2.0", "b", "*", "=", "c", // c = b * 2 "3.0", "c", "-", "=", "d", // d = c - 3 "4.0", "d", "/", "=", "e" // e = d / 4 }; size_t n_program = sizeof( program ) / sizeof( program[0] ); // put the program in the token stack stack< string > token_stack; size_t i = n_program; while(i--) token_stack.push( program[i] ); // domain space vector size_t n = 1; vector< AD > X(n); X[0] = 0.; // declare independent variables and start tape recording CppAD::Independent(X); // x[0] corresponds to a in the stack machine vector< AD > variable(26); variable[0] = X[0]; // calculate the results of the program StackMachine( token_stack , variable); // range space vector size_t m = 4; vector< AD > Y(m); Y[0] = variable[1]; // b = a + 1 Y[1] = variable[2]; // c = (a + 1) * 2 Y[2] = variable[3]; // d = (a + 1) * 2 - 3 Y[3] = variable[4]; // e = ( (a + 1) * 2 - 3 ) / 4 // create f : X -> Y and stop tape recording CppAD::ADFun f(X, Y); // use forward mode to evaluate function at different argument value size_t p = 0; vector x(n); vector y(m); x[0] = 1.; y = f.Forward(p, x); // check function values ok &= (y[0] == x[0] + 1.); ok &= (y[1] == (x[0] + 1.) * 2.); ok &= (y[2] == (x[0] + 1.) * 2. - 3.); ok &= (y[3] == ( (x[0] + 1.) * 2. - 3.) / 4.); // Use forward mode (because x is shorter than y) to calculate Jacobian p = 1; vector dx(n); vector dy(m); dx[0] = 1.; dy = f.Forward(p, dx); ok &= NearEqual(dy[0], 1., eps99, eps99); ok &= NearEqual(dy[1], 2., eps99, eps99); ok &= NearEqual(dy[2], 2., eps99, eps99); ok &= NearEqual(dy[3], .5, eps99, eps99); // Use Jacobian routine (which automatically decides which mode to use) dy = f.Jacobian(x); ok &= NearEqual(dy[0], 1., eps99, eps99); ok &= NearEqual(dy[1], 2., eps99, eps99); ok &= NearEqual(dy[2], 2., eps99, eps99); ok &= NearEqual(dy[3], .5, eps99, eps99); return ok; } // END C++ ================================================ FILE: example/general/sub.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sub.cpp} AD Binary Subtraction: Example and Test ####################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end sub.cpp} */ // BEGIN C++ # include bool Sub(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = .5; CPPAD_TESTVECTOR(AD) x(1); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); AD a = 2. * x[0] - 1.; // AD - double AD b = a - 2; // AD - int AD c = 3. - b; // double - AD AD d = 4 - c; // int - AD // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = x[0] - d; // AD - AD // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value ok &= NearEqual(y[0], x0-4.+3.+2.-2.*x0+1., eps99, eps99); // forward computation of partials w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], -1., eps99, eps99); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], -1., eps99, eps99); // use a VecAD::reference object with subtraction CppAD::VecAD v(1); AD zero(0); v[zero] = b; AD result = 3. - v[zero]; ok &= (result == c); return ok; } // END C++ ================================================ FILE: example/general/sub_eq.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sub_eq.cpp} AD Compound Assignment Subtraction: Example and Test #################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end sub_eq.cpp} */ // BEGIN C++ # include bool SubEq(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = .5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // range space vector size_t m = 2; CPPAD_TESTVECTOR(AD) y(m); y[0] = 3. * x[0]; // initial value y[0] -= 2; // AD -= int y[0] -= 4.; // AD -= double y[1] = y[0] -= x[0]; // use the result of a compound assignment // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value ok &= NearEqual(y[0] , 3.*x0-(2.+4.+x0), eps99, eps99); ok &= NearEqual(y[1] , y[0], eps99, eps99); // forward computation of partials w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 2., eps99, eps99); ok &= NearEqual(dy[1], 2., eps99, eps99); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; w[1] = 0.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], 2., eps99, eps99); // use a VecAD::reference object with computed subtraction CppAD::VecAD v(1); AD zero(0); AD result = 1; v[zero] = 2; result -= v[zero]; ok &= (result == -1); return ok; } // END C++ ================================================ FILE: example/general/tan.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin tan.cpp} The AD tan Function: Example and Test ##################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end tan.cpp} */ // BEGIN C++ # include # include # include bool Tan(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = CppAD::tan(x[0]); // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value double check = std::tan(x0); ok &= NearEqual(y[0] , check, eps, eps); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); check = 1. + std::tan(x0) * std::tan(x0); ok &= NearEqual(dy[0], check, eps, eps); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], check, eps, eps); // use a VecAD::reference object with tan CppAD::VecAD v(1); AD zero(0); v[zero] = x0; AD result = CppAD::tan(v[zero]); check = std::tan(x0); ok &= NearEqual(result, check, eps, eps); return ok; } // END C++ ================================================ FILE: example/general/tanh.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin tanh.cpp} The AD tanh Function: Example and Test ###################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end tanh.cpp} */ // BEGIN C++ # include # include # include bool Tanh(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = CppAD::tanh(x[0]); // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value double check = std::tanh(x0); ok &= NearEqual(y[0] , check, eps, eps); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); check = 1. - std::tanh(x0) * std::tanh(x0); ok &= NearEqual(dy[0], check, eps, eps); // reverse computation of derivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], check, eps, eps); // use a VecAD::reference object with tan CppAD::VecAD v(1); AD zero(0); v[zero] = x0; AD result = CppAD::tanh(v[zero]); check = std::tanh(x0); ok &= NearEqual(result, check, eps, eps); return ok; } // END C++ ================================================ FILE: example/general/tape_index.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin tape_index.cpp} Taping Array Index Operation: Example and Test ############################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end tape_index.cpp} */ // BEGIN C++ # include namespace { double Array(const double &index) { static double array[] = { 5., 4., 3., 2., 1. }; static size_t number = sizeof(array) / sizeof(array[0]); if( index < 0. ) return array[0]; size_t i = static_cast(index); if( i >= number ) return array[number-1]; return array[i]; } // in empty namespace and outside any other routine CPPAD_DISCRETE_FUNCTION(double, Array) } bool TapeIndex(void) { bool ok = true; using CppAD::AD; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) X(n); X[0] = 2.; // array index value X[1] = 3.; // multiplier of array index value // declare independent variables and start tape recording CppAD::Independent(X); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = X[1] * Array( X[0] ); // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // vectors for arguments to the function object f CPPAD_TESTVECTOR(double) x(n); // argument values CPPAD_TESTVECTOR(double) y(m); // function values CPPAD_TESTVECTOR(double) w(m); // function weights CPPAD_TESTVECTOR(double) dw(n); // derivative of weighted function // check function value x[0] = Value(X[0]); x[1] = Value(X[1]); y[0] = Value(Y[0]); ok &= y[0] == x[1] * Array(x[0]); // evaluate f where x has different values x[0] = x[0] + 1.; // new array index value x[1] = x[1] + 1.; // new multiplier value y = f.Forward(0, x); ok &= y[0] == x[1] * Array(x[0]); // evaluate derivaitve of y[0] w[0] = 1.; dw = f.Reverse(1, w); ok &= dw[0] == 0.; // partial w.r.t array index ok &= dw[1] == Array(x[0]); // partial w.r.t multiplier return ok; } // END C++ ================================================ FILE: example/general/taylor_ode.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin taylor_ode.cpp} Taylor's Ode Solver: An Example and Test ######################################## Purpose ******* This example uses the method described in :ref:`taylor_ode-name` to solve an ODE. ODE *** The ODE is defined by :math:`y(0) = 0` and :math:`y^1 (t) = g[ y(t) ]` where the function :math:`g : \B{R}^n \rightarrow \B{R}^n` is defined by .. math:: g(y) = \left( \begin{array}{c} 1 \\ y_1 \\ \vdots \\ y_{n-1} \end{array} \right) and the initial condition is :math:`z(0) = 0`. ODE Solution ************ The solution for this example can be calculated by starting with the first row and then using the solution for the first row to solve the second and so on. Doing this we obtain .. math:: y(t) = \left( \begin{array}{c} t \\ t^2 / 2 \\ \vdots \\ t^n / n ! \end{array} \right) {xrst_literal // BEGIN C++ // END C++ } {xrst_end taylor_ode.cpp} -------------------------------------------------------------------------- */ // BEGIN C++ # include // ========================================================================= // define types for each level namespace { // BEGIN empty namespace typedef CppAD::AD a_double; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR(a_double) a_vector; a_vector ode(const a_vector y) { size_t n = y.size(); a_vector g(n); g[0] = 1; for(size_t k = 1; k < n; k++) g[k] = y[k-1]; return g; } } // ------------------------------------------------------------------------- // use Taylor's method to solve this ordinary differential equaiton bool taylor_ode(void) { // initialize the return value as true bool ok = true; // The ODE does not depend on the argument values // so only tape once, also note that ode does not depend on t size_t n = 5; // number of independent and dependent variables a_vector ay(n), ag(n); CppAD::Independent( ay ); ag = ode(ay); CppAD::ADFun g(ay, ag); // initialize the solution vector at time zero d_vector y(n); for(size_t j = 0; j < n; j++) y[j] = 0.0; size_t order = n; // order of the Taylor method size_t n_step = 4; // number of time steps double dt = 0.5; // step size in time // Taylor coefficients of order k d_vector yk(n), zk(n); // loop with respect to each step of Taylor's method for(size_t i_step = 0; i_step < n_step; i_step++) { // Use Taylor's method to take a step yk = y; // initialize y^{(k)} for k = 0 double dt_kp = dt; // initialize dt^(k+1) for k = 0 for(size_t k = 0; k < order; k++) { // evaluate k-th order Taylor coefficient of z(t) = g(y(t)) zk = g.Forward(k, yk); for(size_t j = 0; j < n; j++) { // convert to (k+1)-Taylor coefficient for y yk[j] = zk[j] / double(k + 1); // add term for to this Taylor coefficient // to solution for y(t, x) y[j] += yk[j] * dt_kp; } // next power of t dt_kp *= dt; } } // check solution of the ODE, // Taylor's method should have no truncation error for this case double eps = 100. * std::numeric_limits::epsilon(); double check = 1.; double t = double(n_step) * dt; for(size_t i = 0; i < n; i++) { check *= t / double(i + 1); ok &= CppAD::NearEqual(y[i], check, eps, eps); } return ok; } // END C++ ================================================ FILE: example/general/unary_minus.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin unary_minus.cpp} AD Unary Minus Operator: Example and Test ######################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end unary_minus.cpp} */ // BEGIN C++ # include bool unary_minus(void) { bool ok = true; using CppAD::AD; // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) x(n); x[0] = 3.; // declare independent variables and start tape recording CppAD::Independent(x); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = - x[0]; // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check values ok &= ( y[0] == -3. ); // forward computation of partials w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); size_t p = 1; dx[0] = 1.; dy = f.Forward(p, dx); ok &= ( dy[0] == -1. ); // dy[0] / dx[0] // reverse computation of dertivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(p, w); ok &= ( dw[0] == -1. ); // dy[0] / dx[0] // use a VecAD::reference object with unary minus CppAD::VecAD v(1); AD zero(0); v[zero] = x[0]; AD result = - v[zero]; ok &= (result == y[0]); return ok; } // END C++ ================================================ FILE: example/general/unary_plus.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin unary_plus.cpp} AD Unary Plus Operator: Example and Test ######################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end unary_plus.cpp} */ // BEGIN C++ # include bool unary_plus(void) { bool ok = true; using CppAD::AD; // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) x(n); x[0] = 3.; // declare independent variables and start tape recording CppAD::Independent(x); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = + x[0]; // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check values ok &= ( y[0] == 3. ); // forward computation of partials w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); size_t p = 1; dx[0] = 1.; dy = f.Forward(p, dx); ok &= ( dy[0] == 1. ); // dy[0] / dx[0] // reverse computation of dertivative of y[0] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; dw = f.Reverse(p, w); ok &= ( dw[0] == 1. ); // dy[0] / dx[0] // use a VecAD::reference object with unary plus CppAD::VecAD v(1); AD zero(0); v[zero] = x[0]; AD result = + v[zero]; ok &= (result == y[0]); return ok; } // END C++ ================================================ FILE: example/general/value.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin value.cpp} Convert From AD to its Base Type: Example and Test ################################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end value.cpp} */ // BEGIN C++ # include bool Value(void) { bool ok = true; using CppAD::AD; using CppAD::Value; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) x(n); x[0] = 3.; x[1] = 4.; // check value before recording ok &= (Value(x[0]) == 3.); ok &= (Value(x[1]) == 4.); // declare independent variables and start tape recording CppAD::Independent(x); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); y[0] = - x[1]; // cannot call Value(x[j]) or Value(y[0]) here (currently variables) AD p = 5.; // p is a parameter (does not depend on x) ok &= (Value(p) == 5.); // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // can call Value(x[j]) or Value(y[0]) here (currently parameters) ok &= (Value(x[0]) == 3.); ok &= (Value(x[1]) == 4.); ok &= (Value(y[0]) == -4.); return ok; } // END C++ ================================================ FILE: example/general/var2par.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin var2par.cpp} Convert a Variable or Dynamic Parameter a Constant: Example and Test #################################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end var2par.cpp} */ // BEGIN C++ # include bool Var2Par(void) { bool ok = true; using CppAD::AD; using CppAD::Value; using CppAD::Var2Par; // independent variables size_t nx = 2; CPPAD_TESTVECTOR(AD) ax(nx); ax[0] = 3.; ax[1] = 4.; // independent dynamic parameters size_t np = 1; CPPAD_TESTVECTOR(AD) ap(np); ap[0] = 5.; // declare independent variables and dynamic parameters CppAD::Independent(ax, ap); // range space vector size_t ny = 2; CPPAD_TESTVECTOR(AD) ay(ny); ay[0] = - ax[1] * Var2Par(ax[0]); // same as ay[0] = -ax[1] * 3.; ay[1] = - ax[1] * Var2Par(ap[0]); // same as ay[1] = -ax[1] * 5.; // Must convert these objects to constants before calling Value ok &= ( Value( Var2Par(ax[0]) ) == 3. ); ok &= ( Value( Var2Par(ax[1]) ) == 4. ); ok &= ( Value( Var2Par(ap[0]) ) == 5. ); ok &= ( Value( Var2Par(ay[0]) ) == -12. ); ok &= ( Value( Var2Par(ay[1]) ) == -20. ); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // All AD object are currently constants ok &= (Value(ax[0]) == 3.); ok &= (Value(ax[1]) == 4.); ok &= (Value(ap[0]) == 5.); ok &= (Value(ay[0]) == -12.); ok &= (Value(ay[1]) == -20.); // evaluate zero order forward mode // (note that the only real variable in this recording is x[1]) CPPAD_TESTVECTOR(double) x(nx), p(np), y(ny); x[0] = 6.; x[1] = 7.; p[0] = 8.; f.new_dynamic(p); y = f.Forward(0, x); ok &= y[0] == - x[1] * 3.0; ok &= y[1] == - x[1] * 5.0; return ok; } // END C++ ================================================ FILE: example/general/vec_ad.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin vec_ad.cpp} AD Vectors that Record Index Operations: Example and Test ######################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end vec_ad.cpp} */ // BEGIN C++ # include # include namespace { // return the vector x that solves the following linear system // a[0] * x[0] + a[1] * x[1] = b[0] // a[2] * x[0] + a[3] * x[1] = b[1] // in a way that will record pivot operations on the AD tape typedef CPPAD_TESTVECTOR(CppAD::AD) Vector; Vector Solve(const Vector &a , const Vector &b) { using namespace CppAD; assert(a.size() == 4 && b.size() == 2); // copy the vector b into the VecAD object B VecAD B(2); AD u; for(u = 0; u < 2; u += 1.) B[u] = b[ size_t( Integer(u) ) ]; // copy the matrix a into the VecAD object A VecAD A(4); for(u = 0; u < 4; u += 1.) A[u] = a [ size_t( Integer(u) ) ]; // tape AD operation sequence that determines the row of A // with maximum absolute element in column zero AD zero(0), one(1); AD rmax = CondExpGt(fabs(a[0]), fabs(a[2]), zero, one); // divide row rmax by A(rmax, 0) A[rmax * 2 + 1] = A[rmax * 2 + 1] / A[rmax * 2 + 0]; B[rmax] = B[rmax] / A[rmax * 2 + 0]; A[rmax * 2 + 0] = one; // subtract A(other,0) times row A(rmax, *) from row A(other,*) AD other = one - rmax; A[other * 2 + 1] = A[other * 2 + 1] - A[other * 2 + 0] * A[rmax * 2 + 1]; B[other] = B[other] - A[other * 2 + 0] * B[rmax]; A[other * 2 + 0] = zero; // back substitute to compute the solution vector x. // Note that the columns of A correspond to rows of x. // Also note that A[rmax * 2 + 0] is equal to one. CPPAD_TESTVECTOR(AD) x(2); x[1] = B[other] / A[other * 2 + 1]; x[0] = B[rmax] - A[rmax * 2 + 1] * x[1]; return x; } } bool vec_ad(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 4; CPPAD_TESTVECTOR(double) x(n); CPPAD_TESTVECTOR(AD) X(n); // 2 * identity matrix (rmax in Solve will be 0) X[0] = x[0] = 2.; X[1] = x[1] = 0.; X[2] = x[2] = 0.; X[3] = x[3] = 2.; // declare independent variables and start tape recording CppAD::Independent(X); // define the vector b CPPAD_TESTVECTOR(double) b(2); CPPAD_TESTVECTOR(AD) B(2); B[0] = b[0] = 0.; B[1] = b[1] = 1.; // range space vector solves X * Y = b size_t m = 2; CPPAD_TESTVECTOR(AD) Y(m); Y = Solve(X, B); // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // By Cramer's rule: // y[0] = [ b[0] * x[3] - x[1] * b[1] ] / [ x[0] * x[3] - x[1] * x[2] ] // y[1] = [ x[0] * b[1] - b[0] * x[2] ] / [ x[0] * x[3] - x[1] * x[2] ] double den = x[0] * x[3] - x[1] * x[2]; double dsq = den * den; double num0 = b[0] * x[3] - x[1] * b[1]; double num1 = x[0] * b[1] - b[0] * x[2]; // check value ok &= NearEqual(Y[0] , num0 / den, eps99, eps99); ok &= NearEqual(Y[1] , num1 / den, eps99, eps99); // forward computation of partials w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dx[1] = 0.; dx[2] = 0.; dx[3] = 0.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 0. - num0 * x[3] / dsq, eps99, eps99); ok &= NearEqual(dy[1], b[1] / den - num1 * x[3] / dsq, eps99, eps99); // compute the solution for a new x matrix such that pivioting // on the original rmax row would divide by zero CPPAD_TESTVECTOR(double) y(m); x[0] = 0.; x[1] = 2.; x[2] = 2.; x[3] = 0.; // new values for Cramer's rule den = x[0] * x[3] - x[1] * x[2]; dsq = den * den; num0 = b[0] * x[3] - x[1] * b[1]; num1 = x[0] * b[1] - b[0] * x[2]; // check values y = f.Forward(0, x); ok &= NearEqual(y[0] , num0 / den, eps99, eps99); ok &= NearEqual(y[1] , num1 / den, eps99, eps99); // forward computation of partials w.r.t. x[1] dx[0] = 0.; dx[1] = 1.; dx[2] = 0.; dx[3] = 0.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0],-b[1] / den + num0 * x[2] / dsq, eps99, eps99); ok &= NearEqual(dy[1], 0. + num1 * x[2] / dsq, eps99, eps99); // reverse computation of derivative of y[0] w.r.t x CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; w[1] = 0.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], 0. - num0 * x[3] / dsq, eps99, eps99); ok &= NearEqual(dw[1],-b[1] / den + num0 * x[2] / dsq, eps99, eps99); ok &= NearEqual(dw[2], 0. + num0 * x[1] / dsq, eps99, eps99); ok &= NearEqual(dw[3], b[0] / den - num0 * x[0] / dsq, eps99, eps99); return ok; } // END C++ ================================================ FILE: example/get_started/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the example/get_started directory tests # SET(source_list get_started.cpp) # set_compile_flags( example_get_started "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(example_get_started EXCLUDE_FROM_ALL ${source_list}) # TARGET_LINK_LIBRARIES(example_get_started ${cppad_lib} ${colpack_libs}) # # check_example_get_started add_check_executable(check_example get_started) ================================================ FILE: example/get_started/get_started.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin get_started.cpp} Getting Started Using CppAD to Compute Derivatives ################################################## Purpose ******* Demonstrate the use of CppAD by computing the derivative of a simple example function. Function ******** The example function :math:`f : \B{R} \rightarrow \B{R}` is defined by .. math:: f(x) = a_0 + a_1 * x^1 + \cdots + a_{k-1} * x^{k-1} where *a* is a fixed vector of length *k* . Derivative ********** The derivative of :math:`f(x)` is given by .. math:: f' (x) = a_1 + 2 * a_2 * x + \cdots + (k-1) * a_{k-1} * x^{k-2} Value ***** For the particular case in this example, :math:`k` is equal to 5, :math:`a = (1, 1, 1, 1, 1)`, and :math:`x = 3`. If follows that .. math:: f' ( 3 ) = 1 + 2 * 3 + 3 * 3^2 + 4 * 3^3 = 142 Include File ************ The following command, in the program below, includes the CppAD package: # ``include `` Poly **** The routine ``Poly`` , defined below, evaluates a polynomial. A general purpose polynomial evaluation routine is documented and distributed with CppAD; see :ref:`Poly-name` . CppAD Namespace *************** All of the functions and objects defined by CppAD are in the ``CppAD`` namespace. In the example below, ``using CppAD::AD`` ; enables one to abbreviate ``CppAD::AD`` using just ``AD`` . CppAD Preprocessor Symbols ************************** All the :ref:`preprocessor-name` symbols defined by CppAD begin with ``CPPAD_`` (some deprecated symbols begin with ``CppAD_`` ). The preprocessor symbol :ref:`CPPAD_TESTVECTOR` is used in the example below. Program ******* {xrst_spell_off} {xrst_code cpp} */ # include // standard input/output # include // standard vector # include // the CppAD package namespace { // begin the empty namespace // define the function Poly(a, x) = a[0] + a[1]*x[1] + ... + a[k-1]*x[k-1] template Type Poly(const CPPAD_TESTVECTOR(double) &a, const Type &x) { size_t k = a.size(); Type y = 0.; // initialize summation Type x_i = 1.; // initialize x^i for(size_t i = 0; i < k; i++) { y += a[i] * x_i; // y = y + a_i * x^i x_i *= x; // x_i = x_i * x } return y; } } // main program int main(void) { using CppAD::AD; // use AD as abbreviation for CppAD::AD using std::vector; // use vector as abbreviation for std::vector // vector of polynomial coefficients size_t k = 5; // number of polynomial coefficients CPPAD_TESTVECTOR(double) a(k); // vector of polynomial coefficients for(size_t i = 0; i < k; i++) a[i] = 1.; // value of polynomial coefficients // domain space vector size_t n = 1; // number of domain space variables vector< AD > ax(n); // vector of domain space variables ax[0] = 3.; // value at which function is recorded // declare independent variables and start recording operation sequence CppAD::Independent(ax); // range space vector size_t m = 1; // number of ranges space variables vector< AD > ay(m); // vector of ranges space variables ay[0] = Poly(a, ax[0]); // record operations that compute ay[0] // store operation sequence in f: X -> Y and stop recording CppAD::ADFun f(ax, ay); // compute derivative using operation sequence stored in f vector jac(m * n); // Jacobian of f (m by n matrix) vector x(n); // domain space vector x[0] = 3.; // argument value for computing derivative jac = f.Jacobian(x); // Jacobian for operation sequence // print the results std::cout << "f'(3) computed by CppAD = " << jac[0] << std::endl; // check if the derivative is correct int error_code; if( jac[0] == 142. ) error_code = 0; // return code for correct case else error_code = 1; // return code for incorrect case return error_code; } /* {xrst_code} {xrst_spell_on} Output ****** Executing the program above will generate the following output: :: f'(3) computed by CppAD = 142 Running ******* After you configure your system using the :ref:`cmake-name` command, you compile and run this example by executing the command ``make check_example_get_started`` in the build directory; i.e., the directory where the cmake command was executed. Exercises ********* Modify the program above to accomplish the following tasks using CppAD: #. Compute and print the derivative of :math:`f(x) = 1 + x + x^2 + x^3 + x^4` at the point :math:`x = 2`. #. Compute and print the derivative of :math:`f(x) = 1 + x + x^2 / 2` at the point :math:`x = .5`. #. Compute and print the derivative of :math:`f(x) = \exp (x) - 1 - x - x^2 / 2` at the point :math:`x = .5`. {xrst_end get_started.cpp} */ ================================================ FILE: example/graph/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list add_op.cpp atom4_op.cpp atom_op.cpp azmul_op.cpp cexp_op.cpp comp_op.cpp discrete_op.cpp div_op.cpp graph.cpp mul_op.cpp pow_op.cpp print_graph.cpp print_op.cpp sub_op.cpp sum_op.cpp switch_var_dyn.cpp unary_op.cpp ) # END_SORT_THIS_LINE_MINUS_2 set_compile_flags(example_graph "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(example_graph EXCLUDE_FROM_ALL ${source_list}) # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_graph ${cppad_lib} ${colpack_libs} ) # # check_example_graph add_check_executable(check_example graph) ================================================ FILE: example/graph/add_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin graph_add_op.cpp} C++ AD Graph add Operator: Example and Test ########################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end graph_add_op.cpp} */ // BEGIN C++ # include bool add_op(void) { bool ok = true; using std::string; // // AD graph example // node_1 : p[0] // node_2 : p[1] // node_3 : x[0] // node_4 : p[0] + p[1] // node_5 : x[0] + ( p[0] + p[1] ) // y[0] = x[0] + ( p[0] + p[1] ) // // C++ graph object CppAD::cpp_graph graph_obj; // // operator being used CppAD::graph::graph_op_enum op_enum; // // set scalars graph_obj.function_name_set("add_op example"); size_t n_dynamic_ind = 2; graph_obj.n_dynamic_ind_set(n_dynamic_ind); size_t n_variable_ind = 1; graph_obj.n_variable_ind_set(n_variable_ind); // // node_4 : p[0] + p[1] op_enum = CppAD::graph::add_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(1); graph_obj.operator_arg_push_back(2); // // node_5 : x[0] + ( p[0] + p[1] ) graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(3); graph_obj.operator_arg_push_back(4); // // y[0] = x[0] + ( p[0] + p[1] ) graph_obj.dependent_vec_push_back(5); // // f(x, p) = x_0 + ( p_0 + p_1 ) CppAD::ADFun f; f.from_graph(graph_obj); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters CPPAD_TESTVECTOR(double) p(2), x(1); p[0] = 2.0; p[1] = 3.0; x[0] = 4.0; // // compute y = f(x, p) f.new_dynamic(p); CPPAD_TESTVECTOR(double) y = f.Forward(0, x); // // check result ok &= y[0] == x[0] + ( p[0] + p[1] ); // ----------------------------------------------------------------------- // Convert function to graph and back again f.to_graph(graph_obj); f.from_graph(graph_obj); // ----------------------------------------------------------------------- ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters p[0] = 2.0; p[1] = 3.0; x[0] = 4.0; // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= y[0] == x[0] + ( p[0] + p[1] ); // return ok; } // END C++ ================================================ FILE: example/graph/atom4_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin graph_atom4_op.cpp} C++ AD Graph Atomic Four Functions: Example and Test #################################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end graph_atom4_op.cpp} */ // BEGIN C++ # include namespace { class atomic_int_pow : public CppAD::atomic_four { public: atomic_int_pow(void) : CppAD::atomic_four("int_pow") { } private: // for_type bool for_type( size_t call_id , const CppAD::vector& type_x , CppAD::vector& type_y ) override { type_y[0] = type_x[0]; return true; } // forward bool forward( size_t call_id , const CppAD::vector& select_y , size_t order_low , size_t order_up , const CppAD::vector& taylor_x , CppAD::vector& taylor_y ) override { // order_up if( order_up != 0 ) return false; // // taylor_y taylor_y[0] = 1.0; for(size_t i = 0; i < call_id; ++i) taylor_y[0] *= taylor_x[0]; // return true; } }; } bool atom4_op(void) { bool ok = true; using std::string; // // reciprocal atomic_int_pow int_pow; // ----------------------------------------------------------------------- // // This function has an atomic function operator with name int_pow // node_1 : p[0] // node_2 : x[0] // node_3 : p[0] + x[0] // node_4 : int_pow( p[0] + x[0] ) // y[0] = ( p[0] + x[0] ) ** call_id // // call_id size_t call_id = 2; // // C++ graph object CppAD::cpp_graph graph_obj; graph_obj.initialize(); // // operator being used CppAD::graph::graph_op_enum op_enum; // graph_obj.function_name_set("g(p; x)"); graph_obj.n_dynamic_ind_set(1); graph_obj.n_variable_ind_set(1); // // node_3 : p[0] + x[0] op_enum = CppAD::graph::add_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(1); graph_obj.operator_arg_push_back(2); // // node_4 : f( p[0] + x[0] ) // // name_index, n_result, n_arg come before first_node size_t name_index = graph_obj.atomic_name_vec_size(); graph_obj.atomic_name_vec_push_back("int_pow"); // op_enum = CppAD::graph::atom4_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(name_index); // name_index graph_obj.operator_arg_push_back(call_id); // call_id graph_obj.operator_arg_push_back(1); // n_result graph_obj.operator_arg_push_back(1); // n_node_arg graph_obj.operator_arg_push_back(3); // first and last node arg // // y[0] = int_pow( p[0] + x[0] ) = ( p[0] + x[0] ) ** call_id graph_obj.dependent_vec_push_back(4); // ------------------------------------------------------------------------ CppAD::ADFun g; g.from_graph(graph_obj); // ------------------------------------------------------------------------ ok &= g.Domain() == 1; ok &= g.Range() == 1; ok &= g.size_dyn_ind() == 1; // // set p in g(p; x) CPPAD_TESTVECTOR(double) p(1); p[0] = 2.0; g.new_dynamic(p); // // evaluate g(p; x) CPPAD_TESTVECTOR(double) x(1), y(1); x[0] = 3.0; y = g.Forward(0, x); // // check value double eps99 = 99.0 * std::numeric_limits::epsilon(); double check = std::pow( p[0] + x[0], double(call_id) ); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // ------------------------------------------------------------------------ g.to_graph(graph_obj); g.from_graph(graph_obj); // ------------------------------------------------------------------------ ok &= g.Domain() == 1; ok &= g.Range() == 1; ok &= g.size_dyn_ind() == 1; // // set p in g(p; x) p[0] = 4.0; g.new_dynamic(p); // // evaluate g(p; x) x[0] = 5.0; y = g.Forward(0, x); // // check value check = std::pow( p[0] + x[0], double(call_id) ); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // ------------------------------------------------------------------------ return ok; } // END C++ ================================================ FILE: example/graph/atom_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin graph_atom_op.cpp} C++ AD Graph Atomic Three Functions: Example and Test ##################################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end graph_atom_op.cpp} */ // BEGIN C++ # include bool atom_op(void) { bool ok = true; using std::string; // ----------------------------------------------------------------------- // Define f_0 (x_0, x_1; p) = x_1 + p_0 * x_0 // // This function does not have an atomic function operator // node_1 : p[0] // node_2 : x[0] // node_3 : x[1] // node_4 : p[0] * x[0] // node_5 : x[1] + p[0] * x[0] // y[0] = x[1] + p[0] * x[0] // // C++ graph object CppAD::cpp_graph graph_obj; // // operator being used CppAD::graph::graph_op_enum op_enum; // // set scalars graph_obj.function_name_set("f(x; p)"); size_t n_dynamic_ind = 1; graph_obj.n_dynamic_ind_set(n_dynamic_ind); size_t n_variable_ind = 2; graph_obj.n_variable_ind_set(n_variable_ind); // // node_4 : p[0] * x[0] op_enum = CppAD::graph::mul_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(1); graph_obj.operator_arg_push_back(2); // // node_5 : x[1] + p[0] * x[0] op_enum = CppAD::graph::add_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(3); graph_obj.operator_arg_push_back(4); // // y[0] = x[1] + p[0] * x[0] graph_obj.dependent_vec_push_back(5); // // f(x, p) = x_1 + p_0 * x_0 CppAD::ADFun f; f.from_graph(graph_obj); // ok &= f.Domain() == 2; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // A ckhpoint_two function with name f(x; p) is derived from // an atomic_three function with the same name. bool internal_bool = false; bool use_hes_sparsity = false; bool use_base2ad = false; bool use_in_parallel = false; CppAD::chkpoint_two chk_f(f, "f(x; p)", internal_bool, use_hes_sparsity, use_base2ad, use_in_parallel ); // ----------------------------------------------------------------------- // g (u_0, u_1; p, q) = f(u_0 + q_0, u_1 + q_1, p) // = u_1 + q_1 + p_0 * ( u_0 + q_0 ) // // This function has an atomic function operator with name f(x; p) // node_1 : q[0] // node_2 : q[1] // node_3 : u[0] // node_4 : u[1] // node_5 : u[0] + q[0] // node_6 : u[1] + q[1] // node_7 : f( u[0] + q[0], u[1] + q[1]; p) // y[0] = u[1] + q[1] + p[0] * (u[0] + q[0]) // graph_obj.initialize(); // graph_obj.function_name_set("g(u; p, q)"); n_dynamic_ind = 2; graph_obj.n_dynamic_ind_set(n_dynamic_ind); n_variable_ind = 2; graph_obj.n_variable_ind_set(n_variable_ind); // // node_5 : u[0] + q[0] op_enum = CppAD::graph::add_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(3); graph_obj.operator_arg_push_back(1); // // node_6 : u[1] + q[1] graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(4); graph_obj.operator_arg_push_back(2); // // node_7 : f( u[0] + q[0], u[1] + q[1]; p) // // name_index, n_result, n_arg come before first_node size_t name_index = graph_obj.atomic_name_vec_size(); graph_obj.atomic_name_vec_push_back("f(x; p)"); // op_enum = CppAD::graph::atom_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(name_index); // name_index graph_obj.operator_arg_push_back(1); // n_result graph_obj.operator_arg_push_back(2); // n_node_arg graph_obj.operator_arg_push_back(5); // first node arg graph_obj.operator_arg_push_back(6); // second node arg // // y[0] = u[1] + q[1] + p[0] * (u[0] + q[0]) graph_obj.dependent_vec_push_back(7); // ------------------------------------------------------------------------ CppAD::ADFun g; g.from_graph(graph_obj); // ------------------------------------------------------------------------ ok &= g.Domain() == 2; ok &= g.Range() == 1; ok &= g.size_dyn_ind() == 2; // // set p in g(u; p, q) CPPAD_TESTVECTOR(float) p(1); p[0] = 2.0; chk_f.new_dynamic(p); // // set q in g(u; p, q) CPPAD_TESTVECTOR(float) q(2); q[0] = 3.0; q[1] = 4.0; g.new_dynamic(q); // // evaluate g(u; p, q) CPPAD_TESTVECTOR(float) u(2), y(1); u[0] = 5.0; u[1] = 6.0; y = g.Forward(0, u); // // check value ok &= y[0] == u[1] + q[1] + p[0] * (u[0] + q[0]); // ------------------------------------------------------------------------ g.to_graph(graph_obj); g.from_graph(graph_obj); // ------------------------------------------------------------------------ ok &= g.Domain() == 2; ok &= g.Range() == 1; ok &= g.size_dyn_ind() == 2; // // set p in g(u; p, q) p[0] = 3.0; chk_f.new_dynamic(p); // // set q in g(u; p, q) q[0] = 4.0; q[1] = 5.0; g.new_dynamic(q); // // evaluate g(u; p, q) u[0] = 6.0; u[1] = 7.0; y = g.Forward(0, u); // // check value ok &= y[0] == u[1] + q[1] + p[0] * (u[0] + q[0]); // ------------------------------------------------------------------------ return ok; } // END C++ ================================================ FILE: example/graph/azmul_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin graph_azmul_op.cpp} C++ AD Graph add Operator: Example and Test ########################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end graph_azmul_op.cpp} */ // BEGIN C++ # include bool azmul_op(void) { bool ok = true; using std::string; // // AD graph example // node_1 : p[0] // node_2 : p[1] // node_3 : x[0] // node_4 : azmul(p[0] , p[1]) // node_5 : azmul(x[0] , p[1]) // y[0] = azmul(p[0] , p[1]) // y[1] = azmul(x[0] , p[1]) // // C++ graph object CppAD::cpp_graph graph_obj; // // operator being used CppAD::graph::graph_op_enum op_enum; // // set scalars graph_obj.function_name_set("azmul_op example"); size_t n_dynamic_ind = 2; graph_obj.n_dynamic_ind_set(n_dynamic_ind); size_t n_variable_ind = 1; graph_obj.n_variable_ind_set(n_variable_ind); // // node_4 : azmul(p[0], p[1]) op_enum = CppAD::graph::azmul_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(1); graph_obj.operator_arg_push_back(2); // // node_5 : azmul(x[0], p[1]) graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(3); graph_obj.operator_arg_push_back(2); // // y[0] = azmul(p[0], p[1]) // y[1] = azmul(x[0], x[1]) graph_obj.dependent_vec_push_back(4); graph_obj.dependent_vec_push_back(5); // // f(x, p) = y CppAD::ADFun f; f.from_graph(graph_obj); ok &= f.Domain() == 1; ok &= f.Range() == 2; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters CPPAD_TESTVECTOR(double) p(2), x(1); p[0] = 2.0; p[1] = 3.0; x[0] = 4.0; // // compute y = f(x, p) f.new_dynamic(p); CPPAD_TESTVECTOR(double) y = f.Forward(0, x); // // check result ok &= y[0] == p[0] * p[1]; ok &= y[1] == x[0] * p[1]; // ----------------------------------------------------------------------- // Convert function to graph and back again f.to_graph(graph_obj); f.from_graph(graph_obj); // ----------------------------------------------------------------------- ok &= f.Domain() == 1; ok &= f.Range() == 2; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters p[0] = 3.0; p[1] = std::numeric_limits::quiet_NaN(); x[0] = 0.0; // // suppress checking for nan in y f.check_for_nan(false); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= std::isnan(y[0]); ok &= y[1] == 0.0; // return ok; } // END C++ ================================================ FILE: example/graph/cexp_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin graph_cexp_op.cpp} C++ AD Graph Conditional Expressions: Example and Test ###################################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end graph_cexp_op.cpp} */ // BEGIN C++ # include bool cexp_op(void) { bool ok = true; using std::string; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : cexp_le(p[0], x[0], p[0], x[0]) // y[0] = cexp_le(p[0], x[0], p[0], x[0]) // // // C++ graph object CppAD::cpp_graph graph_obj; // // operator being used CppAD::graph::graph_op_enum op_enum; // // set scalars graph_obj.function_name_set("cexp_op example"); size_t n_dynamic_ind = 1; graph_obj.n_dynamic_ind_set(n_dynamic_ind); size_t n_variable_ind = 1; graph_obj.n_variable_ind_set(n_variable_ind); graph_obj.constant_vec_push_back(-0.1); // // node_4 : cexp_le(p[0], x[0], p[0], x[0]) op_enum = CppAD::graph::cexp_le_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(1); graph_obj.operator_arg_push_back(2); graph_obj.operator_arg_push_back(1); graph_obj.operator_arg_push_back(2); // // y[0] = cexp_le(p[0], x[0], p[0], x[0]) graph_obj.dependent_vec_push_back(4); // // f(x, p) = cexp_le(p[0], x[0], p[0], x[0]) CppAD::ADFun f; f.from_graph(graph_obj); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // set independent variables and parameters CPPAD_TESTVECTOR(double) p(1), x(1); p[0] = 0.2; x[0] = 0.3; // // compute y = f(x, p) f.new_dynamic(p); CPPAD_TESTVECTOR(double) y = f.Forward(0, x); // // check result double check; if( p[0] <= x[0] ) check= p[0]; else check = x[0]; // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // ---------------------------------------------------------------------- // Convert to Graph graph and back again f.to_graph(graph_obj); f.from_graph(graph_obj); // ---------------------------------------------------------------------- // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // END C++ ================================================ FILE: example/graph/comp_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin graph_comp_op.cpp} C++ AD Graph Comparison Operators: Example and Test ################################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end graph_comp_op.cpp} */ // BEGIN C++ # include bool comp_op(void) { bool ok = true; using std::string; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // : x[0] < p[0] // node_3 : p[0] - x[0] // node_4 : log( p[0] - x[0] ) // y[0] = log( p[0] - x[0] ) // // C++ graph object CppAD::cpp_graph graph_obj; // // operator being used CppAD::graph::graph_op_enum op_enum; // // set scalars graph_obj.function_name_set("comp_op example"); size_t n_dynamic_ind = 1; graph_obj.n_dynamic_ind_set(n_dynamic_ind); size_t n_variable_ind = 1; graph_obj.n_variable_ind_set(n_variable_ind); // // x[0] < p[0] op_enum = CppAD::graph::comp_lt_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(2); graph_obj.operator_arg_push_back(1); // // node_3 : p[0] - x[0] op_enum = CppAD::graph::sub_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(1); graph_obj.operator_arg_push_back(2); // // node_4 : log( p[0] - x[0] ) op_enum = CppAD::graph::log_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(3); // // y[0] = log( p[0] - x[0] ) graph_obj.dependent_vec_push_back(4); // // f(x, p) = log( p[0] - x[0] ) CppAD::ADFun f; f.from_graph(graph_obj); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // set independent variables and parameters CPPAD_TESTVECTOR(double) p(1), x(1); p[0] = 0.3; x[0] = 0.2; // // compute y = f(x, p) f.new_dynamic(p); CPPAD_TESTVECTOR(double) y = f.Forward(0, x); // // x[0] < p[0] so comparison should not have changed ok &= f.compare_change_number() == 0; // // check result double check = std::log( p[0] - x[0] ); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // case where comparison is false f.check_for_nan(false); // suppress checking for nan for this test x[0] = 0.4; y = f.Forward(0, x); ok &= f.compare_change_number() == 1; // // ----------------------------------------------------------------------- // Convert to Graph graph and back again f.to_graph(graph_obj); // std::cout << graph; f.from_graph(graph_obj); // ----------------------------------------------------------------------- // // compute y = f(x, p) f.new_dynamic(p); x[0] = 0.2; y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // case where comparison is false x[0] = 0.4; y = f.Forward(0, x); ok &= f.compare_change_number() == 1; // return ok; } // END C++ ================================================ FILE: example/graph/discrete_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin graph_discrete_op.cpp} C++ AD Graph add Operator: Example and Test ########################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end graph_discrete_op.cpp} */ // BEGIN C++ # include namespace { double heaviside(const double &x) { if( x < 0.0 ) return 0.0; if( x == 0.0 ) return 0.5; return 1.0; } CPPAD_DISCRETE_FUNCTION(double, heaviside) } bool discrete_op(void) { bool ok = true; using std::string; // // The discrete function does not exist until its AD version is called heaviside( CppAD::AD( 0.0 ) ); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : heaviside(p[0]) // node_4 : heaviside(p[1]) // node_5 = heaviside(p[0]) + heaviside(p[1]) // y[0] = heaviside(p[0]) + heaviside(p[1]) // // C++ graph object CppAD::cpp_graph graph_obj; // // operator being used CppAD::graph::graph_op_enum op_enum; // // set scalars graph_obj.function_name_set("discrete_op example"); size_t n_dynamic_ind = 1; graph_obj.n_dynamic_ind_set(n_dynamic_ind); size_t n_variable_ind = 1; graph_obj.n_variable_ind_set(n_variable_ind); // // name_index corresponding to heaviside function size_t name_index = graph_obj.discrete_name_vec_size(); graph_obj.discrete_name_vec_push_back("heaviside"); // // heaviside(p[0]) op_enum = CppAD::graph::discrete_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(name_index); // name_index graph_obj.operator_arg_push_back(1); // node arg // // heaviside(x[0]) graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(name_index); // name_index graph_obj.operator_arg_push_back(2); // node arg // // heaviside(p[0]) + heaviside(x[0]) op_enum = CppAD::graph::add_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(3); // first node arg graph_obj.operator_arg_push_back(4); // second node arg // // y[0] = heaviside(p[0]) + heaviside(x[0]) graph_obj.dependent_vec_push_back(5); // // f(x, p) = heaviside(p[0]) + heaviside(x[0]) CppAD::ADFun f; f.from_graph(graph_obj); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // set independent variables and parameters CPPAD_TESTVECTOR(double) p(1), x(1); p[0] = 0.0; x[0] = 2.0; // // compute y = f(x, p) f.new_dynamic(p); CPPAD_TESTVECTOR(double) y = f.Forward(0, x); // // check result ok &= y[0] == heaviside(p[0]) + heaviside(x[0]); // ----------------------------------------------------------------------- // Convert function to graph and back again f.to_graph(graph_obj); f.from_graph(graph_obj); // ----------------------------------------------------------------------- ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // set independent variables and parameters p[0] = -2.0; x[0] = 2.0; // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= y[0] == heaviside(p[0]) + heaviside(x[0]); // return ok; } // END C++ ================================================ FILE: example/graph/div_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin graph_div_op.cpp} C++ AD Graph div Operator: Example and Test ########################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end graph_div_op.cpp} */ // BEGIN C++ # include bool div_op(void) { bool ok = true; using std::string; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : p[1] // node_3 : x[0] // node_4 : p[0] / p[1] // node_5 : x[0] / ( p[1] / p[0] ) // y[0] = p[0] / p[1] // y[1] = x[0] / ( p[0] / p[1] ) // // // C++ graph object CppAD::cpp_graph graph_obj; // // operator being used CppAD::graph::graph_op_enum op_enum; // // set scalars graph_obj.function_name_set("div example"); size_t n_dynamic_ind = 2; graph_obj.n_dynamic_ind_set(n_dynamic_ind); size_t n_variable_ind = 1; graph_obj.n_variable_ind_set(n_variable_ind); // // node_4 : p[0] / p[1] op_enum = CppAD::graph::div_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(1); graph_obj.operator_arg_push_back(2); // // node_5 : x[0] / ( p[1] / p[0] ) graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(3); graph_obj.operator_arg_push_back(4); // // y[0] = p[0] / p[1] // y[1] = x[0] / ( p[0] / p[1] ) graph_obj.dependent_vec_push_back(4); graph_obj.dependent_vec_push_back(5); // // f(x, p) = [ p_0 / p_1 , x_0 * p_1 / p_0 ] CppAD::ADFun f; f.from_graph(graph_obj); // ok &= f.Domain() == 1; ok &= f.Range() == 2; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters CPPAD_TESTVECTOR(double) p(2), x(1); p[0] = 2.0; p[1] = 3.0; x[0] = 4.0; // // compute y = f(x, p) f.new_dynamic(p); CPPAD_TESTVECTOR(double) y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0] , p[0] / p[1] , eps99, eps99 ); ok &= CppAD::NearEqual(y[1] , x[0] / ( p[0] / p[1] ), eps99, eps99 ); // ----------------------------------------------------------------------- // Convert to Graph graph and back again f.to_graph(graph_obj); // std::cout << "json = " << json; f.from_graph(graph_obj); // ----------------------------------------------------------------------- ok &= f.Domain() == 1; ok &= f.Range() == 2; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters p[0] = 2.0; p[1] = 3.0; x[0] = 4.0; // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0] , p[0] / p[1] , eps99, eps99 ); ok &= CppAD::NearEqual(y[1] , x[0] / ( p[0] / p[1] ), eps99, eps99 ); // return ok; } // END C++ ================================================ FILE: example/graph/graph.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin graph.cpp} graph Examples and Tests Driver ############################### Running These Tests ******************* After executing the :ref:`cmake-name` command form the :ref:`download@Distribution Directory`, you can build and run these tests with the commands:: cd build make check_example_graph Note that your choice of :ref:`cmake@generator` may require using an different version of make; e.g., ``ninja`` . {xrst_literal // BEGIN C++ // END C++ } {xrst_end graph.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ // CPPAD_HAS_* defines # include // for thread_alloc # include // test runner # include // BEGIN_SORT_THIS_LINE_PLUS_2 // external compiled tests extern bool add_op(void); extern bool atom4_op(void); extern bool atom_op(void); extern bool azmul_op(void); extern bool cexp_op(void); extern bool comp_op(void); extern bool discrete_op(void); extern bool div_op(void); extern bool mul_op(void); extern bool pow_op(void); extern bool print_graph(void); extern bool print_op(void); extern bool sub_op(void); extern bool sum_op(void); extern bool switch_var_dyn(void); extern bool unary_op(void); // END_SORT_THIS_LINE_MINUS_1 // main program that runs all the tests int main(void) { std::string group = "example/graph"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // BEGIN_SORT_THIS_LINE_PLUS_2 // external compiled tests Run( add_op, "add_op" ); Run( atom4_op, "atom4_op" ); Run( atom_op, "atom_op" ); Run( azmul_op, "azmul_op" ); Run( cexp_op, "cexp_op" ); Run( comp_op, "comp_op" ); Run( discrete_op, "discrete_op" ); Run( div_op, "div_op" ); Run( mul_op, "mul_op" ); Run( pow_op, "pow_op" ); Run( print_graph, "print_graph" ); Run( print_op, "print_op" ); Run( sub_op, "sub_op" ); Run( sum_op, "sum_op" ); Run( switch_var_dyn, "switch_var_dyn" ); Run( unary_op, "unary_op" ); // END_SORT_THIS_LINE_MINUS_1 // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } // END C++ ================================================ FILE: example/graph/mul_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin graph_mul_op.cpp} C++ AD Graph mul Operator: Example and Test ########################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end graph_mul_op.cpp} */ // BEGIN C++ # include bool mul_op(void) { bool ok = true; using std::string; // // AD graph example // node_1 : p[0] // node_2 : p[1] // node_3 : x[0] // node_4 : p[0] * p[1] // node_5 : x[0] * p[0] * p[1] // y[0] = x[0] * p[0] * p[1] // // C++ graph object CppAD::cpp_graph graph_obj; // // operator being used CppAD::graph::graph_op_enum op_enum; // // set scalars graph_obj.function_name_set("mul_op example"); size_t n_dynamic_ind = 2; graph_obj.n_dynamic_ind_set(n_dynamic_ind); size_t n_variable_ind = 1; graph_obj.n_variable_ind_set(n_variable_ind); // // node_4 : p[0] * p[1] op_enum = CppAD::graph::mul_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(1); graph_obj.operator_arg_push_back(2); // // node_5 : x[0] * p[0] * p[1] graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(3); graph_obj.operator_arg_push_back(4); // // y[0] = x[0] * p[0] * p[1] graph_obj.dependent_vec_push_back(5); // // f(x, p) = x_0 * p_0 * p_1 CppAD::ADFun f; f.from_graph(graph_obj); // ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters CPPAD_TESTVECTOR(double) p(2), x(1); p[0] = 2.0; p[1] = 3.0; x[0] = 4.0; // // compute y = f(x, p) f.new_dynamic(p); CPPAD_TESTVECTOR(double) y = f.Forward(0, x); // // check result ok &= y[0] == x[0] * p[0] * p[1]; // ----------------------------------------------------------------------- // Convert to Graph graph and back again f.to_graph(graph_obj); // std::cout << "json = " << json; f.from_graph(graph_obj); // ----------------------------------------------------------------------- ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters p[0] = 2.0; p[1] = 3.0; x[0] = 4.0; // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= y[0] == x[0] * p[0] * p[1]; // return ok; } // END C++ ================================================ FILE: example/graph/pow_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin graph_pow_op.cpp} C++ AD Graph pow Operator: Example and Test ########################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end graph_pow_op.cpp} */ // BEGIN C++ # include bool pow_op(void) { bool ok = true; using std::string; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : p[1] // node_3 : x[0] // node_4 : pow(p[0] , p[1]) // node_5 : pow(x[0] , p[1]) // y[0] = pow(p[0] , p[1]) // y[1] = pow(x[0] , p[1]) // // C++ graph object CppAD::cpp_graph graph_obj; // // operator being used CppAD::graph::graph_op_enum op_enum; // // set scalars graph_obj.function_name_set("pow_op example"); size_t n_dynamic_ind = 2; graph_obj.n_dynamic_ind_set(n_dynamic_ind); size_t n_variable_ind = 1; graph_obj.n_variable_ind_set(n_variable_ind); // // node_4 : pow(p[0], p[1]) op_enum = CppAD::graph::pow_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(1); graph_obj.operator_arg_push_back(2); // // node_5 : pow(x[0], p[1]) graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(3); graph_obj.operator_arg_push_back(2); // // y[0] = pow(p[0], p[1]) // y[1] = pow(x[0], x[1]) graph_obj.dependent_vec_push_back(4); graph_obj.dependent_vec_push_back(5); // // f(x, p) = y CppAD::ADFun f; f.from_graph(graph_obj); ok &= f.Domain() == 1; ok &= f.Range() == 2; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters CPPAD_TESTVECTOR(double) p(2), x(1); p[0] = 2.0; p[1] = 3.0; x[0] = 4.0; // // compute y = f(x, p) f.new_dynamic(p); CPPAD_TESTVECTOR(double) y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], pow(p[0], p[1]), eps99, eps99); ok &= CppAD::NearEqual(y[1], pow(x[0], p[1]), eps99, eps99); // ----------------------------------------------------------------------- // Convert function to graph and back again f.to_graph(graph_obj); f.from_graph(graph_obj); // ----------------------------------------------------------------------- ok &= f.Domain() == 1; ok &= f.Range() == 2; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters p[0] = 3.0; p[1] = 4.0; x[0] = 5.0; // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], pow(p[0], p[1]), eps99, eps99); ok &= CppAD::NearEqual(y[1], pow(x[0], p[1]), eps99, eps99); // return ok; } // END C++ ================================================ FILE: example/graph/print_graph.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin print_graph.cpp} Print a C++ AD Graph: Example and Test ###################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end print_graph.cpp} */ // BEGIN C++ # include bool print_graph(void) { bool ok = true; using std::string; // // AD graph example // node_1 : p[0] // node_2 : p[1] // node_3 : x[0] // node_4 : p[0] + p[1] // node_5 : x[0] + ( p[0] + p[1] ) // y[0] = x[0] + ( p[0] + p[1] ) // // C++ graph object CppAD::cpp_graph graph_obj; // // operator being used CppAD::graph::graph_op_enum op_enum; // // set scalars graph_obj.function_name_set("print_graph example"); size_t n_dynamic_ind = 2; graph_obj.n_dynamic_ind_set(n_dynamic_ind); size_t n_variable_ind = 1; graph_obj.n_variable_ind_set(n_variable_ind); // // node_4 : p[0] + p[1] op_enum = CppAD::graph::add_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(1); graph_obj.operator_arg_push_back(2); // // node_5 : x[0] + ( p[0] + p[1] ) graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(3); graph_obj.operator_arg_push_back(4); // // y[0] = x[0] + ( p[0] + p[1] ) graph_obj.dependent_vec_push_back(5); // // get output of print command std::stringstream os; graph_obj.print(os); // std::string check = "print_graph example\n" " 1 p[0]\n" " 2 p[1]\n" " 3 x[0]\n" " 4 add 1 2\n" " 5 add 3 4\n" "y nodes = 5\n" ; std::string str = os.str(); ok &= str == check; // return ok; } // END C++ ================================================ FILE: example/graph/print_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin graph_print_op.cpp} C++ AD Graph print Operator: Example and Test ############################################# Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end graph_print_op.cpp} */ // BEGIN C++ # include bool print_op(void) { bool ok = true; using std::string; std::stringstream stream_out; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // : print(p[0], "p[0] = ", p[0], "\n") // : print(x[0], "x[0] = ", x[0], "\n") // node_3 : log(p[0]) // node_4 : log(x[0]) // node_5 = log(p[0]) + log(x[0]) // y[0] = log(p[0]) + log(x[0]) // // C++ graph object CppAD::cpp_graph graph_obj; // // operator being used CppAD::graph::graph_op_enum op_enum; // // set scalars graph_obj.function_name_set("print_op example"); size_t n_dynamic_ind = 1; graph_obj.n_dynamic_ind_set(n_dynamic_ind); size_t n_variable_ind = 1; graph_obj.n_variable_ind_set(n_variable_ind); // // if p[0] <= 0: print "p[0] = ", p[0], "\n" op_enum = CppAD::graph::print_graph_op; graph_obj.operator_vec_push_back(op_enum); size_t before = graph_obj.print_text_vec_size(); graph_obj.print_text_vec_push_back("p[0] = "); graph_obj.operator_arg_push_back(before); size_t after = graph_obj.print_text_vec_size(); graph_obj.print_text_vec_push_back("\n"); graph_obj.operator_arg_push_back(after); graph_obj.operator_arg_push_back(1); graph_obj.operator_arg_push_back(1); // // if x[0] <= 0: print "x[0] = ", x[0], "\n" graph_obj.operator_vec_push_back(op_enum); before = graph_obj.print_text_vec_size(); graph_obj.print_text_vec_push_back("x[0] = "); graph_obj.operator_arg_push_back(before); graph_obj.operator_arg_push_back(after); graph_obj.operator_arg_push_back(2); graph_obj.operator_arg_push_back(2); // // node_3 : log(p[0]) op_enum = CppAD::graph::log_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(1); // // node_4 : log(x[0]) graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(2); // // node_5: log(p[0]) + log(x[0]) op_enum = CppAD::graph::add_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(3); graph_obj.operator_arg_push_back(4); // // y[0] = log(p[0]) + log(x[0]) graph_obj.dependent_vec_push_back(5); // // f(x, p) = log(x) + log(p) CppAD::ADFun f; f.from_graph(graph_obj); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // set independent variables and parameters CPPAD_TESTVECTOR(double) p(1), x(1); p[0] = 1.0; x[0] = 2.0; // // compute y = f(x, p) f.new_dynamic(p); CPPAD_TESTVECTOR(double) y = f.Forward(0, x, stream_out); // // check result ok &= stream_out.str() == ""; double check = std::log(p[0]) + std::log(x[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // ----------------------------------------------------------------------- // Convert function to graph and back again f.to_graph(graph_obj); f.from_graph(graph_obj); // ----------------------------------------------------------------------- ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // set independent variables and parameters p[0] = 1.0; x[0] = -2.0; // // compute y = f(x, p) f.new_dynamic(p); f.check_for_nan(false); y = f.Forward(0, x, stream_out); // // check result ok &= stream_out.str() == "x[0] = -2\n"; ok &= std::isnan(y[0]); // return ok; } // END C++ ================================================ FILE: example/graph/sub_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin graph_sub_op.cpp} C++ AD Graph sub Operator: Example and Test ########################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end graph_sub_op.cpp} */ // BEGIN C++ # include bool sub_op(void) { bool ok = true; using std::string; // // AD graph example // node_1 : p[0] // node_2 : p[1] // node_3 : x[0] // node_4 : p[0] - p[1] // node_5 : x[0] - ( p[0] - p[1] ) // y[0] = x[0] - ( p[0] - p[1] ) // // C++ graph object CppAD::cpp_graph graph_obj; // // operator being used CppAD::graph::graph_op_enum op_enum; // // set scalars graph_obj.function_name_set("sub_op example"); size_t n_dynamic_ind = 2; graph_obj.n_dynamic_ind_set(n_dynamic_ind); size_t n_variable_ind = 1; graph_obj.n_variable_ind_set(n_variable_ind); // // node_4 : p[0] - p[1] op_enum = CppAD::graph::sub_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(1); graph_obj.operator_arg_push_back(2); // // node_5 : x[0] - ( p[0] - p[1] ) graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(3); graph_obj.operator_arg_push_back(4); // // y[0] = x[0] - ( p[0] - p[1] ) graph_obj.dependent_vec_push_back(5); // // f(x, p) = x_0 - ( p_0 - p_1 ) CppAD::ADFun f; f.from_graph(graph_obj); // ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters CPPAD_TESTVECTOR(double) p(2), x(1); p[0] = 2.0; p[1] = 3.0; x[0] = 4.0; // // compute y = f(x, p) f.new_dynamic(p); CPPAD_TESTVECTOR(double) y = f.Forward(0, x); // // check result ok &= y[0] == x[0] - ( p[0] - p[1] ); // ----------------------------------------------------------------------- // Convert to Graph graph and back again f.to_graph(graph_obj); // std::cout << "json = " << json; f.from_graph(graph_obj); // ----------------------------------------------------------------------- ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters p[0] = 2.0; p[1] = 3.0; x[0] = 4.0; // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= y[0] == x[0] - ( p[0] - p[1] ); // return ok; } // END C++ ================================================ FILE: example/graph/sum_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin graph_sum_op.cpp} C++ AD Graph sum Operator: Example and Test ########################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end graph_sum_op.cpp} */ // BEGIN C++ # include bool sum_op(void) { bool ok = true; using std::string; // // AD graph example // node_1 : p[0] // node_2 : p[1] // node_3 : p[2] // node_4 : x[0] // node_5 : p[0] + p[1] + p[2] // node_6 : x[0] + p[0] + p[1] + p[2] // y[0] = x[0] + p[0] + p[1] + p[2] // // // C++ graph object CppAD::cpp_graph graph_obj; // // operator being used CppAD::graph::graph_op_enum op_enum; // // set scalars graph_obj.function_name_set("sum_op example"); size_t n_dynamic_ind = 3; graph_obj.n_dynamic_ind_set(n_dynamic_ind); size_t n_variable_ind = 1; graph_obj.n_variable_ind_set(n_variable_ind); // // node_5 : p[0] + p[1] + p[2] // op_enum = CppAD::graph::sum_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(3); // n_node_arg graph_obj.operator_arg_push_back(1); // first node arg graph_obj.operator_arg_push_back(2); // second node arg graph_obj.operator_arg_push_back(3); // third node are // // node_6 : x[0] + p[0] + p[1] + p[2] // // n_arg comes before first_node graph_obj.operator_arg_push_back(2); graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(4); graph_obj.operator_arg_push_back(5); // // y[0] = x[0] + p[0] + p[1] + p[2] graph_obj.dependent_vec_push_back(6); // // f(x, p) = x_0 + p_0 + p_1 + p_2 CppAD::ADFun f; f.from_graph(graph_obj); // ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 3; // // set independent variables and parameters CPPAD_TESTVECTOR(double) p(3), x(1); for(size_t j = 0; j < 3; ++j) p[j] = double(j + 1); x[0] = 5.0; // // compute y = f(x, p) f.new_dynamic(p); CPPAD_TESTVECTOR(double) y = f.Forward(0, x); // // check result ok &= y[0] == x[0] + p[0] + p[1] + p[2]; // ----------------------------------------------------------------------- // Convert to Graph graph and back again f.to_graph(graph_obj); // std::cout << "json = " << json; f.from_graph(graph_obj); // ----------------------------------------------------------------------- ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 3; // // set independent variables and parameters for(size_t j = 0; j < 3; ++j) p[j] = double(j + 1); x[0] = 5.0; // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= y[0] == x[0] + p[0] + p[1] + p[2]; // return ok; } // END C++ ================================================ FILE: example/graph/switch_var_dyn.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin switch_var_dyn.cpp} Switching Between Variables and Dynamic Parameters: Example and Test #################################################################### Function ******** For each :ref:`ADFun-name` object there is a corresponding function :math:`f(x, p)` where :ref:`independent@x` is the vector of independent variables and *p* is the vector of independent :ref:`Independent@dynamic` parameters. Convert a Function to a Graph ***************************** The :ref:`to_graph-name` routine can be used to convert a ``ADFun`` to a graph representation; see :ref:`cpp_ad_graph-name` . Convert a Graph to a Function ***************************** The :ref:`from_graph-name` routine can be used to convert a graph back to a function. During this conversion, it is possible to change dynamic parameters to variables and variables to dynamic parameters; see :ref:`from_graph@dyn2var` and *var2dyn* in the ``from_graph`` documentation. Note that many such conversions can be done using the same ``cpp_ad_graph`` object. Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end switch_var_dyn.cpp} */ // BEGIN C++ # include bool switch_var_dyn(void) { bool ok = true; using std::string; // // f(x_0, x_1, x_2) = y_0 = x_2 * ( x_0 + x_1 ); CPPAD_TESTVECTOR( CppAD::AD ) ax(3), ay(1); for(size_t j = 0; j < 3; ++j) ax[j] = CppAD::AD(j); Independent(ax); ay[0] = ax[2] * ( ax[0] + ax[1] ); CppAD::ADFun f(ax, ay); ok &= f.Domain() == 3; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 0; // // set independent variables and parameters CPPAD_TESTVECTOR(double) p(0), x(3); x[0] = 2.0; x[1] = 3.0; x[2] = 4.0; // // compute y = f(x) f.new_dynamic(p); CPPAD_TESTVECTOR(double) y = f.Forward(0, x); // // check result ok &= y[0] == x[2] * ( x[0] + x[1] ); // ----------------------------------------------------------------------- // // C++ graph object CppAD::cpp_graph graph_obj; f.to_graph(graph_obj); // // change x[0]->p[0], x[1]->p[1], x[2]->x[0] CppAD::vector dyn2var(0), var2dyn(3); var2dyn[0] = true; var2dyn[1] = true; var2dyn[2] = false; f.from_graph(graph_obj, dyn2var, var2dyn); p.resize(2); x.resize(1); // ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters p[0] = 1.0; p[1] = 2.0; x[0] = 3.0; // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= y[0] == x[0] * ( p[0] + p[1] ); // return ok; } // END C++ ================================================ FILE: example/graph/unary_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin graph_unary_op.cpp} Graph Unary Operator: Example and Test ###################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end graph_unary_op.cpp} */ // BEGIN C++ # include bool unary_op(void) { bool ok = true; using std::string; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : sin(p[0]) // node_5 : sin(x[0]) // node_6 : sin(c[0]) // node_7 : sin(p[0]) + sin(x[0]) + sin(c[0]) // y[0] = sin(p[0]) + sin(x[0]) + sin(c[0]) // // C++ graph object CppAD::cpp_graph graph_obj; // // operator being used CppAD::graph::graph_op_enum op_enum; // // set scalars graph_obj.function_name_set("unary_op example"); size_t n_dynamic_ind = 1; graph_obj.n_dynamic_ind_set(n_dynamic_ind); size_t n_variable_ind = 1; graph_obj.n_variable_ind_set(n_variable_ind); graph_obj.constant_vec_push_back( -0.1 ); // // node_4 : sin(p[0]) op_enum = CppAD::graph::sin_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(1); // // node_5 : sin(x[0]) graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(2); // // node_6 : sin(c[0]) graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(3); // // node_7 : sin(p[0]) + sin(x[0]) + sin(c[0]) // // n_arg comes before first_node graph_obj.operator_arg_push_back(3); // op_enum op_enum = CppAD::graph::sum_graph_op; graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(4); graph_obj.operator_arg_push_back(5); graph_obj.operator_arg_push_back(6); // // y[0] = sin(p[0]) + sin(x[0]) + sin(c[0]) graph_obj.dependent_vec_push_back(7); // // f(x, p) = sin(p_0) + sin(x_0) + sin(c_0) CppAD::ADFun f; f.from_graph(graph_obj); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function CPPAD_TESTVECTOR(double) c(1); c[0] = -0.1; // // set independent variables and parameters CPPAD_TESTVECTOR(double) p(1), x(1); p[0] = 0.2; x[0] = 0.3; // // compute y = f(x, p) f.new_dynamic(p); CPPAD_TESTVECTOR(double) y = f.Forward(0, x); // // check result double check = std::sin(p[0]) + std::sin(x[0]) + std::sin(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // ----------------------------------------------------------------------- // Convert to Graph graph and back again f.to_graph(graph_obj); f.from_graph(graph_obj); // ----------------------------------------------------------------------- // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // END C++ ================================================ FILE: example/ipopt_solve/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-23 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the example/ipopt_solve directory tests # Local link directories to search, as determined by pkg-config for ipopt LINK_DIRECTORIES( ${ipopt_LIBRARY_DIRS} ) # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list get_started.cpp ipopt_solve.cpp ode_inverse.cpp retape.cpp ) # END_SORT_THIS_LINE_MINUS_2 set_compile_flags( example_ipopt_solve "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(example_ipopt_solve EXCLUDE_FROM_ALL ${source_list}) # # libraries to be linked into the specified target, # as determined by pkg-config for ipopt TARGET_LINK_LIBRARIES(example_ipopt_solve ${cppad_lib} ${ipopt_LINK_LIBRARIES} ${colpack_libs} ) # check_example_ipopt_solve add_check_executable(check_example ipopt_solve) ================================================ FILE: example/ipopt_solve/get_started.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ipopt_solve_get_started.cpp} {xrst_spell lc } Nonlinear Programming Using CppAD and Ipopt: Example and Test ############################################################# Purpose ******* This example program demonstrates how to use :ref:`ipopt_solve-name` to solve the example problem in the Ipopt documentation; i.e., the problem .. math:: \begin{array}{lc} {\rm minimize \; } & x_1 * x_4 * (x_1 + x_2 + x_3) + x_3 \\ {\rm subject \; to \; } & x_1 * x_2 * x_3 * x_4 \geq 25 \\ & x_1^2 + x_2^2 + x_3^2 + x_4^2 = 40 \\ & 1 \leq x_1, x_2, x_3, x_4 \leq 5 \end{array} Configuration Requirement ************************* This example will be compiled and tested provided :ref:`cmake@include_ipopt` is on the cmake command line. {xrst_literal // BEGIN C++ // END C++ } {xrst_end ipopt_solve_get_started.cpp} */ // BEGIN C++ # include namespace { using CppAD::AD; class FG_eval { public: typedef CPPAD_TESTVECTOR( AD ) ADvector; void operator()(ADvector& fg, const ADvector& x) { assert( fg.size() == 3 ); assert( x.size() == 4 ); // Fortran style indexing AD x1 = x[0]; AD x2 = x[1]; AD x3 = x[2]; AD x4 = x[3]; // f(x) fg[0] = x1 * x4 * (x1 + x2 + x3) + x3; // g_1 (x) fg[1] = x1 * x2 * x3 * x4; // g_2 (x) fg[2] = x1 * x1 + x2 * x2 + x3 * x3 + x4 * x4; // return; } }; } bool get_started(void) { bool ok = true; size_t i; typedef CPPAD_TESTVECTOR( double ) Dvector; // number of independent variables (domain dimension for f and g) size_t nx = 4; // number of constraints (range dimension for g) size_t ng = 2; // initial value of the independent variables Dvector xi(nx); xi[0] = 1.0; xi[1] = 5.0; xi[2] = 5.0; xi[3] = 1.0; // lower and upper limits for x Dvector xl(nx), xu(nx); for(i = 0; i < nx; i++) { xl[i] = 1.0; xu[i] = 5.0; } // lower and upper limits for g Dvector gl(ng), gu(ng); gl[0] = 25.0; gu[0] = 1.0e19; gl[1] = 40.0; gu[1] = 40.0; // object that computes objective and constraints FG_eval fg_eval; // options std::string options; // turn off any printing options += "Integer print_level 0\n"; options += "String sb yes\n"; // maximum number of iterations options += "Integer max_iter 10\n"; // approximate accuracy in first order necessary conditions; // see Mathematical Programming, Volume 106, Number 1, // Pages 25-57, Equation (6) options += "Numeric tol 1e-6\n"; // derivative testing options += "String derivative_test second-order\n"; // maximum amount of random perturbation; e.g., // when evaluation finite diff options += "Numeric point_perturbation_radius 0.\n"; // place to return solution CppAD::ipopt::solve_result solution; // solve the problem CppAD::ipopt::solve( options, xi, xl, xu, gl, gu, fg_eval, solution ); // // Check some of the solution values // ok &= solution.status == CppAD::ipopt::solve_result::success; // double check_x[] = { 1.000000, 4.743000, 3.82115, 1.379408 }; double check_zl[] = { 1.087871, 0., 0., 0. }; double check_zu[] = { 0., 0., 0., 0. }; double rel_tol = 1e-6; // relative tolerance double abs_tol = 1e-6; // absolute tolerance for(i = 0; i < nx; i++) { ok &= CppAD::NearEqual( check_x[i], solution.x[i], rel_tol, abs_tol ); ok &= CppAD::NearEqual( check_zl[i], solution.zl[i], rel_tol, abs_tol ); ok &= CppAD::NearEqual( check_zu[i], solution.zu[i], rel_tol, abs_tol ); } return ok; } // END C++ ================================================ FILE: example/ipopt_solve/ipopt_solve.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ipopt_solve.cpp} ipopt_solve Examples and Tests Driver ##################################### Running These Tests ******************* After executing the :ref:`cmake-name` command form the :ref:`download@Distribution Directory`, you can build and run these tests with the commands:: cd build make check_example_ipopt_solve Note that your choice of :ref:`cmake@generator` may require using an different version of make; e.g., ``ninja`` . {xrst_literal // BEGIN C++ // END C++ } {xrst_end ipopt_solve.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ // ipopt solve include file # include // external complied tests extern bool get_started(void); extern bool ode_inverse(void); extern bool retape(void); // test runner # include // main program that runs all the tests int main(void) { std::string group = "example/ipopt_solve"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // external compiled tests Run( get_started, "get_started" ); Run( ode_inverse, "ode_inverse" ); Run( retape, "retape" ); // // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } // END C++ ================================================ FILE: example/ipopt_solve/ode_inverse.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ipopt_solve_ode_inverse.cpp} {xrst_spell nz } ODE Inverse Problem Definitions: Source Code ############################################ Purpose ******* This example demonstrates how to invert for parameters in a ODE where the solution of the ODE is numerically approximated. Forward Problem *************** We consider the following ordinary differential equation: .. math:: :nowrap: \begin{eqnarray} \partial_t y_0 ( t , a ) & = & - a_1 * y_0 (t, a ) \\ \partial_t y_1 (t , a ) & = & + a_1 * y_0 (t, a ) - a_2 * y_1 (t, a ) \end{eqnarray} with the initial conditions .. math:: y_0 (0 , a) = ( a_0 , 0 )^\R{T} Our forward problem is stated as follows: Given :math:`a \in \B{R}^3` determine the value of :math:`y ( t , a )`, for :math:`t \in R`, that solves the initial value problem above. Measurements ************ Suppose we are also given measurement times :math:`s \in \B{R}^5` and a measurement vector :math:`z \in \B{R}^4` and for :math:`i = 0, \ldots, 3`, we model :math:`z_i` by .. math:: z_i = y_1 ( s_{i+1} , a) + e_i where :math:`e_{i-1} \sim {\bf N} (0 , \sigma^2 )` is the measurement noise, and :math:`\sigma > 0` is the standard deviation of the noise. Simulation Analytic Solution ============================ The following analytic solution to the forward problem is used to simulate a data set: .. math:: :nowrap: \begin{eqnarray} y_0 (t , a) & = & a_0 * \exp( - a_1 * t ) \\ y_1 (t , a) & = & a_0 * a_1 * \frac{\exp( - a_2 * t ) - \exp( -a_1 * t )}{ a_1 - a_2 } \end{eqnarray} Simulation Parameter Values =========================== .. list-table:: :widths: auto * - :math:`\bar{a}_0 = 1` - initial value of :math:`y_0 (t, a)` * - :math:`\bar{a}_1 = 2` - transfer rate from compartment zero to compartment one * - :math:`\bar{a}_2 = 1` - transfer rate from compartment one to outside world * - :math:`\sigma = 0` - standard deviation of measurement noise * - :math:`e_i = 0` - simulated measurement noise, :math:`i = 1 , \ldots , Nz` * - :math:`s_i = i * .5` - time corresponding to the *i*-th measurement, :math:`i = 0 , \ldots , 3` Simulated Measurement Values ============================ The simulated measurement values are given by the equation .. math:: :nowrap: \begin{eqnarray} z_i & = & e_i + y_1 ( s_{i+1} , \bar{a} ) \\ & = & \bar{a}_0 * \bar{a}_1 * \frac{\exp( - \bar{a}_2 * s_i ) - \exp( -\bar{a}_1 * s_i )} { \bar{a}_1 - \bar{a}_2 } \end{eqnarray} for :math:`i = 0, \ldots , 3`. Inverse Problem *************** The maximum likelihood estimate for :math:`a` given :math:`z` solves the following optimization problem .. math:: :nowrap: \begin{eqnarray} {\rm minimize} \; & \sum_{i=0}^3 ( z_i - y_1 ( s_{i+1} , a ) )^2 & \;{\rm w.r.t} \; a \in \B{R}^3 \end{eqnarray} Trapezoidal Approximation ************************* We are given a number of approximation points per measurement interval :math:`np` and define the time grid :math:`t \in \B{R}^{4 \cdot np + 1}` as follows: :math:`t_0 = s_0` and for :math:`i = 0 , 1 , 2, 3`, :math:`j = 1 , \ldots , np` .. math:: t_{i \cdot np + j} = s_i + (s_{i+1} - s{i}) \frac{i}{np} We note that for :math:`i = 1 , \ldots , 4`, :math:`t_{i \cdot np} = s_i`. This example uses a trapezoidal approximation to solve the ODE. Given :math:`a \in \B{R}^3` and :math:`y^{k-1} \approx y( t_{k-1} , a )`, the a trapezoidal method approximates :math:`y ( t_j , a )` by the value :math:`y^k \in \B{R}^2` ) that solves the equation .. math:: y^k = y^{k-1} + \frac{G( y^k , a ) + G( y^{k-1} , a ) }{2} * (t_k - t_{k-1}) where :math:`G : \B{R}^2 \times \B{R}^3 \rightarrow \B{R}^2` is defined by .. math:: :nowrap: \begin{eqnarray} G_0 ( y , a ) & = & - a_1 * y_0 \\ G_1 ( y , a ) & = & + a_1 * y_0 - a_2 * y_1 \end{eqnarray} Solution Method *************** We use constraints to embed the forward problem in the inverse problem. To be specific, we solve the optimization problem .. math:: :nowrap: \begin{eqnarray} {\rm minimize} & \sum_{i=0}^3 ( z_i - y_1^{(i+1) \cdot np} )^2 & \; {\rm w.r.t} \; a \in \B{R}^3 \; y^0 \in \B{R}^2 , \ldots , y^{3 \cdot np -1} \in \B{R}^2 \\ {\rm subject \; to} 0 = y^0 - ( a_0 , 0 )^\R{T} \\ & 0 = y^k - y^{k-1} - \frac{G( y^k , a ) + G( y^{k-1} , a ) }{2} (t_k - t_{k-1}) & \; {\rm for} \; k = 1 , \ldots , 4 \cdot np \end{eqnarray} The code below we using the notation :math:`x \in \B{3 + (4 \cdot np + 1) \cdot 2}` defined by .. math:: x = \left( a_0, a_1, a_2 , y_0^0, y_1^0, \ldots , y_0^{4 \cdot np}, y_1^{4 \cdots np} \right) Source ****** The following source code implements the ODE inversion method proposed above: {xrst_literal // BEGIN C++ // END C++ } {xrst_end ipopt_solve_ode_inverse.cpp} ------------------------------------------------------------------------------ */ // BEGIN C++ # include namespace { using CppAD::AD; // value of a during simulation a[0], a[1], a[2] double a_[] = {2.0, 1.0, 0.5}; // number of components in a size_t na_ = sizeof(a_) / sizeof(a_[0]); // function used to simulate data double yone(double t) { return a_[0]*a_[1] * (exp(-a_[2]*t) - exp(-a_[1]*t)) / (a_[1] - a_[2]); } // time points were we have data (no data at first point) double s_[] = {0.0, 0.5, 1.0, 1.5, 2.0 }; // Simulated data for case with no noise (first point is not used) double z_[] = {yone(s_[1]), yone(s_[2]), yone(s_[3]), yone(s_[4])}; size_t nz_ = sizeof(z_) / sizeof(z_[0]); // number of trapozoidal approximation points per measurement interval size_t np_ = 40; class FG_eval { private: public: // derived class part of constructor typedef CPPAD_TESTVECTOR( AD ) ADvector; // Evaluation of the objective f(x), and constraints g(x) void operator()(ADvector& fg, const ADvector& x) { CPPAD_TESTVECTOR( AD ) a(na_); size_t i, j, k; // extract the vector a for(i = 0; i < na_; i++) a[i] = x[i]; // compute the object f(x) fg[0] = 0.0; for(i = 0; i < nz_; i++) { k = (i + 1) * np_; AD y_1 = x[na_ + 2 * k + 1]; AD dif = z_[i] - y_1; fg[0] += dif * dif; } // constraint corresponding to initial value y(0, a) // Note that this constraint is invariant with size of dt fg[1] = x[na_+0] - a[0]; fg[2] = x[na_+1] - 0.0; // constraints corresponding to trapozoidal approximation for(i = 0; i < nz_; i++) { // spacing between grid point double dt = (s_[i+1] - s_[i]) / static_cast(np_); for(j = 1; j <= np_; j++) { k = i * np_ + j; // compute derivative at y^k AD y_0 = x[na_ + 2 * k + 0]; AD y_1 = x[na_ + 2 * k + 1]; AD G_0 = - a[1] * y_0; AD G_1 = + a[1] * y_0 - a[2] * y_1; // compute derivative at y^{k-1} AD ym_0 = x[na_ + 2 * (k-1) + 0]; AD ym_1 = x[na_ + 2 * (k-1) + 1]; AD Gm_0 = - a[1] * ym_0; AD Gm_1 = + a[1] * ym_0 - a[2] * ym_1; // constraint should be zero fg[1 + 2*k ] = y_0 - ym_0 - dt*(G_0 + Gm_0)/2.; fg[2 + 2*k ] = y_1 - ym_1 - dt*(G_1 + Gm_1)/2.; // scale g(x) so it has similar size as f(x) fg[1 + 2*k ] /= dt; fg[2 + 2*k ] /= dt; } } } }; } bool ode_inverse(void) { bool ok = true; size_t i; typedef CPPAD_TESTVECTOR( double ) Dvector; // number of components in the function g size_t ng = (np_ * nz_ + 1) * 2; // number of independent variables size_t nx = na_ + ng; // initial value for the variables we are optimizing w.r.t Dvector xi(nx), xl(nx), xu(nx); for(i = 0; i < nx; i++) { xi[i] = 0.0; // initial value xl[i] = -1e19; // no lower limit xu[i] = +1e19; // no upper limit } for(i = 0; i < na_; i++) xi[0] = 1.5; // initial value for a // all the difference equations are constrained to be zero Dvector gl(ng), gu(ng); for(i = 0; i < ng; i++) { gl[i] = 0.0; gu[i] = 0.0; } // object defining both f(x) and g(x) FG_eval fg_eval; // options std::string options; // Use sparse matrices for calculation of Jacobians and Hessians // with forward mode for Jacobian (seems to be faster for this case). options += "Sparse true forward\n"; // turn off any printing options += "Integer print_level 0\n"; options += "String sb yes\n"; // maximum number of iterations options += "Integer max_iter 30\n"; // approximate accuracy in first order necessary conditions; // see Mathematical Programming, Volume 106, Number 1, // Pages 25-57, Equation (6) options += "Numeric tol 1e-6\n"; // place to return solution CppAD::ipopt::solve_result solution; // solve the problem CppAD::ipopt::solve( options, xi, xl, xu, gl, gu, fg_eval, solution ); // // Check some of the solution values // ok &= solution.status == CppAD::ipopt::solve_result::success; // double rel_tol = 1e-4; // relative tolerance double abs_tol = 1e-4; // absolute tolerance for(i = 0; i < na_; i++) ok &= CppAD::NearEqual( a_[i], solution.x[i], rel_tol, abs_tol); return ok; } // END C++ ================================================ FILE: example/ipopt_solve/retape.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ipopt_solve_retape.cpp} {xrst_spell retaping } Nonlinear Programming Retaping: Example and Test ################################################ Purpose ******* This example program demonstrates a case were the ``ipopt::solve`` argument :ref:`ipopt_solve@options@Retape` should be true. {xrst_literal // BEGIN C++ // END C++ } {xrst_end ipopt_solve_retape.cpp} */ // BEGIN C++ # include namespace { using CppAD::AD; class FG_eval { public: typedef CPPAD_TESTVECTOR( AD ) ADvector; void operator()(ADvector& fg, const ADvector& x) { assert( fg.size() == 1 ); assert( x.size() == 1 ); // compute the Huber function using a conditional // statement that depends on the value of x. double eps = 0.1; if( fabs(x[0]) <= eps ) fg[0] = x[0] * x[0] / (2.0 * eps); else fg[0] = fabs(x[0]) - eps / 2.0; return; } }; } bool retape(void) { bool ok = true; typedef CPPAD_TESTVECTOR( double ) Dvector; // number of independent variables (domain dimension for f and g) size_t nx = 1; // number of constraints (range dimension for g) size_t ng = 0; // initial value, lower and upper limits, for the independent variables Dvector xi(nx), xl(nx), xu(nx); xi[0] = 2.0; xl[0] = -1e+19; xu[0] = +1e+19; // lower and upper limits for g Dvector gl(ng), gu(ng); // object that computes objective and constraints FG_eval fg_eval; // options std::string options; // retape operation sequence for each new x options += "Retape true\n"; // turn off any printing options += "Integer print_level 0\n"; options += "String sb yes\n"; // maximum number of iterations options += "Integer max_iter 10\n"; // approximate accuracy in first order necessary conditions; // see Mathematical Programming, Volume 106, Number 1, // Pages 25-57, Equation (6) options += "Numeric tol 1e-9\n"; // derivative testing options += "String derivative_test second-order\n"; // maximum amount of random perturbation; e.g., // when evaluation finite diff options += "Numeric point_perturbation_radius 0.\n"; // place to return solution CppAD::ipopt::solve_result solution; // solve the problem CppAD::ipopt::solve( options, xi, xl, xu, gl, gu, fg_eval, solution ); // // Check some of the solution values // ok &= solution.status == CppAD::ipopt::solve_result::success; double rel_tol = 1e-6; // relative tolerance double abs_tol = 1e-6; // absolute tolerance ok &= CppAD::NearEqual( solution.x[0], 0.0, rel_tol, abs_tol); return ok; } // END C++ ================================================ FILE: example/ipopt_solve/test.sh.in ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- export LD_LIBRARY_PATH=@CPPAD_IPOPT_LD_PATH@ ./solve ================================================ FILE: example/jit/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-23 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the example/git directory tests # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list atomic.cpp compare_change.cpp compile.cpp dynamic.cpp get_started.cpp jit.cpp ) # END_SORT_THIS_LINE_MINUS_2 # set_compile_flags( example_jit "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(example_jit EXCLUDE_FROM_ALL ${source_list}) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_jit ${cppad_lib} ${colpack_libs} ) # # check_example_jit add_check_executable(check_example jit) ================================================ FILE: example/jit/atomic.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin jit_atomic.cpp} Atomic Callbacks in JIT Function: Example and Test ################################################## Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end jit_atomic.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include # include # if CPPAD_USE_CPLUSPLUS_2017 # include # endif // DLL_EXT # ifndef _WIN32 # define DLL_EXT ".so" # else # define DLL_EXT ".dll" # endif // ---------------------------------------------------------------------------- namespace { // BEGIN_EMPTY_NAMESPACE // // write_c_file std::string write_c_file(size_t index, const std::string& csrc) { // std::string file_name = "atomic_" + CppAD::to_string(index) + ".c"; // // write file_name std::ofstream os; os.open(file_name, std::ios::out); os << csrc; os.close(); // return file_name; } // // atomic_fun class atomic_fun : public CppAD::atomic_four { private: const std::string name_; public: atomic_fun(const std::string& name) : CppAD::atomic_four(name), name_(name) {} private: bool for_type( size_t call_id , const CppAD::vector& type_x , CppAD::vector& type_y ) override { type_y[0] = type_x[0]; return true; } // forward double bool forward( size_t call_id , const CppAD::vector& select_y , size_t order_low , size_t order_up , const CppAD::vector& taylor_x , CppAD::vector& taylor_y ) override { if( order_up != 0 ) return false;; taylor_y[0] = 1.0 / taylor_x[0]; return true; } public: // forward_zero std::string forward_zero(void) { std::string csrc = "# include \n" "int cppad_atomic_" + name_ + "(\n"; csrc +=R"_( size_t call_id, size_t nx, const double* x, size_t ny, double* y, size_t* compare_change) { if( nx != 1 ) return 1; if( ny != 1 ) return 2; y[0] = 1.0 / x[0]; return 0; } )_"; return csrc; } }; } // END_EMPTY_NAMESPACE // --------------------------------------------------------------------------- bool atomic(void) { // ok bool ok = true; // // AD using CppAD::AD; // // atomic_name std::string atomic_name = "reciprocal"; // // reciprocal atomic_fun reciprocal(atomic_name); // // nx, ax size_t nx = 2; CPPAD_TESTVECTOR( AD ) ax(nx); double x0 = 0.5, x1 = 4.0; ax[0] = x0; ax[1] = x1; CppAD::Independent(ax); // // ny, ay size_t ny = nx; CPPAD_TESTVECTOR( AD ) ay(ny); CPPAD_TESTVECTOR( AD ) atom_x(1), atom_y(1); for(size_t j = 0; j < nx; ++j) { atom_x[0] = ax[j]; reciprocal(atom_x, atom_y); ay[j] = atom_y[0]; } // // function_name std::string function_name = "use_reciprocal"; // // f CppAD::ADFun f(ax, ay); f.function_name_set(function_name); // // dll_file std::string dll_file = "atomic" DLL_EXT; // // csrc_files CppAD::vector csrc_files(2); csrc_files[0] = write_c_file(0, reciprocal.forward_zero() ); std::string c_type = "double"; std::stringstream ss; f.to_csrc(ss, c_type); csrc_files[1] = write_c_file(1, ss.str() ); // // create_dll_lib std::map< std::string, std::string > options; std::string err_msg = CppAD::create_dll_lib(dll_file, csrc_files, options); if( err_msg != "" ) { std::cout << "jit_atomic: " << err_msg << "\n"; ok = false; return ok; } // // dll_linker CppAD::link_dll_lib dll_linker(dll_file, err_msg); // // jit_double using CppAD::jit_double; // // jit_function jit_double jit_function = nullptr; if( err_msg != "" ) { std::cout << "jit_atomic: " << err_msg << "\n"; ok = false; } else { // jit_function std::string complete_name = "cppad_jit_" + function_name; jit_function = reinterpret_cast( dll_linker(complete_name, err_msg) ); if( err_msg != "" ) { std::cout << "jit_atomic: " << err_msg << "\n"; ok = false; } } if( ok ) { // // ok // no change CppAD::vector x(nx), y(ny); x[0] = x0; x[1] = x1; for(size_t i = 0; i < ny; ++i) y[i] = std::numeric_limits::quiet_NaN(); size_t compare_change = 0; int flag = jit_function( nx, x.data(), ny, y.data(), &compare_change ); ok &= flag == 0; ok &= compare_change == 0; for(size_t i = 0; i < ny; ++i) ok &= y[i] == 1.0 / x[i]; } return ok; } // END C++ ================================================ FILE: example/jit/compare_change.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin jit_compare_change.cpp} The JIT compare_change Argument: Example and Test ################################################# Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end jit_compare_change.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include # include # include # include // DLL_EXT # ifdef _WIN32 # define DLL_EXT ".dll" # else # define DLL_EXT ".so" # endif # include bool compare_change(void) { bool ok = true; // using CppAD::AD; using CppAD::ADFun; using CppAD::Independent; using CppAD::NearEqual; // // nx, ny size_t nx = 2, ny = 1; // // f(x) = x_0 + x_1 CPPAD_TESTVECTOR( AD ) ax(nx), ay(ny); ax[0] = 0.0; ax[1] = 1.0; Independent(ax); if( ax[0] < ax[1] ) ay[0] = ax[1] - ax[0]; else ay[0] = ax[0] - ax[1]; ADFun f(ax, ay); f.function_name_set("f"); // // csrc_file // created in std::filesystem::current_path std::string c_type = "double"; std::string csrc_file = "compare_change.c"; std::ofstream ofs; ofs.open(csrc_file , std::ofstream::out); f.to_csrc(ofs, c_type); ofs.close(); // // dll_file // created in std::filesystem::current_path std::string dll_file = "jit_to_csrc" DLL_EXT; CPPAD_TESTVECTOR( std::string) csrc_files(1); csrc_files[0] = csrc_file; std::map< std::string, std::string > options; std::string err_msg = CppAD::create_dll_lib(dll_file, csrc_files, options); if( err_msg != "" ) { std::cerr << "jit_to_csrc: err_msg = " << err_msg << "\n"; return false; } // dll_linker CppAD::link_dll_lib dll_linker(dll_file, err_msg); if( err_msg != "" ) { std::cerr << "jit_to_csrc: err_msg = " << err_msg << "\n"; return false; } // // void_ptr std::string function_name = "cppad_jit_f"; void* void_ptr = dll_linker(function_name, err_msg); if( err_msg != "" ) { std::cerr << "jit_to_csrc: err_msg = " << err_msg << "\n"; return false; } // // jit_double using CppAD::jit_double; // // f_ptr jit_double f_ptr = reinterpret_cast(void_ptr); // // x, y, compare_change // y = f(x) size_t compare_change = 0; std::vector x(nx), y(ny); x[0] = 0.1; x[1] = 0.2; f_ptr(nx, x.data(), ny, y.data(), &compare_change); // // ok // comparison same as when recorded (compare_change the same) ok &= compare_change == 0; // // ok double eps99 = 99.0 * std::numeric_limits::epsilon(); double check = x[1] - x[0]; ok &= NearEqual(y[0], check, eps99, eps99); // // x, y, compare_change x[0] = 0.5; x[1] = 0.1; f_ptr(nx, x.data(), ny, y.data(), &compare_change); // // ok // comparison different from when recorded (compare_change increased) ok &= compare_change == 1; // // ok check = x[1] - x[0]; ok &= NearEqual(y[0], check, eps99, eps99); // // x, y, compare_change x[0] = 1.0; x[1] = 5.0; f_ptr(nx, x.data(), ny, y.data(), &compare_change); // // ok // comparison as as when recorded (compare_change the same) ok &= compare_change == 1; // // ok check = x[1] - x[0]; ok &= NearEqual(y[0], check, eps99, eps99); // return ok; } // END C++ ================================================ FILE: example/jit/compile.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin jit_compile.cpp} JIT Compiler Options: Example and Test ###################################### compile ******* This example demonstrates setting the JIT :ref:`create_dll_lib@options@compile` option. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end jit_compile.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include # include # include # include // DLL_EXT # ifdef _WIN32 # define DLL_EXT ".dll" # else # define DLL_EXT ".so" # endif # include bool compile(void) { bool ok = true; // using CppAD::AD; using CppAD::ADFun; using CppAD::Independent; using CppAD::NearEqual; // // compile std::string compile = ""; # if CPPAD_C_COMPILER_MSVC_FLAGS const char* cmd = CPPAD_C_COMPILER_CMD "/HELP 1> temp.1 2> temp.2"; if( std::system(cmd) == 0 ) compile = CPPAD_C_COMPILER_CMD " /EHs /EHc /c /TC /O2"; # endif # if CPPAD_C_COMPILER_GNU_FLAGS const char* cmd = CPPAD_C_COMPILER_CMD " --version > temp"; if( std::system(cmd) == 0 ) compile = CPPAD_C_COMPILER_CMD " -c -fPIC -O2"; # endif // if( compile == "" ) { std::cout << ": cannot determine C compiler to use so skipping test: "; return ok; } // std::cout << "compile = " << compile << "\n"; // // nx, ny size_t nx = 2, ny = 1; // // f(x) = x_0 + x_1 CPPAD_TESTVECTOR( AD ) ax(nx), ay(ny); ax[0] = 0.0; ax[1] = 1.0; Independent(ax); ay[0] = ax[0] + ax[1]; ADFun f(ax, ay); f.function_name_set("f"); // // csrc_file // created in std::filesystem::current_path std::string c_type = "double"; std::string csrc_file = "compile.c"; std::ofstream ofs; ofs.open(csrc_file , std::ofstream::out); f.to_csrc(ofs, c_type); ofs.close(); // // dll_file // created in std::filesystem::current_path std::string dll_file = "jit_compile" DLL_EXT; CPPAD_TESTVECTOR( std::string) csrc_files(1); csrc_files[0] = csrc_file; std::map< std::string, std::string > options; if( compile != "" ) options["compile"] = compile; std::string err_msg = CppAD::create_dll_lib(dll_file, csrc_files, options); if( err_msg != "" ) { std::cerr << "jit_compile: err_msg = " << err_msg << "\n"; return false; } // dll_linker CppAD::link_dll_lib dll_linker(dll_file, err_msg); if( err_msg != "" ) { std::cerr << "jit_compile: err_msg = " << err_msg << "\n"; return false; } // // void_ptr std::string function_name = "cppad_jit_f"; void* void_ptr = dll_linker(function_name, err_msg); if( err_msg != "" ) { std::cerr << "jit_compile: err_msg = " << err_msg << "\n"; return false; } // // jit_double using CppAD::jit_double; // // f_ptr jit_double f_ptr = reinterpret_cast(void_ptr); // // x, y, compare_change // y = f(x) size_t compare_change = 0; std::vector x(nx), y(ny); x[0] = 0.3; x[1] = 0.5; f_ptr(nx, x.data(), ny, y.data(), &compare_change); // // ok ok &= compare_change == 0; // // ok double eps99 = 99.0 * std::numeric_limits::epsilon(); double check = x[0] + x[1]; ok &= NearEqual(y[0], check, eps99, eps99); // return ok; } // END C++ ================================================ FILE: example/jit/dynamic.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin jit_dynamic.cpp} JIT With Dynamic Parameters: Example and Test ############################################# Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end jit_dynamic.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include # include # include # include // DLL_EXT # ifdef _WIN32 # define DLL_EXT ".dll" # else # define DLL_EXT ".so" # endif # include bool dynamic(void) { bool ok = true; // using CppAD::AD; using CppAD::ADFun; using CppAD::Independent; using CppAD::NearEqual; // // nx, ny size_t np = 2, nx = 2, ny = 1; // // f(x) = x_0 + x_1 CPPAD_TESTVECTOR( AD ) ap(np), ax(nx), ay(ny); ap[0] = 0.0; ap[1] = 1.0; ax[0] = 2.0; ax[1] = 4.0; Independent(ax, ap); ay[0] = ap[0] * cos(ax[0]) + ap[1] * sin(ax[1]); ADFun f(ax, ay); f.function_name_set("f"); // // csrc_file // created in std::filesystem::current_path std::string c_type = "double"; std::string csrc_file = "dynamic.c"; std::ofstream ofs; ofs.open(csrc_file , std::ofstream::out); f.to_csrc(ofs, c_type); ofs.close(); // // dll_file // created in std::filesystem::current_path std::string dll_file = "jit_dynamic" DLL_EXT; CPPAD_TESTVECTOR( std::string) csrc_files(1); csrc_files[0] = csrc_file; std::map< std::string, std::string > options; std::string err_msg = CppAD::create_dll_lib(dll_file, csrc_files, options); if( err_msg != "" ) { std::cerr << "jit_dynamic: err_msg = " << err_msg << "\n"; return false; } // dll_linker CppAD::link_dll_lib dll_linker(dll_file, err_msg); if( err_msg != "" ) { std::cerr << "jit_dynamic: err_msg = " << err_msg << "\n"; return false; } // // void_ptr std::string function_name = "cppad_jit_f"; void* void_ptr = dll_linker(function_name, err_msg); if( err_msg != "" ) { std::cerr << "dynamic: err_msg = " << err_msg << "\n"; return false; } // // jit_double using CppAD::jit_double; // // f_ptr jit_double f_ptr = reinterpret_cast(void_ptr); // // u, y, compare_change // y = f(u) size_t nu = np + nx; size_t compare_change = 0; std::vector u(nu), y(ny); u[0] = 0.3; // p[0] u[1] = 0.5; // p[0] u[2] = 0.7; // x[0] u[3] = 0.9; // x[1] f_ptr(nu, u.data(), ny, y.data(), &compare_change); // // ok ok &= compare_change == 0; // // ok // f(u) = p[0] * cos(x[0]) + p[1] * sin(x[1]) double eps99 = 99.0 * std::numeric_limits::epsilon(); double check = u[0] * std::cos(u[2]) + u[1] * std::sin(u[3]); ok &= NearEqual(y[0], check, eps99, eps99); // return ok; } // END C++ ================================================ FILE: example/jit/get_started.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin jit_get_started.cpp} JIT Computation of Derivatives: Example and Test ################################################ Purpose ******* This is a simple example using CppAD for Just In Time (JIT) compilation, linking, and running of C source code that computes derivatives. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end jit_get_started.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include # include # include # include # include # if CPPAD_USE_CPLUSPLUS_2017 # include # endif // DLL_EXT # ifdef _WIN32 # define DLL_EXT ".dll" # else # define DLL_EXT ".so" # endif # include bool get_started(void) { bool ok = true; // // p # if CPPAD_USE_CPLUSPLUS_2017 std::filesystem::path p; # endif // using CppAD::AD; using CppAD::ADFun; using CppAD::Independent; using CppAD::NearEqual; // // f // f_0 (x_0, x_1) = x_0 * cos(x_1) // f_1 (x_0, x_1) = x_0 * sin(x_1) size_t n = 2; size_t m = 2; CPPAD_TESTVECTOR( AD ) ax(n), ay(m); for(size_t j = 0; j < n; ++j) ax[j] = AD( j + 1); Independent(ax); ay[0] = ax[0] * cos( ax[1] ); ay[1] = ax[0] * sin( ax[1] ); ADFun f(ax, ay); ADFun< AD , double > af = f.base2ad(); // // g(x) = f'(x) // g(x) = [ cos(x_1) , - x_0 * sin(x_1) ] // [ sin(x_1) , x_0 * cos(x_1) ] Independent(ax); CPPAD_TESTVECTOR( AD ) az(m * n); az = af.Jacobian(ax); ADFun g(ax, az); g.function_name_set("g"); // // csrc_file // created in std::filesystem::current_path() std::string c_type = "double"; std::string csrc_file = "get_started.c"; std::ofstream ofs; ofs.open(csrc_file , std::ofstream::out); g.to_csrc(ofs, c_type); ofs.close(); // // p, ok # if CPPAD_USE_CPLUSPLUS_2017 p = std::filesystem::current_path(); p /= csrc_file; ok &= std::filesystem::exists(p); # endif // // dll_file // created in std::filesystem::current_path() std::string dll_file = "jit_get_started" DLL_EXT; CPPAD_TESTVECTOR( std::string) csrc_files(1); csrc_files[0] = csrc_file; std::map< std::string, std::string > options; std::string err_msg = CppAD::create_dll_lib(dll_file, csrc_files, options); if( err_msg != "" ) { std::cerr << "jit_get_started: err_msg = " << err_msg << "\n"; return false; } // // p, ok # if CPPAD_USE_CPLUSPLUS_2017 p = std::filesystem::current_path(); p /= dll_file; ok &= std::filesystem::exists(p); # endif // // dll_linker CppAD::link_dll_lib dll_linker(dll_file, err_msg); if( err_msg != "" ) { std::cerr << "jit_get_started: err_msg = " << err_msg << "\n"; return false; } // // void_ptr std::string function_name = "cppad_jit_g"; void* void_ptr = dll_linker(function_name, err_msg); if( err_msg != "" ) { std::cerr << "jit_get_started: err_msg = " << err_msg << "\n"; return false; } // // jit_double using CppAD::jit_double; // // g_ptr jit_double g_ptr = reinterpret_cast(void_ptr); // // x, z, compare_change // z = g(x) size_t nx = n, nz = m * n, compare_change = 0; std::vector x(nx), z(nz); x[0] = 0.3; x[1] = 0.5; g_ptr(nx, x.data(), nz, z.data(), &compare_change); // // ok ok &= compare_change == 0; // // eps99 double eps99 = 99.0 * std::numeric_limits::epsilon(); // // ok // df_0 / dx_0 is stored in z[0 * n + 0] double value = z[ 0 * n + 0]; double check = std::cos(x[1]); ok &= NearEqual(value, check, eps99, eps99); // // ok // df_0 / dx_1 is stored in z[0 * n + 1] value = z[ 0 * n + 1]; check = - x[0] * std::sin(x[1]); ok &= NearEqual(value, check, eps99, eps99); // // ok // df_1 / dx_0 is stored in z[1 * n + 0] value = z[ 1 * n + 0]; check = std::sin(x[1]); ok &= NearEqual(value, check, eps99, eps99); // // ok // df_1 / dx_1 is stored in z[1 * n + 1] value = z[ 1 * n + 1]; check = x[0] * std::cos(x[1]); ok &= NearEqual(value, check, eps99, eps99); // return ok; } // END C++ ================================================ FILE: example/jit/jit.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin jit.cpp} jit Examples and Tests Driver ############################# Running These Tests ******************* After executing the :ref:`cmake-name` command form the :ref:`download@Distribution Directory`, you can build and run these tests with the commands:: cd build make check_example_jit Note that your choice of :ref:`cmake@generator` may require using an different version of make; e.g., ``ninja`` . {xrst_literal // BEGIN C++ // END C++ } {xrst_end jit.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ // CPPAD_HAS_* defines # include // system include files used for I/O # include // for thread_alloc # include // test runner # include // BEGIN_SORT_THIS_LINE_PLUS_1 extern bool atomic(void); extern bool compare_change(void); extern bool compile(void); extern bool dynamic(void); extern bool get_started(void); // END_SORT_THIS_LINE_MINUS_1 // main program that runs all the tests int main(void) { bool ok = true; // # if ! (CPPAD_C_COMPILER_MSVC_FLAGS || CPPAD_C_COMPILER_GNU_FLAGS) std::cout << "Do not know how to use this C compiler to create a DLL\n"; std::cout << CPPAD_C_COMPILER_CMD << "\n"; std::cout << "Skipping example/jit\n"; # else std::string group = "example/jit"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // BEGIN_SORT_THIS_LINE_PLUS_1 Run( atomic, "atomic" ); Run( compare_change, "compare_change" ); Run( compile, "compile" ); Run( dynamic, "dynamic" ); Run( get_started, "get_started" ); // END_SORT_THIS_LINE_MINUS_1 // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end ok = Run.summary(memory_ok); # endif // return static_cast( ! ok ); } // END C++ ================================================ FILE: example/jit/jit.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin example_jit} JIT Creation, Compilation, and Linking of C Source Code ####################################################### Restrictions ************ These examples are not tested when the :ref:`cmake@cppad_link_flags` contain ``-m32`` . Contents ******** {xrst_toc_table example/jit/get_started.cpp example/jit/compare_change.cpp example/jit/compile.cpp example/jit/atomic.cpp example/jit/dynamic.cpp } {xrst_end example_jit} ================================================ FILE: example/json/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list add_op.cpp atom4_op.cpp atom_op.cpp azmul_op.cpp cexp_op.cpp comp_op.cpp discrete_op.cpp div_op.cpp from_json.cpp get_started.cpp json.cpp mul_op.cpp pow_op.cpp print_op.cpp sparse.cpp sub_op.cpp sum_op.cpp to_json.cpp unary_op.cpp ) # END_SORT_THIS_LINE_MINUS_2 set_compile_flags(example_json "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(example_json EXCLUDE_FROM_ALL ${source_list}) # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_json ${cppad_lib} ${colpack_libs} ) # # check_example_json add_check_executable(check_example json) ================================================ FILE: example/json/add_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin json_add_op.cpp} Json add Operator: Example and Test ################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end json_add_op.cpp} */ // BEGIN C++ # include bool add_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; // // AD graph example // node_1 : p[0] // node_2 : p[1] // node_3 : x[0] // node_4 : p[0] + p[1] // node_5 : x[0] + ( p[0] + p[1] ) // y[0] = x[0] + ( p[0] + p[1] ) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'add_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'add', 'n_arg':2 } ,\n" " { 'op_code':2, 'name':'sub', 'n_arg':2 } ]\n" " ],\n" " 'n_dynamic_ind' : 2,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 0, [ ] ],\n" " 'op_usage_vec' : [ 2, [\n" " [ 1, 1, 2 ] ,\n" // p[0] + p[1] " [ 1, 3, 4 ] ]\n" // x[0] + ( p[0] + p[1] ) " ],\n" " 'dependent_vec' : [ 1, [5] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = x_0 + ( p_0 + p_1 ) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters vector p(2), x(1); p[0] = 2.0; p[1] = 3.0; x[0] = 4.0; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result ok &= y[0] == x[0] + ( p[0] + p[1] ); // ----------------------------------------------------------------------- // Convert to Json graph and back again json = f.to_json(); // std::cout << "json = " << json; f.from_json(json); // ----------------------------------------------------------------------- ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters p[0] = 2.0; p[1] = 3.0; x[0] = 4.0; // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= y[0] == x[0] + ( p[0] + p[1] ); // return ok; } // END C++ ================================================ FILE: example/json/atom4_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin json_atom4_op.cpp} Json Atomic Function Operator: Example and Test ############################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end json_atom4_op.cpp} */ // BEGIN C++ # include namespace { class atomic_avg : public CppAD::atomic_four { public: atomic_avg(void) : CppAD::atomic_four("avg") { } private: // for_type bool for_type( size_t call_id , const CppAD::vector& type_x , CppAD::vector& type_y ) override { type_y[0] = type_x[0]; return true; } // forward bool forward( size_t call_id , const CppAD::vector& select_y , size_t order_low , size_t order_up , const CppAD::vector& taylor_x , CppAD::vector& taylor_y ) override { // order_up if( order_up != 0 ) return false; // // n size_t n = taylor_x.size(); if( n == 0 ) return false; // // taylor_y double sum = 0.0; for(size_t j = 0; j < n; ++j) sum += taylor_x[j]; taylor_y[0] = sum / double(n); // return true; } }; } bool atom4_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; // // avg atomic_avg avg; // // ----------------------------------------------------------------------- // g (p; x) = avg( [ p_0 * x_0, p_0 * x_1 ] ) // = p_0 * (x_0 + x_1) / 2 // // This function has an atomic function operator with name avg // node_1 : p[0] // node_2 : x[0] // node_3 : x[1] // node_4 : p[0] * x[0] // node_5 : p[0] * x[1] // node_6 : avg( p[0] * x[0], p[0] * x[1] ) // y[0] = p[0] * ( x[0] + x[1] ) / 2 std::string json = "{\n" " 'function_name' : 'g(p; x)',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'atom4' } ,\n" " { 'op_code':2, 'name':'mul', 'n_arg':2 } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" // p[0] " 'n_variable_ind' : 2,\n" // x[0], x[1] " 'constant_vec' : [ 0, [ ] ],\n" " 'op_usage_vec' : [ 3, [\n" " [ 2, 1, 2 ] ,\n" // p[0] * x[0] " [ 2, 1, 3 ] ,\n" // p[0] * x[1] // avg( p[0] * x[0], p[0] * x[1] ) " [ 1, 'avg', 0, 1, 2, [ 4, 5 ] ] ]\n" " ],\n" " 'dependent_vec' : [ 1, [6] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // ------------------------------------------------------------------------ CppAD::ADFun g; g.from_json(json); // ------------------------------------------------------------------------ ok &= g.Domain() == 2; ok &= g.Range() == 1; ok &= g.size_dyn_ind() == 1; // // set p in g(p; x) vector p(1); p[0] = 2.0; g.new_dynamic(p); // // evaluate g(p; x) vector x(2), y(1); x[0] = 3.0; x[1] = 4.0; y = g.Forward(0, x); // // check value ok &= y[0] == p[0] * (x[0] + x[1]) / 2.0; // ------------------------------------------------------------------------ json = g.to_json(); g.from_json(json); // ------------------------------------------------------------------------ ok &= g.Domain() == 2; ok &= g.Range() == 1; ok &= g.size_dyn_ind() == 1; // // set p in g(p; x) p[0] = 5.0; g.new_dynamic(p); // // evaluate g(p; x) x[0] = 6.0; x[1] = 7.0; y = g.Forward(0, x); // // check value ok &= y[0] == p[0] * (x[0] + x[1]) / 2.0; // ------------------------------------------------------------------------ return ok; } // END C++ ================================================ FILE: example/json/atom_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin json_atom_op.cpp} Json Atomic Function Three Operator: Example and Test ##################################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end json_atom_op.cpp} */ // BEGIN C++ # include bool atom_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; // use single quote in graphs to avoid having to escape double quote // // ----------------------------------------------------------------------- // Define f_0 (x_0, x_1; p) = x_1 + p_0 * x_0 // // This function does not have an atomic function operator // node_1 : p[0] // node_2 : x[0] // node_3 : x[1] // node_4 : p[0] * x[0] // node_5 : x[1] + p[0] * x[0] // y[0] = x[1] + p[0] * x[0] std::string json = "{\n" " 'function_name' : 'f(x; p)',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'mul', 'n_arg':2 } ,\n" " { 'op_code':2, 'name':'add', 'n_arg':2 } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" // p[0] " 'n_variable_ind' : 2,\n" // x[0], x[1] " 'constant_vec' : [ 0, [ ] ],\n" " 'op_usage_vec' : [ 2, [\n" " [ 1, 1, 2 ] ,\n" // p[0] * x[0] " [ 2, 3, 4 ] ]\n" // x[1] + p[0] * x[0] " ],\n" " 'dependent_vec' : [ 1, [5] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 2; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // A ckhpoint_two function with name f(x; p) is derived from // an atomic_three function with the same name. bool internal_bool = false; bool use_hes_sparsity = false; bool use_base2ad = false; bool use_in_parallel = false; CppAD::chkpoint_two chk_f(f, "f(x; p)", internal_bool, use_hes_sparsity, use_base2ad, use_in_parallel ); // ----------------------------------------------------------------------- // g (u_0, u_1; p, q) = f(u_0 + q_0, u_1 + q_1, p) // = u_1 + q_1 + p_0 * ( u_0 + q_0 ) // // This function has an atomic function operator with name f(x; p) // node_1 : q[0] // node_2 : q[1] // node_3 : u[0] // node_4 : u[1] // node_5 : u[0] + q[0] // node_6 : u[1] + q[1] // node_7 : f( u[0] + q[0], u[1] + q[1]; p) // y[0] = u[1] + q[1] + p[0] * (u[0] + q[0]) json = "{\n" " 'function_name' : 'g(u; p, q)',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'atom' } ,\n" " { 'op_code':2, 'name':'add', 'n_arg':2 } ]\n" " ],\n" " 'n_dynamic_ind' : 2,\n" // q[0], q[1] " 'n_variable_ind' : 2,\n" // u[0], u[1] " 'constant_vec' : [ 0, [ ] ],\n" " 'op_usage_vec' : [ 3, [\n" " [ 2, 3, 1 ] ,\n" // u[0] + q[0] " [ 2, 4, 2 ] ,\n" // u[1] + q[1] // f(u_0 + q_0, u_1 + q_1; p) = u[1] + q[1] + p[0] * (u[0] + q[0]) " [ 1, 'f(x; p)', 1, 2, [ 5, 6 ] ] ]\n" " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // ------------------------------------------------------------------------ CppAD::ADFun g; g.from_json(json); // ------------------------------------------------------------------------ ok &= g.Domain() == 2; ok &= g.Range() == 1; ok &= g.size_dyn_ind() == 2; // // set p in g(u; p, q) vector p(1); p[0] = 2.0; chk_f.new_dynamic(p); // // set q in g(u; p, q) vector q(2); q[0] = 3.0; q[1] = 4.0; g.new_dynamic(q); // // evaluate g(u; p, q) vector u(2), y(1); u[0] = 5.0; u[1] = 6.0; y = g.Forward(0, u); // // check value ok &= y[0] == u[1] + q[1] + p[0] * (u[0] + q[0]); // ------------------------------------------------------------------------ json = g.to_json(); // std::cout << json; g.from_json(json); // ------------------------------------------------------------------------ ok &= g.Domain() == 2; ok &= g.Range() == 1; ok &= g.size_dyn_ind() == 2; // // set p in g(u; p, q) p[0] = 3.0; chk_f.new_dynamic(p); // // set q in g(u; p, q) q[0] = 4.0; q[1] = 5.0; g.new_dynamic(q); // // evaluate g(u; p, q) u[0] = 6.0; u[1] = 7.0; y = g.Forward(0, u); // // check value ok &= y[0] == u[1] + q[1] + p[0] * (u[0] + q[0]); // ------------------------------------------------------------------------ return ok; } // END C++ ================================================ FILE: example/json/azmul_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin json_azmul_op.cpp} Json azmul Operator: Example and Test ##################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end json_azmul_op.cpp} */ // BEGIN C++ # include bool azmul_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : p[1] // node_3 : x[0] // node_4 : x[1] // node_5 : azmul(p[0], p[1]) // node_6 : azmul(x[0], x[1]) // node_7 = azmul(p[0], p[1]) + azmul(x[0], x[1]) // y[0] = azmul(p[0], p[1]) + azmul(x[0], x[1]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'azmul_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'add', 'n_arg':2 } ,\n" " { 'op_code':2, 'name':'azmul', 'n_arg':2 } ]\n" " ],\n" " 'n_dynamic_ind' : 2,\n" " 'n_variable_ind' : 2,\n" " 'constant_vec' : [ 0, [ ] ],\n" " 'op_usage_vec' : [ 3, [\n" " [ 2, 1, 2 ] ,\n" // azmul(p[0], p[1]) " [ 2, 3, 4 ] ,\n" // azmul(x[0], ) " [ 1, 5, 6 ] ]\n" // azmul(p[0], p[1]) + azmul(x[0], x[1]) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = azmul(p_0, p_1) + azmul(x_0, x_1); CppAD::ADFun f; f.from_json(json); // ok &= f.Domain() == 2; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters vector p(2), x(2); p[0] = 2.0; p[1] = 3.0; x[0] = 4.0; x[1] = 5.0; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check_y0 = 0.0; if( p[0] != 0.0 ) check_y0 += p[0] * p[1]; if( x[0] != 0.0 ) check_y0 += x[0] * x[1]; ok &= NearEqual(y[0], check_y0, eps99, eps99); // ----------------------------------------------------------------------- // Convert to Json graph and back again json = f.to_json(); // std::cout << "json = " << json; f.from_json(json); // ----------------------------------------------------------------------- ok &= f.Domain() == 2; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters p[0] = 0.0; p[1] = std::numeric_limits::quiet_NaN(); x[0] = 2.0; x[1] = 3.0; // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result check_y0 = 0.0; if( p[0] != 0.0 ) check_y0 += p[0] * p[1]; if( x[0] != 0.0 ) check_y0 += x[0] * x[1]; ok &= NearEqual(y[0], check_y0, eps99, eps99); // return ok; } // END C++ ================================================ FILE: example/json/cexp_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin json_cexp_op.cpp} Json Conditional Expressions: Example and Test ############################################## Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end json_cexp_op.cpp} */ // BEGIN C++ # include bool cexp_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : cexp_le(p[0], x[0], p[0], x[0]) // y[0] = cexp_le(p[0], x[0], p[0], x[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'cexp_op example',\n" " 'op_define_vec' : [ 1, [\n" " { 'op_code':1, 'name':'cexp_le', 'n_arg':4 } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ -0.1 ] ],\n" // c[0] " 'op_usage_vec' : [ 1, [\n" " [ 1, 1, 2, 1, 2 ] ]\n" // cexp_le(p[0], x[0], p[0], x[0]) " ],\n" " 'dependent_vec' : [ 1, [4] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = cexp_le(p[0], x[0], p[0], x[0]) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // set independent variables and parameters vector p(1), x(1); p[0] = 0.2; x[0] = 0.3; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check; if( p[0] <= x[0] ) check= p[0]; else check = x[0]; // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // ---------------------------------------------------------------------- // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // ---------------------------------------------------------------------- // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // END C++ ================================================ FILE: example/json/comp_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin json_comp_op.cpp} Json Comparison Operators: Example and Test ########################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end json_comp_op.cpp} */ // BEGIN C++ # include bool comp_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // : x[0] < p[0] // node_3 : p[0] - x[0] // node_4 : log( p[0] - x[0] ) // y[0] = log( p[0] - x[0] ) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'comp_op example',\n" " 'op_define_vec' : [ 3, [\n" " { 'op_code':1, 'name':'comp_lt' } ,\n" " { 'op_code':2, 'name':'sub', 'n_arg':2 } ,\n" " { 'op_code':3, 'name':'log', 'n_arg':1 } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 0, [ ] ],\n" " 'op_usage_vec' : [ 3, [\n" " [ 1, 0, 2, [2, 1 ] ] ,\n" // x[0] < p[0] " [ 2, 1, 2 ] ,\n" // p[0] - x[0] " [ 3, 3 ] ]\n" // log( p[0] - x[0] ) " ],\n" " 'dependent_vec' : [ 1, [4] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = log( p[0] - x[0] ) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // set independent variables and parameters vector p(1), x(1); p[0] = 0.3; x[0] = 0.2; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // x[0] < p[0] so comparison should not have changed ok &= f.compare_change_number() == 0; // // check result double check = std::log( p[0] - x[0] ); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // case where comparison is false f.check_for_nan(false); // suppress checking for nan for this test x[0] = 0.4; y = f.Forward(0, x); ok &= f.compare_change_number() == 1; // // ----------------------------------------------------------------------- // Convert to Json graph and back again json = f.to_json(); // std::cout << json; f.from_json(json); // ----------------------------------------------------------------------- // // compute y = f(x, p) f.new_dynamic(p); x[0] = 0.2; y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // case where comparison is false x[0] = 0.4; y = f.Forward(0, x); ok &= f.compare_change_number() == 1; // return ok; } // END C++ ================================================ FILE: example/json/discrete_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin json_discrete_op.cpp} Json add Operator: Example and Test ################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end json_discrete_op.cpp} */ // BEGIN C++ # include namespace { double heaviside(const double &x) { if( x < 0.0 ) return 0.0; if( x == 0.0 ) return 0.5; return 1.0; } CPPAD_DISCRETE_FUNCTION(double, heaviside) } bool discrete_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; // // The discrete function does not exist until its AD version is called heaviside( CppAD::AD( 0.0 ) ); // // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : heaviside(p[0]) // node_4 : heaviside(p[1]) // node_5 = heaviside(p[0]) + heaviside(p[1]) // y[0] = heaviside(p[0]) + heaviside(p[1]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'discrete_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'add', 'n_arg':2 } ,\n" " { 'op_code':2, 'name':'discrete' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 0, [ ] ],\n" " 'op_usage_vec' : [ 3, [\n" " [ 2, 'heaviside', 1, 1, [ 1 ] ] ,\n" // heaviside(p[0]) " [ 2, 'heaviside', 1, 1, [ 2 ] ] ,\n" // heaviside(x[0]) " [ 1, 3, 4 ] ]\n" // heaviside(p[0]) + heaviside(x[0]) " ],\n" " 'dependent_vec' : [ 1, [5] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = x_0 + ( p_0 + p_1 ) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // set independent variables and parameters CPPAD_TESTVECTOR(double) p(1), x(1); p[0] = 0.0; x[0] = 2.0; // // compute y = f(x, p) f.new_dynamic(p); CPPAD_TESTVECTOR(double) y = f.Forward(0, x); // // check result ok &= y[0] == heaviside(p[0]) + heaviside(x[0]); // ----------------------------------------------------------------------- // Convert to Json graph and back again json = f.to_json(); // std::cout << "json = " << json; f.from_json(json); // ----------------------------------------------------------------------- ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // set independent variables and parameters p[0] = -2.0; x[0] = 2.0; // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= y[0] == heaviside(p[0]) + heaviside(x[0]); // return ok; } // END C++ ================================================ FILE: example/json/div_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin json_div_op.cpp} Json div Operator: Example and Test ################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end json_div_op.cpp} */ // BEGIN C++ # include bool div_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : p[1] // node_3 : x[0] // node_4 : p[0] / p[1] // node_5 : x[0] / ( p[1] / p[0] ) // y[0] = p[0] / p[1] // y[1] = x[0] / ( p[0] / p[1] ) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'div example',\n" " 'op_define_vec' : [ 1, [\n" " { 'op_code':1, 'name':'div', 'n_arg':2 } ]\n" " ],\n" " 'n_dynamic_ind' : 2,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 0, [ ] ],\n" " 'op_usage_vec' : [ 2, [\n" " [ 1, 1, 2 ] ,\n" // p[0] / p[1] " [ 1, 3, 4 ] ]\n" // x[0] / ( p[0] / p[1] ) " ],\n" " 'dependent_vec' : [ 2, [4, 5] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = [ p_0 / p_1 , x_0 * p_1 / p_0 ] CppAD::ADFun f; f.from_json(json); // ok &= f.Domain() == 1; ok &= f.Range() == 2; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters vector p(2), x(1); p[0] = 2.0; p[1] = 3.0; x[0] = 4.0; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result ok &= NearEqual(y[0] , p[0] / p[1] , eps99, eps99 ); ok &= NearEqual(y[1] , x[0] / ( p[0] / p[1] ), eps99, eps99 ); // ----------------------------------------------------------------------- // Convert to Json graph and back again json = f.to_json(); // std::cout << "json = " << json; f.from_json(json); // ----------------------------------------------------------------------- ok &= f.Domain() == 1; ok &= f.Range() == 2; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters p[0] = 2.0; p[1] = 3.0; x[0] = 4.0; // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= NearEqual(y[0] , p[0] / p[1] , eps99, eps99 ); ok &= NearEqual(y[1] , x[0] / ( p[0] / p[1] ), eps99, eps99 ); // return ok; } // END C++ ================================================ FILE: example/json/from_json.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin from_json.cpp} Convert Jason Graph to an ADFun Object: Example and Test ######################################################## Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end from_json.cpp} */ // BEGIN C++ # include bool from_json(void) { bool ok = true; using CppAD::vector; // // An AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : x[1] // node_4 : -2.0 // node_5 : p[0] + x[0] + x[1] // node_6 : (p[0] + x[0] + x[1]) * (p[0] + x[0] + x[1]) // y[0] = (p[0] + x[0] + x[1]) * (p[0] + x[0] + x[1]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'from_json example',\n" " 'op_define_vec' : [ 3, [\n" " { 'op_code':1, 'name':'add', 'n_arg':2 } ,\n" " { 'op_code':2, 'name':'mul', 'n_arg':2 } ,\n" " { 'op_code':3, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 2,\n" " 'constant_vec' : [ 1, [ -2.0 ] ],\n" " 'op_usage_vec' : [ 2, [\n" " [ 3, 1, 3, [1, 2, 3 ] ] ,\n" " [ 2, 5, 5 ] ] \n" " ],\n" " 'dependent_vec' : [ 1, [6] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // CppAD::ADFun fun; fun.from_json(json); // // Compute function value vector p(1), x(2); p[0] = 1.0; x[0] = 2.0; x[1] = 3.0; fun.new_dynamic(p); vector y = fun.Forward(0, x); ok &= y[0] == (p[0] + x[0] + x[1]) * (p[0] + x[0] + x[1]); // // Conpute derivative value vector jac = fun.Jacobian(x); ok &= jac[0] == 2.0 * (p[0] + x[0] + x[1]); ok &= jac[1] == 2.0 * (p[0] + x[0] + x[1]); // return ok; } // END C++ ================================================ FILE: example/json/get_started.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin json_get_started.cpp} {xrst_spell dx } Json Get Started: Example and Test ################################## Notation ******** .. csv-table:: :widths: auto Notation,Description,size :ref:`json_ad_graph@Node Indices@p`,vector of dynamic parameters,1 :ref:`json_ad_graph@Node Indices@x`,vector of independent variables,1 :ref:`json_ad_graph@Node Indices@c`,vector of constants,1 y,vector of dependent variables,1 Node Table ********** .. csv-table:: :widths: auto index,value 1,p[0] 2,x[0] 3,c[0] 4,sin(p[0]) 5,sin(x[0]) 6,sin(c[0]) 7,sin(p[0]) + sin(x[0]) + sin(c[0]) y[0],sin(p[0]) + sin(x[0]) + sin(c[0]) Include ******* Include the CppAD core functions: {xrst_spell_off} {xrst_code cpp} */ # include /* {xrst_code} {xrst_spell_on} Syntax ****** | *ok* = ``get_started`` () {xrst_spell_off} {xrst_code cpp} */ bool get_started(void) { /* {xrst_code} {xrst_spell_on} Setup ***** {xrst_spell_off} {xrst_code cpp} */ bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); /* {xrst_code} {xrst_spell_on} Function ******** Begin Function ============== See :ref:`json_ad_graph@AD Graph@function` : {xrst_spell_off} {xrst_code cpp} */ std::string json = "{\n" " 'function_name' : 'get_started example',\n" /* {xrst_code} {xrst_spell_on} Begin op_define_vec =================== see :ref:`json_ad_graph@op_define_vec` : {xrst_spell_off} {xrst_code cpp} */ " 'op_define_vec' : [ 2, [\n" /* {xrst_code} {xrst_spell_on} Define Unary ============ see :ref:`json_graph_op@Unary Operators` : {xrst_spell_off} {xrst_code cpp} */ " { 'op_code':1, 'name':'sin', 'n_arg':1 } ,\n" /* {xrst_code} {xrst_spell_on} Define Sum ========== see :ref:`json_graph_op@sum` : {xrst_spell_off} {xrst_code cpp} */ " { 'op_code':2, 'name':'sum' } ]\n" /* {xrst_code} {xrst_spell_on} End op_define_vec ================= {xrst_spell_off} {xrst_code cpp} */ " ],\n" /* {xrst_code} {xrst_spell_on} n_dynamic_ind ============= see :ref:`json_ad_graph@dynamic_ind_vec@n_dynamic_ind` : {xrst_spell_off} {xrst_code cpp} */ " 'n_dynamic_ind' : 1,\n" /* {xrst_code} {xrst_spell_on} n_variable_ind ============== see :ref:`json_ad_graph@variable_ind_vec@n_variable_ind` : {xrst_spell_off} {xrst_code cpp} */ " 'n_variable_ind' : 1,\n" /* {xrst_code} {xrst_spell_on} constant_vec ============ see :ref:`json_ad_graph@constant_vec` : {xrst_spell_off} {xrst_code cpp} */ " 'constant_vec' : [ 1, [ -0.1 ] ],\n" // c[0] /* {xrst_code} {xrst_spell_on} Begin op_usage_vec ================== see :ref:`json_ad_graph@op_usage_vec` : {xrst_spell_off} {xrst_code cpp} */ " 'op_usage_vec' : [ 4, [\n" /* {xrst_code} {xrst_spell_on} op_usage ======== see op_usage with :ref:`json_ad_graph@op_usage@n_arg In Definition` : {xrst_spell_off} {xrst_code cpp} */ " [ 1, 1] ,\n" // sin(p[0]) " [ 1, 2] ,\n" // sin(x[0]) " [ 1, 3] ,\n" // sin(c[0]) /* {xrst_code} {xrst_spell_on} see op_usage with :ref:`json_ad_graph@op_usage@n_arg Not In Definition` : {xrst_spell_off} {xrst_code cpp} */ " [ 2, 1, 3, [4, 5, 6] ] ]\n" // sin(p[0])+sin(x[0])+sin(c[0]) /* {xrst_code} {xrst_spell_on} End op_usage_vec ================ {xrst_spell_off} {xrst_code cpp} */ " ],\n" /* {xrst_code} {xrst_spell_on} dependent_vec ============= see :ref:`dependent_var` {xrst_spell_off} {xrst_code cpp} */ " 'dependent_vec' : [ 1, [7] ] \n" /* {xrst_code} {xrst_spell_on} End Function ============ {xrst_spell_off} {xrst_code cpp} */ "}\n"; /* {xrst_code} {xrst_spell_on} Convert Single to Double Quotes ******************************* {xrst_spell_off} {xrst_code cpp} */ for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; /* {xrst_code} {xrst_spell_on} double f(x, p) ************** {xrst_spell_off} {xrst_code cpp} */ CppAD::ADFun f; f.from_json(json); /* {xrst_code} {xrst_spell_on} Check f(x, p) ************* {xrst_spell_off} {xrst_code cpp} */ vector c(1), p(1), x(1), y(1); c[0] = -0.1; // must match value in graph p[0] = 0.2; // can be any value x[0] = 0.3; // can be any value // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // f(x, p) = sin(p_0) + sin(x_0) + sin(c_0) double check = std::sin(p[0]) + std::sin(x[0]) + std::sin(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); /* {xrst_code} {xrst_spell_on} AD f(x, p) ****************** {xrst_spell_off} {xrst_code cpp} */ CppAD::ADFun< AD, double > af( f.base2ad() ); /* {xrst_code} {xrst_spell_on} Evaluate Derivative ******************* {xrst_spell_off} {xrst_code cpp} */ // set independent variables and parameters vector< AD > ap(1), ax(1); ap[0] = 0.2; ax[0] = 0.3; // // record computation of z = d/dx f(x, p) CppAD::Independent(ax, ap); af.new_dynamic(ap); vector< AD > az = af.Jacobian(ax); /* {xrst_code} {xrst_spell_on} double g(x, p) = d/dx f(x, p) ***************************** {xrst_spell_off} {xrst_code cpp} */ CppAD::ADFun g(ax, az); /* {xrst_code} {xrst_spell_on} Convert to Json and Back ************************ {xrst_spell_off} {xrst_code cpp} */ json = g.to_json(); g.from_json(json); /* {xrst_code} {xrst_spell_on} Check g(x, p) ************* {xrst_spell_off} {xrst_code cpp} */ c[0] = -0.1; // must match value in graph p[0] = 0.3; // can be any value x[0] = 0.4; // can be any value // // compute z = g(x, p) g.new_dynamic(p); vector z = g.Forward(0, x); // // g(x, p) = d/dx f(x, p) = d/dx sin(x) = cos(x) check = std::cos(x[0]); ok &= CppAD::NearEqual(z[0], check, eps99, eps99); // return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end json_get_started.cpp} */ ================================================ FILE: example/json/json.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin json.cpp} json Examples and Tests Driver ############################## Running These Tests ******************* After executing the :ref:`cmake-name` command form the :ref:`download@Distribution Directory`, you can build and run these tests with the commands:: cd build make check_example_json Note that your choice of :ref:`cmake@generator` may require using an different version of make; e.g., ``ninja`` . {xrst_literal // BEGIN C++ // END C++ } {xrst_end json.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ // CPPAD_HAS_* defines # include // for thread_alloc # include // test runner # include // BEGIN_SORT_THIS_LINE_PLUS_2 // external compiled tests extern bool add_op(void); extern bool atom4_op(void); extern bool atom_op(void); extern bool azmul_op(void); extern bool cexp_op(void); extern bool comp_op(void); extern bool discrete_op(void); extern bool div_op(void); extern bool from_json(void); extern bool get_started(void); extern bool mul_op(void); extern bool pow_op(void); extern bool print_op(void); extern bool sparse(void); extern bool sub_op(void); extern bool sum_op(void); extern bool to_json(void); extern bool unary_op(void); // END_SORT_THIS_LINE_MINUS_1 // main program that runs all the tests int main(void) { std::string group = "example/json"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // BEGIN_SORT_THIS_LINE_PLUS_2 // external compiled tests Run( add_op, "add_op" ); Run( atom4_op, "atom4_op" ); Run( azmul_op, "azmul_op" ); Run( cexp_op, "cexp_op" ); Run( comp_op, "comp_op" ); Run( discrete_op, "discrete_op" ); Run( div_op, "div_op" ); Run( from_json, "from_json" ); Run( get_started, "get_started" ); Run( mul_op, "mul_op" ); Run( pow_op, "pow_op" ); Run( print_op, "print_op" ); Run( sparse, "sparse" ); Run( sub_op, "sub_op" ); Run( sum_op, "sum_op" ); Run( to_json, "to_json" ); Run( unary_op, "unary_op" ); // END_SORT_THIS_LINE_MINUS_1 // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } // END C++ ================================================ FILE: example/json/mul_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin json_mul_op.cpp} Json mul Operator: Example and Test ################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end json_mul_op.cpp} */ // BEGIN C++ # include bool mul_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; // // AD graph example // node_1 : p[0] // node_2 : p[1] // node_3 : x[0] // node_4 : p[0] * p[1] // node_5 : x[0] * p[0] * p[1] // y[0] = x[0] * p[0] * p[1] // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'mul_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'add', 'n_arg':2 } ,\n" " { 'op_code':2, 'name':'mul', 'n_arg':2 } ]\n" " ],\n" " 'n_dynamic_ind' : 2,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 0, [ ] ],\n" " 'op_usage_vec' : [ 2, [\n" " [ 2, 1, 2 ] ,\n" // p[0] * p[1] " [ 2, 3, 4 ] ]\n" // x[0] * p[0] * p[1] " ],\n" " 'dependent_vec' : [ 1, [5] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = x_0 * p_0 * p_1 CppAD::ADFun f; f.from_json(json); // ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters vector p(2), x(1); p[0] = 2.0; p[1] = 3.0; x[0] = 4.0; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result ok &= y[0] == x[0] * p[0] * p[1]; // ----------------------------------------------------------------------- // Convert to Json graph and back again json = f.to_json(); // std::cout << "json = " << json; f.from_json(json); // ----------------------------------------------------------------------- ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters p[0] = 2.0; p[1] = 3.0; x[0] = 4.0; // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= y[0] == x[0] * p[0] * p[1]; // return ok; } // END C++ ================================================ FILE: example/json/pow_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin json_pow_op.cpp} Json pow Operator: Example and Test ################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end json_pow_op.cpp} */ // BEGIN C++ # include bool pow_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : p[1] // node_3 : x[0] // node_4 : pow(p[0], p[1]) // node_5 : pow(p[0], x[0]) // node_6 = pow(p[0], p[1]) + pow(p[0], x[0]) // y[0] = pow(p[0], p[1]) + pow(p[0], x[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'pow_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'add', 'n_arg':2 } ,\n" " { 'op_code':2, 'name':'pow', 'n_arg':2 } ]\n" " ],\n" " 'n_dynamic_ind' : 2,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 0, [ ] ],\n" " 'op_usage_vec' : [ 3, [\n" " [ 2, 1, 2 ] ,\n" // pow(p[0], p[1]) " [ 2, 1, 3 ] ,\n" // pow(p[0], x[0]) " [ 1, 4, 5 ] ]\n" // pow(p[0], p[1]) + pow(p[0], x[0]) " ],\n" " 'dependent_vec' : [ 1, [6] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = pow(p_0, p_1) + pow(p_0, x_0) CppAD::ADFun f; f.from_json(json); // ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters vector p(2), x(1); p[0] = 2.0; p[1] = 3.0; x[0] = 4.0; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check_y0 = std::pow(p[0], p[1]) + std::pow(p[0], x[0]); ok &= NearEqual(y[0], check_y0, eps99, eps99); // ----------------------------------------------------------------------- // Convert to Json graph and back again json = f.to_json(); // std::cout << "json = " << json; f.from_json(json); // ----------------------------------------------------------------------- ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters p[0] = 2.0; p[1] = 3.0; x[0] = 4.0; // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= NearEqual(y[0], check_y0, eps99, eps99); // return ok; } // END C++ ================================================ FILE: example/json/print_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin json_print_op.cpp} Json AD Graph print Operator: Example and Test ############################################## Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end json_print_op.cpp} */ // BEGIN C++ # include bool print_op(void) { bool ok = true; using std::string; std::stringstream stream_out; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // : print(p[0], "p[0] = ", p[0], "\n") // : print(x[0], "x[0] = ", x[0], "\n") // node_3 : log(p[0]) // node_4 : log(x[0]) // node_5 = log(p[0]) + log(x[0]) // y[0] = log(p[0]) + log(x[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'print_op example',\n" " 'op_define_vec' : [ 3, [\n" " { 'op_code':1, 'name':'print' } ,\n" " { 'op_code':2, 'name':'log', 'n_arg':1 } ,\n" " { 'op_code':3, 'name':'add', 'n_arg':2 } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 0, [ ] ],\n" " 'op_usage_vec' : [ 5, [\n" " [ 1, 'p[0] = ', '\n', 0, 2, [1, 1 ] ] ,\n" // first print " [ 1, 'x[0] = ', '\n', 0, 2, [2, 2 ] ] ,\n" // second print " [ 2, 1 ] ,\n" // log(p[0]) " [ 2, 2 ] ,\n" // log(x[0]) " [ 3, 3, 4 ] ]\n" // log(p[0]) + log(x[0]) " ],\n" " 'dependent_vec' : [ 1, [5] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = log(p[0]) + log(x[0]) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // set independent variables and parameters CPPAD_TESTVECTOR(double) p(1), x(1); p[0] = 1.0; x[0] = 2.0; // // compute y = f(x, p) f.new_dynamic(p); CPPAD_TESTVECTOR(double) y = f.Forward(0, x, stream_out); // // check result ok &= stream_out.str() == ""; double check = std::log(p[0]) + std::log(x[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // ----------------------------------------------------------------------- // Convert function to json and back again json = f.to_json(); // std::cout << json; f.from_json(json); // ----------------------------------------------------------------------- ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // set independent variables and parameters p[0] = 1.0; x[0] = -2.0; // // compute y = f(x, p) f.new_dynamic(p); f.check_for_nan(false); y = f.Forward(0, x, stream_out); // // check result ok &= stream_out.str() == "x[0] = -2\n"; ok &= std::isnan(y[0]); // return ok; } // END C++ ================================================ FILE: example/json/sparse.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin json_sparse.cpp} Json Representation of a Sparse Matrix: Example and Test ######################################################## Discussion ********** The example using a CppAD Json to represent the sparse matrix .. math:: \partial_x f(x, p) = \left( \begin{array}{ccc} p_0 & 0 & 0 \\ 0 & p_1 & 0 \\ 0 & 0 & c_0 \end{array} \right) where :math:`c_0` is the constant 3. Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end json_sparse.cpp} */ // BEGIN C++ # include bool sparse(void) { bool ok = true; using CppAD::vector; typedef vector s_vector; typedef vector d_vector; // // An AD graph example // node_1 : p[0] // node_2 : p[1] // node_3 : x[0] // node_4 : x[1] // node_5 : x[2] // node_6 : c[0] // node_7 : p[0] * x[0] // node_8 : p[1] * x[1] // node_9 : c[0] * x[2] // y[0] = p[0] * x[0] // y[1] = p[1] * x[1] // y[2] = c[0] * x[0] // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'sparse example',\n" " 'op_define_vec' : [ 1, [\n" " { 'op_code':1, 'name':'mul', 'n_arg':2 } ]\n" " ],\n" " 'n_dynamic_ind' : 2,\n" " 'n_variable_ind' : 3,\n" " 'constant_vec' : [ 1, [ 3.0 ] ],\n" " 'op_usage_vec' : [ 3, [\n" " [ 1, 1, 3 ] , \n" " [ 1, 2, 4 ] , \n" " [ 1, 6, 5 ] ] \n" " ],\n" " 'dependent_vec' : [ 3, [7, 8, 9] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // CppAD::ADFun fun; fun.from_json(json); ok &= fun.Domain() == 3; ok &= fun.Range() == 3; ok &= fun.size_dyn_ind() == 2; // // Point at which we are going to evaluate the Jacobian of f(x, p). // The Jacobian is constant w.r.t. x, so the value of x does not matter. vector p(2), x(3); p[0] = 1.0; p[1] = 2.0; for(size_t j = 0; j < x.size(); ++j) x[j] = 0.0; // // set the dynamic parameters fun.new_dynamic(p); // // compute the sparsity pattern for f_x(x, p) vector select_domain(3); vector select_range(3); for(size_t i = 0; i < 3; ++i) { select_domain[i] = true; select_range[i] = true; } bool transpose = false; CppAD::sparse_rc pattern; fun.subgraph_sparsity(select_domain, select_range, transpose, pattern); // // compute the entire Jacobian CppAD::sparse_rcv subset(pattern); fun.subgraph_jac_rev(x, subset); // // information in the sparse Jacobian const s_vector& row( subset.row() ); const s_vector& col( subset.col() ); const d_vector& val( subset.val() ); size_t nnz = subset.nnz(); s_vector row_major = subset.row_major(); // // check number of non-zero elements in sparse matrix ok &= nnz == 3; // // check first element of matrix (in row major order) size_t k = row_major[0]; ok &= row[k] == 0; ok &= col[k] == 0; ok &= val[k] == p[0]; // // check second element of matrix k = row_major[1]; ok &= row[k] == 1; ok &= col[k] == 1; ok &= val[k] == p[1]; // // check third element of matrix k = row_major[2]; ok &= row[k] == 2; ok &= col[k] == 2; ok &= val[k] == 3.0; // return ok; } // END C++ ================================================ FILE: example/json/sub_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin json_sub_op.cpp} Json sub Operator: Example and Test ################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end json_sub_op.cpp} */ // BEGIN C++ # include bool sub_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; // // AD graph example // node_1 : p[0] // node_2 : p[1] // node_3 : x[0] // node_4 : p[0] - p[1] // node_5 : x[0] - ( p[0] - p[1] ) // y[0] = x[0] - ( p[0] - p[1] ) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'sub_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'add', 'n_arg':2 } ,\n" " { 'op_code':2, 'name':'sub', 'n_arg':2 } ]\n" " ],\n" " 'n_dynamic_ind' : 2,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 0, [ ] ],\n" " 'op_usage_vec' : [ 2, [\n" " [ 2, 1, 2 ] ,\n" // p[0] - p[1] " [ 2, 3, 4 ] ]\n" // x[0] - ( p[0] - p[1] ) " ],\n" " 'dependent_vec' : [ 1, [5] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = x_0 - ( p_0 - p_1 ) CppAD::ADFun f; f.from_json(json); // ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters vector p(2), x(1); p[0] = 2.0; p[1] = 3.0; x[0] = 4.0; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result ok &= y[0] == x[0] - ( p[0] - p[1] ); // ----------------------------------------------------------------------- // Convert to Json graph and back again json = f.to_json(); // std::cout << "json = " << json; f.from_json(json); // ----------------------------------------------------------------------- ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 2; // // set independent variables and parameters p[0] = 2.0; p[1] = 3.0; x[0] = 4.0; // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= y[0] == x[0] - ( p[0] - p[1] ); // return ok; } // END C++ ================================================ FILE: example/json/sum_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin json_sum_op.cpp} Json sum Operator: Example and Test ################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end json_sum_op.cpp} */ // BEGIN C++ # include bool sum_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; // // AD graph example // node_1 : p[0] // node_2 : p[1] // node_3 : p[2] // node_4 : x[0] // node_5 : p[0] + p[1] + p[2] // node_6 : x[0] + p[0] + p[1] + p[2] // y[0] = x[0] + p[0] + p[1] + p[2] // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'sum_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'add', 'n_arg':2 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 3,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 0, [ ] ],\n" " 'op_usage_vec' : [ 2, [\n" " [ 2, 1, 3, [1, 2, 3] ] ,\n" // p[0] + p[1] + p[2] " [ 2, 1, 2, [4, 5 ] ] ]\n" // x[0] + p[0] + p[1] + p[2] " ],\n" " 'dependent_vec' : [ 1, [6] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = x_0 + p_0 + p_1 + p_2 CppAD::ADFun f; f.from_json(json); // ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 3; // // set independent variables and parameters vector p(3), x(1); for(size_t j = 0; j < 3; ++j) p[j] = double(j + 1); x[0] = 5.0; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result ok &= y[0] == x[0] + p[0] + p[1] + p[2]; // ----------------------------------------------------------------------- // Convert to Json graph and back again json = f.to_json(); // std::cout << "json = " << json; f.from_json(json); // ----------------------------------------------------------------------- ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 3; // // set independent variables and parameters for(size_t j = 0; j < 3; ++j) p[j] = double(j + 1); x[0] = 5.0; // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= y[0] == x[0] + p[0] + p[1] + p[2]; // return ok; } // END C++ ================================================ FILE: example/json/to_json.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin to_json.cpp} Convert an ADFun Object to a Json AD Graph: Example and Test ############################################################ Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end to_json.cpp} */ // BEGIN C++ # include bool to_json(void) { bool ok = true; using CppAD::vector; using CppAD::AD; // // An AD graph example // node_1 : x[0] // node_2 : x[1] // node_3 : x[0] + x[1] // node_4 : (x[0] + x[1]) * x[1] // y[0] = (x[0] + x[1]) * x[1] // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'to_json example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'add', 'n_arg':2 } ,\n" " { 'op_code':2, 'name':'mul', 'n_arg':2 } ]\n" " ],\n" " 'n_dynamic_ind' : 0,\n" " 'n_variable_ind' : 2,\n" " 'constant_vec' : [ 0, [ ] ],\n" " 'op_usage_vec' : [ 2, [\n" " [ 1, 1, 2 ] ,\n" " [ 2, 3, 2 ] ]\n" " ],\n" " 'dependent_vec' : [ 1, [4] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x) = (x_0 + x_1) * x_1 CppAD::ADFun< AD > af; af.from_json(json); ok &= af.Domain() == 2; ok &= af.Range() == 1; // // Declare independent variables for a new recording vector< AD > ax(2); ax[0] = 1.0; ax[1] = 2.0; CppAD::Independent(ax); // // Compute f(x) af.Forward(0, ax); // // Compute z = f'(x) vector< AD > aw(1), az(2); aw[0] = 1.0; az = af.Reverse(1, aw); // // define g(x) = f'(x) CppAD::ADFun g(ax, az); // ------------------------------------------------------------------------ // Convert to Json graph and back json = g.to_json(); // std::cout << json; g.from_json(json); // ------------------------------------------------------------------------ // // Evaluate function corresponding to g vector x(2), z(2); x[0] = 3.0; x[1] = 4.0; z = g.Forward(0, x); // // should be derivative of f ok &= z[0] == x[1]; ok &= z[1] == x[0] + 2.0 * x[1]; // return ok; } // END C++ ================================================ FILE: example/json/unary_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin json_unary_op.cpp} Json Unary Operators: Example and Test ###################################### Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end json_unary_op.cpp} */ // BEGIN C++ # include bool unary_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : sin(p[0]) // node_5 : sin(x[0]) // node_6 : sin(c[0]) // node_7 : sin(p[0]) + sin(x[0]) + sin(c[0]) // y[0] = sin(p[0]) + sin(x[0]) + sin(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'unary_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'sin', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ -0.1 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // sin(p[0]) " [ 1, 2] ,\n" // sin(x[0]) " [ 1, 3] ,\n" // sin(c[0]) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // sin(p[0])+sin(x[0])+sin(c[0]) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = sin(p_0) + sin(x_0) + sin(c_0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function vector c(1); c[0] = -0.1; // // set independent variables and parameters vector p(1), x(1); p[0] = 0.2; x[0] = 0.3; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = std::sin(p[0]) + std::sin(x[0]) + std::sin(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // ----------------------------------------------------------------------- // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // ----------------------------------------------------------------------- // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // END C++ ================================================ FILE: example/multi_thread/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- # initialize list as empty SET(check_example_multi_thread_depends "") # Define the operation # CHECK_LIBRARY_EXISTS (LIBRARY FUNCTION LOCATION VARIABLE) # LIBRARY - the name of the library you are looking for # FUNCTION - the name of the function # LOCATION - location where the library should be found # VARIABLE - variable to store the result INCLUDE(CheckLibraryExists) # # openmp IF ( OpenMP_CXX_FOUND ) # OpenMP_CXX_FLAGS - flags to add to the CXX compiler for OpenMP support ADD_SUBDIRECTORY(openmp) ENDIF ( OpenMP_CXX_FOUND ) # # pthread_lib_path SET( pthread_ok FALSE ) FIND_LIBRARY(pthread_lib_path pthread) MESSAGE(STATUS "pthread library path = ${pthread_lib_path}") IF ( pthread_lib_path ) SET(CMAKE_REQUIRED_DEFINITIONS "") SET(CMAKE_REQUIRED_FLAGS "") SET(CMAKE_REQUIRED_INCLUDES "") SET(CMAKE_REQUIRED_LIBRARIES "") CHECK_LIBRARY_EXISTS( pthread pthread_barrier_wait ${pthread_lib_path} pthread_ok ) IF ( pthread_ok ) ADD_SUBDIRECTORY(pthread) ENDIF ( pthread_ok ) ENDIF ( pthread_lib_path ) IF( NOT pthread_ok ) SET( pthread_lib_path "" ) ENDIF( NOT pthread_ok ) # # sthread # Put sthread after pthread because it can sometimes require pthread_lib_path # https://stackoverflow.com/questions/46994982 ADD_SUBDIRECTORY(sthread) # # bthread IF ( Boost_FOUND ) SET(CMAKE_REQUIRED_DEFINITIONS "") SET(CMAKE_REQUIRED_FLAGS "") SET(CMAKE_REQUIRED_INCLUDES "${Boost_INCLUDE_DIRS}") SET(CMAKE_REQUIRED_LIBRARIES "${Boost_LIBRARIES}") SET(source " # include int main(void) { boost::barrier wait(1); return 0; }" ) compile_source_test(${cmake_defined_ok} "${source}" boost_multi_thread_ok ) # IF( boost_multi_thread_ok ) ADD_SUBDIRECTORY(bthread) ENDIF( boost_multi_thread_ok ) # ENDIF ( Boost_FOUND ) IF( NOT( "${check_example_multi_thread_depends}" STREQUAL "" ) ) # # check_example_multi_thread ADD_CUSTOM_TARGET( check_example_multi_thread DEPENDS ${check_example_multi_thread_depends} ) MESSAGE(STATUS "make check_example_multi_thread: available") # # Change check depends in parent environment add_to_list(check_example_depends check_example_multi_thread) SET(check_example_depends "${check_example_depends}" PARENT_SCOPE) # ENDIF( NOT( "${check_example_multi_thread_depends}" STREQUAL "" ) ) ================================================ FILE: example/multi_thread/bthread/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the example/multi_thread/bthread directory tests # Inherit build type from ../CMakeList.txt # Local include directories to search (not in package_prefix/include) INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/.. ) # add_executable( [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] # source1 source2 ... sourceN # ) SET(source_list ../thread_test.cpp ../team_example.cpp ../harmonic.cpp ../multi_atomic_two.cpp ../multi_atomic_three.cpp ../multi_chkpoint_one.cpp ../multi_chkpoint_two.cpp ../multi_newton.cpp a11c_bthread.cpp get_started.cpp team_bthread.cpp ) set_compile_flags( example_multi_thread_bthread "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE( example_multi_thread_bthread EXCLUDE_FROM_ALL ${source_list} ) # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_multi_thread_bthread ${cppad_lib} ${colpack_libs} ${Boost_LIBRARIES} ) # check_example_multi_thread_bthread add_check_executable(check_example_multi_thread bthread get_started) ================================================ FILE: example/multi_thread/bthread/a11c_bthread.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin a11c_bthread.cpp} A Simple Boost Thread Example and Test ###################################### Purpose ******* This example just demonstrates Boost threads and does not use CppAD at all. Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end a11c_bthread.cpp} ---------------------------------------------------------------------------- */ // BEGIN C++ # include # include # include # include // for size_t # include // # define NUMBER_THREADS 4 namespace { // Begin empty namespace class worker_t { private: int n_; float* a_; float* b_; public: void setup(size_t n, float* a, float* b) { n_ = static_cast(n); a_ = a; b_ = b; } // Beginning of Example A.1.1.1c of OpenMP 2.5 standard document void a1(int n, float *a, float *b) { int i; for(i = 1; i < n; i++) b[i] = (a[i] + a[i-1]) / 2.0f; return; } // End of Example A.1.1.1c of OpenMP 2.5 standard document void operator()() { a1(n_, a_, b_); } }; } bool a11c(void) { bool ok = true; // Test setup size_t i, j, n_total = 10; float *a = new float[n_total]; float *b = new float[n_total]; for(i = 0; i < n_total; i++) a[i] = float(i); // number of threads size_t number_threads = NUMBER_THREADS; // set of workers worker_t worker[NUMBER_THREADS]; // threads for each worker boost::thread* bthread[NUMBER_THREADS]; // Break the work up into sub work for each thread size_t n = n_total / number_threads; size_t n_tmp = n; float* a_tmp = a; float* b_tmp = b; worker[0].setup(n_tmp, a_tmp, b_tmp); for(j = 1; j < number_threads; j++) { n_tmp = n + 1; a_tmp = a_tmp + n - 1; b_tmp = b_tmp + n - 1; if( j == (number_threads - 1) ) n_tmp = n_total - j * n + 1; worker[j].setup(n_tmp, a_tmp, b_tmp); // create this thread bthread[j] = new boost::thread(worker[j]); } // do this threads portion of the work worker[0](); // wait for other threads to finish for(j = 1; j < number_threads; j++) { bthread[j]->join(); delete bthread[j]; } // check the result float eps = 100.f * std::numeric_limits::epsilon(); for(i = 1; i < n ; i++) ok &= std::fabs( (2. * b[i] - a[i] - a[i-1]) / b[i] ) <= eps; delete [] a; delete [] b; return ok; } // END C++ ================================================ FILE: example/multi_thread/bthread/get_started.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin bthread_get_started.cpp} {xrst_template , example/multi_thread/template/get_started.xrst title: Getting Started Using @Name@ Threads With CppAD start source code after: // BEGIN_C++ end source code before: // END_C++ @Name@ , Boost @####@ , ##### @DEFAULT@ , USE_DEFAULT_ADFUN_CONSTRUCTOR } {xrst_end bthread_get_started.cpp} ------------------------------------------------------------------------------ */ // BEGIN_C++ # include # include # define USE_DEFAULT_ADFUN_CONSTRUCTOR 1 namespace { // // d_vector, ad_vector, fun_vector, bthread_vector typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR( CppAD::AD ) ad_vector; typedef CPPAD_TESTVECTOR( CppAD::ADFun ) fun_vector; typedef CPPAD_TESTVECTOR( boost::thread* ) bthread_vector; // // std::vector does not support the data method; see // https://en.cppreference.com/w/cpp/container/vector_bool // 'Does not necessarily store its elements as a contiguous array.' typedef CppAD::vector b_vector; // // thread_specific_data_ void cleanup(size_t* thread_num) { delete thread_num; return; } boost::thread_specific_ptr thread_specific_data_(cleanup); // // sequential_execution_ bool sequential_execution_ = true; // // in_parallel bool in_parallel(void) { return ! sequential_execution_; } // // thread_number size_t thread_number(void) { // return thread_all_[thread_num].thread_num return *thread_specific_data_.get(); } // // partial double partial( CppAD::ADFun& f, size_t j, const d_vector& x ) { size_t nx = x.size(); d_vector dx(nx), dy(1); for(size_t k = 0; k < nx; ++k) dx[k] = 0.0; dx[j] = 1.0; f.Forward(0, x); dy = f.Forward(1, dx); return dy[0]; } // // run_one_thread void run_one_thread( size_t thread_num , CppAD::ADFun* f_ptr , size_t j_begin , size_t j_end , const d_vector* x_ptr , d_vector* Jac_ptr , bool* ok_ptr ) { // // x, Jac, ok CppAD::ADFun& f = *f_ptr; const d_vector& x = *x_ptr; d_vector& Jac = *Jac_ptr; bool& ok = *ok_ptr; // // thread_specific_data_ // This sets up the thread_number function for this thread. if( thread_num != 0 ) { thread_specific_data_.reset(new size_t(thread_num) ); ok &= thread_number() == thread_num; } // // f // This will cause an assert if Taylor coefficients were allocated // by a different thread. f.capacity_order(0); // // Jac for(size_t j = j_begin; j < j_end; ++j) Jac[j] = partial(f, j, x); } } bool get_started(void) { // ok bool ok = true; // // eps99 double eps99 = 99.0 * std::numeric_limits::epsilon(); // // nx, ax size_t nx = 10; ad_vector ax(nx); for(size_t j = 0; j < nx; ++j) ax[j] = 1.0; CppAD::Independent(ax); // // fun ad_vector ay(1); ay[0] = ax[0]; for(size_t j = 1; j < nx; ++j) ay[0] *= ax[j]; # if USE_DEFAULT_ADFUN_CONSTRUCTOR CppAD::ADFun fun; fun.Dependent(ax, ay); # else // This allocates memory for first order Taylor coefficients using thread 0. // An assert will occur at f.capacity_order(0) in run_one_thread when // it is called by a different thread. CppAD::ADFun fun(ax, ay); # endif // // num_threads, f_thread, ok_thread size_t num_threads = 4; fun_vector f_thread(num_threads); b_vector ok_thread(num_threads); for(size_t thread_num = 0; thread_num < num_threads; ++thread_num) { f_thread[thread_num] = fun; ok_thread[thread_num] = true; } // // x d_vector x(nx); for(size_t j = 0; j < nx; ++j) x[j] = 1.0 + 1.0 / double(j+1); // // thread_specific_data_ // must be set for this thread before calling parall_setup or parallel_ad { size_t thread_num = 0; thread_specific_data_.reset(new size_t(thread_num) ); ok &= thread_number() == thread_num; } // // parallel_setup CppAD::thread_alloc::parallel_setup( num_threads, in_parallel, thread_number ); // // parallel_ad CppAD::parallel_ad(); // // hold_memory // optional and may improve speed if you do a lot of memory allocation CppAD::thread_alloc::hold_memory(true); // // thread_ptr bthread_vector thread_ptr(num_threads - 1); // // Jac d_vector Jac(nx); // // n_per_thread, n_extra size_t n_per_thread = nx / num_threads; size_t n_extra = nx % num_threads; // // sequential_execution_ sequential_execution_ = false; ok &= in_parallel(); // // Jac // Launch num_threads - 1 boost threads size_t j_begin = n_per_thread; size_t j_end; for(size_t thread_num = 1; thread_num < num_threads; ++thread_num) { j_end = j_begin + n_per_thread; if( thread_num <= n_extra ) ++j_end; CppAD::ADFun* f_ptr = &f_thread[thread_num]; bool* ok_ptr = &ok_thread[thread_num]; thread_ptr[thread_num-1] = new boost::thread( run_one_thread, thread_num, f_ptr, j_begin, j_end, &x, &Jac, ok_ptr ); j_begin = j_end; } ok &= j_end == nx; { // run master thread's indices size_t thread_num = 0; j_begin = 0; j_end = j_begin + n_per_thread; CppAD::ADFun* f_ptr = &f_thread[thread_num]; bool* ok_ptr = &ok_thread[thread_num]; run_one_thread(thread_num, f_ptr, j_begin, j_end, &x, &Jac, ok_ptr); } // wait for other threads to finish for(size_t thread_num = 1; thread_num < num_threads; ++thread_num) thread_ptr[thread_num-1]->join(); // // sequential_execution_ sequential_execution_ = true; CppAD::thread_alloc::parallel_setup(1, nullptr, nullptr); ok &= ! in_parallel(); // // hold_memory // free memory for other threads before this (the master thread) ok &= thread_number() == 0; CppAD::thread_alloc::hold_memory(false); for(size_t thread_num = 1; thread_num < num_threads; ++thread_num) { CppAD::thread_alloc::free_available(thread_num); ok &= ok_thread[thread_num]; } ok &= ok_thread[0]; CppAD::thread_alloc::free_available(0); // // j for(size_t j = 0; j < nx; ++j) { // // check double check = 1.0; for(size_t k = 0; k < nx; ++k) if(k != j) check *= x[k]; // // ok ok &= CppAD::NearEqual(Jac[j], check, eps99, eps99); } return ok; } // END_C++ ================================================ FILE: example/multi_thread/bthread/team_bthread.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin team_bthread.cpp} Boost Thread Implementation of a Team of AD Threads ################################################### See :ref:`team_thread.hpp-name` for this routines specifications. {xrst_literal // BEGIN C++ // END C++ } {xrst_end team_bthread.cpp} */ // BEGIN C++ # include # include # include "../team_thread.hpp" # define MAX_NUMBER_THREADS 48 namespace { using CppAD::thread_alloc; // number of threads in the team size_t num_threads_ = 1; // no need to cleanup up thread specific data void cleanup(size_t*) { return; } // thread specific pointer the thread number (initialize as null) boost::thread_specific_ptr thread_num_ptr_(cleanup); // type of the job currently being done by each thread enum thread_job_t { init_enum, work_enum, join_enum } thread_job_; // barrier used to wait for other threads to finish work boost::barrier* wait_for_work_ = nullptr; // barrier used to wait for master thread to set next job boost::barrier* wait_for_job_ = nullptr; // Are we in sequential mode; i.e., other threads are waiting for // master thread to set up next job ? bool sequential_execution_ = true; // structure with information for one thread typedef struct { // The thread boost::thread* bthread; // CppAD thread number as global (pointed to by thread_num_ptr_) size_t thread_num; // true if no error for this thread, false otherwise. bool ok; } thread_one_t; // vector with information for all threads thread_one_t thread_all_[MAX_NUMBER_THREADS]; // pointer to function that does the work for one thread void (* worker_)(void) = nullptr; // --------------------------------------------------------------------- // in_parallel() bool in_parallel(void) { return ! sequential_execution_; } // --------------------------------------------------------------------- // thread_number() size_t thread_number(void) { // return thread_all_[thread_num].thread_num return *thread_num_ptr_.get(); } // -------------------------------------------------------------------- // function that gets called by boost thread constructor void thread_work(size_t thread_num) { bool ok = wait_for_work_ != nullptr; ok &= wait_for_job_ != nullptr; ok &= thread_num != 0; // thread specific storage of thread number for this thread thread_num_ptr_.reset(& thread_all_[thread_num].thread_num ); while( true ) { // Use wait_for_jog_ to give master time in sequential mode // (so it can change global information like thread_job_) wait_for_job_->wait(); // case where we are terminating this thread (no more work) if( thread_job_ == join_enum) break; // only other case once wait_for_job_ has been completed (so far) ok &= thread_job_ == work_enum; worker_(); // Use wait_for_work_ to inform master that our work is done and // that this thread will not use global information until // passing its barrier wait_for_job_ above. wait_for_work_->wait(); } thread_all_[thread_num].ok &= ok; return; } } bool team_create(size_t num_threads) { bool ok = true;; if( num_threads > MAX_NUMBER_THREADS ) { std::cerr << "team_create: num_threads greater than "; std::cerr << MAX_NUMBER_THREADS << std::endl; exit(1); } // check that we currently do not have multiple threads running ok = num_threads_ == 1; ok &= wait_for_work_ == nullptr; ok &= wait_for_job_ == nullptr; ok &= sequential_execution_; size_t thread_num; for(thread_num = 0; thread_num < num_threads; thread_num++) { // Each thread gets a pointer to its version of this thread_num // so it knows which section of thread_all it is working with thread_all_[thread_num].thread_num = thread_num; // initialize thread_all_[thread_num].ok = true; thread_all_[0].bthread = nullptr; } // Finish setup of thread_all_ for this thread thread_num_ptr_.reset(& thread_all_[0].thread_num); // Now that thread_number() has necessary information for the case // num_threads_ == 1, and while still in sequential mode, // call setup for using CppAD::AD in parallel mode. thread_alloc::parallel_setup(num_threads, in_parallel, thread_number); thread_alloc::hold_memory(true); CppAD::parallel_ad(); // now change num_threads_ to its final value. num_threads_ = num_threads; // initialize two barriers, one for work done, one for new job ready wait_for_work_ = new boost::barrier( (unsigned int) num_threads ); wait_for_job_ = new boost::barrier( (unsigned int) num_threads ); // initial job for the threads thread_job_ = init_enum; if( num_threads > 1 ) sequential_execution_ = false; // This master thread is already running, we need to create // num_threads - 1 more threads for(thread_num = 1; thread_num < num_threads; thread_num++) { // Create the thread with thread number equal to thread_num thread_all_[thread_num].bthread = new boost::thread(thread_work, thread_num); } // Current state is other threads are at wait_for_job_. // This master thread (thread zero) has not completed wait_for_job_ sequential_execution_ = true; return ok; } bool team_work(void worker(void)) { // Current state is other threads are at wait_for_job_. // This master thread (thread zero) has not completed wait_for_job_ bool ok = sequential_execution_; ok &= thread_number() == 0; ok &= wait_for_work_ != nullptr; ok &= wait_for_job_ != nullptr; // set global version of this work routine worker_ = worker; // set the new job that other threads are waiting for thread_job_ = work_enum; // Enter parallel execution when master thread calls wait_for_job_ if( num_threads_ > 1 ) sequential_execution_ = false; wait_for_job_->wait(); // Now do the work in this thread and then wait // until all threads have completed wait_for_work_ worker(); wait_for_work_->wait(); // Current state is other threads are at wait_for_job_. // This master thread (thread zero) has not completed wait_for_job_ sequential_execution_ = true; size_t thread_num; for(thread_num = 0; thread_num < num_threads_; thread_num++) ok &= thread_all_[thread_num].ok; return ok; } bool team_destroy(void) { // Current state is other threads are at wait_for_job_. // This master thread (thread zero) has not completed wait_for_job_ bool ok = sequential_execution_; ok &= thread_number() == 0; ok &= wait_for_work_ != nullptr; ok &= wait_for_job_ != nullptr; // set the new job that other threads are waiting for thread_job_ = join_enum; // enter parallel execution soon as master thread completes wait_for_job_ if( num_threads_ > 1 ) sequential_execution_ = false; wait_for_job_->wait(); // now wait for the other threads to be destroyed size_t thread_num; ok &= thread_all_[0].bthread == nullptr; for(thread_num = 1; thread_num < num_threads_; thread_num++) { thread_all_[thread_num].bthread->join(); delete thread_all_[thread_num].bthread; thread_all_[thread_num].bthread = nullptr; } // now we are down to just the master thread (thread zero) sequential_execution_ = true; // destroy wait_for_work_ delete wait_for_work_; wait_for_work_ = nullptr; // destroy wait_for_job_ delete wait_for_job_; wait_for_job_ = nullptr; // check ok before changing num_threads_ for(thread_num = 0; thread_num < num_threads_; thread_num++) ok &= thread_all_[thread_num].ok; // now inform CppAD that there is only one thread num_threads_ = 1; thread_alloc::parallel_setup(num_threads_, nullptr, nullptr); thread_alloc::hold_memory(false); CppAD::parallel_ad(); return ok; } const char* team_name(void) { return "bthread"; } // END C++ ================================================ FILE: example/multi_thread/harmonic.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin harmonic_common} Common Variables Used by Multi-threading Sum of 1/i ################################################### Purpose ******* This source code defines the common include files, defines, and variables that are used by the summation that defines the harmonic series .. math:: 1 + 1/2 + 1/3 + ... + 1/n Source ****** {xrst_literal // BEGIN COMMON C++ // END COMMON C++ } {xrst_end harmonic_common} */ // BEGIN COMMON C++ # include # include "harmonic.hpp" # include "team_thread.hpp" # define MAX_NUMBER_THREADS 48 namespace { using CppAD::thread_alloc; // fast multi-threadeding memory allocator // Number of threads, set by previous call to harmonic_time // (zero means one thread with no multi-threading setup) size_t num_threads_ = 0; // value of mega_sum, set by previous call to harmonic_time. size_t mega_sum_; // structure with information for one thread typedef struct { // index to start summation at (worker input) // set by previous call to harmonic_setup size_t start; // index to end summation at (worker input) // set by previous call to harmonic_setup size_t stop; // summation for this thread // set by worker double sum; // false if an error occurs, true otherwise // set by worker bool ok; } work_one_t; // vector with information for all threads // (use pointers instead of values to avoid false sharing) work_one_t* work_all_[MAX_NUMBER_THREADS]; } // END COMMON C++ /* ------------------------------------------------------------------------------ {xrst_begin harmonic_setup} Set Up Multi-threading Sum of 1/i ################################# Syntax ****** | *ok* = ``harmonic_setup`` ( *num_sum* ) Purpose ******* This routine does the setup for splitting the summation that defines the harmonic series .. math:: 1 + 1/2 + 1/3 + ... + 1/n into separate parts for each thread. Thread ****** It is assumed that this function is called by thread zero, and all the other threads are blocked (waiting). num_sum ******* The argument *num_sum* has prototype ``size_t`` *num_sum* It specifies the value of :math:`n` in the summation. Source ****** {xrst_literal // BEGIN SETUP C++ // END SETUP C++ } {xrst_end harmonic_setup} */ // BEGIN SETUP C++ namespace { bool harmonic_setup(size_t num_sum) { // sum = 1/num_sum + 1/(num_sum-1) + ... + 1 size_t num_threads = std::max(num_threads_, size_t(1)); bool ok = num_threads == thread_alloc::num_threads(); ok &= thread_alloc::thread_num() == 0; ok &= num_sum >= num_threads; // for(size_t thread_num = 0; thread_num < num_threads; thread_num++) { // allocate separate memory for this thread to avoid false sharing size_t min_bytes(sizeof(work_one_t)), cap_bytes; void* v_ptr = thread_alloc::get_memory(min_bytes, cap_bytes); work_all_[thread_num] = static_cast(v_ptr); // // in case this thread's worker does not get called work_all_[thread_num]->ok = false; // parameters that define the work for this and previous thread if( thread_num == 0 ) work_all_[0]->start = 1; else { size_t index = (num_sum * thread_num) / num_threads; work_all_[thread_num-1]->stop = index; work_all_[thread_num]->start = index; } } work_all_[num_threads-1]->stop = num_sum + 1; return ok; } } // END SETUP C++ /* ------------------------------------------------------------------------------- {xrst_begin harmonic_worker} Do One Thread's Work for Sum of 1/i ################################### Syntax ****** ``harmonic_worker`` () Purpose ******* This routines computes the sum the summation that defines the harmonic series 1/ *start* + 1/( *start* +1) + ... + 1/( *end* ``-1`` ) start ***** This is the value of the :ref:`harmonic_common-name` information *start* = ``work_all_`` [ *thread_num* ] ``->start`` end *** This is the value of the :ref:`harmonic_common-name` information *end* = ``work_all_`` [ *thread_num* ] ``->end`` thread_num ********** This is the number for the current thread; see :ref:`thread_num` . Source ****** {xrst_literal // BEGIN WORKER C++ // END WORKER C++ } {xrst_end harmonic_worker} */ // BEGIN WORKER C++ namespace { void harmonic_worker(void) { // sum = 1/(stop-1) + 1/(stop-2) + ... + 1/start size_t thread_num = thread_alloc::thread_num(); size_t num_threads = std::max(num_threads_, size_t(1)); bool ok = thread_num < num_threads; size_t start = work_all_[thread_num]->start; size_t stop = work_all_[thread_num]->stop; double sum = 0.; ok &= stop > start; size_t i = stop; while( i > start ) { i--; sum += 1. / double(i); } work_all_[thread_num]->sum = sum; work_all_[thread_num]->ok = ok; } } // END WORKER C++ /* ------------------------------------------------------------------------------- {xrst_begin harmonic_takedown} Take Down Multi-threading Sum of 1/i #################################### Syntax ****** *ok* = ``harmonic_takedown`` ( *sum* ) Purpose ******* This routine does the takedown for splitting the summation that defines the harmonic series .. math:: s = 1 + 1/2 + 1/3 + ... + 1/n into separate parts for each thread; see :ref:`harmonic_setup-name` . Thread ****** It is assumed that this function is called by thread zero, and all the other threads have completed their work and are blocked (waiting). sum *** This argument has prototype ``double&`` *sum* The input value of the argument does not matter. Upon return it is the value of the summation; i.e. :math:`s`. Source ****** {xrst_literal // BEGIN TAKEDOWN C++ // END TAKEDOWN C++ } {xrst_end harmonic_takedown} */ // BEGIN TAKEDOWN C++ namespace { bool harmonic_takedown(double& sum) { // sum = 1/num_sum + 1/(num_sum-1) + ... + 1 bool ok = true; ok &= thread_alloc::thread_num() == 0; size_t num_threads = std::max(num_threads_, size_t(1)); sum = 0.; // // go down so that free memory for other threads before memory for master size_t thread_num = num_threads; while(thread_num--) { // check that this tread was ok with the work it did ok &= work_all_[thread_num]->ok; // // add this threads contribution to the sum sum += work_all_[thread_num]->sum; // // delete problem specific information void* v_ptr = static_cast( work_all_[thread_num] ); thread_alloc::return_memory( v_ptr ); // // check that there is no longer any memory inuse by this thread // (for general applications, the master might still be using memory) ok &= thread_alloc::inuse(thread_num) == 0; // // return all memory being held for future use by this thread thread_alloc::free_available(thread_num); } return ok; } } // END TAKEDOWN C++ /* ------------------------------------------------------------------------------ {xrst_begin harmonic_sum} Multi-Threaded Implementation of Summation of 1/i ################################################# Syntax ****** *ok* = ``harmonic_sum`` ( *sum* , *num_sum* ) Purpose ******* Multi-threaded computation of the summation that defines the harmonic series .. math:: s = 1 + 1/2 + 1/3 + ... + 1/n Thread ****** It is assumed that this function is called by thread zero, and all the other threads are blocked (waiting). ok ** This return value has prototype ``bool`` *ok* If this return value is false, an error occurred during ``harmonic`` . sum *** This argument has prototype ``double&`` *sum* The input value of the argument does not matter. Upon return it is the value of the summation; i.e. :math:`s`. num_sum ******* This argument has prototype ``size_t`` *num_sum* It specifies the number of terms in the summation; i.e. :math:`n`. Source ****** {xrst_literal // BEGIN SUM C++ // END SUM C++ } {xrst_end harmonic_sum} */ // BEGIN SUM C++ namespace { bool harmonic_sum(double& sum, size_t num_sum) { // sum = 1/num_sum + 1/(num_sum-1) + ... + 1 bool ok = true; ok &= thread_alloc::thread_num() == 0; // setup the work for multi-threading ok &= harmonic_setup(num_sum); // now do the work for each thread if( num_threads_ > 0 ) team_work( harmonic_worker ); else harmonic_worker(); // combine the result for each thread and takedown the multi-threading. ok &= harmonic_takedown(sum); return ok; } } // END SUM C++ /* ------------------------------------------------------------------------------- {xrst_begin harmonic_time} Timing Test of Multi-Threaded Summation of 1/i ############################################## Syntax ****** | *ok* = ``harmonic_time`` ( | |tab| *time_out* , *test_time* , *num_threads* , *mega_sum* | ) Purpose ******* Runs a correctness and timing test for a multi-threaded computation of the summation that defines the harmonic series .. math:: 1 + 1/2 + 1/3 + ... + 1/n Thread ****** It is assumed that this function is called by thread zero in sequential mode; i.e., not :ref:`in_parallel` . ok ** This return value has prototype ``bool`` *ok* If it is true, ``harmonic_time`` passed the correctness test. Otherwise it is false. time_out ******** This argument has prototype ``double&`` *time_out* The input value of the argument does not matter. Upon return it is the number of wall clock seconds required for to compute the summation. test_time ********* Is the minimum amount of wall clock time that the test should take. The number of repeats for the test will be increased until this time is reached. The reported *time_out* is the total wall clock time divided by the number of repeats. num_threads *********** This argument has prototype ``size_t`` *num_threads* It specifies the number of threads that are available for this test. If it is zero, the test is run without the multi-threading environment and 1 == ``thread_alloc::num_threads`` () when ``harmonic_time`` is called. If it is non-zero, the test is run with the multi-threading and *num_threads* = ``thread_alloc::num_threads`` () when ``harmonic_time`` is called. mega_sum ******** This argument has prototype ``size_t&`` *mega_sum* and is greater than zero. The value :math:`n` in the summation is equal to :math:`10^6` times *mega_sum* . Source ****** {xrst_literal // BEGIN TIME C++ // END TIME C++ } {xrst_end harmonic_time} */ // BEGIN TIME C++ # include # include # include # include # include // Note there is no mention of parallel mode in the documentation for // speed_test (so it is safe to use without special consideration). # include namespace { // value of sum resulting from most recent call to test_once double sum_ = 0.; // void test_once(void) { if( mega_sum_ < 1 ) { std::cerr << "harmonic_time: mega_sum < 1" << std::endl; exit(1); } size_t num_sum = mega_sum_ * 1000000; bool ok = harmonic_sum(sum_, num_sum); if( ! ok ) { std::cerr << "harmonic: error" << std::endl; exit(1); } return; } // void test_repeat(size_t repeat) { size_t i; for(i = 0; i < repeat; i++) test_once(); return; } } // This is the only routine that is accessible outside of this file bool harmonic_time( double& time_out, double test_time, size_t num_threads, size_t mega_sum) { bool ok = true; ok &= thread_alloc::thread_num() == 0; // arguments passed to harmonic_sum num_threads_ = num_threads; mega_sum_ = mega_sum; // create team of threads ok &= thread_alloc::in_parallel() == false; if( num_threads > 0 ) { team_create(num_threads); ok &= num_threads == thread_alloc::num_threads(); } else { ok &= 1 == thread_alloc::num_threads(); } // run the test case and set the time return value time_out = CppAD::time_test(test_repeat, test_time); // destroy team of threads if( num_threads > 0 ) team_destroy(); ok &= thread_alloc::in_parallel() == false; // Correctness check double eps1000 = double(mega_sum_) * 1e3 * std::numeric_limits::epsilon(); size_t i = mega_sum_ * 1000000; double check = 0.; while(i) check += 1. / double(i--); ok &= std::fabs(sum_ - check) <= eps1000; return ok; } // END TIME C++ ================================================ FILE: example/multi_thread/harmonic.hpp ================================================ # ifndef CPPAD_EXAMPLE_MULTI_THREAD_HARMONIC_HPP # define CPPAD_EXAMPLE_MULTI_THREAD_HARMONIC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- bool harmonic_time( double& time_out, double test_time, size_t num_threads, size_t mega_sum ); # endif ================================================ FILE: example/multi_thread/harmonic.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin harmonic.cpp} Multi-Threading Harmonic Summation Example / Test ################################################# Source File *********** All of the routines below are located in the file :: example/multi_thread/harmonic.cpp Contents ******** {xrst_toc_table example/multi_thread/harmonic.cpp } {xrst_end harmonic.cpp} ================================================ FILE: example/multi_thread/multi_atomic_three.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin multi_atomic_three_user} Defines a atomic_three Operation that Computes Square Root ########################################################## Syntax ****** | ``atomic_user`` *a_square_root* | *a_square_root* ( *au* , *ay* ) Purpose ******* This atomic function operation computes a square root using Newton's method. It is meant to be very inefficient in order to demonstrate timing results. au ** This argument has prototype ``const`` *ADvector* & *au* where *ADvector* is a :ref:`simple vector class` with elements of type ``AD`` . The size of *au* is three. num_itr ======= We use the notation *num_itr* = ``size_t`` ( ``Integer`` ( *au* [0] ) ) for the number of Newton iterations in the computation of the square root function. The component *au* [0] must be a :ref:`glossary@Parameter` . y_initial ========= We use the notation *y_initial* = *au* [1] for the initial value of the Newton iterate. y_squared ========= We use the notation *y_squared* = *au* [2] for the value we are taking the square root of. ay ** This argument has prototype *ADvector* & *ay* The size of *ay* is one and *ay* [0] is the square root of *y_squared* . Limitations *********** Only zero order forward mode is implements for the ``atomic_user`` class. Source ****** {xrst_literal // BEGIN USER C++ // END USER C++ } {xrst_end multi_atomic_three_user} */ // BEGIN USER C++ // includes used by all source code in multi_atomic_three.cpp file # include # include "multi_atomic_three.hpp" # include "team_thread.hpp" // namespace { using CppAD::thread_alloc; // multi-threading memory allocator using CppAD::vector; // uses thread_alloc typedef CppAD::ad_type_enum ad_type_enum; // constant, dynamic or variable class atomic_user : public CppAD::atomic_three { public: // ctor atomic_user(void) : CppAD::atomic_three("atomic_square_root") { } private: // for_type bool for_type( const vector& parameter_u , const vector& type_u , vector& type_y ) override { bool ok = parameter_u.size() == 3; ok &= type_u.size() == 3; ok &= type_y.size() == 1; if( ! ok ) return false; ok &= type_u[0] < CppAD::variable_enum; if( ! ok ) return false; type_y[0] = std::max( type_u[0], type_u[1] ); type_y[0] = std::max( type_y[0], type_u[2] ); // return true; } // forward bool forward( const vector& parameter_u , const vector& type_u , size_t need_y , size_t order_low , size_t order_up , const vector& taylor_u , vector& taylor_y ) override { # ifndef NDEBUG size_t n = taylor_u.size() / (order_up + 1); size_t m = taylor_y.size() / (order_up + 1); assert( n == 3 ); assert( m == 1 ); # endif // only implementing zero order forward for this example if( order_up != 0 ) return false; // extract components of argument vector size_t num_itr = size_t( taylor_u[0] ); double y_initial = taylor_u[1]; double y_squared = taylor_u[2]; // Use Newton's method to solve f(y) = y^2 = y_squared double y_itr = y_initial; for(size_t itr = 0; itr < num_itr; itr++) { // solve (y - y_itr) * f'(y_itr) = y_squared - y_itr^2 double fp_itr = 2.0 * y_itr; y_itr = y_itr + (y_squared - y_itr * y_itr) / fp_itr; } // return the Newton approximation for f(y) = y_squared taylor_y[0] = y_itr; return true; } }; } // END USER C++ /* {xrst_begin multi_atomic_three_common} Multi-Threaded atomic_three Common Information ############################################## Purpose ******* This source code defines the common variables that are used by the ``multi_atomic_three_`` *name* functions. Source ****** {xrst_literal // BEGIN COMMON C++ // END COMMON C++ } {xrst_end multi_atomic_three_common} */ // BEGIN COMMON C++ namespace { // Number of threads, set by multi_atomic_three_time // (zero means one thread with no multi-threading setup) size_t num_threads_ = 0; // Number of Newton iterations, set by multi_atomic_three_time size_t num_itr_; // We can use one atomic_atomic function for all threads because // there is no member data that gets changed during worker call. // This needs to stay in scope for as long as a recording will use it. // We cannot be in parallel mode when this object is created or deleted. // We use a pointer so that there is no left over memory in thread zero. atomic_user* a_square_root_ = 0; // structure with information for one thread typedef struct { // used by worker to compute the square root, set by multi_atomic_three_setup CppAD::ADFun* fun; // // value we are computing square root of, set by multi_atomic_three_setup vector* y_squared; // // square root, set by worker vector* square_root; // // false if an error occurs, true otherwise, set by worker bool ok; } work_one_t; // // Vector with information for all threads // (uses pointers instead of values to avoid false sharing) // allocated by multi_atomic_three_setup, freed by multi_atomic_three_takedown work_one_t* work_all_[CPPAD_MAX_NUM_THREADS]; } // END COMMON C++ /* ------------------------------------------------------------------------------- {xrst_begin multi_atomic_three_setup} Multi-Threaded atomic_three Set Up ################################## Syntax ****** *ok* = ``multi_atomic_three_setup`` ( *y_squared* ) Purpose ******* This routine splits up the computation into the individual threads. Thread ****** It is assumed that this function is called by thread zero and all the other threads are blocked (waiting). y_squared ********* This argument has prototype ``const vector&`` *y_squared* and its size is equal to the number of equations to solve. It is the values that we are computing the square root of. ok ** This return value has prototype ``bool`` *ok* If it is false, ``multi_atomic_three_setup`` detected an error. Source ****** {xrst_literal // BEGIN SETUP C++ // END SETUP C++ } {xrst_end multi_atomic_three_setup} */ // BEGIN SETUP C++ namespace { bool multi_atomic_three_setup(const vector& y_squared) { using CppAD::AD; size_t num_threads = std::max(num_threads_, size_t(1)); bool ok = num_threads == thread_alloc::num_threads(); ok &= thread_alloc::thread_num() == 0; // // declare independent variable variable vector vector< AD > ax(1); ax[0] = 2.0; CppAD::Independent(ax); // // argument and result for atomic function vector< AD > au(3), ay(1); au[0] = AD( num_itr_ ); // num_itr au[1] = ax[0]; // y_initial au[2] = ax[0]; // y_squared // put atomic function operation in recording (*a_square_root_)(au, ay); // // f(u) = sqrt(u) CppAD::ADFun fun(ax, ay); // // number of square roots for each thread size_t per_thread = (y_squared.size() + num_threads - 1) / num_threads; size_t y_index = 0; // for(size_t thread_num = 0; thread_num < num_threads; thread_num++) { // allocate separate memory for each thread to avoid false sharing size_t min_bytes(sizeof(work_one_t)), cap_bytes; void* v_ptr = thread_alloc::get_memory(min_bytes, cap_bytes); work_all_[thread_num] = static_cast(v_ptr); // // Run constructor on work_all_[thread_num]->fun work_all_[thread_num]->fun = new CppAD::ADFun; // // Run constructor on work_all_[thread_num] vectors work_all_[thread_num]->y_squared = new vector; work_all_[thread_num]->square_root = new vector; // // Each worker gets a separate copy of fun. This is necessary because // the Taylor coefficients will be set by each thread. *(work_all_[thread_num]->fun) = fun; // // values we are computing square root of for this thread ok &= 0 == work_all_[thread_num]->y_squared->size(); for(size_t i = 0; i < per_thread; i++) if( y_index < y_squared.size() ) work_all_[thread_num]->y_squared->push_back(y_squared[y_index++]); // // set to false in case this thread's worker does not get called work_all_[thread_num]->ok = false; } ok &= y_index == y_squared.size(); // return ok; } } // END SETUP C++ /* ------------------------------------------------------------------------------ {xrst_begin multi_atomic_three_worker} Multi-Threaded atomic_three Worker ################################## Purpose ******* This routine does the computation for one thread. Source ****** {xrst_literal // BEGIN WORKER C++ // END WORKER C++ } {xrst_end multi_atomic_three_worker} */ // BEGIN WORKER C++ namespace { void multi_atomic_three_worker(void) { size_t thread_num = thread_alloc::thread_num(); size_t num_threads = std::max(num_threads_, size_t(1)); bool ok = thread_num < num_threads; // vector x(1), y(1); size_t n = work_all_[thread_num]->y_squared->size(); work_all_[thread_num]->square_root->resize(n); for(size_t i = 0; i < n; i++) { x[0] = (* work_all_[thread_num]->y_squared )[i]; y = work_all_[thread_num]->fun->Forward(0, x); // (* work_all_[thread_num]->square_root )[i] = y[0]; } work_all_[thread_num]->ok = ok; } } // END WORKER C++ /* ------------------------------------------------------------------------------ {xrst_begin multi_atomic_three_takedown} Multi-Threaded atomic_three Take Down ##################################### Syntax ****** *ok* = ``multi_atomic_three_takedown`` ( *square_root* ) Purpose ******* This routine gathers up the results for each thread and frees memory that was allocated by :ref:`multi_atomic_three_setup-name` . Thread ****** It is assumed that this function is called by thread zero and all the other threads are blocked (waiting). square_root *********** This argument has prototype ``vector&`` *square_root* The input value of *square_root* does not matter. Upon return, it has the same size and is the element by element square root of :ref:`multi_atomic_three_setup@y_squared` . ok ** This return value has prototype ``bool`` *ok* If it is false, ``multi_atomic_three_takedown`` detected an error. Source ****** {xrst_literal // BEGIN TAKEDOWN C++ // END TAKEDOWN C++ } {xrst_end multi_atomic_three_takedown} */ // BEGIN TAKEDOWN C++ namespace { bool multi_atomic_three_takedown(vector& square_root) { bool ok = true; ok &= thread_alloc::thread_num() == 0; size_t num_threads = std::max(num_threads_, size_t(1)); // // extract square roots in original order square_root.resize(0); for(size_t thread_num = 0; thread_num < num_threads; thread_num++) { // results for this thread size_t n = work_all_[thread_num]->square_root->size(); for(size_t i = 0; i < n; i++) square_root.push_back((* work_all_[thread_num]->square_root )[i]); } // // go down so that free memory for other threads before memory for master size_t thread_num = num_threads; while(thread_num--) { // check that this tread was ok with the work it did ok &= work_all_[thread_num]->ok; // // run destructor on vector object for this thread delete work_all_[thread_num]->y_squared; delete work_all_[thread_num]->square_root; // // run destructor on function object for this thread delete work_all_[thread_num]->fun; // // delete problem specific information void* v_ptr = static_cast( work_all_[thread_num] ); thread_alloc::return_memory( v_ptr ); // // check that there is no longer any memory inuse by this thread if( thread_num > 0 ) { ok &= 0 == thread_alloc::inuse(thread_num); // // return all memory being held for future use by this thread thread_alloc::free_available(thread_num); } } return ok; } } // END TAKEDOWN C++ /* {xrst_begin multi_atomic_three_run} Run Multi-Threaded atomic_three Calculation ########################################### Syntax ****** *ok* = ``multi_atomic_three_run`` ( *y_squared* , *square_root* ) Thread ****** It is assumed that this function is called by thread zero and all the other threads are blocked (waiting). y_squared ********* This argument has prototype ``const vector&`` *y_squared* and its size is equal to the number of threads. It is the values that we are computing the square root of. square_root *********** This argument has prototype ``vector&`` *square_root* The input value of *square_root* does not matter. Upon return, it has the same size and is the element by element square root of *y_squared* . ok ** This return value has prototype ``bool`` *ok* If it is false, ``multi_atomic_three_run`` detected an error. Source ****** {xrst_literal // BEGIN RUN C++ // END RUN C++ } {xrst_end multi_atomic_three_run} ------------------------------------------------------------------------------ */ // BEGIN RUN C++ namespace { bool multi_atomic_three_run( const vector& y_squared , vector& square_root ) { bool ok = true; ok &= thread_alloc::thread_num() == 0; // setup the work for multi-threading ok &= multi_atomic_three_setup(y_squared); // now do the work for each thread if( num_threads_ > 0 ) team_work( multi_atomic_three_worker ); else multi_atomic_three_worker(); // combine the result for each thread and takedown the multi-threading. ok &= multi_atomic_three_takedown(square_root); return ok; } } // END RUN C++ /* ------------------------------------------------------------------------------ {xrst_begin multi_atomic_three_time} Timing Test for Multi-Threaded atomic_three Calculation ####################################################### Syntax ****** | *ok* = ``multi_atomic_three_time`` ( | |tab| *time_out* , *test_time* , *num_threads* , *num_solve* | ) Thread ****** It is assumed that this function is called by thread zero in sequential mode; i.e., not :ref:`in_parallel` . time_out ******** This argument has prototype ``double&`` *time_out* Its input value of the argument does not matter. Upon return it is the number of wall clock seconds used by :ref:`multi_atomic_three_run-name` . test_time ********* This argument has prototype ``double`` *test_time* and is the minimum amount of wall clock time that the test should take. The number of repeats for the test will be increased until this time is reached. The reported *time_out* is the total wall clock time divided by the number of repeats. num_threads *********** This argument has prototype ``size_t`` *num_threads* It specifies the number of threads that are available for this test. If it is zero, the test is run without the multi-threading environment and 1 == ``thread_alloc::num_threads`` () If it is non-zero, the test is run with the multi-threading and *num_threads* = ``thread_alloc::num_threads`` () num_solve ********* This specifies the number of square roots that will be solved for. ok ** The return value has prototype ``bool`` *ok* If it is true, ``harmonic_time`` passed the correctness test and ``multi_atomic_three_time`` did not detect an error. Otherwise it is false. {xrst_end multi_atomic_three_time} */ // BEGIN TIME C++ namespace { // values we are taking the square root of vector y_squared_; // results of the square root calculations vector square_root_; // void test_once(void) { bool ok = multi_atomic_three_run(y_squared_, square_root_); if( ! ok ) { std::cerr << "multi_atomic_three_run: error" << std::endl; exit(1); } return; } // void test_repeat(size_t repeat) { size_t i; for(i = 0; i < repeat; i++) test_once(); return; } } // This is the only routine that is accessible outside of this file bool multi_atomic_three_time( double& time_out, double test_time, size_t num_threads, size_t num_solve ) { bool ok = true; // size_t initial_inuse = thread_alloc::inuse(0); // number of threads, zero for no multi-threading num_threads_ = num_threads; // number of Newton iterations num_itr_ = 20; // values we are talking the square root of y_squared_.resize(num_solve); for(size_t i_solve = 0; i_solve < num_solve; i_solve++) y_squared_[i_solve] = double(i_solve) + 2.0; // must create a_square_root_ sequential mode a_square_root_ = new atomic_user; // create team of threads ok &= thread_alloc::in_parallel() == false; if( num_threads > 0 ) { team_create(num_threads); ok &= num_threads == thread_alloc::num_threads(); } else { ok &= 1 == thread_alloc::num_threads(); } // run the test case and set the time return value time_out = CppAD::time_test(test_repeat, test_time); // destroy team of threads if( num_threads > 0 ) team_destroy(); ok &= thread_alloc::in_parallel() == false; // must delete a_square_root_ in sequential mode delete a_square_root_; // correctness check ok &= square_root_.size() == num_solve; double eps99 = 99.0 * std::numeric_limits::epsilon(); for(size_t i = 0; i < num_solve; i++) { double check = std::sqrt( y_squared_[i] ); ok &= std::fabs( square_root_[i] / check - 1.0 ) <= eps99; } y_squared_.clear(); square_root_.clear(); // // check that no static variables in this file are holding onto memory ok &= initial_inuse == thread_alloc::inuse(0); // return ok; } // END TIME C++ ================================================ FILE: example/multi_thread/multi_atomic_three.hpp ================================================ # ifndef CPPAD_EXAMPLE_MULTI_THREAD_MULTI_ATOMIC_THREE_HPP # define CPPAD_EXAMPLE_MULTI_THREAD_MULTI_ATOMIC_THREE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- bool multi_atomic_three_time( double& time_out, double test_time, size_t num_threads, size_t num_solve ); # endif ================================================ FILE: example/multi_thread/multi_atomic_three.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin multi_atomic_three.cpp} Multi-Threading atomic_three Example / Test ########################################### Source File *********** All of the routines below are located in the file :: example/multi_thread/multi_atomic_three.cpp Contents ******** {xrst_toc_table example/multi_thread/multi_atomic_three.cpp } {xrst_end multi_atomic_three.cpp} ================================================ FILE: example/multi_thread/multi_atomic_two.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin multi_atomic_two_user app} Defines a atomic_two Operation that Computes Square Root ######################################################## Syntax ****** | ``atomic_user`` *a_square_root* | *a_square_root* ( *au* , *ay* ) Purpose ******* This atomic function operation computes a square root using Newton's method. It is meant to be very inefficient in order to demonstrate timing results. au ** This argument has prototype ``const`` *ADvector* & *au* where *ADvector* is a :ref:`simple vector class` with elements of type ``AD`` . The size of *au* is three. num_itr ======= We use the notation *num_itr* = ``size_t`` ( ``Integer`` ( *au* [0] ) ) for the number of Newton iterations in the computation of the square root function. The component *au* [0] must be a :ref:`glossary@Parameter` . y_initial ========= We use the notation *y_initial* = *au* [1] for the initial value of the Newton iterate. y_squared ========= We use the notation *y_squared* = *au* [2] for the value we are taking the square root of. ay ** This argument has prototype *ADvector* & *ay* The size of *ay* is one and *ay* [0] is the square root of *y_squared* . Limitations *********** Only zero order forward mode is implements for the ``atomic_user`` class. Source ****** {xrst_literal // BEGIN USER C++ // END USER C++ } {xrst_end multi_atomic_two_user} */ // BEGIN USER C++ // includes used by all source code in multi_atomic_two.cpp file # include # include "multi_atomic_two.hpp" # include "team_thread.hpp" // namespace { using CppAD::thread_alloc; // fast multi-threading memory allocator using CppAD::vector; // uses thread_alloc class atomic_user : public CppAD::atomic_base { public: // ctor atomic_user(void) : CppAD::atomic_base("atomic_square_root") { } private: // forward mode routine called by CppAD bool forward( size_t p , size_t q , const vector& vu , vector& vy , const vector& tu , vector& ty ) override { # ifndef NDEBUG size_t n = tu.size() / (q + 1); size_t m = ty.size() / (q + 1); assert( n == 3 ); assert( m == 1 ); # endif // only implementing zero order forward for this example if( q != 0 ) return false; // extract components of argument vector size_t num_itr = size_t( tu[0] ); double y_initial = tu[1]; double y_squared = tu[2]; // check for setting variable information if( vu.size() > 0 ) { if( vu[0] ) return false; vy[0] = vu[1] || vu[2]; } // Use Newton's method to solve f(y) = y^2 = y_squared double y_itr = y_initial; for(size_t itr = 0; itr < num_itr; itr++) { // solve (y - y_itr) * f'(y_itr) = y_squared - y_itr^2 double fp_itr = 2.0 * y_itr; y_itr = y_itr + (y_squared - y_itr * y_itr) / fp_itr; } // return the Newton approximation for f(y) = y_squared ty[0] = y_itr; return true; } }; } // END USER C++ /* {xrst_begin multi_atomic_two_common app} Multi-Threaded atomic_two Common Information ############################################ Purpose ******* This source code defines the common variables that are used by the ``multi_atomic_two_`` *name* functions. Source ****** {xrst_literal // BEGIN COMMON C++ // END COMMON C++ } {xrst_end multi_atomic_two_common} */ // BEGIN COMMON C++ namespace { // Number of threads, set by multi_atomic_two_time // (zero means one thread with no multi-threading setup) size_t num_threads_ = 0; // Number of Newton iterations, set by multi_atomic_two_time size_t num_itr_; // We can use one atomic_atomic function for all threads because // there is no member data that gets changed during worker call. // This needs to stay in scope for as long as a recording will use it. // We cannot be in parallel mode when this object is created or deleted. // We use a pointer so that there is no left over memory in thread zero. atomic_user* a_square_root_ = 0; // structure with information for one thread typedef struct { // used by worker to compute the square root, set by multi_atomic_two_setup CppAD::ADFun* fun; // // value we are computing square root of, set by multi_atomic_two_setup vector* y_squared; // // square root, set by worker vector* square_root; // // false if an error occurs, true otherwise, set by worker bool ok; } work_one_t; // // Vector with information for all threads // (uses pointers instead of values to avoid false sharing) // allocated by multi_atomic_two_setup, freed by multi_atomic_two_takedown work_one_t* work_all_[CPPAD_MAX_NUM_THREADS]; } // END COMMON C++ /* ------------------------------------------------------------------------------- {xrst_begin multi_atomic_two_setup app} Multi-Threaded atomic_two Set Up ################################ Syntax ****** *ok* = ``multi_atomic_two_setup`` ( *y_squared* ) Purpose ******* This routine splits up the computation into the individual threads. Thread ****** It is assumed that this function is called by thread zero and all the other threads are blocked (waiting). y_squared ********* This argument has prototype ``const vector&`` *y_squared* and its size is equal to the number of equations to solve. It is the values that we are computing the square root of. ok ** This return value has prototype ``bool`` *ok* If it is false, ``multi_atomic_two_setup`` detected an error. Source ****** {xrst_literal // BEGIN SETUP C++ // END SETUP C++ } {xrst_end multi_atomic_two_setup} */ // BEGIN SETUP C++ namespace { bool multi_atomic_two_setup(const vector& y_squared) { using CppAD::AD; size_t num_threads = std::max(num_threads_, size_t(1)); bool ok = num_threads == thread_alloc::num_threads(); ok &= thread_alloc::thread_num() == 0; // // declare independent variable variable vector vector< AD > ax(1); ax[0] = 2.0; CppAD::Independent(ax); // // argument and result for atomic function vector< AD > au(3), ay(1); au[0] = AD( num_itr_ ); // num_itr au[1] = ax[0]; // y_initial au[2] = ax[0]; // y_squared // put atomic function operation in recording (*a_square_root_)(au, ay); // // f(u) = sqrt(u) CppAD::ADFun fun(ax, ay); // // number of square roots for each thread size_t per_thread = (y_squared.size() + num_threads - 1) / num_threads; size_t y_index = 0; // for(size_t thread_num = 0; thread_num < num_threads; thread_num++) { // allocate separate memory for each thread to avoid false sharing size_t min_bytes(sizeof(work_one_t)), cap_bytes; void* v_ptr = thread_alloc::get_memory(min_bytes, cap_bytes); work_all_[thread_num] = static_cast(v_ptr); // // Run constructor on work_all_[thread_num]->fun work_all_[thread_num]->fun = new CppAD::ADFun; // // Run constructor on work_all_[thread_num] vectors work_all_[thread_num]->y_squared = new vector; work_all_[thread_num]->square_root = new vector; // // Each worker gets a separate copy of fun. This is necessary because // the Taylor coefficients will be set by each thread. *(work_all_[thread_num]->fun) = fun; // // values we are computing square root of for this thread ok &= 0 == work_all_[thread_num]->y_squared->size(); for(size_t i = 0; i < per_thread; i++) if( y_index < y_squared.size() ) work_all_[thread_num]->y_squared->push_back(y_squared[y_index++]); // // set to false in case this thread's worker does not get called work_all_[thread_num]->ok = false; } ok &= y_index == y_squared.size(); // return ok; } } // END SETUP C++ /* ------------------------------------------------------------------------------ {xrst_begin multi_atomic_two_worker app} Multi-Threaded atomic_two Worker ################################ Purpose ******* This routine does the computation for one thread. Source ****** {xrst_literal // BEGIN WORKER C++ // END WORKER C++ } {xrst_end multi_atomic_two_worker} */ // BEGIN WORKER C++ namespace { void multi_atomic_two_worker(void) { size_t thread_num = thread_alloc::thread_num(); size_t num_threads = std::max(num_threads_, size_t(1)); bool ok = thread_num < num_threads; // vector x(1), y(1); size_t n = work_all_[thread_num]->y_squared->size(); work_all_[thread_num]->square_root->resize(n); for(size_t i = 0; i < n; i++) { x[0] = (* work_all_[thread_num]->y_squared )[i]; y = work_all_[thread_num]->fun->Forward(0, x); // (* work_all_[thread_num]->square_root )[i] = y[0]; } work_all_[thread_num]->ok = ok; } } // END WORKER C++ /* ------------------------------------------------------------------------------ {xrst_begin multi_atomic_two_takedown app} Multi-Threaded atomic_two Take Down ################################### Syntax ****** *ok* = ``multi_atomic_two_takedown`` ( *square_root* ) Purpose ******* This routine gathers up the results for each thread and frees memory that was allocated by :ref:`multi_atomic_two_setup-name` . Thread ****** It is assumed that this function is called by thread zero and all the other threads are blocked (waiting). square_root *********** This argument has prototype ``vector&`` *square_root* The input value of *square_root* does not matter. Upon return, it has the same size and is the element by element square root of :ref:`multi_atomic_two_setup@y_squared` . ok ** This return value has prototype ``bool`` *ok* If it is false, ``multi_atomic_two_takedown`` detected an error. Source ****** {xrst_literal // BEGIN TAKEDOWN C++ // END TAKEDOWN C++ } {xrst_end multi_atomic_two_takedown} */ // BEGIN TAKEDOWN C++ namespace { bool multi_atomic_two_takedown(vector& square_root) { bool ok = true; ok &= thread_alloc::thread_num() == 0; size_t num_threads = std::max(num_threads_, size_t(1)); // // extract square roots in original order square_root.resize(0); for(size_t thread_num = 0; thread_num < num_threads; thread_num++) { // results for this thread size_t n = work_all_[thread_num]->square_root->size(); for(size_t i = 0; i < n; i++) square_root.push_back((* work_all_[thread_num]->square_root )[i]); } // // go down so that free memory for other threads before memory for master size_t thread_num = num_threads; while(thread_num--) { // check that this tread was ok with the work it did ok &= work_all_[thread_num]->ok; // // run destructor on vector object for this thread delete work_all_[thread_num]->y_squared; delete work_all_[thread_num]->square_root; // // run destructor on function object for this thread delete work_all_[thread_num]->fun; // // delete problem specific information void* v_ptr = static_cast( work_all_[thread_num] ); thread_alloc::return_memory( v_ptr ); // // check that there is no longer any memory inuse by this thread if( thread_num > 0 ) { ok &= 0 == thread_alloc::inuse(thread_num); // // return all memory being held for future use by this thread thread_alloc::free_available(thread_num); } } return ok; } } // END TAKEDOWN C++ /* {xrst_begin multi_atomic_two_run app} Run Multi-Threaded atomic_two Calculation ######################################### Syntax ****** *ok* = ``multi_atomic_two_run`` ( *y_squared* , *square_root* ) Thread ****** It is assumed that this function is called by thread zero and all the other threads are blocked (waiting). y_squared ********* This argument has prototype ``const vector&`` *y_squared* and its size is equal to the number of threads. It is the values that we are computing the square root of. square_root *********** This argument has prototype ``vector&`` *square_root* The input value of *square_root* does not matter. Upon return, it has the same size and is the element by element square root of *y_squared* . ok ** This return value has prototype ``bool`` *ok* If it is false, ``multi_atomic_two_run`` detected an error. Source ****** {xrst_literal // BEGIN RUN C++ // END RUN C++ } {xrst_end multi_atomic_two_run} ------------------------------------------------------------------------------ */ // BEGIN RUN C++ namespace { bool multi_atomic_two_run( const vector& y_squared , vector& square_root ) { bool ok = true; ok &= thread_alloc::thread_num() == 0; // setup the work for multi-threading ok &= multi_atomic_two_setup(y_squared); // now do the work for each thread if( num_threads_ > 0 ) team_work( multi_atomic_two_worker ); else multi_atomic_two_worker(); // combine the result for each thread and takedown the multi-threading. ok &= multi_atomic_two_takedown(square_root); return ok; } } // END RUN C++ /* ------------------------------------------------------------------------------ {xrst_begin multi_atomic_two_time app} Timing Test for Multi-Threaded atomic_two Calculation ##################################################### Syntax ****** | *ok* = ``multi_atomic_two_time`` ( | |tab| *time_out* , *test_time* , *num_threads* , *num_solve* | ) Thread ****** It is assumed that this function is called by thread zero in sequential mode; i.e., not :ref:`in_parallel` . time_out ******** This argument has prototype ``double&`` *time_out* Its input value of the argument does not matter. Upon return it is the number of wall clock seconds used by :ref:`multi_atomic_two_run-name` . test_time ********* This argument has prototype ``double`` *test_time* and is the minimum amount of wall clock time that the test should take. The number of repeats for the test will be increased until this time is reached. The reported *time_out* is the total wall clock time divided by the number of repeats. num_threads *********** This argument has prototype ``size_t`` *num_threads* It specifies the number of threads that are available for this test. If it is zero, the test is run without the multi-threading environment and 1 == ``thread_alloc::num_threads`` () If it is non-zero, the test is run with the multi-threading and *num_threads* = ``thread_alloc::num_threads`` () num_solve ********* This specifies the number of square roots that will be solved for. ok ** The return value has prototype ``bool`` *ok* If it is true, ``harmonic_time`` passed the correctness test and ``multi_atomic_two_time`` did not detect an error. Otherwise it is false. {xrst_end multi_atomic_two_time} */ // BEGIN TIME C++ namespace { // values we are taking the square root of vector y_squared_; // results of the square root calculations vector square_root_; // void test_once(void) { bool ok = multi_atomic_two_run(y_squared_, square_root_); if( ! ok ) { std::cerr << "multi_atomic_two_run: error" << std::endl; exit(1); } return; } // void test_repeat(size_t repeat) { size_t i; for(i = 0; i < repeat; i++) test_once(); return; } } // This is the only routine that is accessible outside of this file bool multi_atomic_two_time( double& time_out, double test_time, size_t num_threads, size_t num_solve ) { bool ok = true; // size_t initial_inuse = thread_alloc::inuse(0); // number of threads, zero for no multi-threading num_threads_ = num_threads; // number of Newton iterations num_itr_ = 20; // values we are talking the square root of y_squared_.resize(num_solve); for(size_t i_solve = 0; i_solve < num_solve; i_solve++) y_squared_[i_solve] = double(i_solve) + 2.0; // must create a_square_root_ sequential mode a_square_root_ = new atomic_user; // create team of threads ok &= thread_alloc::in_parallel() == false; if( num_threads > 0 ) { team_create(num_threads); ok &= num_threads == thread_alloc::num_threads(); } else { ok &= 1 == thread_alloc::num_threads(); } // run the test case and set the time return value time_out = CppAD::time_test(test_repeat, test_time); // destroy team of threads if( num_threads > 0 ) team_destroy(); ok &= thread_alloc::in_parallel() == false; // must delete a_square_root_ in sequential mode delete a_square_root_; // free static variables in atomic_base class CppAD::atomic_base::clear(); // correctness check ok &= square_root_.size() == num_solve; double eps99 = 99.0 * std::numeric_limits::epsilon(); for(size_t i = 0; i < num_solve; i++) { double check = std::sqrt( y_squared_[i] ); ok &= std::fabs( square_root_[i] / check - 1.0 ) <= eps99; } y_squared_.clear(); square_root_.clear(); // // check that no static variables in this file are holding onto memory ok &= initial_inuse == thread_alloc::inuse(0); // return ok; } // END TIME C++ ================================================ FILE: example/multi_thread/multi_atomic_two.hpp ================================================ # ifndef CPPAD_EXAMPLE_MULTI_THREAD_MULTI_ATOMIC_TWO_HPP # define CPPAD_EXAMPLE_MULTI_THREAD_MULTI_ATOMIC_TWO_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- bool multi_atomic_two_time( double& time_out, double test_time, size_t num_threads, size_t num_solve ); # endif ================================================ FILE: example/multi_thread/multi_atomic_two.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin multi_atomic_two.cpp app} Multi-Threading atomic_two Example / Test ######################################### Deprecated 2019-08-05 ********************* This example is deprecated, use :ref:`multi_atomic_three.cpp-name` instead. Source File *********** All of the routines below are located in the file :: example/multi_thread/multi_atomic_two.cpp Contents ******** {xrst_toc_table example/multi_thread/multi_atomic_two.cpp } {xrst_end multi_atomic_two.cpp} ================================================ FILE: example/multi_thread/multi_chkpoint_one.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin multi_chkpoint_one_algo app} chkpoint_one Algorithm that Computes Square Root ################################################ Syntax ****** | *checkpoint_algo* ( *au* , *ay* ) Purpose ******* This algorithm computes a square root using Newton's method. It is meant to be very inefficient in order to demonstrate timing results. au ** This argument has prototype ``const`` *ADvector* & *au* where *ADvector* is a :ref:`simple vector class` with elements of type ``AD`` . The size of *au* is three. y_initial ========= We use the notation *y_initial* = *au* [0] for the initial value of the Newton iterate. y_squared ========= We use the notation *y_squared* = *au* [1] for the value we are taking the square root of. ay ** This argument has prototype *ADvector* & *ay* The size of *ay* is one and *ay* [0] is the square root of *y_squared* . Source ****** {xrst_literal // BEGIN ALGO C++ // END ALGO C++ } {xrst_end multi_chkpoint_one_algo} */ // BEGIN ALGO C++ // includes used by all source code in multi_chkpoint_one.cpp file # include # include "multi_chkpoint_one.hpp" # include "team_thread.hpp" // namespace { using CppAD::thread_alloc; // fast multi-threading memory allocator using CppAD::vector; // uses thread_alloc // typedef CppAD::AD a_double; void checkpoint_algo(const vector& au , vector& ay) { // extract components of argument vector a_double y_initial = au[0]; a_double y_squared = au[1]; // Use Newton's method to solve f(y) = y^2 = y_squared a_double y_itr = y_initial; for(size_t itr = 0; itr < 20; itr++) { // solve (y - y_itr) * f'(y_itr) = y_squared - y_itr^2 a_double fp_itr = 2.0 * y_itr; y_itr = y_itr + (y_squared - y_itr * y_itr) / fp_itr; } // return the Newton approximation for f(y) = y_squared ay[0] = y_itr; } } // END ALGO C++ /* {xrst_begin multi_chkpoint_one_common app} Multi-Threaded chkpoint_one Common Information ############################################## Purpose ******* This source code defines the common variables that are used by the ``multi_chkpoint_one_`` *name* functions. Source ****** {xrst_literal // BEGIN COMMON C++ // END COMMON C++ } {xrst_end multi_chkpoint_one_common} */ // BEGIN COMMON C++ namespace { // Number of threads, set by multi_chkpoint_one_time // (zero means one thread with no multi-threading setup) size_t num_threads_ = 0; // We can use one checkpoint function for all threads because // there is no member data that gets changed during worker call. // This needs to stay in scope for as long as a recording will use it. // We cannot be in parallel mode when this object is created or deleted. // We use a pointer so that there is no left over memory in thread zero. CppAD::checkpoint* a_square_root_ = nullptr; // structure with information for one thread typedef struct { // used by worker to compute the square root, // set by multi_chkpoint_one_setup CppAD::ADFun* fun; // // value we are computing square root of, set by multi_chkpoint_one_setup vector* y_squared; // // square root, set by worker vector* square_root; // // false if an error occurs, true otherwise, set by worker bool ok; } work_one_t; // // Vector with information for all threads // (uses pointers instead of values to avoid false sharing) // allocated by multi_chkpoint_one_setup, freed by multi_chkpoint_one_takedown work_one_t* work_all_[CPPAD_MAX_NUM_THREADS]; } // END COMMON C++ /* ------------------------------------------------------------------------------- {xrst_begin multi_chkpoint_one_setup app} Multi-Threaded chkpoint_one Set Up ################################## Syntax ****** *ok* = ``multi_chkpoint_one_setup`` ( *y_squared* ) Purpose ******* This routine splits up the computation into the individual threads. Thread ****** It is assumed that this function is called by thread zero and all the other threads are blocked (waiting). y_squared ********* This argument has prototype ``const vector&`` *y_squared* and its size is equal to the number of equations to solve. It is the values that we are computing the square root of. ok ** This return value has prototype ``bool`` *ok* If it is false, ``multi_chkpoint_one_setup`` detected an error. Source ****** {xrst_literal // BEGIN SETUP C++ // END SETUP C++ } {xrst_end multi_chkpoint_one_setup} */ // BEGIN SETUP C++ namespace { bool multi_chkpoint_one_setup(const vector& y_squared) { size_t num_threads = std::max(num_threads_, size_t(1)); bool ok = num_threads == thread_alloc::num_threads(); ok &= thread_alloc::thread_num() == 0; // // declare independent variable variable vector vector ax(1); ax[0] = 2.0; CppAD::Independent(ax); // // argument and result for checkpoint algorithm vector au(2), ay(1); au[0] = ax[0]; // y_initial au[1] = ax[0]; // y_squared // put user checkpoint function in recording (*a_square_root_)(au, ay); // f(u) = sqrt(u) CppAD::ADFun fun(ax, ay); // // number of square roots for each thread size_t per_thread = (y_squared.size() + num_threads - 1) / num_threads; size_t y_index = 0; // for(size_t thread_num = 0; thread_num < num_threads; thread_num++) { // allocate separate memory for each thread to avoid false sharing size_t min_bytes(sizeof(work_one_t)), cap_bytes; void* v_ptr = thread_alloc::get_memory(min_bytes, cap_bytes); work_all_[thread_num] = static_cast(v_ptr); // // Run constructor on work_all_[thread_num]->fun work_all_[thread_num]->fun = new CppAD::ADFun; // // Run constructor on work_all_[thread_num] vectors work_all_[thread_num]->y_squared = new vector; work_all_[thread_num]->square_root = new vector; // // Each worker gets a separate copy of fun. This is necessary because // the Taylor coefficients will be set by each thread. *(work_all_[thread_num]->fun) = fun; // // values we are computing square root of for this thread ok &= 0 == work_all_[thread_num]->y_squared->size(); for(size_t i = 0; i < per_thread; i++) if( y_index < y_squared.size() ) work_all_[thread_num]->y_squared->push_back(y_squared[y_index++]); // // set to false in case this thread's worker does not get called work_all_[thread_num]->ok = false; } ok &= y_index == y_squared.size(); // return ok; } } // END SETUP C++ /* ------------------------------------------------------------------------------ {xrst_begin multi_chkpoint_one_worker app} Multi-Threaded chkpoint_one Worker ################################## Purpose ******* This routine does the computation for one thread. Source ****** {xrst_literal // BEGIN WORKER C++ // END WORKER C++ } {xrst_end multi_chkpoint_one_worker} */ // BEGIN WORKER C++ namespace { void multi_chkpoint_one_worker(void) { size_t thread_num = thread_alloc::thread_num(); size_t num_threads = std::max(num_threads_, size_t(1)); bool ok = thread_num < num_threads; // vector x(1), y(1); size_t n = work_all_[thread_num]->y_squared->size(); work_all_[thread_num]->square_root->resize(n); for(size_t i = 0; i < n; i++) { x[0] = (* work_all_[thread_num]->y_squared )[i]; y = work_all_[thread_num]->fun->Forward(0, x); // (* work_all_[thread_num]->square_root )[i] = y[0]; } work_all_[thread_num]->ok = ok; } } // END WORKER C++ /* ------------------------------------------------------------------------------ {xrst_begin multi_chkpoint_one_takedown app} Multi-Threaded chkpoint_one Take Down ##################################### Syntax ****** *ok* = ``multi_chkpoint_one_takedown`` ( *square_root* ) Purpose ******* This routine gathers up the results for each thread and frees memory that was allocated by :ref:`multi_chkpoint_one_setup-name` . Thread ****** It is assumed that this function is called by thread zero and all the other threads are blocked (waiting). square_root *********** This argument has prototype ``vector&`` *square_root* The input value of *square_root* does not matter. Upon return, it has the same size and is the element by element square root of :ref:`multi_chkpoint_one_setup@y_squared` . ok ** This return value has prototype ``bool`` *ok* If it is false, ``multi_chkpoint_one_takedown`` detected an error. Source ****** {xrst_literal // BEGIN TAKEDOWN C++ // END TAKEDOWN C++ } {xrst_end multi_chkpoint_one_takedown} */ // BEGIN TAKEDOWN C++ namespace { bool multi_chkpoint_one_takedown(vector& square_root) { bool ok = true; ok &= thread_alloc::thread_num() == 0; size_t num_threads = std::max(num_threads_, size_t(1)); // // extract square roots in original order square_root.resize(0); for(size_t thread_num = 0; thread_num < num_threads; thread_num++) { // results for this thread size_t n = work_all_[thread_num]->square_root->size(); for(size_t i = 0; i < n; i++) square_root.push_back((* work_all_[thread_num]->square_root )[i]); } // // go down so that free memory for other threads before memory for master size_t thread_num = num_threads; while(thread_num--) { // check that this tread was ok with the work it did ok &= work_all_[thread_num]->ok; // // run destructor on vector object for this thread delete work_all_[thread_num]->y_squared; delete work_all_[thread_num]->square_root; // // run destructor on function object for this thread delete work_all_[thread_num]->fun; // // delete problem specific information void* v_ptr = static_cast( work_all_[thread_num] ); thread_alloc::return_memory( v_ptr ); // // Note that checkpoint object has memory connect to each thread // thread_alloc::inuse(thread_num) cannot be zero until it is deleted if( thread_num > 0 ) { ok &= thread_alloc::inuse(thread_num) > 0; // // return all memory that is not in use and // but being held for future use by this thread thread_alloc::free_available(thread_num); } } return ok; } } // END TAKEDOWN C++ /* {xrst_begin multi_chkpoint_one_run app} Run Multi-Threaded chkpoint_one Calculation ########################################### Syntax ****** *ok* = ``multi_chkpoint_one_run`` ( *y_squared* , *square_root* ) Thread ****** It is assumed that this function is called by thread zero and all the other threads are blocked (waiting). y_squared ********* This argument has prototype ``const vector&`` *y_squared* and its size is equal to the number of threads. It is the values that we are computing the square root of. square_root *********** This argument has prototype ``vector&`` *square_root* The input value of *square_root* does not matter. Upon return, it has the same size and is the element by element square root of *y_squared* . ok ** This return value has prototype ``bool`` *ok* If it is false, ``multi_chkpoint_one_run`` detected an error. Source ****** {xrst_literal // BEGIN RUN C++ // END RUN C++ } {xrst_end multi_chkpoint_one_run} ------------------------------------------------------------------------------ */ // BEGIN RUN C++ namespace { bool multi_chkpoint_one_run( const vector& y_squared , vector& square_root ) { bool ok = true; ok &= thread_alloc::thread_num() == 0; // setup the work for multi-threading ok &= multi_chkpoint_one_setup(y_squared); // now do the work for each thread if( num_threads_ > 0 ) team_work( multi_chkpoint_one_worker ); else multi_chkpoint_one_worker(); // combine the result for each thread and takedown the multi-threading. ok &= multi_chkpoint_one_takedown(square_root); return ok; } } // END RUN C++ /* ------------------------------------------------------------------------------ {xrst_begin multi_chkpoint_one_time app} Timing Test for Multi-Threaded chkpoint_one Calculation ####################################################### Syntax ****** | *ok* = ``multi_chkpoint_one_time`` ( | |tab| *time_out* , *test_time* , *num_threads* , *num_solve* | ) Thread ****** It is assumed that this function is called by thread zero in sequential mode; i.e., not :ref:`in_parallel` . time_out ******** This argument has prototype ``double&`` *time_out* Its input value of the argument does not matter. Upon return it is the number of wall clock seconds used by :ref:`multi_chkpoint_one_run-name` . test_time ********* This argument has prototype ``double`` *test_time* and is the minimum amount of wall clock time that the test should take. The number of repeats for the test will be increased until this time is reached. The reported *time_out* is the total wall clock time divided by the number of repeats. num_threads *********** This argument has prototype ``size_t`` *num_threads* It specifies the number of threads that are available for this test. If it is zero, the test is run without the multi-threading environment and 1 == ``thread_alloc::num_threads`` () If it is non-zero, the test is run with the multi-threading and *num_threads* = ``thread_alloc::num_threads`` () num_solve ********* This specifies the number of square roots that will be solved for. ok ** The return value has prototype ``bool`` *ok* If it is true, ``harmonic_time`` passed the correctness test and ``multi_chkpoint_one_time`` did not detect an error. Otherwise it is false. {xrst_end multi_chkpoint_one_time} */ // BEGIN TIME C++ namespace { // values we are taking the square root of vector y_squared_; // results of the square root calculations vector square_root_; // void test_once(void) { bool ok = multi_chkpoint_one_run(y_squared_, square_root_); if( ! ok ) { std::cerr << "multi_chkpoint_one_run: error" << std::endl; exit(1); } return; } // void test_repeat(size_t repeat) { size_t i; for(i = 0; i < repeat; i++) test_once(); return; } } // This is the only routine that is accessible outside of this file bool multi_chkpoint_one_time( double& time_out, double test_time, size_t num_threads, size_t num_solve ) { bool ok = true; // size_t initial_inuse = thread_alloc::inuse(0); // number of threads, zero for no multi-threading num_threads_ = num_threads; // values we are talking the square root of y_squared_.resize(num_solve); for(size_t i_solve = 0; i_solve < num_solve; i_solve++) y_squared_[i_solve] = double(i_solve) + 2.0; // must create a_square_root_ in sequential mode vector au(2), ay(1); au[0] = 2.0; au[1] = 2.0; a_square_root_ = new CppAD::checkpoint( "square_root", checkpoint_algo, au, ay ); // create team of threads ok &= thread_alloc::in_parallel() == false; if( num_threads > 0 ) { team_create(num_threads); ok &= num_threads == thread_alloc::num_threads(); } else { ok &= 1 == thread_alloc::num_threads(); } // run the test case and set the time return value time_out = CppAD::time_test(test_repeat, test_time); // destroy team of threads if( num_threads > 0 ) team_destroy(); ok &= thread_alloc::in_parallel() == false; // must delete a_square_root_ in sequential mode delete a_square_root_; // free static variables in atomic_base class CppAD::atomic_base::clear(); // correctness check ok &= square_root_.size() == num_solve; double eps99 = 99.0 * std::numeric_limits::epsilon(); for(size_t i = 0; i < num_solve; i++) { double check = std::sqrt( y_squared_[i] ); ok &= std::fabs( square_root_[i] / check - 1.0 ) <= eps99; } // // free memory in CppAD vectors that are still in scope y_squared_.clear(); square_root_.clear(); au.clear(); ay.clear(); // // check that no static variables in this file are holding onto memory ok &= initial_inuse == thread_alloc::inuse(0); // return ok; } // END TIME C++ ================================================ FILE: example/multi_thread/multi_chkpoint_one.hpp ================================================ # ifndef CPPAD_EXAMPLE_MULTI_THREAD_MULTI_CHKPOINT_ONE_HPP # define CPPAD_EXAMPLE_MULTI_THREAD_MULTI_CHKPOINT_ONE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- bool multi_chkpoint_one_time( double& time_out, double test_time, size_t num_threads, size_t num_solve ); # endif ================================================ FILE: example/multi_thread/multi_chkpoint_one.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin multi_chkpoint_one.cpp app} Multi-Threading chkpoint_one Example / Test ########################################### Deprecated 2019-08-06 ********************* The example has been deprecated, use :ref:`multi_chkpoint_two.cpp-name` instead. Source File *********** All of the routines below are located in the file :: example/multi_thread/multi_chkpoint_one.cpp Contents ******** {xrst_toc_table example/multi_thread/multi_chkpoint_one.cpp } {xrst_end multi_chkpoint_one.cpp} ================================================ FILE: example/multi_thread/multi_chkpoint_two.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin multi_chkpoint_two_algo} chkpoint_two Algorithm that Computes Square Root ################################################ Syntax ****** | *checkpoint_algo* ( *au* , *ay* ) Purpose ******* This algorithm computes a square root using Newton's method. It is meant to be very inefficient in order to demonstrate timing results. au ** This argument has prototype ``const`` *ADvector* & *au* where *ADvector* is a :ref:`simple vector class` with elements of type ``AD`` . The size of *au* is three. y_initial ========= We use the notation *y_initial* = *au* [0] for the initial value of the Newton iterate. y_squared ========= We use the notation *y_squared* = *au* [1] for the value we are taking the square root of. ay ** This argument has prototype *ADvector* & *ay* The size of *ay* is one and *ay* [0] is the square root of *y_squared* . Source ****** {xrst_literal // BEGIN ALGO C++ // END ALGO C++ } {xrst_end multi_chkpoint_two_algo} */ // BEGIN ALGO C++ // includes used by all source code in multi_chkpoint_two.cpp file # include # include "multi_chkpoint_two.hpp" # include "team_thread.hpp" // namespace { using CppAD::thread_alloc; // fast multi-threading memory allocator using CppAD::vector; // uses thread_alloc // typedef CppAD::AD a_double; void checkpoint_algo(const vector& au , vector& ay) { // extract components of argument vector a_double y_initial = au[0]; a_double y_squared = au[1]; // Use Newton's method to solve f(y) = y^2 = y_squared a_double y_itr = y_initial; for(size_t itr = 0; itr < 20; itr++) { // solve (y - y_itr) * f'(y_itr) = y_squared - y_itr^2 a_double fp_itr = 2.0 * y_itr; y_itr = y_itr + (y_squared - y_itr * y_itr) / fp_itr; } // return the Newton approximation for f(y) = y_squared ay[0] = y_itr; } } // END ALGO C++ /* {xrst_begin multi_chkpoint_two_common} Multi-Threaded chkpoint_two Common Information ############################################## Purpose ******* This source code defines the common variables that are used by the ``multi_chkpoint_two_`` *name* functions. Source ****** {xrst_literal // BEGIN COMMON C++ // END COMMON C++ } {xrst_end multi_chkpoint_two_common} */ // BEGIN COMMON C++ namespace { // Number of threads, set by multi_chkpoint_two_time // (zero means one thread with no multi-threading setup) size_t num_threads_ = 0; // We can use one checkpoint function for all threads because // there is no member data that gets changed during worker call. // This needs to stay in scope for as long as a recording will use it. // We cannot be in parallel mode when this object is created or deleted. // We use a pointer so that there is no left over memory in thread zero. CppAD::chkpoint_two* a_square_root_ = nullptr; // structure with information for one thread typedef struct { // used by worker to compute the square root, // set by multi_chkpoint_two_setup CppAD::ADFun* fun; // // value we are computing square root of, set by multi_chkpoint_two_setup vector* y_squared; // // square root, set by worker vector* square_root; // // false if an error occurs, true otherwise, set by worker bool ok; } work_one_t; // // Vector with information for all threads // (uses pointers instead of values to avoid false sharing) // allocated by multi_chkpoint_two_setup, freed by multi_chkpoint_two_takedown work_one_t* work_all_[CPPAD_MAX_NUM_THREADS]; } // END COMMON C++ /* ------------------------------------------------------------------------------- {xrst_begin multi_chkpoint_two_setup} Multi-Threaded chkpoint_two Set Up ################################## Syntax ****** *ok* = ``multi_chkpoint_two_setup`` ( *y_squared* ) Purpose ******* This routine splits up the computation into the individual threads. Thread ****** It is assumed that this function is called by thread zero and all the other threads are blocked (waiting). y_squared ********* This argument has prototype ``const vector&`` *y_squared* and its size is equal to the number of equations to solve. It is the values that we are computing the square root of. ok ** This return value has prototype ``bool`` *ok* If it is false, ``multi_chkpoint_two_setup`` detected an error. Source ****** {xrst_literal // BEGIN SETUP C++ // END SETUP C++ } {xrst_end multi_chkpoint_two_setup} */ // BEGIN SETUP C++ namespace { bool multi_chkpoint_two_setup(const vector& y_squared) { size_t num_threads = std::max(num_threads_, size_t(1)); bool ok = num_threads == thread_alloc::num_threads(); ok &= thread_alloc::thread_num() == 0; // // declare independent variable variable vector vector ax(1); ax[0] = 2.0; CppAD::Independent(ax); // // argument and result for checkpoint algorithm vector au(2), ay(1); au[0] = ax[0]; // y_initial au[1] = ax[0]; // y_squared // put user checkpoint function in recording (*a_square_root_)(au, ay); // f(u) = sqrt(u) CppAD::ADFun fun(ax, ay); // // number of square roots for each thread size_t per_thread = (y_squared.size() + num_threads - 1) / num_threads; size_t y_index = 0; // for(size_t thread_num = 0; thread_num < num_threads; thread_num++) { // allocate separate memory for each thread to avoid false sharing size_t min_bytes(sizeof(work_one_t)), cap_bytes; void* v_ptr = thread_alloc::get_memory(min_bytes, cap_bytes); work_all_[thread_num] = static_cast(v_ptr); // // Run constructor on work_all_[thread_num]->fun work_all_[thread_num]->fun = new CppAD::ADFun; // // Run constructor on work_all_[thread_num] vectors work_all_[thread_num]->y_squared = new vector; work_all_[thread_num]->square_root = new vector; // // Each worker gets a separate copy of fun. This is necessary because // the Taylor coefficients will be set by each thread. *(work_all_[thread_num]->fun) = fun; // // values we are computing square root of for this thread ok &= 0 == work_all_[thread_num]->y_squared->size(); for(size_t i = 0; i < per_thread; i++) if( y_index < y_squared.size() ) work_all_[thread_num]->y_squared->push_back(y_squared[y_index++]); // // set to false in case this thread's worker does not get called work_all_[thread_num]->ok = false; } ok &= y_index == y_squared.size(); // return ok; } } // END SETUP C++ /* ------------------------------------------------------------------------------ {xrst_begin multi_chkpoint_two_worker} Multi-Threaded chkpoint_two Worker ################################## Purpose ******* This routine does the computation for one thread. Source ****** {xrst_literal // BEGIN WORKER C++ // END WORKER C++ } {xrst_end multi_chkpoint_two_worker} */ // BEGIN WORKER C++ namespace { void multi_chkpoint_two_worker(void) { size_t thread_num = thread_alloc::thread_num(); size_t num_threads = std::max(num_threads_, size_t(1)); bool ok = thread_num < num_threads; // vector x(1), y(1); size_t n = work_all_[thread_num]->y_squared->size(); work_all_[thread_num]->square_root->resize(n); for(size_t i = 0; i < n; i++) { x[0] = (* work_all_[thread_num]->y_squared )[i]; y = work_all_[thread_num]->fun->Forward(0, x); // (* work_all_[thread_num]->square_root )[i] = y[0]; } work_all_[thread_num]->ok = ok; } } // END WORKER C++ /* ------------------------------------------------------------------------------ {xrst_begin multi_chkpoint_two_takedown} Multi-Threaded chkpoint_two Take Down ##################################### Syntax ****** *ok* = ``multi_chkpoint_two_takedown`` ( *square_root* ) Purpose ******* This routine gathers up the results for each thread and frees memory that was allocated by :ref:`multi_chkpoint_two_setup-name` . Thread ****** It is assumed that this function is called by thread zero and all the other threads are blocked (waiting). square_root *********** This argument has prototype ``vector&`` *square_root* The input value of *square_root* does not matter. Upon return, it has the same size and is the element by element square root of :ref:`multi_chkpoint_two_setup@y_squared` . ok ** This return value has prototype ``bool`` *ok* If it is false, ``multi_chkpoint_two_takedown`` detected an error. Source ****** {xrst_literal // BEGIN TAKEDOWN C++ // END TAKEDOWN C++ } {xrst_end multi_chkpoint_two_takedown} */ // BEGIN TAKEDOWN C++ namespace { bool multi_chkpoint_two_takedown(vector& square_root) { bool ok = true; ok &= thread_alloc::thread_num() == 0; size_t num_threads = std::max(num_threads_, size_t(1)); // // extract square roots in original order square_root.resize(0); for(size_t thread_num = 0; thread_num < num_threads; thread_num++) { // results for this thread size_t n = work_all_[thread_num]->square_root->size(); for(size_t i = 0; i < n; i++) square_root.push_back((* work_all_[thread_num]->square_root )[i]); } // // go down so that free memory for other threads before memory for master size_t thread_num = num_threads; while(thread_num--) { // check that this tread was ok with the work it did ok &= work_all_[thread_num]->ok; // // run destructor on vector object for this thread delete work_all_[thread_num]->y_squared; delete work_all_[thread_num]->square_root; // // run destructor on function object for this thread delete work_all_[thread_num]->fun; // // delete problem specific information void* v_ptr = static_cast( work_all_[thread_num] ); thread_alloc::return_memory( v_ptr ); // // Note that checkpoint object has memory connect to each thread // thread_alloc::inuse(thread_num) cannot be zero until it is deleted if( thread_num > 0 ) { ok &= thread_alloc::inuse(thread_num) > 0; // // return all memory that is not in use and // but being held for future use by this thread thread_alloc::free_available(thread_num); } } return ok; } } // END TAKEDOWN C++ /* {xrst_begin multi_chkpoint_two_run} Run Multi-Threaded chkpoint_two Calculation ########################################### Syntax ****** *ok* = ``multi_chkpoint_two_run`` ( *y_squared* , *square_root* ) Thread ****** It is assumed that this function is called by thread zero and all the other threads are blocked (waiting). y_squared ********* This argument has prototype ``const vector&`` *y_squared* and its size is equal to the number of threads. It is the values that we are computing the square root of. square_root *********** This argument has prototype ``vector&`` *square_root* The input value of *square_root* does not matter. Upon return, it has the same size and is the element by element square root of *y_squared* . ok ** This return value has prototype ``bool`` *ok* If it is false, ``multi_chkpoint_two_run`` detected an error. Source ****** {xrst_literal // BEGIN RUN C++ // END RUN C++ } {xrst_end multi_chkpoint_two_run} ------------------------------------------------------------------------------ */ // BEGIN RUN C++ namespace { bool multi_chkpoint_two_run( const vector& y_squared , vector& square_root ) { bool ok = true; ok &= thread_alloc::thread_num() == 0; // setup the work for multi-threading ok &= multi_chkpoint_two_setup(y_squared); // now do the work for each thread if( num_threads_ > 0 ) team_work( multi_chkpoint_two_worker ); else multi_chkpoint_two_worker(); // combine the result for each thread and takedown the multi-threading. ok &= multi_chkpoint_two_takedown(square_root); return ok; } } // END RUN C++ /* ------------------------------------------------------------------------------ {xrst_begin multi_chkpoint_two_time} Timing Test for Multi-Threaded chkpoint_two Calculation ####################################################### Syntax ****** | *ok* = ``multi_chkpoint_two_time`` ( | |tab| *time_out* , *test_time* , *num_threads* , *num_solve* | ) Thread ****** It is assumed that this function is called by thread zero in sequential mode; i.e., not :ref:`in_parallel` . time_out ******** This argument has prototype ``double&`` *time_out* Its input value of the argument does not matter. Upon return it is the number of wall clock seconds used by :ref:`multi_chkpoint_two_run-name` . test_time ********* This argument has prototype ``double`` *test_time* and is the minimum amount of wall clock time that the test should take. The number of repeats for the test will be increased until this time is reached. The reported *time_out* is the total wall clock time divided by the number of repeats. num_threads *********** This argument has prototype ``size_t`` *num_threads* It specifies the number of threads that are available for this test. If it is zero, the test is run without the multi-threading environment and 1 == ``thread_alloc::num_threads`` () If it is non-zero, the test is run with the multi-threading and *num_threads* = ``thread_alloc::num_threads`` () num_solve ********* This specifies the number of square roots that will be solved for. ok ** The return value has prototype ``bool`` *ok* If it is true, ``harmonic_time`` passed the correctness test and ``multi_chkpoint_two_time`` did not detect an error. Otherwise it is false. {xrst_end multi_chkpoint_two_time} */ // BEGIN TIME C++ namespace { // values we are taking the square root of vector y_squared_; // results of the square root calculations vector square_root_; // void test_once(void) { bool ok = multi_chkpoint_two_run(y_squared_, square_root_); if( ! ok ) { std::cerr << "multi_chkpoint_two_run: error" << std::endl; exit(1); } return; } // void test_repeat(size_t repeat) { size_t i; for(i = 0; i < repeat; i++) test_once(); return; } } // This is the only routine that is accessible outside of this file bool multi_chkpoint_two_time( double& time_out, double test_time, size_t num_threads, size_t num_solve ) { bool ok = true; // size_t initial_inuse = thread_alloc::inuse(0); // number of threads, zero for no multi-threading num_threads_ = num_threads; // values we are talking the square root of y_squared_.resize(num_solve); for(size_t i_solve = 0; i_solve < num_solve; i_solve++) y_squared_[i_solve] = double(i_solve) + 2.0; // create a_square_root_ in sequential mode { // create corresponding ADFun inside block so it gets destroyed // and does not have thread_alloc memory inuse at the end vector au(2), ay(1); au[0] = 2.0; au[1] = 2.0; CppAD::Independent(au); checkpoint_algo(au, ay); CppAD::ADFun fun(au, ay); // // create chkpoint_two version of algorithm const char* name = "square_root"; bool internal_bool = false; bool use_hes_sparsity = false; bool use_base2ad = false; bool use_in_parallel = true; a_square_root_ = new CppAD::chkpoint_two( fun, name, internal_bool, use_hes_sparsity, use_base2ad, use_in_parallel ); } // create team of threads ok &= thread_alloc::in_parallel() == false; if( num_threads > 0 ) { team_create(num_threads); ok &= num_threads == thread_alloc::num_threads(); } else { ok &= 1 == thread_alloc::num_threads(); } // run the test case and set the time return value time_out = CppAD::time_test(test_repeat, test_time); // destroy team of threads if( num_threads > 0 ) team_destroy(); ok &= thread_alloc::in_parallel() == false; // must delete a_square_root_ in sequential mode delete a_square_root_; // correctness check ok &= square_root_.size() == num_solve; double eps99 = 99.0 * std::numeric_limits::epsilon(); for(size_t i = 0; i < num_solve; i++) { double check = std::sqrt( y_squared_[i] ); ok &= std::fabs( square_root_[i] / check - 1.0 ) <= eps99; } // // free memory in CppAD vectors that are still in scope y_squared_.clear(); square_root_.clear(); // // check that no static variables in this file are holding onto memory ok &= initial_inuse == thread_alloc::inuse(0); // return ok; } // END TIME C++ ================================================ FILE: example/multi_thread/multi_chkpoint_two.hpp ================================================ # ifndef CPPAD_EXAMPLE_MULTI_THREAD_MULTI_CHKPOINT_TWO_HPP # define CPPAD_EXAMPLE_MULTI_THREAD_MULTI_CHKPOINT_TWO_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- bool multi_chkpoint_two_time( double& time_out, double test_time, size_t num_threads, size_t num_solve ); # endif ================================================ FILE: example/multi_thread/multi_chkpoint_two.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin multi_chkpoint_two.cpp} Multi-Threading chkpoint_two Example / Test ########################################### Source File *********** All of the routines below are located in the file :: example/multi_thread/multi_chkpoint_two.cpp Contents ******** {xrst_toc_table example/multi_thread/multi_chkpoint_two.cpp } {xrst_end multi_chkpoint_two.cpp} ================================================ FILE: example/multi_thread/multi_newton.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin multi_newton_common} Common Variables use by Multi-Threaded Newton Method #################################################### Purpose ******* This source code defined the common include files, defines, and variables that are used by the multi-newton method. Source ****** {xrst_literal // BEGIN COMMON C++ // END COMMON C++ } {xrst_end multi_newton_common} */ // BEGIN COMMON C++ # include # include # include # include # include "multi_newton.hpp" # include "team_thread.hpp" # define USE_THREAD_ALLOC_FOR_WORK_ALL 1 namespace { using CppAD::thread_alloc; // fast multi-threadeding memory allocator using CppAD::vector; // uses thread_alloc // number of threads, set by multi_newton_time. size_t num_threads_ = 0; // function we are finding zeros of, set by multi_newton_time void (*fun_)(double x, double& f, double& df) = 0; // convergence criteria, set by multi_newton_setup double epsilon_ = 0.; // maximum number of iterations, set by multi_newton_setup size_t max_itr_ = 0; // length for all sub-intervals double sub_length_ = 0.; // structure with information for one thread typedef struct { // number of sub intervals (worker input) size_t num_sub; // beginning of interval (worker input) double xlow; // end of interval (worker input) double xup; // vector of zero candidates (worker output) // after call to multi_newton_setup: x.size() == 0 // after call to multi_newton_work: x.size() is number of zeros // after call to multi_newton_takedown: x.size() == 0 vector x; // false if an error occurs, true otherwise (worker output) bool ok; } work_one_t; // vector with information for all threads // after call to multi_newton_setup: work_all.size() == num_threads // after call to multi_newton_takedown: work_all.size() == 0 // (use pointers instead of values to avoid false sharing) vector work_all_; } // END COMMON C++ /* ------------------------------------------------------------------------------- {xrst_begin multi_newton_setup} {xrst_spell xlow xup } Set Up Multi-Threaded Newton Method ################################### Syntax ****** | *ok* = ``multi_newton_setup`` ( | |tab| *num_sub* , *xlow* , *xup* , *epsilon* , *max_itr* , *num_threads* | ) Purpose ******* These routine does the setup for splitting finding all the zeros in an interval into separate sub-intervals, one for each thread. Thread ****** It is assumed that this function is called by thread zero, and all the other threads are blocked (waiting). num_sub ******* See *num_sub* in :ref:`multi_newton_run` . xlow **** See *xlow* in :ref:`multi_newton_run` . xup *** See *xup* in :ref:`multi_newton_run` . epsilon ******* See *epsilon* in :ref:`multi_newton_run` . max_itr ******* See *max_itr* in :ref:`multi_newton_run` . num_threads *********** See *num_threads* in :ref:`multi_newton_run` . Source ****** {xrst_literal // BEGIN SETUP C++ // END SETUP C++ } {xrst_end multi_newton_setup} */ // BEGIN SETUP C++ namespace { bool multi_newton_setup( size_t num_sub , double xlow , double xup , double epsilon , size_t max_itr , size_t num_threads ) { num_threads = std::max(num_threads_, size_t(1)); bool ok = num_threads == thread_alloc::num_threads(); ok &= thread_alloc::thread_num() == 0; // inputs that are same for all threads epsilon_ = epsilon; max_itr_ = max_itr; // resize the work vector to accommodate the number of threads ok &= work_all_.size() == 0; work_all_.resize(num_threads); // length of each sub interval sub_length_ = (xup - xlow) / double(num_sub); // determine values that are specific to each thread size_t num_min = num_sub / num_threads; // minimum num_sub size_t num_more = num_sub % num_threads; // number that have one more size_t sum_num = 0; // sum with respect to thread of num_sub size_t thread_num, num_sub_thread; for(thread_num = 0; thread_num < num_threads; thread_num++) { # if USE_THREAD_ALLOC_FOR_WORK_ALL // allocate separate memory for this thread to avoid false sharing size_t min_bytes(sizeof(work_one_t)), cap_bytes; void* v_ptr = thread_alloc::get_memory(min_bytes, cap_bytes); work_all_[thread_num] = static_cast(v_ptr); // thread_alloc is a raw memory allocator; i.e., it does not call // the constructor for the objects it creates. The vector // class requires it's constructor to be called so we do it here new(& (work_all_[thread_num]->x) ) vector(); # else work_all_[thread_num] = new work_one_t; # endif // number of sub-intervalse for this thread if( thread_num < num_more ) num_sub_thread = num_min + 1; else num_sub_thread = num_min; // when thread_num == 0, xlow_thread == xlow double xlow_thread = xlow + double(sum_num) * sub_length_; // when thread_num == num_threads - 1, xup_thread = xup double xup_thread = xlow + double(sum_num + num_sub_thread) * sub_length_; if( thread_num == num_threads - 1 ) xup_thread = xup; // update sum_num for next time through loop sum_num += num_sub_thread; // input information specific to this thread work_all_[thread_num]->num_sub = num_sub_thread; work_all_[thread_num]->xlow = xlow_thread; work_all_[thread_num]->xup = xup_thread; ok &= work_all_[thread_num]->x.size() == 0; // in case this thread does not get called work_all_[thread_num]->ok = false; } ok &= sum_num == num_sub; return ok; } } // END SETUP C++ /* ------------------------------------------------------------------------------ {xrst_begin multi_newton_worker} {xrst_spell xlow xup } Do One Thread's Work for Multi-Threaded Newton Method ##################################################### Syntax ****** ``multi_newton_worker`` () Purpose ******* This function finds all the zeros in the interval [ *low* , *up* ] . low *** This is the value of the :ref:`multi_newton_common-name` information *low* = ``work_all_`` [ *thread_num* ] ``->xlow`` up ** This is the value of the :ref:`multi_newton_common-name` information *up* = ``work_all_`` [ *thread_num* ] ``->xup`` thread_num ********** This is the number for the current thread; see :ref:`thread_num` . Source ****** {xrst_literal // BEGIN WORKER C++ // END WORKER C++ } {xrst_end multi_newton_worker} */ // BEGIN WORKER C++ namespace { void multi_newton_worker(void) { // Split [xlow, xup] into num_sub intervales and // look for one zero in each sub-interval. size_t thread_num = thread_alloc::thread_num(); size_t num_threads = std::max(num_threads_, size_t(1)); bool ok = thread_num < num_threads; size_t num_sub = work_all_[thread_num]->num_sub; double xlow = work_all_[thread_num]->xlow; double xup = work_all_[thread_num]->xup; vector& x = work_all_[thread_num]->x; // check arguments ok &= max_itr_ > 0; ok &= num_sub > 0; ok &= xlow < xup; ok &= x.size() == 0; // check for special case where there is nothing for this thread to do if( num_sub == 0 ) { work_all_[thread_num]->ok = ok; return; } // check for a zero on each sub-interval size_t i; double xlast = xlow - 2.0 * sub_length_; // over sub_length_ away from x_low double flast = 2.0 * epsilon_; // any value > epsilon_ would do for(i = 0; i < num_sub; i++) { // note that when i == 0, xlow_i == xlow (exactly) double xlow_i = xlow + double(i) * sub_length_; // note that when i == num_sub - 1, xup_i = xup (exactly) double xup_i = xup - double(num_sub - i - 1) * sub_length_; // initial point for Newton iterations double xcur = (xup_i + xlow_i) / 2.; // Newton iterations bool more_itr = true; size_t itr = 0; // initialize these values to avoid MSC C++ warning double fcur=0.0, dfcur=0.0; while( more_itr ) { fun_(xcur, fcur, dfcur); // check end of iterations if( fabs(fcur) <= epsilon_ ) more_itr = false; if( (xcur == xlow_i ) && (fcur * dfcur > 0.) ) more_itr = false; if( (xcur == xup_i) && (fcur * dfcur < 0.) ) more_itr = false; // next Newton iterate if( more_itr ) { xcur = xcur - fcur / dfcur; // keep in bounds xcur = std::max(xcur, xlow_i); xcur = std::min(xcur, xup_i); more_itr = ++itr < max_itr_; } } if( fabs( fcur ) <= epsilon_ ) { // check for case where xcur is lower bound for this // sub-interval and upper bound for previous sub-interval if( fabs(xcur - xlast) >= sub_length_ ) { x.push_back( xcur ); xlast = xcur; flast = fcur; } else if( fabs(fcur) < fabs(flast) ) { x[ x.size() - 1] = xcur; xlast = xcur; flast = fcur; } } } work_all_[thread_num]->ok = ok; } } // END WORKER C++ /* ------------------------------------------------------------------------------- {xrst_begin multi_newton_takedown} {xrst_spell xout } Take Down Multi-threaded Newton Method ###################################### Syntax ****** *ok* = ``harmonic_takedown`` ( *xout* ) Purpose ******* This routine does the takedown for splitting the Newton method into sub-intervals. Thread ****** It is assumed that this function is called by thread zero, and all the other threads have completed their work and are blocked (waiting). xout **** See :ref:`multi_newton_run` . Source ****** {xrst_literal // BEGIN TAKEDOWN C++ // END TAKEDOWN C++ } {xrst_end multi_newton_takedown} */ // BEGIN TAKEDOWN C++ namespace { bool multi_newton_takedown(vector& xout) { // number of threads in the calculation size_t num_threads = std::max(num_threads_, size_t(1)); // remove duplicates and points that are not solutions xout.resize(0); bool ok = true; ok &= thread_alloc::thread_num() == 0; // initialize as more that sub_length_ / 2 from any possible solution double xlast = - sub_length_; for(size_t thread_num = 0; thread_num < num_threads; thread_num++) { vector& x = work_all_[thread_num]->x; size_t i; for(i = 0; i < x.size(); i++) { // check for case where this point is lower limit for this // thread and upper limit for previous thread if( fabs(x[i] - xlast) >= sub_length_ ) { xout.push_back( x[i] ); xlast = x[i]; } else { double fcur, flast, df; fun_(x[i], fcur, df); fun_(xlast, flast, df); if( fabs(fcur) < fabs(flast) ) { xout[ xout.size() - 1] = x[i]; xlast = x[i]; } } } // check that this thread was ok with the work it did ok &= work_all_[thread_num]->ok; } // go down so free memory for other threads before memory for master size_t thread_num = num_threads; while(thread_num--) { # if USE_THREAD_ALLOC_FOR_WORK_ALL // call the destructor for vector destructor work_all_[thread_num]->x.~vector(); // delete the raw memory allocation void* v_ptr = static_cast( work_all_[thread_num] ); thread_alloc::return_memory( v_ptr ); # else delete work_all_[thread_num]; # endif // Note that xout corresponds to memory that is inuse by master // (so we can only check have freed all their memory). if( thread_num > 0 ) { // check that there is no longer any memory inuse by this thread ok &= thread_alloc::inuse(thread_num) == 0; // return all memory being held for future use by this thread thread_alloc::free_available(thread_num); } } // now we are done with the work_all_ vector so free its memory // (because it is a static variable) work_all_.clear(); return ok; } } // END TAKEDOWN C++ /* ------------------------------------------------------------------------------ {xrst_begin multi_newton_run} {xrst_spell df xlow xout xup } A Multi-Threaded Newton's Method ################################ Syntax ****** | ``ok`` = ``multi_newton_run`` ( *xout* , | |tab| ``fun`` , ``num_sub`` , ``xlow`` , ``xup`` , ``epsilon`` , ``max_itr`` , ``num_threads`` | ) Purpose ******* Multi-threaded determination of the argument values :math:`x`, in the interval :math:`[a, b]` (where :math:`a < b`), such that :math:`f(x) = 0`. Thread ****** It is assumed that this function is called by thread zero, and all the other threads are blocked (waiting). Method ****** For :math:`i = 0 , \ldots , n`, we define the *i*-th grid point :math:`g_i` by .. math:: g_i = a \frac{n - i}{n} + b \frac{i}{n} For :math:`i = 0 , \ldots , n-1`, we define the *i*-th sub-interval of :math:`[a, b]` by .. math:: I_i = [ g_i , g_{i+1} ] Newton's method is applied starting at the center of each of the sub-intervals :math:`I_i` for :math:`i = 0 , \ldots , n-1` and at most one zero is found for each sub-interval. ok ** The return value *ok* has prototype ``bool`` *ok* If an error occurs, it is false, otherwise it is true. xout **** The argument *xout* has the prototype ``vector&`` *xout* The input size and value of the elements of *xout* do not matter. Upon return from ``multi_newton`` , the size of *xout* is less than or equal the number of sub-intervals :math:`n` and .. math:: | f( xout[i] ) | \leq epsilon for each valid index 0 <= ``i`` < ``xout`` . *size* () . Two :math:`x` solutions are considered equal (and joined as one) if the absolute difference between the solutions is less than :math:`(b - a) / n`. fun *** The argument *fun* has prototype ``void`` *fun* ( ``double`` *x* , ``double&`` *f* , ``double&`` *df* ) This function must evaluate :math:`f(x)`, and its derivative :math:`f^{(1)} (x)`, using the syntax *fun* ( *x* , *f* , *df* ) where the arguments to *fun* have the prototypes | |tab| ``double`` *x* | |tab| ``double&`` *f* | |tab| ``double&`` *df* . The input values of *f* and *df* do not matter. Upon return they are :math:`f(x)` and :math:`f^{(1)} (x)` respectively. num_sub ******* The argument *num_sub* has prototype ``size_t`` *num_sub* It specifies the number of sub-intervals; i.e., :math:`n`. xlow **** The argument *xlow* has prototype ``double`` *xlow* It specifies the lower limit for the entire search interval; i.e., :math:`a`. xup *** The argument *xup* has prototype ``double`` *xup* It specifies the upper limit for the entire search interval; i.e., :math:`b`. epsilon ******* The argument *epsilon* has prototype ``double`` *epsilon* It specifies the convergence criteria for Newton's method in terms of how small the function value must be. max_itr ******* The argument *max_itr* has prototype ``size_t`` *max_itr* It specifies the maximum number of iterations of Newton's method to try before giving up on convergence (on each sub-interval). num_threads *********** This argument has prototype ``size_t`` *num_threads* It specifies the number of threads that are available for this test. If it is zero, the test is run without the multi-threading environment. Source ****** {xrst_literal // BEGIN SOLVE C++ // END SOLVE C++ } {xrst_end multi_newton_run} --------------------------------------------------------------------------- */ // BEGIN SOLVE C++ namespace { bool multi_newton_run( vector& xout , void fun(double x, double& f, double& df) , size_t num_sub , double xlow , double xup , double epsilon , size_t max_itr , size_t num_threads ) { bool ok = true; ok &= thread_alloc::thread_num() == 0; // setup the work for num_threads threads ok &= multi_newton_setup( num_sub, xlow, xup, epsilon, max_itr, num_threads ); // now do the work for each thread if( num_threads > 0 ) team_work( multi_newton_worker ); else multi_newton_worker(); // now combine the results for all the threads ok &= multi_newton_takedown(xout); return ok; } } // END SOLVE C++ /* ----------------------------------------------------------------------------- {xrst_begin multi_newton_time} . Timing Test of Multi-Threaded Newton Method ########################################### Syntax ****** | *ok* = ``multi_newton_time`` ( *time_out* , *test_time* , *num_threads* , | |tab| *num_zero* , *num_sub* , *num_sum* , *use_ad* | ) Purpose ******* Runs correctness and timing test for a multi-threaded Newton method. This test uses Newton's method to determine all the zeros of the sine function on an interval. CppAD, or hand coded derivatives, can be used to calculate the derivatives used by Newton's method. The calculation can be done in parallel on the different sub-intervals. In addition, the calculation can be done without multi-threading. Thread ****** It is assumed that this function is called by thread zero in sequential mode; i.e., not :ref:`in_parallel` . ok ** This return value has prototype ``bool`` *ok* If it is true, ``multi_newton_time`` passed the correctness test. Otherwise it is false. time_out ******** This argument has prototype ``double&`` *time_out* The input value of the argument does not matter. Upon return it is the number of wall clock seconds required for the multi-threaded Newton method can compute all the zeros. test_time ********* Is the minimum amount of wall clock time that the test should take. The number of repeats for the test will be increased until this time is reached. The reported *time_out* is the total wall clock time divided by the number of repeats. num_threads *********** This argument has prototype ``size_t`` *num_threads* It specifies the number of threads that are available for this test. If it is zero, the test is run without multi-threading and 1 == ``thread_alloc::num_threads`` () when ``multi_newton_time`` is called. If it is non-zero, the test is run with multi-threading and *num_threads* == ``thread_alloc::num_threads`` () when ``multi_newton_time`` is called. num_zero ******** This argument has prototype ``size_t`` *num_zero* and it must be greater than one. It specifies the actual number of zeros in the test function :math:`\sin(x)`. To be specific, ``multi_newton_time`` will attempt to determine all of the values of :math:`x` for which :math:`\sin(x) = 0` and :math:`x` is in the interval [ 0 , ( *num_zero* ``- 1`` ) * *pi* ] . num_sub ******* This argument has prototype ``size_t`` *num_sub* It specifies the number of sub-intervals to divide the total interval into. It must be greater than *num_zero* (so that the correctness test can check we have found all the zeros). num_sum ******* This argument has prototype ``size_t`` *num_sum* and must be greater than zero. The actual function used by the Newton method is .. math:: f(x) = \frac{1}{n} \sum_{i=1}^{n} \sin (x) where :math:`n` is equal to *num_sum* . Larger values of *num_sum* simulate a case where the evaluation of the function :math:`f(x)` takes more time. use_ad ****** This argument has prototype ``bool`` *user_ad* If *use_ad* is ``true`` , then derivatives will be computed using CppAD. Note that this derivative computation includes re-taping the function for each value of :math:`x` (even though re-taping is not necessary). If *use_ad* is ``false`` , derivatives will be computed using a hand coded routine. Source ****** {xrst_literal // BEGIN TIME C++ // END TIME C++ } {xrst_end multi_newton_time} */ // BEGIN TIME C++ namespace { // empty namespace // values correspond to arguments in previous call to multi_newton_time size_t num_zero_; // number of zeros of f(x) in the total interval size_t num_sub_; // number of sub-intervals to split calculation into size_t num_sum_; // larger values make f(x) take longer to calculate // value of xout corresponding to most recent call to test_once vector xout_; // A version of the sine function that can be made as slow as we like template Float f_eval(Float x) { Float sum = 0.; size_t i; for(i = 0; i < num_sum_; i++) sum += sin(x); return sum / Float(num_sum_); } // Direct calculation of derivative with same number of floating point // operations as for f_eval. double df_direct(double x) { double sum = 0.; size_t i; for(i = 0; i < num_sum_; i++) sum += cos(x); return sum / double(num_sum_); } // AD calculation of detivative void fun_ad(double x, double& f, double& df) { using CppAD::AD; // use vector because it uses fast multi-threaded memory alloc vector< AD > X(1), Y(1); X[0] = x; CppAD::Independent(X); Y[0] = f_eval(X[0]); CppAD::ADFun F(X, Y); vector dx(1), dy(1); dx[0] = 1.; dy = F.Forward(1, dx); f = Value( Y[0] ); df = dy[0]; return; } // evaluate the function and its derivative void fun_no(double x, double& f, double& df) { f = f_eval(x); df = df_direct(x); return; } // Run computation of all the zeros once void test_once(void) { if( num_zero_ == 0 ) { std::cerr << "multi_newton_time: num_zero == 0" << std::endl; exit(1); } double pi = 4. * std::atan(1.); double xlow = 0.; double xup = double(num_zero_ - 1) * pi; double eps = xup * 100. * CppAD::numeric_limits::epsilon(); size_t max_itr = 20; // note that fun_ is set to fun_ad or fun_no by multi_newton_time bool ok = multi_newton_run( xout_ , fun_ , num_sub_ , xlow , xup , eps , max_itr , num_threads_ ); if( ! ok ) { std::cerr << "multi_newton: error" << std::endl; exit(1); } return; } // Repeat computation of all the zeros a specified number of times void test_repeat(size_t repeat) { size_t i; for(i = 0; i < repeat; i++) test_once(); return; } } // end empty namespace // This is the only routine that is accessible outside of this file bool multi_newton_time( double& time_out , double test_time , size_t num_threads , size_t num_zero , size_t num_sub , size_t num_sum , bool use_ad ) { bool ok = true; ok &= thread_alloc::thread_num() == 0; ok &= num_sub > num_zero; // Set local namespace environment variables num_threads_ = num_threads; if( use_ad ) fun_ = fun_ad; else fun_ = fun_no; // num_zero_ = num_zero; num_sub_ = num_sub; num_sum_ = num_sum; // create team of threads ok &= thread_alloc::in_parallel() == false; if( num_threads > 0 ) { team_create(num_threads); ok &= num_threads == thread_alloc::num_threads(); } else { ok &= 1 == thread_alloc::num_threads(); } // run the test case and set time return value time_out = CppAD::time_test(test_repeat, test_time); // destroy team of threads if( num_threads > 0 ) team_destroy(); ok &= thread_alloc::in_parallel() == false; // // correctness check double pi = 4. * std::atan(1.); double xup = double(num_zero_ - 1) * pi; double eps = xup * 100. * CppAD::numeric_limits::epsilon(); ok &= (xout_.size() == num_zero); size_t i = 0; for(i = 0; i < xout_.size(); i++) ok &= std::fabs( xout_[i] - pi * double(i)) <= 2 * eps; // xout_ is a static variable, so clear it to free its memory xout_.clear(); // return correctness check result return ok; } // END TIME C++ ================================================ FILE: example/multi_thread/multi_newton.hpp ================================================ # ifndef CPPAD_EXAMPLE_MULTI_THREAD_MULTI_NEWTON_HPP # define CPPAD_EXAMPLE_MULTI_THREAD_MULTI_NEWTON_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- bool multi_newton_time( double& time_out , double test_time , size_t num_threads , size_t num_zero , size_t num_sub , size_t num_sum , bool use_ad ); # endif ================================================ FILE: example/multi_thread/multi_newton.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin multi_newton.cpp} Multi-Threaded Newton Method Example / Test ########################################### Source File *********** All of the routines below are located in the file :: example/multi_thread/multi_newton.cpp Contents ******** {xrst_toc_table example/multi_thread/multi_newton.cpp } {xrst_end multi_newton.cpp} ================================================ FILE: example/multi_thread/openmp/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the example/multi_thread/openmp directory tests # Inherit build type from ../CMakeList.txt # Local include directories to search (not in package_prefix/include) INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/.. ) # Cannot find specifications for CMAKE_CXX_FLAGS in version 2.6 documentation # so using ADD_DEFINITIONS instead. ADD_DEFINITIONS( ${OpenMP_CXX_FLAGS} ) # add_executable( [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] # source1 source2 ... sourceN # ) SET(source_list ../thread_test.cpp ../team_example.cpp ../harmonic.cpp ../multi_atomic_two.cpp ../multi_atomic_three.cpp ../multi_chkpoint_one.cpp ../multi_chkpoint_two.cpp ../multi_newton.cpp a11c_openmp.cpp get_started.cpp team_openmp.cpp ) set_compile_flags( example_multi_thread_openmp "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE( example_multi_thread_openmp EXCLUDE_FROM_ALL ${source_list} ) TARGET_LINK_LIBRARIES( example_multi_thread_openmp PRIVATE ${cppad_lib} ${colpack_libs} ${OpenMP_CXX_LIBRARIES} ) # Extra flags used by linker for openmp support SET(CMAKE_EXE_LINKER_FLAGS "${OpenMP_CXX_FLAGS} ${cppad_link_flags}" ) # check_example_multi_thread_openmp add_check_executable(check_example_multi_thread openmp get_started) ================================================ FILE: example/multi_thread/openmp/a11c_openmp.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin a11c_openmp.cpp} A Simple OpenMP Example and Test ################################ Purpose ******* This example just demonstrates OpenMP and does not use CppAD at all. Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end a11c_openmp.cpp} ---------------------------------------------------------------------------- */ // BEGIN C++ # include # include # include # include // for size_t # include // # define NUMBER_THREADS 4 namespace { // Beginning of Example A.1.1.1c of OpenMP 2.5 standard document --------- void a1(int n, float *a, float *b) { // Original had 'int i;' instead of 'int i = 0;' // Silence warning by clang 9.0.0: variable 'i' is uninitialized. int i = 0; # pragma omp parallel for for(i = 1; i < n; i++) /* i is private by default */ { assert( omp_get_num_threads() == NUMBER_THREADS ); b[i] = (a[i] + a[i-1]) / float(2); } } // End of Example A.1.1.1c of OpenMP 2.5 standard document --------------- } bool a11c(void) { bool ok = true; // Test setup size_t i, n = 1000; float *a = new float[n]; float *b = new float[n]; for(i = 0; i < n; i++) a[i] = float(i); int n_thread = NUMBER_THREADS; // number of threads in parallel regions omp_set_dynamic(0); // off dynamic thread adjust omp_set_num_threads(n_thread); // set the number of threads a1(int(n), a, b); // check the result float eps = float(100) * std::numeric_limits::epsilon(); for(i = 1; i < n ; i++) ok &= std::fabs( (float(2) * b[i] - a[i] - a[i-1]) / b[i] ) <= eps; delete [] a; delete [] b; return ok; } // END C++ ================================================ FILE: example/multi_thread/openmp/get_started.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin openmp_get_started.cpp} {xrst_template , example/multi_thread/template/get_started.xrst title: Getting Started Using @Name@ Threads With CppAD start source code after: // BEGIN_C++ end source code before: // END_C++ @Name@ , OpenMP @####@ , ###### @DEFAULT@ , USE_DEFAULT_ADFUN_CONSTRUCTOR } {xrst_end openmp_get_started.cpp} ------------------------------------------------------------------------------ */ // BEGIN_C++ # include # include # define USE_DEFAULT_ADFUN_CONSTRUCTOR 1 namespace { // // d_vector, ad_vector, fun_vector typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR( CppAD::AD ) ad_vector; typedef CPPAD_TESTVECTOR( CppAD::ADFun ) fun_vector; // // in_parallel bool in_parallel(void) { return omp_in_parallel() != 0; } // // thread_number size_t thread_number(void) { return static_cast( omp_get_thread_num() ); } // // partial double partial( CppAD::ADFun& f, size_t j, const d_vector& x ) { // f // This will cause an assert if Taylor coefficients were allocated // by a different thread. f.capacity_order(0); // size_t nx = x.size(); d_vector dx(nx), dy(1); for(size_t k = 0; k < nx; ++k) dx[k] = 0.0; dx[j] = 1.0; f.Forward(0, x); dy = f.Forward(1, dx); return dy[0]; } } bool get_started(void) { // ok bool ok = true; // // eps99 double eps99 = 99.0 * std::numeric_limits::epsilon(); // // nx, ax size_t nx = 10; ad_vector ax(nx); for(size_t j = 0; j < nx; ++j) ax[j] = 1.0; CppAD::Independent(ax); // // fun ad_vector ay(1); ay[0] = ax[0]; for(size_t j = 1; j < nx; ++j) ay[0] *= ax[j]; # if USE_DEFAULT_ADFUN_CONSTRUCTOR CppAD::ADFun fun; fun.Dependent(ax, ay); # else // This allocates memory for first order Taylor coefficients using thread 0. // An assert will occur at f.capacity_order(0) in run_one_thread when // it is called by a different thread. CppAD::ADFun fun(ax, ay); # endif // // num_threads, f_thread size_t num_threads = 4; fun_vector f_thread(num_threads); for(size_t i = 0; i < num_threads; ++i) f_thread[i] = fun; // // x d_vector x(nx); for(size_t j = 0; j < nx; ++j) ax[j] = 1.0 + 1.0 / double(j+1); // // parallel_setup omp_set_num_threads( int(num_threads) ); ok &= ! in_parallel(); CppAD::thread_alloc::parallel_setup( num_threads, in_parallel, thread_number ); // // parallel_ad CppAD::parallel_ad(); // // hold_memory // optional and may improve speed if you do a lot of memory allocation CppAD::thread_alloc::hold_memory(true); // // Jac ad_vector Jac(nx); // // j // OpenMP does not allow one to use a size_t here. int int_j; # pragma omp parallel for for(int_j = 0; int_j < int(nx); ++int_j) { size_t j = size_t(int_j); size_t thread_num = thread_number(); CppAD::ADFun& f = f_thread[thread_num]; Jac[j] = partial(f, j, x); } // // hold_memory // free memory for other threads before this (the master thread) CppAD::thread_alloc::parallel_setup(1, nullptr, nullptr); ok &= ! in_parallel(); CppAD::thread_alloc::hold_memory(false); for(size_t i = 1; i < num_threads; ++i) CppAD::thread_alloc::free_available(i); CppAD::thread_alloc::free_available(0); // // j for(int_j = 0; int_j < int(nx); ++int_j) { size_t j = size_t(int_j); // // check double check = 1.0; for(size_t k = 0; k < nx; ++k) if(k != j) check *= x[j]; // // ok ok &= CppAD::NearEqual(Jac[j], check, eps99, eps99); } return ok; } // END_C++ ================================================ FILE: example/multi_thread/openmp/team_openmp.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin team_openmp.cpp} OpenMP Implementation of a Team of AD Threads ############################################# See :ref:`team_thread.hpp-name` for this routines specifications. {xrst_literal // BEGIN C++ // END C++ } {xrst_end team_openmp.cpp} */ // BEGIN C++ # include # include # include "../team_thread.hpp" namespace { using CppAD::thread_alloc; // number of threads in this team size_t num_threads_; // used to inform CppAD when we are in parallel execution mode bool in_parallel(void) { return omp_in_parallel() != 0; } // used to inform CppAD of the current thread number size_t thread_num(void) { return static_cast( omp_get_thread_num() ); } } bool team_create(size_t num_threads) { bool ok = ! in_parallel(); ok &= thread_num() == 0;; ok &= num_threads > 0; // Turn off dynamic thread adjustment omp_set_dynamic(0); // Set the number of OpenMP threads omp_set_num_threads( int(num_threads) ); // setup for using CppAD::AD in parallel thread_alloc::parallel_setup(num_threads, in_parallel, thread_num); thread_alloc::hold_memory(true); CppAD::parallel_ad(); // inform team_work of number of threads num_threads_ = num_threads; return ok; } bool team_work(void worker(void)) { bool ok = ! in_parallel(); ok &= thread_num() == 0;; ok &= num_threads_ > 0; int number_threads = int(num_threads_); int thread_num; # pragma omp parallel for for(thread_num = 0; thread_num < number_threads; thread_num++) worker(); // end omp parallel for return ok; } bool team_destroy(void) { bool ok = ! in_parallel(); ok &= thread_num() == 0;; ok &= num_threads_ > 0; // inform team_work of number of threads num_threads_ = 1; // Set the number of OpenMP threads to one omp_set_num_threads( int(num_threads_) ); // inform CppAD no longer in multi-threading mode thread_alloc::parallel_setup(num_threads_, nullptr, nullptr); thread_alloc::hold_memory(false); CppAD::parallel_ad(); return ok; } const char* team_name(void) { return "openmp"; } // END C++ ================================================ FILE: example/multi_thread/pthread/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the example/multi_thread/pthread directory tests # Inherit build type from ../CMakeList.txt # Local include directories to search (not in package_prefix/include) INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/.. ) # add_executable( [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] # source1 source2 ... sourceN # ) SET(source_list ../thread_test.cpp ../team_example.cpp ../harmonic.cpp ../multi_atomic_two.cpp ../multi_atomic_three.cpp ../multi_chkpoint_one.cpp ../multi_chkpoint_two.cpp ../multi_newton.cpp a11c_pthread.cpp get_started.cpp team_pthread.cpp ) set_compile_flags( example_multi_thread_pthread "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE( example_multi_thread_pthread EXCLUDE_FROM_ALL ${source_list} ) # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_multi_thread_pthread ${cppad_lib} ${colpack_libs} ${pthread_lib_path} ) # check_example_multi_thread_pthread add_check_executable(check_example_multi_thread pthread get_started) ================================================ FILE: example/multi_thread/pthread/a11c_pthread.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin a11c_pthread.cpp} {xrst_spell pthreads } A Simple Parallel Pthread Example and Test ########################################## Purpose ******* This example just demonstrates pthreads and does not use CppAD at all. Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end a11c_pthread.cpp} ---------------------------------------------------------------------------- */ // BEGIN C++ # include # include # include # include // for size_t # include # define NUMBER_THREADS 4 # ifdef NDEBUG # define CHECK_ZERO(expression) expression # else # define CHECK_ZERO(expression) assert( expression == 0 ); # endif namespace { // Beginning of Example A.1.1.1c of OpenMP 2.5 standard document --------- void a1(int n, float *a, float *b) { int i; for(i = 1; i < n; i++) b[i] = (a[i] + a[i-1]) / 2.0f; return; } // End of Example A.1.1.1c of OpenMP 2.5 standard document --------------- struct start_arg { int n; float* a; float* b; }; void* start_routine(void* arg_vptr) { start_arg* arg = static_cast( arg_vptr ); a1(arg->n, arg->a, arg->b); void* no_status = nullptr; pthread_exit(no_status); return no_status; } } bool a11c(void) { bool ok = true; // Test setup int i, j, n_total = 10; float *a = new float[size_t(n_total)]; float *b = new float[size_t(n_total)]; for(i = 0; i < n_total; i++) a[i] = float(i); // number of threads int n_thread = NUMBER_THREADS; // the threads pthread_t thread[NUMBER_THREADS]; // arguments to start_routine struct start_arg arg[NUMBER_THREADS]; // attr pthread_attr_t attr; CHECK_ZERO( pthread_attr_init( &attr ) ); CHECK_ZERO( pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) ); // // Break the work up into sub work for each thread int n = n_total / n_thread; arg[0].n = n; arg[0].a = a; arg[0].b = b; for(j = 1; j < n_thread; j++) { arg[j].n = n + 1; arg[j].a = arg[j-1].a + n - 1; arg[j].b = arg[j-1].b + n - 1; if( j == (n_thread - 1) ) arg[j].n = n_total - j * n + 1; } for(j = 0; j < n_thread; j++) { // inform each thread of which block it is working on void* arg_vptr = static_cast( &arg[j] ); CHECK_ZERO( pthread_create( &thread[j], &attr, start_routine, arg_vptr ) ); } for(j = 0; j < n_thread; j++) { void* no_status = nullptr; CHECK_ZERO( pthread_join(thread[j], &no_status) ); } // check the result float eps = 100.0f * std::numeric_limits::epsilon(); for(i = 1; i < n ; i++) ok &= std::fabs( (2. * b[i] - a[i] - a[i-1]) / b[i] ) <= eps; delete [] a; delete [] b; return ok; } // END C++ ================================================ FILE: example/multi_thread/pthread/get_started.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin pthread_get_started.cpp} {xrst_spell posix } {xrst_template , example/multi_thread/template/get_started.xrst title: Getting Started Using @Name@ Threads With CppAD start source code after: // BEGIN_C++ end source code before: // END_C++ @Name@ , Posix @####@ , ##### @DEFAULT@ , USE_DEFAULT_ADFUN_CONSTRUCTOR } {xrst_end pthread_get_started.cpp} ------------------------------------------------------------------------------ */ // BEGIN_C++ # include # include # define USE_DEFAULT_ADFUN_CONSTRUCTOR 1 namespace { // // d_vector, ad_vector, fun_vector typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR( CppAD::AD ) ad_vector; typedef CPPAD_TESTVECTOR( CppAD::ADFun ) fun_vector; typedef CPPAD_TESTVECTOR( pthread_t ) pthread_vector; // // thread_info, thread_info_vector typedef struct { size_t thread_num; CppAD::ADFun* f_ptr; size_t j_begin; size_t j_end; const d_vector* x_ptr; d_vector* Jac_ptr; bool ok; } thread_info; typedef CPPAD_TESTVECTOR( thread_info ) thread_info_vector; // // thread_specific_key_ pthread_key_t thread_specific_key_; // // thread_specific_destructor void thread_specific_destructor(void* thread_num_vptr) { return; } // // sequential_execution_ bool sequential_execution_ = true; // // in_parallel bool in_parallel(void) { return ! sequential_execution_; } // // thread_number size_t thread_number(void) { // get thread specific information void* thread_num_vptr = pthread_getspecific(thread_specific_key_); size_t* thread_num_ptr = static_cast(thread_num_vptr); size_t thread_num = *thread_num_ptr; return thread_num; } // // partial double partial( CppAD::ADFun& f, size_t j, const d_vector& x ) { size_t nx = x.size(); d_vector dx(nx), dy(1); for(size_t k = 0; k < nx; ++k) dx[k] = 0.0; dx[j] = 1.0; f.Forward(0, x); dy = f.Forward(1, dx); return dy[0]; } // // run_one_thread void* run_one_thread(void* thread_info_vptr) { // // thread_num, f_ptr, j_begin, j_end, x_ptr, Jac_ptr thread_info* thread_info_ptr = static_cast(thread_info_vptr); size_t thread_num = thread_info_ptr->thread_num; CppAD::ADFun* f_ptr = thread_info_ptr->f_ptr; size_t j_begin = thread_info_ptr->j_begin; size_t j_end = thread_info_ptr->j_end; const d_vector* x_ptr = thread_info_ptr->x_ptr; d_vector* Jac_ptr = thread_info_ptr->Jac_ptr; // // f, Jac, ok CppAD::ADFun& f = *f_ptr; d_vector& Jac = *Jac_ptr; bool& ok = thread_info_ptr->ok; // // rc int rc; // // pthread_setspecific, ok // This sets up the thread_number function for this thread. if( thread_num != 0 ) { rc = pthread_setspecific(thread_specific_key_, &thread_num); ok &= rc == 0; ok &= thread_number() == thread_num; } // // f // This will cause an assert if Taylor coefficients were allocated // by a different thread. f.capacity_order(0); // // Jac for(size_t j = j_begin; j < j_end; ++j) Jac[j] = partial(f, j, *x_ptr); // return nullptr; } } bool get_started(void) { // ok bool ok = true; // // eps99 double eps99 = 99.0 * std::numeric_limits::epsilon(); // // nx, ax size_t nx = 10; ad_vector ax(nx); for(size_t j = 0; j < nx; ++j) ax[j] = 1.0; CppAD::Independent(ax); // // fun ad_vector ay(1); ay[0] = ax[0]; for(size_t j = 1; j < nx; ++j) ay[0] *= ax[j]; # if USE_DEFAULT_ADFUN_CONSTRUCTOR CppAD::ADFun fun; fun.Dependent(ax, ay); # else // This allocates memory for first order Taylor coefficients using thread 0. // An assert will occur at f.capacity_order(0) in run_one_thread when // it is called by a different thread. CppAD::ADFun fun(ax, ay); # endif // // num_threads, f_thread size_t num_threads = 4; fun_vector f_thread(num_threads); for(size_t i = 0; i < num_threads; ++i) f_thread[i] = fun; // // x d_vector x(nx); for(size_t j = 0; j < nx; ++j) x[j] = 1.0 + 1.0 / double(j+1); // // Jac d_vector Jac(nx); // // n_per_thread, n_extra size_t n_per_thread = nx / num_threads; size_t n_extra = nx % num_threads; // // thread_info_vec thread_info_vector thread_info_vec(num_threads); size_t j_begin = 0; size_t j_end; for(size_t thread_num = 0; thread_num < num_threads; ++thread_num) { j_end = j_begin + n_per_thread; if( thread_num < n_extra ) ++j_end; // thread_info_vec[thread_num].thread_num = thread_num; thread_info_vec[thread_num].f_ptr = &f_thread[thread_num]; thread_info_vec[thread_num].j_begin = j_begin; thread_info_vec[thread_num].j_end = j_end; thread_info_vec[thread_num].x_ptr = &x; thread_info_vec[thread_num].Jac_ptr = &Jac; thread_info_vec[thread_num].ok = true; // j_begin = j_end; } ok &= j_end == nx; // // rc int rc; // // thread_specific_key_, ok rc = pthread_key_create(&thread_specific_key_, thread_specific_destructor); ok &= rc == 0; // // thread_setspecific, ok // must be set for this thread before calling parall_setup or parallel_ad rc = pthread_setspecific( thread_specific_key_, &thread_info_vec[0].thread_num ); ok &= rc == 0; ok &= thread_number() == 0; // // parallel_setup CppAD::thread_alloc::parallel_setup( num_threads, in_parallel, thread_number ); // // parallel_ad CppAD::parallel_ad(); // // hold_memory // optional and may improve speed if you do a lot of memory allocation CppAD::thread_alloc::hold_memory(true); // // ptread_vec pthread_vector pthread_vec(num_threads - 1); // // sequential_execution_, ok sequential_execution_ = false; ok &= in_parallel(); // // Jac, pthread_vec, ok // Launch num_threads - 1 posix threads for(size_t thread_num = 1; thread_num < num_threads; ++thread_num) { pthread_attr_t* no_attr = nullptr; rc = pthread_create( &pthread_vec[thread_num-1], no_attr, run_one_thread, &thread_info_vec[thread_num] ); ok &= rc == 0; } { // run master thread's indices size_t thread_num = 0; run_one_thread(&thread_info_vec[thread_num]); } // wait for other threads to finish for(size_t thread_num = 1; thread_num < num_threads; ++thread_num) { void* no_status = nullptr; rc = pthread_join(pthread_vec[thread_num-1], &no_status); ok &= rc == 0; } // // sequential_execution_, ok sequential_execution_ = true; CppAD::thread_alloc::parallel_setup(1, nullptr, nullptr); ok &= ! in_parallel(); // // hold_memory, ok // free memory for other threads before this (the master thread) ok &= thread_number() == 0; CppAD::thread_alloc::hold_memory(false); for(size_t thread_num = 1; thread_num < num_threads; ++thread_num) { ok &= thread_info_vec[thread_num].ok; CppAD::thread_alloc::free_available(thread_num); } ok &= thread_info_vec[0].ok; CppAD::thread_alloc::free_available(0); // // ok for(size_t j = 0; j < nx; ++j) { // // check double check = 1.0; for(size_t k = 0; k < nx; ++k) if(k != j) check *= x[k]; // // ok ok &= CppAD::NearEqual(Jac[j], check, eps99, eps99); } return ok; } // END_C++ ================================================ FILE: example/multi_thread/pthread/team_pthread.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin team_pthread.cpp} {xrst_spell destructors } Pthread Implementation of a Team of AD Threads ############################################## See :ref:`team_thread.hpp-name` for this routines specifications. Bug in Cygwin ************* There is a bug in ``pthread_exit`` , using cygwin 5.1 and g++ version 4.3.4, whereby calling ``pthread_exit`` is not the same as returning from the corresponding routine. To be specific, destructors for the vectors are not called and a memory leaks result. Set the following preprocessor symbol to 1 to demonstrate this bug: {xrst_spell_off} {xrst_code cpp} */ # define DEMONSTRATE_BUG_IN_CYGWIN 0 /* {xrst_code} {xrst_spell_on} {xrst_literal // BEGIN C++ // END C++ } {xrst_end team_pthread.cpp} */ // BEGIN C++ # include # include # include "../team_thread.hpp" # define MAX_NUMBER_THREADS 48 // It seems that when a barrier is passed, its counter is automatically reset // to its original value and it can be used again, but where is this // stated in the pthreads speicifcations ? namespace { using CppAD::thread_alloc; // number of threads in the team size_t num_threads_ = 1; // key for accessing thread specific information pthread_key_t thread_specific_key_; // no need to destroy thread specific information void thread_specific_destructor(void* thread_num_vptr) { return; } // type of the job currently being done by each thread enum thread_job_t { init_enum, work_enum, join_enum } thread_job_; // barrier used to wait for other threads to finish work pthread_barrier_t wait_for_work_; // barrier used to wait for master thread to set next job pthread_barrier_t wait_for_job_; // Are we in sequential mode; i.e., other threads are waiting for // master thread to set up next job ? bool sequential_execution_ = true; // structure with information for one thread typedef struct { // cppad unique identifier for thread that uses this struct size_t thread_num; // pthread unique identifier for thread that uses this struct pthread_t pthread_id; // true if no error for this thread, false otherwise. bool ok; } thread_one_t; // vector with information for all threads thread_one_t thread_all_[MAX_NUMBER_THREADS]; // pointer to function that does the work for one thread void (* worker_)(void) = nullptr; // --------------------------------------------------------------------- // in_parallel() bool in_parallel(void) { return ! sequential_execution_; } // --------------------------------------------------------------------- // thread_number() size_t thread_number(void) { // get thread specific information void* thread_num_vptr = pthread_getspecific(thread_specific_key_); size_t* thread_num_ptr = static_cast(thread_num_vptr); size_t thread_num = *thread_num_ptr; if( thread_num >= num_threads_ ) { std::cerr << "thread_number: program error" << std::endl; exit(1); } return thread_num; } // -------------------------------------------------------------------- // function that gets called by pthread_create void* thread_work(void* thread_num_vptr) { int rc; bool ok = true; // Set thread specific data where other routines can access it rc = pthread_setspecific(thread_specific_key_, thread_num_vptr); ok &= rc == 0; // thread_num to problem specific information for this thread size_t thread_num = *static_cast(thread_num_vptr); // master thread does not use this routine ok &= thread_num > 0; while( true ) { // Use wait_for_job_ to give master time in sequential mode // (so it can change global information like thread_job_) rc = pthread_barrier_wait(&wait_for_job_); ok &= (rc == 0 || rc == PTHREAD_BARRIER_SERIAL_THREAD); // case where we are terminating this thread (no more work) if( thread_job_ == join_enum ) break; // only other case once wait_for_job_ barrier is passed (so far) ok &= thread_job_ == work_enum; worker_(); // Use wait_for_work_ to inform master that our work is done and // that this thread will not use global information until // passing its barrier wait_for_job_ above. rc = pthread_barrier_wait(&wait_for_work_); ok &= (rc == 0 || rc == PTHREAD_BARRIER_SERIAL_THREAD); } thread_all_[thread_num].ok = ok; # if DEMONSTRATE_BUG_IN_CYGWIN // Terminate this thread void* no_status = nullptr; pthread_exit(no_status); # endif return nullptr; } } bool team_create(size_t num_threads) { bool ok = true;; int rc; if( num_threads > MAX_NUMBER_THREADS ) { std::cerr << "team_create: num_threads greater than "; std::cerr << MAX_NUMBER_THREADS << std::endl; exit(1); } // check that we currently do not have multiple threads running ok = num_threads_ == 1; ok &= sequential_execution_; size_t thread_num; for(thread_num = 0; thread_num < num_threads; thread_num++) { // Each thread gets a pointer to its version of this thread_num // so it knows which section of thread_all_ it is working with thread_all_[thread_num].thread_num = thread_num; // initialize thread_all_[thread_num].ok = true; } // Finish setup of thread_all_ for this thread thread_all_[0].pthread_id = pthread_self(); // create a key for thread specific information rc = pthread_key_create(&thread_specific_key_,thread_specific_destructor); ok &= (rc == 0); // set thread specific information for this (master thread) void* thread_num_vptr = static_cast(&(thread_all_[0].thread_num)); rc = pthread_setspecific(thread_specific_key_, thread_num_vptr); ok &= (rc == 0); // Now that thread_number() has necessary information for this thread // (number zero), and while still in sequential mode, // call setup for using CppAD::AD in parallel mode. thread_alloc::parallel_setup(num_threads, in_parallel, thread_number); thread_alloc::hold_memory(true); CppAD::parallel_ad(); // Now change num_threads_ to its final value. Waiting till now allows // calls to thread_number during parallel_setup to check thread_num == 0. num_threads_ = num_threads; // initialize two barriers, one for work done, one for new job ready pthread_barrierattr_t* no_barrierattr = nullptr; rc = pthread_barrier_init( &wait_for_work_, no_barrierattr, (unsigned int) num_threads ); ok &= (rc == 0); rc = pthread_barrier_init( &wait_for_job_, no_barrierattr, (unsigned int) num_threads ); ok &= (rc == 0); // structure used to create the threads pthread_t pthread_id; // default for pthread_attr_setdetachstate is PTHREAD_CREATE_JOINABLE pthread_attr_t* no_attr= nullptr; // initial job for the threads thread_job_ = init_enum; if( num_threads > 1 ) sequential_execution_ = false; // This master thread is already running, we need to create // num_threads - 1 more threads for(thread_num = 1; thread_num < num_threads; thread_num++) { // Create the thread with thread number equal to thread_num thread_num_vptr = static_cast ( &(thread_all_[thread_num].thread_num) ); rc = pthread_create( &pthread_id , no_attr , thread_work , thread_num_vptr ); thread_all_[thread_num].pthread_id = pthread_id; ok &= (rc == 0); } // Current state is other threads are at wait_for_job_. // This master thread (thread zero) has not completed wait_for_job_ sequential_execution_ = true; return ok; } bool team_work(void worker(void)) { int rc; // Current state is other threads are at wait_for_job_. // This master thread (thread zero) has not completed wait_for_job_ bool ok = sequential_execution_; ok &= thread_number() == 0; // set global version of this work routine worker_ = worker; // set the new job that other threads are waiting for thread_job_ = work_enum; // enter parallel execution soon as master thread completes wait_for_job_ if( num_threads_ > 1 ) sequential_execution_ = false; // wait until all threads have completed wait_for_job_ rc = pthread_barrier_wait(&wait_for_job_); ok &= (rc == 0 || rc == PTHREAD_BARRIER_SERIAL_THREAD); // Now do the work in this thread and then wait // until all threads have completed wait_for_work_ worker(); rc = pthread_barrier_wait(&wait_for_work_); ok &= (rc == 0 || rc == PTHREAD_BARRIER_SERIAL_THREAD); // Current state is other threads are at wait_for_job_. // This master thread (thread zero) has not completed wait_for_job_ sequential_execution_ = true; size_t thread_num; for(thread_num = 0; thread_num < num_threads_; thread_num++) ok &= thread_all_[thread_num].ok; return ok; } bool team_destroy(void) { int rc; // Current state is other threads are at wait_for_job_. // This master thread (thread zero) has not completed wait_for_job_ bool ok = sequential_execution_; ok &= thread_number() == 0; // set the new job that other threads are waiting for thread_job_ = join_enum; // Enter parallel execution soon as master thread completes wait_for_job_ if( num_threads_ > 1 ) sequential_execution_ = false; rc = pthread_barrier_wait(&wait_for_job_); ok &= (rc == 0 || rc == PTHREAD_BARRIER_SERIAL_THREAD); // now wait for the other threads to exit size_t thread_num; for(thread_num = 1; thread_num < num_threads_; thread_num++) { void* no_status = nullptr; rc = pthread_join( thread_all_[thread_num].pthread_id, &no_status ); ok &= (rc == 0); } // now we are down to just the master thread (thread zero) sequential_execution_ = true; // destroy the key for thread specific data pthread_key_delete(thread_specific_key_); // destroy wait_for_work_ rc = pthread_barrier_destroy(&wait_for_work_); ok &= (rc == 0); // destroy wait_for_job_ rc = pthread_barrier_destroy(&wait_for_job_); ok &= (rc == 0); // check ok before changing num_threads_ for(thread_num = 0; thread_num < num_threads_; thread_num++) ok &= thread_all_[thread_num].ok; // now inform CppAD that there is only one thread num_threads_ = 1; thread_alloc::parallel_setup(num_threads_, nullptr, nullptr); thread_alloc::hold_memory(false); CppAD::parallel_ad(); return ok; } const char* team_name(void) { return "pthread"; } // END C++ ================================================ FILE: example/multi_thread/sthread/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the example/multi_thread/sthread directory tests # Inherit build type from ../CMakeList.txt # Local include directories to search (not in package_prefix/include) INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/.. ) # add_executable( [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] # source1 source2 ... sourceN # ) SET(source_list ../thread_test.cpp ../team_example.cpp ../harmonic.cpp ../multi_atomic_two.cpp ../multi_atomic_three.cpp ../multi_chkpoint_one.cpp ../multi_chkpoint_two.cpp ../multi_newton.cpp a11c_sthread.cpp get_started.cpp team_sthread.cpp ) set_compile_flags( example_multi_thread_sthread "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE( example_multi_thread_sthread EXCLUDE_FROM_ALL ${source_list} ) # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_multi_thread_sthread ${cppad_lib} ${colpack_libs} ${pthread_lib_path} ) # check_example_multi_thread_sthread add_check_executable(check_example_multi_thread sthread get_started) ================================================ FILE: example/multi_thread/sthread/a11c_sthread.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin a11c_sthread.cpp} A Simple Boost Thread Example and Test ###################################### Purpose ******* This example just demonstrates Boost threads and does not use CppAD at all. Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end a11c_sthread.cpp} ---------------------------------------------------------------------------- */ // BEGIN C++ # include # include # include # include // for size_t # include // # define NUMBER_THREADS 4 namespace { // Begin empty namespace class worker_t { private: int n_; float* a_; float* b_; public: void setup(size_t n, float* a, float* b) { n_ = static_cast(n); a_ = a; b_ = b; } // Beginning of Example A.1.1.1c of OpenMP 2.5 standard document void a1(int n, float *a, float *b) { int i; for(i = 1; i < n; i++) b[i] = (a[i] + a[i-1]) / 2.0f; return; } // End of Example A.1.1.1c of OpenMP 2.5 standard document void operator()() { a1(n_, a_, b_); } }; } bool a11c(void) { bool ok = true; // Test setup size_t i, j, n_total = 10; float *a = new float[n_total]; float *b = new float[n_total]; for(i = 0; i < n_total; i++) a[i] = float(i); // number of threads size_t number_threads = NUMBER_THREADS; // set of workers worker_t worker[NUMBER_THREADS]; // threads for each worker std::thread* sthread[NUMBER_THREADS]; // Break the work up into sub work for each thread size_t n = n_total / number_threads; size_t n_tmp = n; float* a_tmp = a; float* b_tmp = b; worker[0].setup(n_tmp, a_tmp, b_tmp); for(j = 1; j < number_threads; j++) { n_tmp = n + 1; a_tmp = a_tmp + n - 1; b_tmp = b_tmp + n - 1; if( j == (number_threads - 1) ) n_tmp = n_total - j * n + 1; worker[j].setup(n_tmp, a_tmp, b_tmp); // create this thread sthread[j] = new std::thread(worker[j]); } // do this threads portion of the work worker[0](); // wait for other threads to finish for(j = 1; j < number_threads; j++) { sthread[j]->join(); delete sthread[j]; sthread[j] = nullptr; } // check the result float eps = 100.f * std::numeric_limits::epsilon(); for(i = 1; i < n ; i++) ok &= std::fabs( (2. * b[i] - a[i] - a[i-1]) / b[i] ) <= eps; delete [] a; delete [] b; return ok; } // END C++ ================================================ FILE: example/multi_thread/sthread/get_started.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sthread_get_started.cpp} {xrst_template , example/multi_thread/template/get_started.xrst title: Getting Started Using @Name@ Threads With CppAD start source code after: // BEGIN_C++ end source code before: // END_C++ @Name@ , Standard @####@ , ######## @DEFAULT@ , USE_DEFAULT_ADFUN_CONSTRUCTOR } {xrst_end sthread_get_started.cpp} ------------------------------------------------------------------------------ */ // BEGIN_C++ # include # include # include # define USE_DEFAULT_ADFUN_CONSTRUCTOR 1 namespace { // // d_vector, ad_vector, fun_vector, sthread_vector typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR( CppAD::AD ) ad_vector; typedef CPPAD_TESTVECTOR( CppAD::ADFun ) fun_vector; typedef CPPAD_TESTVECTOR( std::thread* ) sthread_vector; // // std::vector does not support the data method; see // https://en.cppreference.com/w/cpp/container/vector_bool // 'Does not necessarily store its elements as a contiguous array.' typedef CppAD::vector b_vector; // // thread_id2num_ std::map thread_id2num_; // // begin_thread_mutex_ std::mutex begin_thread_mutex_; // // sequential_execution_ bool sequential_execution_ = true; // // in_parallel bool in_parallel(void) { return ! sequential_execution_; } // // thread_number size_t thread_number(void) { std::thread::id thread_id = std::this_thread::get_id(); return thread_id2num_.at( thread_id ); } // // partial double partial( CppAD::ADFun& f, size_t j, const d_vector& x ) { size_t nx = x.size(); d_vector dx(nx), dy(1); for(size_t k = 0; k < nx; ++k) dx[k] = 0.0; dx[j] = 1.0; f.Forward(0, x); dy = f.Forward(1, dx); return dy[0]; } // // run_one_thread void run_one_thread( size_t thread_num , CppAD::ADFun* f_ptr , size_t j_begin , size_t j_end , const d_vector* x_ptr , d_vector* Jac_ptr , bool* ok_ptr ) { // // begin_thread_mutex_ begin_thread_mutex_.lock(); begin_thread_mutex_.unlock(); // // f, x, Jac, ok CppAD::ADFun& f = *f_ptr; const d_vector& x = *x_ptr; d_vector& Jac = *Jac_ptr; bool& ok = *ok_ptr; // // ok ok &= thread_number() == thread_num; // // f // This will cause an assert if Taylor coefficients were allocated // by a different thread. f.capacity_order(0); // // Jac for(size_t j = j_begin; j < j_end; ++j) Jac[j] = partial(f, j, x); } } bool get_started(void) { // ok bool ok = true; // // eps99 double eps99 = 99.0 * std::numeric_limits::epsilon(); // // nx, ax size_t nx = 10; ad_vector ax(nx); for(size_t j = 0; j < nx; ++j) ax[j] = 1.0; CppAD::Independent(ax); // // fun ad_vector ay(1); ay[0] = ax[0]; for(size_t j = 1; j < nx; ++j) ay[0] *= ax[j]; # if USE_DEFAULT_ADFUN_CONSTRUCTOR CppAD::ADFun fun; fun.Dependent(ax, ay); # else // This allocates memory for first order Taylor coefficients using thread 0. // An assert will occur at f.capacity_order(0) in run_one_thread when // it is called by a different thread. CppAD::ADFun fun(ax, ay); # endif // // num_threads, f_thread, ok_thread size_t num_threads = 4; fun_vector f_thread(num_threads); b_vector ok_thread(num_threads); for(size_t thread_num = 0; thread_num < num_threads; ++thread_num) { f_thread[thread_num] = fun; ok_thread[thread_num] = true; } // // x d_vector x(nx); for(size_t j = 0; j < nx; ++j) x[j] = 1.0 + 1.0 / double(j+1); // // thread_id2num_ // must setup for this thread before calling parallel_setup thread_id2num_.clear(); std::thread::id thread_id = std::this_thread::get_id(); size_t thread_num = 0; thread_id2num_[thread_id] = thread_num; // // parallel_setup CppAD::thread_alloc::parallel_setup( num_threads, in_parallel, thread_number ); // // parallel_ad CppAD::parallel_ad(); // // hold_memory // optional and may improve speed if you do a lot of memory allocation CppAD::thread_alloc::hold_memory(true); // // thread_ptr sthread_vector thread_ptr(num_threads - 1); // // Jac d_vector Jac(nx); // // n_per_thread, n_extra size_t n_per_thread = nx / num_threads; size_t n_extra = nx % num_threads; // // sequential_execution_ sequential_execution_ = false; ok &= in_parallel(); // // begin_thread_mutex_ begin_thread_mutex_.lock(); // // Jac // Launch num_threads - 1 threads size_t j_begin = n_per_thread; size_t j_end; for(thread_num = 1; thread_num < num_threads; ++thread_num) { j_end = j_begin + n_per_thread; if( thread_num <= n_extra ) ++j_end; CppAD::ADFun* f_ptr = &f_thread[thread_num]; bool* ok_ptr = &ok_thread[thread_num]; thread_ptr[thread_num-1] = new std::thread( run_one_thread, thread_num, f_ptr, j_begin, j_end, &x, &Jac, ok_ptr ); // // thread_id2num_ thread_id2num_[ thread_ptr[thread_num-1]->get_id() ] = thread_num; // j_begin = j_end; } // // begin_thread_mutex_ begin_thread_mutex_.unlock(); // ok &= j_end == nx; { // run master thread's indices thread_num = 0; j_begin = 0; j_end = j_begin + n_per_thread; CppAD::ADFun* f_ptr = &f_thread[thread_num]; bool* ok_ptr = &ok_thread[thread_num]; run_one_thread(thread_num, f_ptr, j_begin, j_end, &x, &Jac, ok_ptr); } // wait for other threads to finish for(thread_num = 1; thread_num < num_threads; ++thread_num) { thread_ptr[thread_num-1]->join(); delete thread_ptr[thread_num-1]; } // // sequential_execution_ sequential_execution_ = true; CppAD::thread_alloc::parallel_setup(1, nullptr, nullptr); ok &= ! in_parallel(); // // hold_memory // free memory for other threads before this (the master thread) ok &= thread_number() == 0; CppAD::thread_alloc::hold_memory(false); for(thread_num = 1; thread_num < num_threads; ++thread_num) { CppAD::thread_alloc::free_available(thread_num); ok &= ok_thread[thread_num]; } ok &= ok_thread[0]; CppAD::thread_alloc::free_available(0); // // j for(size_t j = 0; j < nx; ++j) { // // check double check = 1.0; for(size_t k = 0; k < nx; ++k) if(k != j) check *= x[k]; // // ok ok &= CppAD::NearEqual(Jac[j], check, eps99, eps99); } return ok; } // END_C++ ================================================ FILE: example/multi_thread/sthread/team_sthread.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin team_sthread.cpp} Standard Thread Implementation of a Team of AD Threads ###################################################### See :ref:`team_thread.hpp-name` for this routines specifications. {xrst_literal // BEGIN C++ // END C++ } {xrst_end team_sthread.cpp} */ // BEGIN C++ # include # include # include # include # include "../team_thread.hpp" namespace { using CppAD::thread_alloc; // // begin_work_mutex_; std::mutex begin_work_mutex_; // // thread_id2num_ std::map< std::thread::id, size_t > thread_id2num_; // // num_threads_ // number of threads in this team size_t num_threads_; // // sequential_execution_ bool sequential_execution_ = true; // // in_parallel // used to inform CppAD when we are in parallel execution mode bool in_parallel(void) { return ! sequential_execution_; } // // thread_number // used to inform CppAD of the current thread number size_t thread_number(void) { return thread_id2num_.at( std::this_thread::get_id() ); } } // team_create bool team_create(size_t num_threads) { bool ok = ! in_parallel(); ok &= num_threads > 0; // // thread_id2num_ // must setup for this thread before calling parallel_setup thread_id2num_.clear(); std::thread::id thread_id = std::this_thread::get_id(); size_t thread_num = 0; thread_id2num_[thread_id] = thread_num; // // setup for using CppAD::AD in parallel thread_alloc::parallel_setup(num_threads, in_parallel, thread_number); thread_alloc::hold_memory(true); CppAD::parallel_ad(); // // num_thread_ num_threads_ = num_threads; // return ok; } // work_wrapper void work_wrapper(void worker(void)) { // begin_work_mutex_ // wait here while thread_id2num_ is changing begin_work_mutex_.lock(); begin_work_mutex_.unlock(); // // now go to work worker(); } // team_work bool team_work( void worker(void) ) { bool ok = sequential_execution_; ok &= num_threads_ > 0; // // begin_work_mutex_ // stop all threads at the beginning of the work routine begin_work_mutex_.lock(); // // sequential_execution sequential_execution_ = false; // // thread_ptr CppAD::vector thread_ptr(num_threads_ - 1); // // thread_num for(size_t thread_num = 1; thread_num < num_threads_; ++thread_num) { // // thread_ptr thread_ptr[thread_num - 1] = new std::thread(work_wrapper, worker); // // thread_id std::thread::id thread_id = thread_ptr[thread_num-1]->get_id(); // // thread_id2num_ thread_id2num_[thread_id] = thread_num; } // // begin_work_mutex_ // Let the threads go begin_work_mutex_.unlock(); // // put this thread to work worker(); // // join for(size_t thread_num = 1; thread_num < num_threads_; ++thread_num) { thread_ptr[thread_num-1]->join(); delete thread_ptr[thread_num-1]; } // // sequential execution sequential_execution_ = true; // return ok; } bool team_destroy(void) { bool ok = ! in_parallel(); ok &= thread_number() == 0;; ok &= num_threads_ > 0; // inform team_work of number of threads num_threads_ = 1; // // inform CppAD no longer in multi-threading mode thread_alloc::parallel_setup(num_threads_, nullptr, nullptr); thread_alloc::hold_memory(false); CppAD::parallel_ad(); // return ok; } const char* team_name(void) { return "sthread"; } // END C++ ================================================ FILE: example/multi_thread/team_example.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin team_example.cpp} {xrst_spell bthread openmp pthread } Using a Team of AD Threads: Example and Test ############################################ Purpose ******* This example demonstrates how use a team of threads with CppAD. thread_team *********** The following three implementations of the :ref:`team_thread.hpp-name` specifications are included: .. csv-table:: :widths: auto team_openmp.cpp,:ref:`team_openmp.cpp-title` team_bthread.cpp,:ref:`team_bthread.cpp-title` team_pthread.cpp,:ref:`team_pthread.cpp-title` Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end team_example.cpp} ------------------------------------------------------------------------------ */ // BEGIN C++ # include # include "team_thread.hpp" # define NUMBER_THREADS 4 namespace { using CppAD::thread_alloc; // structure with information for one thread typedef struct { // function argument (worker input) double x; // false if an error occurs, true otherwise (worker output) bool ok; } work_one_t; // vector with information for all threads // (use pointers instead of values to avoid false sharing) work_one_t* work_all_[NUMBER_THREADS]; // -------------------------------------------------------------------- // function that does the work for one thread void worker(void) { using CppAD::NearEqual; using CppAD::AD; bool ok = true; size_t thread_num = thread_alloc::thread_num(); // CppAD::vector uses the CppAD fast multi-threading allocator CppAD::vector< AD > ax(1), ay(1); ax[0] = work_all_[thread_num]->x; Independent(ax); ay[0] = sqrt( ax[0] * ax[0] ); CppAD::ADFun f(ax, ay); // Check function value corresponds to the identity double eps = 10. * CppAD::numeric_limits::epsilon(); ok &= NearEqual(ay[0], ax[0], eps, eps); // Check derivative value corresponds to the identity. CppAD::vector d_x(1), d_y(1); d_x[0] = 1.; d_y = f.Forward(1, d_x); ok &= NearEqual(d_x[0], 1., eps, eps); // pass back ok information for this thread work_all_[thread_num]->ok = ok; } } // This test routine is only called by the master thread (thread_num = 0). bool team_example(void) { bool ok = true; size_t num_threads = NUMBER_THREADS; // Check that no memory is in use or available at start // (using thread_alloc in sequential mode) size_t thread_num; for(thread_num = 0; thread_num < num_threads; thread_num++) { ok &= thread_alloc::inuse(thread_num) == 0; ok &= thread_alloc::available(thread_num) == 0; } // initialize work_all_ for(thread_num = 0; thread_num < num_threads; thread_num++) { // allocate separate memory for this thread to avoid false sharing size_t min_bytes(sizeof(work_one_t)), cap_bytes; void* v_ptr = thread_alloc::get_memory(min_bytes, cap_bytes); work_all_[thread_num] = static_cast(v_ptr); // in case this thread's worker does not get called work_all_[thread_num]->ok = false; // parameter that defines the work for this thread work_all_[thread_num]->x = double(thread_num) + 1.; } ok &= team_create(num_threads); ok &= team_work(worker); ok &= team_destroy(); // go down so that free memrory for other threads before memory for master thread_num = num_threads; while(thread_num--) { // check that this thread was ok with the work it did ok &= work_all_[thread_num]->ok; // delete problem specific information void* v_ptr = static_cast( work_all_[thread_num] ); thread_alloc::return_memory( v_ptr ); // check that there is no longer any memory inuse by this thread // (for general applications, the master might still be using memory) ok &= thread_alloc::inuse(thread_num) == 0; // return all memory being held for future use by this thread thread_alloc::free_available(thread_num); } return ok; } // END C++ ================================================ FILE: example/multi_thread/team_example.hpp ================================================ # ifndef CPPAD_EXAMPLE_MULTI_THREAD_TEAM_EXAMPLE_HPP # define CPPAD_EXAMPLE_MULTI_THREAD_TEAM_EXAMPLE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- extern bool team_example(void); # endif ================================================ FILE: example/multi_thread/team_thread.hpp ================================================ # ifndef CPPAD_EXAMPLE_MULTI_THREAD_TEAM_THREAD_HPP # define CPPAD_EXAMPLE_MULTI_THREAD_TEAM_THREAD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin team_thread.hpp} {xrst_spell bthread openmp posix pthread sthread } Specifications for A Team of AD Threads ####################################### Syntax ****** | ``include`` ``"team_thread.hpp"`` | *ok* = ``team_create`` ( *num_threads* ) | *ok* = ``team_work`` ( *worker* ) | *ok* = ``team_destroy`` () | *name* = ``team_name`` () Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Purpose ******* These routines start, use, and stop a team of threads that can be used with the CppAD type ``AD`` . Examples are provided for OpenMP threads, Posix threads, Boost threads and Standard threads. Restrictions ************ Calls to the routines ``team_create`` , ``team_work`` , and ``team_destroy`` , must all be done by the master thread; i.e., :ref:`thread_num` must be zero. In addition, they must all be done in sequential execution mode; i.e., when the master thread is the only thread that is running (:ref:`in_parallel` must be false). team_create *********** The argument *num_threads* > 0 specifies the number of threads in this team. This initializes both ``AD`` and ``team_work`` to be used with *num_threads* . If *num_threads* > 1 , *num_threads* ``- 1`` new threads are created and put in a waiting state until ``team_work`` is called. team_work ********* This routine may be called one or more times between the call to ``team_create`` and ``team_destroy`` . Each call to ``team_work`` runs *num_threads* versions of *worker* with the corresponding value of :ref:`thread_num` between zero and *num_threads* ``- 1`` and different for each thread, team_destroy ************ This routine terminates all the other threads except for thread number zero; i.e., it terminates the threads corresponding to *thread_num* = 1 , ... , *num_threads* ``-1`` team_name ********* This routines returns a name that identifies this threading system. The return value is a statically allocated ``'\0'`` terminated C string. ok ** The return value *ok* is ``false`` if an error is detected during the corresponding call. Otherwise it is ``true`` . {xrst_toc_hidden example/multi_thread/openmp/team_openmp.cpp example/multi_thread/bthread/team_bthread.cpp example/multi_thread/pthread/team_pthread.cpp example/multi_thread/sthread/team_sthread.cpp } Example Use *********** Example use of these specifications can be found in the file :ref:`team_example.cpp-name` . Example Implementation ********************** Example implementations of these specifications can be found in the files: .. csv-table:: :widths: auto team_openmp.cpp,:ref:`team_openmp.cpp-title` team_bthread.cpp,:ref:`team_bthread.cpp-title` team_pthread.cpp,:ref:`team_pthread.cpp-title` team_sthread.cpp,:ref:`team_sthread.cpp-title` Speed Test of Implementation **************************** Speed tests of using CppAD with the team implementations above can be found in: .. csv-table:: :widths: auto harmonic.cpp,:ref:`harmonic.cpp-title` multi_newton.cpp,:ref:`multi_newton.cpp-title` {xrst_end team_thread.hpp} */ # include // for size_t // BEGIN PROTOTYPE extern bool team_create(size_t num_threads); extern bool team_work(void worker(void)); extern bool team_destroy(void); extern const char* team_name(void); // END PROTOTYPE # endif ================================================ FILE: example/multi_thread/template/get_started.xrst ================================================ {xrst_comment # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2024 Bradley M. Bell This file is intended to help document the multi threading get_started examples It has the following xrst template comments: title: Getting Started Using @Name@ Threads With CppAD start source code after: // BEGIN_C++ end source code before: // END_C++ It has the following xrst template replacements @Name@ : is the name of this threading system @####@ : is the underlining for the name of the threading system @DEFAULT@ : is macro a macro name. If it is 1 (0), the ADFun default ctor is used (is not used) } Getting Started Using @Name@ Threads With CppAD ######################@####@################### in_parallel *********** see :ref:`ta_parallel_setup@in_parallel` . thread_number ************* see :ref:`ta_parallel_setup@thread_num` . ADFun Constructor ***************** If you use the :ref:`fun_construct@Sequence Constructor` for the original function, you will need to clear the Taylor coefficient memory associated with the function using :ref:`capacity_order-name` ; e.g. :: {xrst_spell_off} CppAD::ADFun fun(ax, ay); fun.capacity_order(0); {xrst_spell_on} If you do not free the Taylor coefficient memory in ``fun`` , the function assignments will allocate zero order Taylor coefficients for each function in ``fun_thread`` using thread zero. Depending on what you do in parallel mode, you may attempt to free that memory using another thread. For example, if you change @DEFAULT@ from 1 to 0, you will get the message:: Attempt to return memory for a different thread while in parallel mode Source Code *********** {xrst_literal // BEGIN_C++ // END_C++ } ================================================ FILE: example/multi_thread/thread_test.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin thread_test.cpp} {xrst_spell posix } Run Multi-Threading Examples and Speed Tests ############################################ Purpose ******* Runs the CppAD multi-threading examples and timing tests: build ***** We use *build* for the directory where you run the :ref:`cmake-name` command. threading ********* If the :ref:`cmake-name` command output indicates that ``openmp``, ``bthread`` , ``pthread`` , or ``sthread`` is available, you can run the program below with *threading* equal to ``openmp``, ``bthread`` , ``pthread`` , or ``sthread`` respectively. program ******* We use the notation *program* for ``example_multi_thread_`` *threading* Running Tests ************* You can build this program and run the default version of its test parameters by executing the following commands: | |tab| ``cd`` *build* | |tab| ``make check_`` *program* After this operation, in the directory *build* / ``example/multi_thread/`` *threading* you can execute the following commands: | . | ./ *program* ``a11c`` | ./ *program* ``get_started`` | ./ *program* ``team_example`` | ./ *program* ``harmonic`` *test_time* *max_threads* *mega_sum* | ./ *program* ``atomic_two`` *test_time* *max_threads* *num_solve* | ./ *program* ``atomic_three`` *test_time* *max_threads* *num_solve* | ./ *program* ``chkpoint_one`` *test_time* *max_threads* *num_solve* | ./ *program* ``chkpoint_two`` *test_time* *max_threads* *num_solve* | ./ *program* ``multi_newton`` *test_time* *max_threads* \\ | |tab| *num_zero* *num_sub* *num_sum* *use_ad* We refer to the values ``a11c`` , ... , ``multi_newton`` as the *test_case* below. {xrst_toc_hidden example/multi_thread/openmp/a11c_openmp.cpp example/multi_thread/bthread/a11c_bthread.cpp example/multi_thread/pthread/a11c_pthread.cpp example/multi_thread/sthread/a11c_sthread.cpp example/multi_thread/openmp/get_started.cpp example/multi_thread/bthread/get_started.cpp example/multi_thread/pthread/get_started.cpp example/multi_thread/sthread/get_started.cpp example/multi_thread/team_example.cpp example/multi_thread/harmonic.xrst example/multi_thread/multi_atomic_three.xrst example/multi_thread/multi_chkpoint_two.xrst example/multi_thread/multi_newton.xrst example/multi_thread/team_thread.hpp } a11c **** The *test_case* ``a11c`` runs the examples :ref:`a11c_openmp.cpp-name` , :ref:`a11c_bthread.cpp-name` , :ref:`a11c_pthread.cpp-name` , and :ref:`a11c_sthread.cpp-name` . These cases demonstrate simple multi-threading, without algorithmic differentiation, using OpenMP, Boost Posix and Standard threads respectively. get_started *********** The *test_case* ``get_started`` runs the examples :ref:`openmp_get_started.cpp-name` , :ref:`bthread_get_started.cpp-name` , :ref:`pthread_get_started.cpp-name` , and :ref:`sthread_get_started.cpp-name` . These cases demonstrate simple multi-threading, with algorithmic differentiation, using OpenMP, Boost Posix and Standard threads respectively. team_example ************ The *test_case* ``team_example`` runs the :ref:`team_example.cpp-name` example. This case demonstrates simple multi-threading with algorithmic differentiation and using a :ref:`team of threads` . test_time ********* All of the other cases include the *test_time* argument. This is the minimum amount of wall clock time that the test should take. The number of repeats for the test will be increased until this time is reached. The reported time is the total wall clock time divided by the number of repeats. max_threads =========== All of the other cases include the *max_threads* argument. This is a non-negative integer specifying the maximum number of threads to use for the test. The specified test is run with the following number of threads: *num_threads* = 0 , ... , *max_threads* The value of zero corresponds to not using the multi-threading system. {xrst_comment -------------------------------------------------------------- } harmonic ******** The *test_case* ``harmonic`` runs the :ref:`harmonic_time-name` example. This is a timing test for a multi-threading example without algorithmic differentiation using a team of threads. mega_sum ======== The command line argument *mega_sum* is an integer greater than or equal one and has the same meaning as in :ref:`harmonic_time` . {xrst_comment -------------------------------------------------------------- } Atomic and Checkpoint ********************* The *test_case* values ``atomic_two`` , ``atomic_three`` , ``chkpoint_one`` , ``chkpoint_two`` , all run the same problem. These cases preforms a timing test for a multi-threading example without algorithmic differentiation using a team of threads. .. csv-table:: :widths: auto *test_case*,Documentation ``atomic_two``,:ref:`multi_atomic_two.cpp-name` ``atomic_three``,:ref:`multi_atomic_three.cpp-name` ``chkpoint_one``,:ref:`multi_chkpoint_one.cpp-name` ``chkpoint_two``,:ref:`multi_chkpoint_two.cpp-name` num_solve ========= The command line argument *num_solve* is an integer specifying the number of solves; see :ref:`multi_atomic_two_time@num_solve` in ``multi_atomic_two_time`` . {xrst_comment -------------------------------------------------------------- } multi_newton ************ The *test_case* ``multi_newton`` runs the :ref:`multi_newton.cpp-name` example. This preforms a timing test for a multi-threading example with algorithmic differentiation using a team of threads. num_zero ======== The command line argument *num_zero* is an integer greater than or equal two and has the same meaning as in :ref:`multi_newton_time` . num_sub ======= The command line argument *num_sub* is an integer greater than or equal one and has the same meaning as in :ref:`multi_newton_time` . num_sum ======= The command line argument *num_sum* is an integer greater than or equal one and has the same meaning as in :ref:`multi_newton_time` . use_ad ====== The command line argument *use_ad* is either ``true`` or ``false`` and has the same meaning as in :ref:`multi_newton_time` . {xrst_comment -------------------------------------------------------------- } Team Implementations ******************** The following routines are used to implement the specific threading systems through the common interface :ref:`team_thread.hpp-name` : .. csv-table:: :widths: auto team_openmp.cpp,:ref:`team_openmp.cpp-title` team_bthread.cpp,:ref:`team_bthread.cpp-title` team_pthread.cpp,:ref:`team_pthread.cpp-title` team_sthread.cpp,:ref:`team_sthread.cpp-title` Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end thread_test.cpp} */ // BEGIN C++ # include # include # include # include # include "team_thread.hpp" # include "team_example.hpp" # include "harmonic.hpp" # include "multi_atomic_two.hpp" # include "multi_atomic_three.hpp" # include "multi_chkpoint_one.hpp" # include "multi_chkpoint_two.hpp" # include "multi_newton.hpp" extern bool a11c(void); extern bool get_started(void); namespace { size_t arg2size_t( const char* arg , int limit , const char* error_msg ) { int i = std::atoi(arg); if( i >= limit ) return size_t(i); std::cerr << "value = " << i << std::endl; std::cerr << error_msg << std::endl; exit(1); } double arg2double( const char* arg , double limit , const char* error_msg ) { double d = std::atof(arg); if( d >= limit ) return d; std::cerr << "value = " << d << std::endl; std::cerr << error_msg << std::endl; exit(1); } } int main(int argc, char *argv[]) { using CppAD::thread_alloc; bool ok = true; using std::cout; using std::endl; // command line usage message const char* usage = "./ a11c\n" "./ get_started\n" "./ team_example\n" "./ harmonic test_time max_threads mega_sum\n" "./ atomic_two test_time max_threads num_solve\n" "./ atomic_three test_time max_threads num_solve\n" "./ chkpoint_one test_time max_threads num_solve\n" "./ chkpoint_two test_time max_threads num_solve\n" "./ multi_newton test_time max_threads \\\n" " num_zero num_sub num_sum use_ad\\\n" "where is example_multi_thread_\n" "and is openmp, bthread, pthread, or sthread"; // command line argument values (assign values to avoid compiler warnings) size_t num_zero=0, num_sub=0, num_sum=0; bool use_ad=true; // put the date and time in the output file std::time_t rawtime; std::time( &rawtime ); const char* gmt = std::asctime( std::gmtime( &rawtime ) ); size_t len = size_t( std::strlen(gmt) ); cout << "gmtime = '"; for(size_t i = 0; i < len; i++) if( gmt[i] != '\n' ) cout << gmt[i]; cout << "';" << endl; // CppAD version number cout << "cppad_version = '" << CPPAD_PACKAGE_STRING << "';" << endl; // put the team name in the output file cout << "thread_system = '" << team_name() << "';" << endl; // print command line as valid matlab/octave cout << "command = '" << argv[0]; for(int i = 1; i < argc; i++) cout << " " << argv[i]; cout << "';" << endl; ok = false; const char* test_name = ""; if( argc > 1 ) test_name = *++argv; bool run_a11c = std::strcmp(test_name, "a11c") == 0; bool run_get_started = std::strcmp(test_name, "get_started") == 0; bool run_team_example = std::strcmp(test_name, "team_example") == 0; bool run_harmonic = std::strcmp(test_name, "harmonic") == 0; bool run_atomic_two = std::strcmp(test_name, "atomic_two") == 0; bool run_atomic_three = std::strcmp(test_name, "atomic_three") == 0; bool run_chkpoint_one = std::strcmp(test_name, "chkpoint_one") == 0; bool run_chkpoint_two = std::strcmp(test_name, "chkpoint_two") == 0; bool run_multi_newton = std::strcmp(test_name, "multi_newton") == 0; if( run_a11c || run_get_started || run_team_example ) ok = (argc == 2); else if( run_harmonic || run_atomic_two || run_atomic_three || run_chkpoint_one || run_chkpoint_two ) ok = (argc == 5); else if( run_multi_newton ) ok = (argc == 8); if( ! ok ) { std::cerr << "test_name = " << test_name << endl; std::cerr << "argc = " << argc << endl; std::cerr << usage << endl; exit(1); } if( run_a11c || run_get_started || run_team_example ) { if( run_a11c ) ok = a11c(); else if( run_get_started ) ok = get_started(); else ok = team_example(); if( thread_alloc::free_all() ) cout << "free_all = true;" << endl; else { ok = false; cout << "free_all = false;" << endl; } if( ok ) cout << "OK = true;" << endl; else cout << "OK = false;" << endl; return ! ok; } // test_time double test_time = arg2double( *++argv, 0., "run: test_time is less than zero" ); // max_threads size_t max_threads = arg2size_t( *++argv, 0, "run: max_threads is less than zero" ); size_t mega_sum = 0; // assignment to avoid compiler warning size_t num_solve = 0; if( run_harmonic ) { // mega_sum mega_sum = arg2size_t( *++argv, 1, "run: mega_sum is less than one" ); } else if( run_atomic_two || run_atomic_three || run_chkpoint_one || run_chkpoint_two ) { // num_solve num_solve = arg2size_t( *++argv, 1, "run: num_solve is less than one" ); } else { ok &= run_multi_newton; if( ! ok ) { cout << "thread_test: program error\n"; return ! ok; } // num_zero num_zero = arg2size_t( *++argv, 2, "run: num_zero is less than two" ); // num_sub num_sub = arg2size_t( *++argv, 1, "run: num_sub is less than one" ); // num_sum num_sum = arg2size_t( *++argv, 1, "run: num_sum is less than one" ); // use_ad ++argv; if( std::strcmp(*argv, "true") == 0 ) use_ad = true; else if( std::strcmp(*argv, "false") == 0 ) use_ad = false; else { std::cerr << "run: use_ad = '" << *argv; std::cerr << "' is not true or false" << endl; exit(1); } } // run the test for each number of threads cout << "time_all = [" << endl; for(size_t num_threads = 0; num_threads <= max_threads; num_threads++) { double time_out; bool this_ok; // run the requested test if( run_harmonic ) this_ok = harmonic_time( time_out, test_time, num_threads, mega_sum ); else if( run_atomic_two ) this_ok = multi_atomic_two_time( time_out, test_time, num_threads, num_solve ); else if( run_atomic_three ) this_ok = multi_atomic_three_time( time_out, test_time, num_threads, num_solve ); else if( run_chkpoint_one ) this_ok = multi_chkpoint_one_time( time_out, test_time, num_threads, num_solve ); else if( run_chkpoint_two ) this_ok = multi_chkpoint_two_time( time_out, test_time, num_threads, num_solve ); else { assert( run_multi_newton); this_ok = multi_newton_time( time_out , test_time , num_threads , num_zero , num_sub , num_sum , use_ad ); } // time_out cout << std::setw(20) << time_out << " % "; // num_threads if( num_threads == 0 ) cout << "no threading"; else cout << num_threads << " threads"; if( this_ok ) cout << " ok" << endl; else cout << " error" << endl; // ok &= this_ok; } cout << "];" << endl; // if( thread_alloc::free_all() ) cout << "free_all = true;" << endl; else { ok = false; cout << "free_all = false;" << endl; } if( ok ) cout << "OK = true;" << endl; else cout << "OK = false;" << endl; return ! ok; } // END C++ ================================================ FILE: example/optimize/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the example/optimize directory tests # # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list compare_op.cpp conditional_skip.cpp cumulative_sum.cpp forward_active.cpp nest_conditional.cpp optimize.cpp optimize_twice.cpp print_for.cpp reverse_active.cpp ) # END_SORT_THIS_LINE_MINUS_2 set_compile_flags( example_optimize "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(example_optimize EXCLUDE_FROM_ALL ${source_list}) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_optimize ${cppad_lib} ${colpack_libs} ) # # check_example_optimize add_check_executable(check_example optimize) ================================================ FILE: example/optimize/compare_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin optimize_compare_op.cpp} Optimize Comparison Operators: Example and Test ############################################### See Also ******** :ref:`cond_exp.cpp-name` {xrst_literal // BEGIN C++ // END C++ } {xrst_end optimize_compare_op.cpp} */ // BEGIN C++ # include namespace { struct tape_size { size_t n_var; size_t n_op; }; template void fun( const std::string& options , const Vector& x, Vector& y, tape_size& before, tape_size& after ) { typedef typename Vector::value_type scalar; // phantom variable with index 0 and independent variables // begin operator, independent variable operators and end operator before.n_var = 1 + x.size(); before.n_op = 2 + x.size(); after.n_var = 1 + x.size(); after.n_op = 2 + x.size(); // Create a variable that is is only used in the comparison operation // It is not used when the comparison operator is not included scalar one = 1. / x[0]; before.n_var += 1; before.n_op += 1; after.n_var += 0; after.n_op += 0; // If we keep comparison operators, we must compute their operands if( options.find("no_compare_op") == std::string::npos ) { after.n_var += 1; after.n_op += 1; } // Create a variable that is used by the result scalar two = x[0] * 5.; before.n_var += 1; before.n_op += 1; after.n_var += 1; after.n_op += 1; // Only one variable created for this comparison operation // but the value depends on which branch is taken. scalar three; if( one < x[0] ) // comparison operator three = two / 2.0; // division operator else three = 2.0 * two; // multiplication operator // comparison and either division of multiplication operator before.n_var += 1; before.n_op += 2; // comparison operator depends on optimization options after.n_var += 1; after.n_op += 1; // check if we are keeping the comparison operator if( options.find("no_compare_op") == std::string::npos ) after.n_op += 1; // results for this operation sequence y[0] = three; before.n_var += 0; before.n_op += 0; after.n_var += 0; after.n_op += 0; } } bool compare_op(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); for(size_t k = 0; k < 2; k++) { // optimization options std::string options = ""; if( k == 0 ) options = "no_compare_op"; // declare independent variables and start tape recording CppAD::Independent(ax); // compute function value tape_size before, after; fun(options, ax, ay, before, after); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); ok &= f.size_order() == 1; // this constructor does 0 order forward ok &= f.size_var() == before.n_var; ok &= f.size_op() == before.n_op; // Optimize the operation sequence f.optimize(options); ok &= f.size_order() == 0; // 0 order forward not present ok &= f.size_var() == after.n_var; ok &= f.size_op() == after.n_op; // Check result for a zero order calculation for a different x, // where the result of the comparison is he same. CPPAD_TESTVECTOR(double) x(n), y(m), check(m); x[0] = 0.75; y = f.Forward(0, x); if ( options == "" ) ok &= f.compare_change_number() == 0; fun(options, x, check, before, after); ok &= NearEqual(y[0], check[0], eps10, eps10); // Check case where result of the comparison is different // (hence one needs to re-tape to get correct result) x[0] = 2.0; y = f.Forward(0, x); if ( options == "" ) ok &= f.compare_change_number() == 1; fun(options, x, check, before, after); ok &= std::fabs(y[0] - check[0]) > 0.5; } return ok; } // END C++ ================================================ FILE: example/optimize/conditional_skip.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin optimize_conditional_skip.cpp} Optimize Conditional Expressions: Example and Test ################################################## See Also ******** :ref:`cond_exp.cpp-name` {xrst_literal // BEGIN C++ // END C++ } {xrst_end optimize_conditional_skip.cpp} */ // BEGIN C++ # include namespace { struct tape_size { size_t n_var; size_t n_op; }; template void fun( const std::string& options , const Vector& x, Vector& y, tape_size& before, tape_size& after ) { typedef typename Vector::value_type scalar; // phantom variable with index 0 and independent variables // begin operator, independent variable operators and end operator before.n_var = 1 + x.size(); before.n_op = 2 + x.size(); after.n_var = 1 + x.size(); after.n_op = 2 + x.size(); // Create a variable that is is only used as left operand // in the comparison operation scalar left = 1. / x[0]; before.n_var += 1; before.n_op += 1; after.n_var += 1; after.n_op += 1; // right operand in comparison operation scalar right = x[0]; before.n_var += 0; before.n_op += 0; after.n_var += 0; after.n_op += 0; // Note that the left and right operand in the CondExpLt comparison // are determined at this point. Hence the conditional skip operator // will be inserted here so that the operations mentioned below can // also be skipped during zero order forward mode. if( options.find("no_conditional_skip") == std::string::npos ) after.n_op += 1; // for conditional skip operation // Create a variable that is only used when comparison result is true // (can be skipped when the comparison result is false) scalar if_true = x[0] * 5.0; before.n_var += 1; before.n_op += 1; after.n_var += 1; after.n_op += 1; // Create two variables only used when the comparison result is false // (can be skipped when the comparison result is true) scalar temp = 5.0 + x[0]; scalar if_false = temp * 3.0; before.n_var += 2; before.n_op += 2; after.n_var += 2; after.n_op += 2; // conditional comparison is 1 / x[0] < x[0] scalar value = CppAD::CondExpLt(left, right, if_true, if_false); before.n_var += 1; before.n_op += 1; after.n_var += 1; after.n_op += 1; // results for this operation sequence y[0] = value; before.n_var += 0; before.n_op += 0; after.n_var += 0; after.n_op += 0; } } bool conditional_skip(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); for(size_t k = 0; k < 2; k++) { // optimization options std::string options = ""; if( k == 0 ) options = "no_conditional_skip"; // declare independent variables and start tape recording CppAD::Independent(ax); // compute function computation tape_size before, after; fun(options, ax, ay, before, after); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); ok &= f.size_order() == 1; // this constructor does 0 order forward ok &= f.size_var() == before.n_var; ok &= f.size_op() == before.n_op; // Optimize the operation sequence f.optimize(options); ok &= f.size_order() == 0; // 0 order forward not present ok &= f.size_var() == after.n_var; ok &= f.size_op() == after.n_op; // Check case where result of the comparison is true (x[0] > 1.0). CPPAD_TESTVECTOR(double) x(n), y(m), check(m); x[0] = 1.75; y = f.Forward(0, x); fun(options, x, check, before, after); ok &= NearEqual(y[0], check[0], eps10, eps10); if( options == "" ) ok &= f.number_skip() == 2; else ok &= f.number_skip() == 0; // Check case where result of the comparison is false (x[0] <= 1.0) x[0] = 0.5; y = f.Forward(0, x); fun(options, x, check, before, after); ok &= NearEqual(y[0], check[0], eps10, eps10); if( options == "" ) ok &= f.number_skip() == 1; else ok &= f.number_skip() == 0; } return ok; } // END C++ ================================================ FILE: example/optimize/cumulative_sum.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin optimize_cumulative_sum.cpp} Optimize Cumulative Sum Operations: Example and Test #################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end optimize_cumulative_sum.cpp} */ // BEGIN C++ # include namespace { struct tape_size { size_t n_var; size_t n_op; }; template void fun( const Vector& x, Vector& y, tape_size& before, tape_size& after ) { typedef typename Vector::value_type scalar; // phantom variable with index 0 and independent variables // begin operator, independent variable operators and end operator before.n_var = 1 + x.size(); before.n_op = 2 + x.size(); after.n_var = 1 + x.size(); after.n_op = 2 + x.size(); // operators that are identical, and that will be made part of the // cumulative summation. Make sure do not replace second variable // using the first and then remove the first as part of the // cumulative summation. scalar first = x[0] + x[1]; scalar second = x[0] + x[1]; before.n_var += 2; before.n_op += 2; after.n_var += 0; after.n_op += 0; // test that subtractions are also included in cumulative summations scalar third = x[1] - 2.0; before.n_var += 1; before.n_op += 1; after.n_var += 0; after.n_op += 0; // the finial summation is converted to a cumulative summation // the other is removed. scalar csum = first + second + third; before.n_var += 2; before.n_op += 2; after.n_var += 1; after.n_op += 1; // results for this operation sequence y[0] = csum; before.n_var += 0; before.n_op += 0; after.n_var += 0; after.n_op += 0; } } bool cumulative_sum(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; ax[1] = 1.5; // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); tape_size before, after; fun(ax, ay, before, after); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); ok &= f.size_order() == 1; // this constructor does 0 order forward ok &= f.size_var() == before.n_var; ok &= f.size_op() == before.n_op; // Optimize the operation sequence f.optimize(); ok &= f.size_order() == 0; // 0 order forward not present ok &= f.size_var() == after.n_var; ok &= f.size_op() == after.n_op; // Check result for a zero order calculation for a different x, CPPAD_TESTVECTOR(double) x(n), y(m), check(m); x[0] = 0.75; x[1] = 2.25; y = f.Forward(0, x); fun(x, check, before, after); ok &= CppAD::NearEqual(y[0], check[0], eps10, eps10); return ok; } // END C++ ================================================ FILE: example/optimize/forward_active.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin optimize_forward_active.cpp} Optimize Forward Activity Analysis: Example and Test #################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end optimize_forward_active.cpp} */ // BEGIN C++ # include namespace { struct tape_size { size_t n_var; size_t n_op; }; template void fun( const Vector& x, Vector& y, tape_size& before, tape_size& after ) { typedef typename Vector::value_type scalar; // phantom variable with index 0 and independent variables // begin operator, independent variable operators and end operator before.n_var = 1 + x.size(); before.n_op = 2 + x.size(); after.n_var = 1 + x.size(); after.n_op = 2 + x.size(); // adding the constant zero does not take any operations scalar zero = 0.0 + x[0]; before.n_var += 0; before.n_op += 0; after.n_var += 0; after.n_op += 0; // multiplication by the constant one does not take any operations scalar one = 1.0 * x[1]; before.n_var += 0; before.n_op += 0; after.n_var += 0; after.n_op += 0; // multiplication by the constant zero does not take any operations // and results in the constant zero. scalar two = 0.0 * x[0]; // operations that only involve constants do not take any operations scalar three = (1.0 + two) * 3.0; before.n_var += 0; before.n_op += 0; after.n_var += 0; after.n_op += 0; // The optimizer will recognize that zero + one = one + zero // for all values of x. scalar four = zero + one; scalar five = one + zero; before.n_var += 2; before.n_op += 2; after.n_var += 1; after.n_op += 1; // The optimizer will recognize that sin(x[3]) = sin(x[3]) // for all values of x. Note that, for computation of derivatives, // sin(x[3]) and cos(x[3]) are stored on the tape as a pair. scalar six = sin(x[2]); scalar seven = sin(x[2]); before.n_var += 4; before.n_op += 2; after.n_var += 2; after.n_op += 1; // If we used addition here, five + seven = zero + one + seven // which would get converted to a cumulative summation operator. scalar eight = five * seven; before.n_var += 1; before.n_op += 1; after.n_var += 1; after.n_op += 1; // Use two, three, four and six in order to avoid a compiler warning // Note that addition of two and three does not take any operations. // Also note that optimizer recognizes four * six == five * seven. scalar nine = eight + four * six * (two + three); before.n_var += 3; before.n_op += 3; after.n_var += 2; after.n_op += 2; // results for this operation sequence y[0] = nine; before.n_var += 0; before.n_op += 0; after.n_var += 0; after.n_op += 0; } } bool forward_active(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 3; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; ax[1] = 1.5; ax[2] = 2.0; // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); tape_size before, after; fun(ax, ay, before, after); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); ok &= f.size_order() == 1; // this constructor does 0 order forward ok &= f.size_var() == before.n_var; ok &= f.size_op() == before.n_op; // Optimize the operation sequence // Note that, for this case, all the optimization was done during // the recording and there is no benefit to the optimization. f.optimize(); ok &= f.size_order() == 0; // 0 order forward not present ok &= f.size_var() == after.n_var; ok &= f.size_op() == after.n_op; // check zero order forward with different argument value CPPAD_TESTVECTOR(double) x(n), y(m), check(m); for(size_t i = 0; i < n; i++) x[i] = double(i + 2); y = f.Forward(0, x); fun(x, check, before, after); ok &= NearEqual(y[0], check[0], eps10, eps10); return ok; } // END C++ ================================================ FILE: example/optimize/nest_conditional.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin optimize_nest_conditional.cpp} Optimize Nested Conditional Expressions: Example and Test ######################################################### See Also ******** :ref:`cond_exp.cpp-name` {xrst_literal // BEGIN C++ // END C++ } {xrst_end optimize_nest_conditional.cpp} */ // BEGIN C++ # include namespace { struct tape_size { size_t n_var; size_t n_op; }; template void fun( const std::string& options , const Vector& x, Vector& y, tape_size& before, tape_size& after ) { typedef typename Vector::value_type scalar; // phantom variable with index 0 and independent variables // begin operator, independent variable operators and end operator before.n_var = 1 + x.size(); before.n_op = 2 + x.size(); after.n_var = 1 + x.size(); after.n_op = 2 + x.size(); // Create a variable that is is only used in the second comparison scalar two = 1. + x[0]; before.n_var += 1; before.n_op += 1; after.n_var += 1; after.n_op += 1; // Conditional skip for second comparison will be inserted here. if( options.find("no_conditional_skip") == std::string::npos ) after.n_op += 1; // for conditional skip operation // Create a variable that is is only used in the first comparison // (can be skipped when second comparison result is false) scalar one = 1. / x[0]; before.n_var += 1; before.n_op += 1; after.n_var += 1; after.n_op += 1; // Conditional skip for first comparison will be inserted here. if( options.find("no_conditional_skip") == std::string::npos ) after.n_op += 1; // for conditional skip operation // value when first comparison if false scalar one_false = 5.0; // Create a variable that is only used when second comparison is true // (can be skipped when it is false) scalar one_true = x[0] / 5.0; before.n_var += 1; before.n_op += 1; after.n_var += 1; after.n_op += 1; // value when second comparison is false scalar two_false = 3.0; // First conditional compaison is 1 / x[0] < x[0] // is only used when second conditional expression is true // (can be skipped when it is false) scalar two_true = CppAD::CondExpLt(one, x[0], one_true, one_false); before.n_var += 1; before.n_op += 1; after.n_var += 1; after.n_op += 1; // Second conditional compaison is 1 + x[0] < x[1] scalar two_value = CppAD::CondExpLt(two, x[1], two_true, two_false); before.n_var += 1; before.n_op += 1; after.n_var += 1; after.n_op += 1; // results for this operation sequence y[0] = two_value; before.n_var += 0; before.n_op += 0; after.n_var += 0; after.n_op += 0; } } bool nest_conditional(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; ax[1] = 0.5; // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); for(size_t k = 0; k < 2; k++) { // optimization options std::string options = ""; if( k == 0 ) options = "no_conditional_skip"; // declare independent variables and start tape recording CppAD::Independent(ax); // compute function computation tape_size before, after; fun(options, ax, ay, before, after); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); ok &= f.size_order() == 1; // this constructor does 0 order forward ok &= f.size_var() == before.n_var; ok &= f.size_op() == before.n_op; // Optimize the operation sequence f.optimize(options); ok &= f.size_order() == 0; // 0 order forward not present ok &= f.size_var() == after.n_var; ok &= f.size_op() == after.n_op; // Check case where result of the second comparison is true // and first comparison is true CPPAD_TESTVECTOR(double) x(n), y(m), check(m); x[0] = 1.75; x[1] = 4.0; y = f.Forward(0, x); fun(options, x, check, before, after); ok &= NearEqual(y[0], check[0], eps10, eps10); ok &= f.number_skip() == 0; // Check case where result of the second comparison is true // and first comparison is false x[0] = 0.5; x[1] = 4.0; y = f.Forward(0, x); fun(options, x, check, before, after); ok &= NearEqual(y[0], check[0], eps10, eps10); if( options == "" ) ok &= f.number_skip() == 1; else ok &= f.number_skip() == 0; // Check case where result of the second comparison is false // and first comparison is true x[0] = 1.75; x[1] = 0.0; y = f.Forward(0, x); fun(options, x, check, before, after); ok &= NearEqual(y[0], check[0], eps10, eps10); if( options == "" ) ok &= f.number_skip() == 3; else ok &= f.number_skip() == 0; // Check case where result of the second comparison is false // and first comparison is false x[0] = 0.5; x[1] = 0.0; y = f.Forward(0, x); fun(options, x, check, before, after); ok &= NearEqual(y[0], check[0], eps10, eps10); if( options == "" ) ok &= f.number_skip() == 3; else ok &= f.number_skip() == 0; } return ok; } // END C++ ================================================ FILE: example/optimize/optimize.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin optimize.cpp} optimize Examples and Tests Driver ################################## Running These Tests ******************* After executing the :ref:`cmake-name` command form the :ref:`download@Distribution Directory`, you can build and run these tests with the commands:: cd build make check_example_optimize Note that your choice of :ref:`cmake@generator` may require using an different version of make; e.g., ``ninja`` . {xrst_literal // BEGIN C++ // END C++ } {xrst_end optimize.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ // for thread_alloc # include // test runner # include // external complied tests extern bool compare_op(void); extern bool conditional_skip(void); extern bool cumulative_sum(void); extern bool forward_active(void); extern bool nest_conditional(void); extern bool print_for(void); extern bool reverse_active(void); extern bool optimize_twice(void); // main program that runs all the tests int main(void) { std::string group = "example/optimize"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // external compiled tests Run( cumulative_sum, "compare_op" ); Run( cumulative_sum, "cumulative_sum" ); Run( conditional_skip, "conditional_skip" ); Run( forward_active, "forward_active" ); Run( nest_conditional, "nest_conditional" ); Run( print_for, "print_for" ); Run( reverse_active, "reverse_active" ); Run( optimize_twice, "re_optimize" ); // // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } // END C++ ================================================ FILE: example/optimize/optimize_twice.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin optimize_twice.cpp} Optimizing Twice: Example and Test ################################## Discussion ********** Before 2019-06-28, optimizing twice was not supported and would fail if cumulative sum operators were present after the first optimization. This is now supported but it is not expected to have much benefit. If you find a case where it does have a benefit, please inform the CppAD developers of this. {xrst_literal // BEGIN C++ // END C++ } {xrst_end optimize_twice.cpp} */ // BEGIN C++ # include bool optimize_twice(void) { bool ok = true; using CppAD::AD; using CppAD::vector; size_t n = 3; vector< AD > ax(n); for(size_t j = 0; j < n; ++j) ax[j] = 0.0; Independent(ax); // AD asum = 0.0; for(size_t j = 0; j < n; ++j) asum += ax[j]; // vector< AD > ay(1); ay[0] = asum * asum; // // This method of recording the function does not do a 0 order forward CppAD::ADFun f; f.Dependent(ax, ay); ok &= f.size_order() == 0; size_t size_var = f.size_var(); // f.optimize(); // creates a cumulative sum operator ok &= f.size_var() <= size_var - (n - 2); size_var = f.size_var(); // f.optimize(); // optimizes a function with a cumulative sum operator ok &= f.size_var() == size_var; // no benefit expected by second optimize // // zero order forward vector x(n), y(1); double sum = 0.0; for(size_t j = 0; j < n; ++j) { x[j] = double(j + 1); sum += x[j]; } y = f.Forward(0, x); double check = sum * sum; ok &= y[0] == check; // vector w(1), dx(n); w[0] = 1.0; dx = f.Reverse(1, w); // for(size_t j = 0; j < n; ++j) ok &= dx[j] == 2.0 * sum; // return ok; } // END C++ ================================================ FILE: example/optimize/print_for.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin optimize_print_for.cpp} Optimize Print Forward Operators: Example and Test ################################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end optimize_print_for.cpp} */ // BEGIN C++ # include namespace { struct tape_size { size_t n_var; size_t n_op; }; void PrintFor( double pos, const char* before, double var, const char* after ) { if( pos <= 0.0 ) std::cout << before << var << after; return; } template void fun( const std::string& options , const Vector& x, Vector& y, tape_size& before, tape_size& after ) { typedef typename Vector::value_type scalar; // phantom variable with index 0 and independent variables // begin operator, independent variable operators and end operator before.n_var = 1 + x.size(); before.n_op = 2 + x.size(); after.n_var = 1 + x.size(); after.n_op = 2 + x.size(); // Argument to PrintFor is only needed // if we are keeping print forward operators scalar minus_one = x[0] - 1.0; before.n_var += 1; before.n_op += 1; if( options.find("no_print_for_op") == std::string::npos ) { after.n_var += 1; after.n_op += 1; } // print argument to log function minus one, if it is <= 0 PrintFor(minus_one, "minus_one == ", minus_one , " is <= 0\n"); before.n_var += 0; before.n_op += 1; if( options.find("no_print_for_op") == std::string::npos ) { after.n_var += 0; after.n_op += 1; } // now compute log y[0] = log( x[0] ); before.n_var += 1; before.n_op += 1; after.n_var += 1; after.n_op += 1; } } bool print_for(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 1.5; // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); for(size_t k = 0; k < 2; k++) { // optimization options std::string options = ""; if( k == 0 ) options = "no_print_for_op"; // declare independent variables and start tape recording CppAD::Independent(ax); // compute function value tape_size before, after; fun(options, ax, ay, before, after); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); ok &= f.size_order() == 1; // this constructor does 0 order forward ok &= f.size_var() == before.n_var; ok &= f.size_op() == before.n_op; // Optimize the operation sequence f.optimize(options); ok &= f.size_order() == 0; // 0 order forward not present ok &= f.size_var() == after.n_var; ok &= f.size_op() == after.n_op; // Check result for a zero order calculation for a different x CPPAD_TESTVECTOR(double) x(n), y(m), check(m); x[0] = 2.75; y = f.Forward(0, x); fun(options, x, check, before, after); ok &= NearEqual(y[0], check[0], eps10, eps10); } return ok; } // END C++ ================================================ FILE: example/optimize/reverse_active.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin optimize_reverse_active.cpp} Optimize Reverse Activity Analysis: Example and Test #################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end optimize_reverse_active.cpp} */ // BEGIN C++ # include namespace { struct tape_size { size_t n_var; size_t n_op; }; template void fun( const Vector& x, Vector& y, tape_size& before, tape_size& after ) { typedef typename Vector::value_type scalar; // phantom variable with index 0 and independent variables // begin operator, independent variable operators and end operator before.n_var = 1 + x.size(); before.n_op = 2 + x.size(); after.n_var = 1 + x.size(); after.n_op = 2 + x.size(); // initialized product of even and odd variables scalar prod_even = x[0]; scalar prod_odd = x[1]; before.n_var += 0; before.n_op += 0; after.n_var += 0; after.n_op += 0; // // compute product of even and odd variables for(size_t i = 2; i < size_t( x.size() ); i++) { if( i % 2 == 0 ) { // prod_even will affect dependent variable prod_even = prod_even * x[i]; before.n_var += 1; before.n_op += 1; after.n_var += 1; after.n_op += 1; } else { // prod_odd will not affect dependent variable prod_odd = prod_odd * x[i]; before.n_var += 1; before.n_op += 1; after.n_var += 0; after.n_op += 0; } } // dependent variable for this operation sequence y[0] = prod_even; before.n_var += 0; before.n_op += 0; after.n_var += 0; after.n_op += 0; } } bool reverse_active(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 6; CPPAD_TESTVECTOR(AD) ax(n); for(size_t i = 0; i < n; i++) ax[i] = AD(i + 1); // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); tape_size before, after; fun(ax, ay, before, after); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); ok &= f.size_order() == 1; // this constructor does 0 order forward ok &= f.size_var() == before.n_var; ok &= f.size_op() == before.n_op; // Optimize the operation sequence f.optimize(); ok &= f.size_order() == 0; // 0 order forward not present ok &= f.size_var() == after.n_var; ok &= f.size_op() == after.n_op; // check zero order forward with different argument value CPPAD_TESTVECTOR(double) x(n), y(m), check(m); for(size_t i = 0; i < n; i++) x[i] = double(i + 2); y = f.Forward(0, x); fun(x, check, before, after); ok &= NearEqual(y[0], check[0], eps10, eps10); return ok; } // END C++ ================================================ FILE: example/print_for/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the example/print_for directory tests # SET(source_list print_for.cpp) # set_compile_flags( example_print_for "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(example_print_for EXCLUDE_FROM_ALL ${source_list}) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_print_for ${cppad_lib} ${colpack_libs} ) # check_example_print_for add_check_executable(check_example print_for) ================================================ FILE: example/print_for/print_for.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin print_for_cout.cpp} Printing During Forward Mode: Example and Test ############################################## Running ******* To build this program and run its correctness test see :ref:`cmake_check-name` . Source Code *********** {xrst_spell_off} {xrst_code cpp} */ # include namespace { using std::cout; using std::endl; using CppAD::AD; // use of PrintFor to check for invalid function arguments AD check_log(const AD& y) { // check during recording if( y <= 0. ) cout << "check_log: y = " << y << " is <= 0" << endl; // check during zero order forward calculation PrintFor(y, "check_log: y == ", y , " which is <= 0\n"); return log(y); } } void print_for(void) { using CppAD::PrintFor; // independent variable vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 1.; Independent(ax); // print a VecAD::reference object that is a parameter CppAD::VecAD av(1); AD Zero(0); av[Zero] = 0.; PrintFor("v[0] = ", av[Zero]); // Print a newline to separate this from previous output, // then print an AD object that is a variable. PrintFor("\nv[0] + x[0] = ", av[0] + ax[0]); // A conditional print that will not generate output when x[0] = 2. PrintFor(ax[0], "\n 2. + x[0] = ", 2. + ax[0], "\n"); // A conditional print that will generate output when x[0] = 2. PrintFor(ax[0] - 2., "\n 3. + x[0] = ", 3. + ax[0], "\n"); // A log evaluations that will result in an error message when x[0] = 2. AD var = 2. - ax[0]; AD log_var = check_log(var); // dependent variable vector size_t m = 2; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = av[Zero] + ax[0]; // define f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // zero order forward with x[0] = 2 CPPAD_TESTVECTOR(double) x(n); x[0] = 2.; cout << "v[0] = 0" << endl; cout << "v[0] + x[0] = 2" << endl; cout << " 3. + x[0] = 5" << endl; cout << "check_log: y == 0 which is <= 0" << endl; // ./makefile.am expects "Test passes" at beginning of next output line cout << "Test passes if four lines above repeat below:" << endl; f.Forward(0, x); return; } int main(void) { print_for(); return 0; } /* {xrst_code} {xrst_spell_on} Output ****** Executing the program above generates the following output: :: v[0] = 0 v[0] + x[0] = 2 Test passes if two lines above repeat below: v[0] = 0 v[0] + x[0] = 2 {xrst_end print_for_cout.cpp} */ ================================================ FILE: example/print_for/test.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- ./print_for | tee test.log sed -e '/^Test passes/,\$$d' < test.log > test.1 sed -e '1,/^Test passes/d' < test.log > test.2 if ! diff test.1 test.2 ; then exit 1 ; fi ================================================ FILE: example/sparse/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list sparse.cpp colpack_hes.cpp colpack_hessian.cpp colpack_jac.cpp colpack_jacobian.cpp conj_grad.cpp dependency.cpp for_hes_sparsity.cpp for_jac_sparsity.cpp for_sparse_hes.cpp for_sparse_jac.cpp rc_sparsity.cpp rev_hes_sparsity.cpp rev_jac_sparsity.cpp rev_sparse_hes.cpp rev_sparse_jac.cpp sparse_hes.cpp sparse_hessian.cpp sparse_jac_for.cpp sparse_jac_rev.cpp sparse_jacobian.cpp sparse_sub_hes.cpp sparsity_sub.cpp sub_sparse_hes.cpp subgraph_hes2jac.cpp subgraph_jac_rev.cpp subgraph_reverse.cpp subgraph_sparsity.cpp ) # END_SORT_THIS_LINE_MINUS_2 IF( cppad_has_eigen ) SET(source_list sparse2eigen.cpp ${source_list} ) ENDIF( cppad_has_eigen ) set_compile_flags( example_sparse "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(example_sparse EXCLUDE_FROM_ALL ${source_list}) # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_sparse ${cppad_lib} ${colpack_libs} ) # check_example_sparse add_check_executable(check_example sparse) ================================================ FILE: example/sparse/colpack_hes.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin colpack_hes.cpp} ColPack: Sparse Hessian Example and Test ######################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end colpack_hes.cpp} */ // BEGIN C++ # include bool colpack_hes(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; typedef CPPAD_TESTVECTOR(AD) a_vector; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CppAD::vector i_vector; typedef CppAD::sparse_rc sparsity; typedef CppAD::sparse_rcv sparse_matrix; double eps = 10. * CppAD::numeric_limits::epsilon(); // // domain space vector size_t n = 5; a_vector a_x(n); for(size_t j = 0; j < n; j++) a_x[j] = AD (0); // // declare independent variables and starting recording CppAD::Independent(a_x); // colpack example case where hessian is a spear head // i.e, H(i, j) non zero implies i = 0, j = 0, or i = j AD sum = 0.0; // partial_0 partial_j = x[j] // partial_j partial_j = x[0] for(size_t j = 1; j < n; j++) sum += a_x[0] * a_x[j] * a_x[j] / 2.0; // // partial_i partial_i = 2 * x[i] for(size_t i = 0; i < n; i++) sum += a_x[i] * a_x[i] * a_x[i] / 3.0; // declare dependent variables size_t m = 1; a_vector a_y(m); a_y[0] = sum; // create f: x -> y and stop tape recording CppAD::ADFun f(a_x, a_y); // new value for the independent variable vector d_vector x(n); for(size_t j = 0; j < n; j++) x[j] = double(j + 1); /* [ 2 2 3 4 5 ] hes = [ 2 5 0 0 0 ] [ 3 0 7 0 0 ] [ 4 0 0 9 0 ] [ 5 0 0 0 11 ] */ // Normally one would use CppAD to compute sparsity pattern, but for this // example we set it directly size_t nr = n; size_t nc = n; size_t nnz = n + 2 * (n - 1); sparsity pattern(nr, nc, nnz); for(size_t k = 0; k < n; k++) { size_t r = k; size_t c = k; pattern.set(k, r, c); } for(size_t i = 1; i < n; i++) { size_t k = n + 2 * (i - 1); size_t r = i; size_t c = 0; pattern.set(k, r, c); pattern.set(k+1, c, r); } // subset of elements to compute // (only compute lower triangle) nnz = n + (n - 1); sparsity lower_triangle(nr, nc, nnz); d_vector check(nnz); for(size_t k = 0; k < n; k++) { size_t r = k; size_t c = k; lower_triangle.set(k, r, c); check[k] = 2.0 * x[k]; if( k > 0 ) check[k] += x[0]; } for(size_t j = 1; j < n; j++) { size_t k = n + (j - 1); size_t r = 0; size_t c = j; lower_triangle.set(k, r, c); check[k] = x[c]; } sparse_matrix subset( lower_triangle ); // check results for both CppAD and Colpack for(size_t i_method = 0; i_method < 4; i_method++) { // coloring method std::string coloring; switch(i_method) { case 0: coloring = "cppad.symmetric"; break; case 1: coloring = "cppad.general"; break; case 2: coloring = "colpack.symmetric"; break; case 3: coloring = "colpack.general"; break; } // // compute Hessian CppAD::sparse_hes_work work; d_vector w(m); w[0] = 1.0; size_t n_sweep = f.sparse_hes( x, w, subset, pattern, coloring, work ); // // check result const d_vector& hes( subset.val() ); for(size_t k = 0; k < nnz; k++) ok &= NearEqual(check[k], hes[k], eps, eps); if( coloring == "cppad.symmetric" || coloring == "colpack.symmetric" ) ok &= n_sweep == 2; else ok &= n_sweep == 5; } return ok; } // END C++ ================================================ FILE: example/sparse/colpack_hessian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin colpack_hessian.cpp} ColPack: Sparse Hessian Example and Test ######################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end colpack_hessian.cpp} */ // BEGIN C++ # include bool colpack_hessian(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; typedef CPPAD_TESTVECTOR(AD) a_vector; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CppAD::vector i_vector; size_t i, j, k, ell; double eps = 10. * CppAD::numeric_limits::epsilon(); // domain space vector size_t n = 5; a_vector a_x(n); for(j = 0; j < n; j++) a_x[j] = AD (0); // declare independent variables and starting recording CppAD::Independent(a_x); // colpack example case where hessian is a spear head // i.e, H(i, j) non zero implies i = 0, j = 0, or i = j AD sum = 0.0; // partial_0 partial_j = x[j] // partial_j partial_j = x[0] for(j = 1; j < n; j++) sum += a_x[0] * a_x[j] * a_x[j] / 2.0; // // partial_i partial_i = 2 * x[i] for(i = 0; i < n; i++) sum += a_x[i] * a_x[i] * a_x[i] / 3.0; // declare dependent variables size_t m = 1; a_vector a_y(m); a_y[0] = sum; // create f: x -> y and stop tape recording CppAD::ADFun f(a_x, a_y); // new value for the independent variable vector d_vector x(n); for(j = 0; j < n; j++) x[j] = double(j + 1); /* [ 2 2 3 4 5 ] hes = [ 2 5 0 0 0 ] [ 3 0 7 0 0 ] [ 4 0 0 9 0 ] [ 5 0 0 0 11 ] */ d_vector check(n * n); for(i = 0; i < n; i++) { for(j = 0; j < n; j++) { size_t index = i * n + j; check[index] = 0.0; if( i == 0 && 1 <= j ) check[index] += x[j]; if( 1 <= i && j == 0 ) check[index] += x[i]; if( i == j ) { check[index] += 2.0 * x[i]; if( i != 0 ) check[index] += x[0]; } } } // Normally one would use f.RevSparseHes to compute // sparsity pattern, but for this example we extract it from check. std::vector< std::set > p(n); i_vector row, col; for(i = 0; i < n; i++) { for(j = 0; j < n; j++) { ell = i * n + j; if( check[ell] != 0. ) { // insert this non-zero entry in sparsity pattern p[i].insert(j); // the Hessian is symmetric, so only lower triangle if( j <= i ) { row.push_back(i); col.push_back(j); } } } } size_t K = row.size(); d_vector hes(K); // default coloring method is cppad.symmetric CppAD::sparse_hessian_work work; ok &= work.color_method == "cppad.symmetric"; // contrast and check results for both CppAD and Colpack for(size_t i_method = 0; i_method < 4; i_method++) { // empty work structure switch(i_method) { case 0: work.color_method = "cppad.symmetric"; break; case 1: work.color_method = "cppad.general"; break; case 2: work.color_method = "colpack.symmetric"; break; case 3: work.color_method = "colpack.general"; break; } // compute Hessian d_vector w(m); w[0] = 1.0; size_t n_sweep = f.SparseHessian(x, w, p, row, col, hes, work); // // check result for(k = 0; k < K; k++) { ell = row[k] * n + col[k]; ok &= NearEqual(check[ell], hes[k], eps, eps); } if( work.color_method == "cppad.symmetric" || work.color_method == "colpack.symmetric" ) ok &= n_sweep == 2; else ok &= n_sweep == 5; // // check that clear resets color_method to cppad.symmetric work.clear(); ok &= work.color_method == "cppad.symmetric"; } return ok; } // END C++ ================================================ FILE: example/sparse/colpack_jac.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin colpack_jac.cpp} ColPack: Sparse Jacobian Example and Test ######################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end colpack_jac.cpp} */ // BEGIN C++ # include bool colpack_jac(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; typedef CPPAD_TESTVECTOR(AD) a_vector; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CppAD::vector i_vector; typedef CppAD::sparse_rc sparsity; typedef CppAD::sparse_rcv sparse_matrix; // domain space vector size_t n = 4; a_vector a_x(n); for(size_t j = 0; j < n; j++) a_x[j] = AD (0); // declare independent variables and starting recording CppAD::Independent(a_x); size_t m = 3; a_vector a_y(m); a_y[0] = a_x[0] + a_x[1]; a_y[1] = a_x[2] + a_x[3]; a_y[2] = a_x[0] + a_x[1] + a_x[2] + a_x[3] * a_x[3] / 2.; // create f: x -> y and stop tape recording CppAD::ADFun f(a_x, a_y); // new value for the independent variable vector d_vector x(n); for(size_t j = 0; j < n; j++) x[j] = double(j); /* [ 1 1 0 0 ] jac = [ 0 0 1 1 ] [ 1 1 1 x_3] */ // Normally one would use CppAD to compute sparsity pattern, but for this // example we set it directly size_t nr = m; size_t nc = n; size_t nnz = 8; sparsity pattern(nr, nc, nnz); d_vector check(nnz); for(size_t k = 0; k < nnz; k++) { size_t r, c; if( k < 2 ) { r = 0; c = k; } else if( k < 4 ) { r = 1; c = k; } else { r = 2; c = k - 4; } pattern.set(k, r, c); if( k == nnz - 1 ) check[k] = x[3]; else check[k] = 1.0; } // using row and column indices to compute non-zero in rows 1 and 2 sparse_matrix subset( pattern ); // check results for both CppAD and Colpack for(size_t i_method = 0; i_method < 4; i_method++) { // coloring method std::string coloring; if( i_method % 2 == 0 ) coloring = "cppad"; else coloring = "colpack"; // CppAD::sparse_jac_work work; size_t group_max = 1; if( i_method / 2 == 0 ) { size_t n_sweep = f.sparse_jac_for( group_max, x, subset, pattern, coloring, work ); ok &= n_sweep == 4; } else { size_t n_sweep = f.sparse_jac_rev( x, subset, pattern, coloring, work ); ok &= n_sweep == 2; } const d_vector& hes( subset.val() ); for(size_t k = 0; k < nnz; k++) ok &= check[k] == hes[k]; } return ok; } // END C++ ================================================ FILE: example/sparse/colpack_jacobian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin colpack_jacobian.cpp} ColPack: Sparse Jacobian Example and Test ######################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end colpack_jacobian.cpp} */ // BEGIN C++ # include bool colpack_jacobian(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; typedef CPPAD_TESTVECTOR(AD) a_vector; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CppAD::vector i_vector; size_t i, j, k, ell; double eps = 10. * CppAD::numeric_limits::epsilon(); // domain space vector size_t n = 4; a_vector a_x(n); for(j = 0; j < n; j++) a_x[j] = AD (0); // declare independent variables and starting recording CppAD::Independent(a_x); size_t m = 3; a_vector a_y(m); a_y[0] = a_x[0] + a_x[1]; a_y[1] = a_x[2] + a_x[3]; a_y[2] = a_x[0] + a_x[1] + a_x[2] + a_x[3] * a_x[3] / 2.; // create f: x -> y and stop tape recording CppAD::ADFun f(a_x, a_y); // new value for the independent variable vector d_vector x(n); for(j = 0; j < n; j++) x[j] = double(j); /* [ 1 1 0 0 ] jac = [ 0 0 1 1 ] [ 1 1 1 x_3] */ d_vector check(m * n); check[0] = 1.; check[1] = 1.; check[2] = 0.; check[3] = 0.; check[4] = 0.; check[5] = 0.; check[6] = 1.; check[7] = 1.; check[8] = 1.; check[9] = 1.; check[10] = 1.; check[11] = x[3]; // Normally one would use f.ForSparseJac or f.RevSparseJac to compute // sparsity pattern, but for this example we extract it from check. std::vector< std::set > p(m); // using row and column indices to compute non-zero in rows 1 and 2 i_vector row, col; for(i = 0; i < m; i++) { for(j = 0; j < n; j++) { ell = i * n + j; if( check[ell] != 0. ) { row.push_back(i); col.push_back(j); p[i].insert(j); } } } size_t K = row.size(); d_vector jac(K); // empty work structure CppAD::sparse_jacobian_work work; ok &= work.color_method == "cppad"; // choose to use ColPack work.color_method = "colpack"; // forward mode size_t n_sweep = f.SparseJacobianForward(x, p, row, col, jac, work); for(k = 0; k < K; k++) { ell = row[k] * n + col[k]; ok &= NearEqual(check[ell], jac[k], eps, eps); } ok &= n_sweep == 4; // reverse mode work.clear(); work.color_method = "colpack"; n_sweep = f.SparseJacobianReverse(x, p, row, col, jac, work); for(k = 0; k < K; k++) { ell = row[k] * n + col[k]; ok &= NearEqual(check[ell], jac[k], eps, eps); } ok &= n_sweep == 2; return ok; } // END C++ ================================================ FILE: example/sparse/conj_grad.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin conj_grad.cpp} {xrst_spell goto } Differentiate Conjugate Gradient Algorithm: Example and Test ############################################################ Purpose ******* The conjugate gradient algorithm is sparse linear solver and a good example where checkpointing can be applied (for each iteration). This example is a preliminary version of a new library routine for the conjugate gradient algorithm. Algorithm ********* Given a positive definite matrix :math:`A \in \B{R}^{n \times n}`, a vector :math:`b \in \B{R}^n`, and tolerance :math:`\varepsilon`, the conjugate gradient algorithm finds an :math:`x \in \B{R}^n` such that :math:`\| A x - b \|^2 / n \leq \varepsilon^2` (or it terminates at a specified maximum number of iterations). #. Input: The matrix :math:`A \in \B{R}^{n \times n}`, the vector :math:`b \in \B{R}^n`, a tolerance :math:`\varepsilon \geq 0`, a maximum number of iterations :math:`m`, and the initial approximate solution :math:`x^0 \in \B{R}^n` (can use zero for :math:`x^0`). #. Initialize: :math:`g^0 = A * x^0 - b`, :math:`d^0 = - g^0`, :math:`s_0 = ( g^0 )^\R{T} g^0`, :math:`k = 0`. #. Convergence Check: if :math:`k = m` or :math:`\sqrt{ s_k / n } < \varepsilon`, return :math:`k` as the number of iterations and :math:`x^k` as the approximate solution. #. Next :math:`x`: :math:`\mu_{k+1} = s_k / [ ( d^k )^\R{T} A d^k ]`, :math:`x^{k+1} = x^k + \mu_{k+1} d^k`. #. Next :math:`g`: :math:`g^{k+1} = g^k + \mu_{k+1} A d^k`, :math:`s_{k+1} = ( g^{k+1} )^\R{T} g^{k+1}`. #. Next :math:`d`: :math:`d^{k+1} = - g^k + ( s_{k+1} / s_k ) d^k`. #. Iterate: :math:`k = k + 1`, goto Convergence Check. {xrst_literal // BEGIN C++ // END C++ } {xrst_end conj_grad.cpp} */ // BEGIN C++ # include # include # include namespace { // Begin empty namespace using CppAD::AD; // A simple matrix multiply c = a * b , where a has n columns // and b has n rows. This should be changed to a function so that // it can efficiently handle the case were A is large and sparse. template // a simple vector class void mat_mul(size_t n, const Vector& a, const Vector& b, Vector& c) { typedef typename Vector::value_type scalar; size_t m, p; m = a.size() / n; p = b.size() / n; assert( m * n == a.size() ); assert( n * p == b.size() ); assert( m * p == c.size() ); size_t i, j, k, ij; for(i = 0; i < m; i++) { for(j = 0; j < p; j++) { ij = i * p + j; c[ij] = scalar(0); for(k = 0; k < n; k++) c[ij] = c[ij] + a[i * m + k] * b[k * p + j]; } } return; } // Solve A * x == b to tolerance epsilon or terminate at m iterations. template // a simple vector class size_t conjugate_gradient( size_t m , // input double epsilon , // input const Vector& A , // input const Vector& b , // input Vector& x ) // input / output { typedef typename Vector::value_type scalar; scalar mu, s_previous; size_t i, k; size_t n = x.size(); assert( A.size() == n * n ); assert( b.size() == n ); Vector g(n), d(n), s(1), Ad(n), dAd(1); // g = A * x mat_mul(n, A, x, g); for(i = 0; i < n; i++) { // g = A * x - b g[i] = g[i] - b[i]; // d = - g d[i] = -g[i]; } // s = g^T * g mat_mul(n, g, g, s); for(k = 0; k < m; k++) { s_previous = s[0]; if( s_previous < epsilon ) return k; // Ad = A * d mat_mul(n, A, d, Ad); // dAd = d^T * A * d mat_mul(n, d, Ad, dAd); // mu = s / d^T * A * d mu = s_previous / dAd[0]; // g = g + mu * A * d for(i = 0; i < n; i++) { x[i] = x[i] + mu * d[i]; g[i] = g[i] + mu * Ad[i]; } // s = g^T * g mat_mul(n, g, g, s); // d = - g + (s / s_previous) * d for(i = 0; i < n; i++) d[i] = - g[i] + ( s[0] / s_previous) * d[i]; } return m; } } // End empty namespace bool conj_grad(void) { bool ok = true; // ---------------------------------------------------------------------- // Setup // ---------------------------------------------------------------------- using CppAD::AD; using CppAD::NearEqual; using CppAD::vector; using std::cout; using std::endl; size_t i, j; // size of the vectors size_t n = 40; vector D(n * n), Dt(n * n), A(n * n), x(n), b(n), c(n); vector< AD > a_A(n * n), a_x(n), a_b(n); // D = diagonally dominant matrix // c = vector of ones for(i = 0; i < n; i++) { c[i] = 1.; double sum = 0; for(j = 0; j < n; j++) if( i != j ) { D[ i * n + j ] = std::rand() / double(RAND_MAX); Dt[j * n + i ] = D[i * n + j ]; sum += D[i * n + j ]; } Dt[ i * n + i ] = D[ i * n + i ] = sum * 1.1; } // A = D^T * D mat_mul(n, Dt, D, A); // b = D^T * c mat_mul(n, Dt, c, b); // copy from double to AD for(i = 0; i < n; i++) { a_b[i] = b[i]; for(j = 0; j < n; j++) a_A[ i * n + j ] = A[ i * n + j ]; } // --------------------------------------------------------------------- // Record the function f : b -> x // --------------------------------------------------------------------- // Make b the independent variable vector Independent(a_b); // Solve A * x = b using conjugate gradient method double epsilon = 1e-7; for(i = 0; i < n; i++) a_x[i] = AD(0); size_t m = n + 1; size_t k = conjugate_gradient(m, epsilon, a_A, a_b, a_x); // create f_cg: b -> x and stop tape recording CppAD::ADFun f(a_b, a_x); // --------------------------------------------------------------------- // Check for correctness // --------------------------------------------------------------------- // conjugate gradient should converge with in n iterations ok &= (k <= n); // accuracy to which we expect values to agree double delta = 10. * epsilon * std::sqrt( double(n) ); // copy x from AD to double for(i = 0; i < n; i++) x[i] = Value( a_x[i] ); // check c = A * x mat_mul(n, A, x, c); for(i = 0; i < n; i++) ok &= NearEqual(c[i] , b[i], delta , delta); // forward computation of partials w.r.t. b[0] vector db(n), dx(n); for(j = 0; j < n; j++) db[j] = 0.; db[0] = 1.; // check db = A * dx delta = 5. * delta; dx = f.Forward(1, db); mat_mul(n, A, dx, c); for(i = 0; i < n; i++) ok &= NearEqual(c[i], db[i], delta, delta); return ok; } // END C++ ================================================ FILE: example/sparse/dependency.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin dependency.cpp} {xrst_spell rl } Computing Dependency: Example and Test ###################################### Discussion ********** The partial of an dependent variable with respect to an independent variable might always be zero even though the dependent variable depends on the value of the dependent variable. Consider the following case .. math:: f(x) = {\rm sign} (x) = \left\{ \begin{array}{rl} +1 & {\rm if} \; x > 0 \\ 0 & {\rm if} \; x = 0 \\ -1 & {\rm if} \; x < 0 \end{array} \right. In this case the value of :math:`f(x)` depends on the value of :math:`x` but CppAD always returns zero for the derivative of the :ref:`sign-name` function. Dependency Pattern ****************** If the *i*-th dependent variables depends on the value of the *j*-th independent variable, the corresponding entry in the dependency pattern is non-zero (true). Otherwise it is zero (false). CppAD uses :ref:`sparsity patterns` to represent dependency patterns. Computation *********** The *dependency* argument to :ref:`for_jac_sparsity` and :ref:`RevSparseJac` is a flag that signals that the dependency pattern (instead of the sparsity pattern) is computed. {xrst_literal // BEGIN C++ // END C++ } {xrst_end dependency.cpp} */ // BEGIN C++ # include namespace { double heavyside(const double& x) { if( x <= 0.0 ) return 0.0; return 1.0; } CPPAD_DISCRETE_FUNCTION(double, heavyside) } bool dependency(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; typedef CPPAD_TESTVECTOR(size_t) SizeVector; typedef CppAD::sparse_rc sparsity; // VecAD object for use later CppAD::VecAD vec_ad(2); vec_ad[0] = 0.0; vec_ad[1] = 1.0; // domain space vector size_t n = 5; CPPAD_TESTVECTOR(AD) ax(n); for(size_t j = 0; j < n; j++) ax[j] = AD(j + 1); // declare independent variables and start tape recording CppAD::Independent(ax); // some AD constants AD azero(0.0), aone(1.0); // range space vector size_t m = n; size_t m1 = n - 1; CPPAD_TESTVECTOR(AD) ay(m); // Note that ay[m1 - j] depends on ax[j] ay[m1 - 0] = sign( ax[0] ); ay[m1 - 1] = CondExpLe( ax[1], azero, azero, aone); ay[m1 - 2] = CondExpLe( azero, ax[2], azero, aone); ay[m1 - 3] = heavyside( ax[3] ); ay[m1 - 4] = vec_ad[ ax[4] - AD(4.0) ]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // sparsity pattern for n by n identity matrix size_t nr = n; size_t nc = n; size_t nnz = n; sparsity pattern_in(nr, nc, nnz); for(size_t k = 0; k < nnz; k++) { size_t r = k; size_t c = k; pattern_in.set(k, r, c); } // compute dependency pattern bool transpose = false; bool dependency = true; // would transpose dependency pattern bool internal_bool = true; // does not affect result sparsity pattern_out; f.for_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); const SizeVector& row( pattern_out.row() ); const SizeVector& col( pattern_out.col() ); SizeVector col_major = pattern_out.col_major(); // check result ok &= pattern_out.nr() == n; ok &= pattern_out.nc() == n; ok &= pattern_out.nnz() == n; for(size_t k = 0; k < n; k++) { ok &= row[ col_major[k] ] == m1 - k; ok &= col[ col_major[k] ] == k; } // ----------------------------------------------------------- // RevSparseJac and set dependency CppAD::vector< std::set > eye_set(m), depend_set(m); for(size_t i = 0; i < m; i++) { ok &= eye_set[i].empty(); eye_set[i].insert(i); } depend_set = f.RevSparseJac(n, eye_set, transpose, dependency); for(size_t i = 0; i < m; i++) { std::set check; check.insert(m1 - i); ok &= depend_set[i] == check; } dependency = false; depend_set = f.RevSparseJac(n, eye_set, transpose, dependency); for(size_t i = 0; i < m; i++) ok &= depend_set[i].empty(); return ok; } // END C++ ================================================ FILE: example/sparse/for_hes_sparsity.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin for_hes_sparsity.cpp} Forward Mode Hessian Sparsity: Example and Test ############################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end for_hes_sparsity.cpp} */ // BEGIN C++ # include bool for_hes_sparsity(void) { bool ok = true; using CppAD::AD; typedef CPPAD_TESTVECTOR(size_t) SizeVector; typedef CppAD::sparse_rc sparsity; // // domain space vector size_t n = 3; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.; ax[1] = 1.; ax[2] = 2.; // declare independent variables and start recording CppAD::Independent(ax); // range space vector size_t m = 2; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = sin( ax[2] ); ay[1] = ax[0] * ax[1]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // include all x components in sparsity pattern CPPAD_TESTVECTOR(bool) select_domain(n); for(size_t j = 0; j < n; j++) select_domain[j] = true; // compute sparsity pattern for H(x) = F_1''(x) CPPAD_TESTVECTOR(bool) select_range(m); select_range[0] = false; select_range[1] = true; bool internal_bool = true; sparsity pattern_out; f.for_hes_sparsity( select_domain, select_range, internal_bool, pattern_out ); size_t nnz = pattern_out.nnz(); ok &= nnz == 2; ok &= pattern_out.nr() == n; ok &= pattern_out.nc() == n; { // check results const SizeVector& row( pattern_out.row() ); const SizeVector& col( pattern_out.col() ); SizeVector row_major = pattern_out.row_major(); // ok &= row[ row_major[0] ] == 0 && col[ row_major[0] ] == 1; ok &= row[ row_major[1] ] == 1 && col[ row_major[1] ] == 0; } // // compute sparsity pattern for H(x) = F_0''(x) select_range[0] = true; select_range[1] = false; f.for_hes_sparsity( select_domain, select_range, internal_bool, pattern_out ); nnz = pattern_out.nnz(); ok &= nnz == 1; ok &= pattern_out.nr() == n; ok &= pattern_out.nc() == n; { // check results const SizeVector& row( pattern_out.row() ); const SizeVector& col( pattern_out.col() ); // ok &= row[0] == 2 && col[0] == 2; } return ok; } // END C++ ================================================ FILE: example/sparse/for_jac_sparsity.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin for_jac_sparsity.cpp} Forward Mode Jacobian Sparsity: Example and Test ################################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end for_jac_sparsity.cpp} */ // BEGIN C++ # include bool for_jac_sparsity(void) { bool ok = true; using CppAD::AD; typedef CPPAD_TESTVECTOR(size_t) SizeVector; typedef CppAD::sparse_rc sparsity; // // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.; ax[1] = 1.; // declare independent variables and start recording CppAD::Independent(ax); // range space vector size_t m = 3; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = ax[0]; ay[1] = ax[0] * ax[1]; ay[2] = ax[1]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // sparsity pattern for the identity matrix size_t nr = n; size_t nc = n; size_t nnz_in = n; sparsity pattern_in(nr, nc, nnz_in); for(size_t k = 0; k < nnz_in; k++) { size_t r = k; size_t c = k; pattern_in.set(k, r, c); } // // Compute sparsity pattern for J(x) = F'(x) bool transpose = false; bool dependency = false; bool internal_bool = false; sparsity pattern_out; f.for_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); size_t nnz = pattern_out.nnz(); ok &= nnz == 4; ok &= pattern_out.nr() == m; ok &= pattern_out.nc() == n; { // check results const SizeVector& row( pattern_out.row() ); const SizeVector& col( pattern_out.col() ); SizeVector col_major = pattern_out.col_major(); // ok &= row[ col_major[0] ] == 0 && col[ col_major[0] ] == 0; ok &= row[ col_major[1] ] == 1 && col[ col_major[1] ] == 0; ok &= row[ col_major[2] ] == 1 && col[ col_major[2] ] == 1; ok &= row[ col_major[3] ] == 2 && col[ col_major[3] ] == 1; // // check that set and not boolean values are stored ok &= (f.size_forward_set() > 0); ok &= (f.size_forward_bool() == 0); } // // note that the transpose of the identity is the identity transpose = true; internal_bool = true; f.for_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); nnz = pattern_out.nnz(); ok &= nnz == 4; ok &= pattern_out.nr() == n; ok &= pattern_out.nc() == m; { // check results const SizeVector& row( pattern_out.row() ); const SizeVector& col( pattern_out.col() ); SizeVector row_major = pattern_out.row_major(); // ok &= col[ row_major[0] ] == 0 && row[ row_major[0] ] == 0; ok &= col[ row_major[1] ] == 1 && row[ row_major[1] ] == 0; ok &= col[ row_major[2] ] == 1 && row[ row_major[2] ] == 1; ok &= col[ row_major[3] ] == 2 && row[ row_major[3] ] == 1; // // check that set and not boolean values are stored ok &= (f.size_forward_set() == 0); ok &= (f.size_forward_bool() > 0); } return ok; } // END C++ ================================================ FILE: example/sparse/for_sparse_hes.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin for_sparse_hes.cpp} Forward Mode Hessian Sparsity: Example and Test ############################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end for_sparse_hes.cpp} */ // BEGIN C++ # include namespace { // ------------------------------------------------------------- // expected sparsity pattern bool check_f0[] = { false, false, false, // partials w.r.t x0 and (x0, x1, x2) false, false, false, // partials w.r.t x1 and (x0, x1, x2) false, false, true // partials w.r.t x2 and (x0, x1, x2) }; bool check_f1[] = { false, true, false, // partials w.r.t x0 and (x0, x1, x2) true, false, false, // partials w.r.t x1 and (x0, x1, x2) false, false, false // partials w.r.t x2 and (x0, x1, x2) }; // define the template function BoolCases in empty namespace template // vector class, elements of type bool bool BoolCases(bool optimize) { bool ok = true; using CppAD::AD; // domain space vector size_t n = 3; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.; ax[1] = 1.; ax[2] = 2.; // declare independent variables and start recording CppAD::Independent(ax); // range space vector size_t m = 2; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = sin( ax[2] ) + ax[0] + ax[1] + ax[2]; ay[1] = ax[0] * ax[1]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); if( optimize ) f.optimize(); // sparsity pattern for diagonal of identity matrix Vector r(n); size_t i, j; for(i = 0; i < n; i++) r[ i ] = true; // compute sparsity pattern for H(x) = F_0^{(2)} (x) Vector s(m); for(i = 0; i < m; i++) s[i] = false; s[0] = true; Vector h(n * n); h = f.ForSparseHes(r, s); // check values for(i = 0; i < n; i++) for(j = 0; j < n; j++) ok &= (h[ i * n + j ] == check_f0[ i * n + j ] ); // compute sparsity pattern for H(x) = F_1^{(2)} (x) for(i = 0; i < m; i++) s[i] = false; s[1] = true; h = f.ForSparseHes(r, s); // check values for(i = 0; i < n; i++) for(j = 0; j < n; j++) ok &= (h[ i * n + j ] == check_f1[ i * n + j ] ); return ok; } // define the template function SetCases in empty namespace template // vector class, elements of type std::set bool SetCases(bool optimize) { bool ok = true; using CppAD::AD; // domain space vector size_t n = 3; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.; ax[1] = 1.; ax[2] = 2.; // declare independent variables and start recording CppAD::Independent(ax); // range space vector size_t m = 2; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = sin( ax[2] ); ay[1] = ax[0] * ax[1]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); if( optimize ) f.optimize(); // sparsity pattern for the diagonal of the identity matrix Vector r(1); size_t i; for(i = 0; i < n; i++) r[0].insert(i); // compute sparsity pattern for H(x) = F_0^{(2)} (x) Vector s(1); assert( s[0].empty() ); s[0].insert(0); Vector h(n); h = f.ForSparseHes(r, s); // check values std::set::iterator itr; size_t j; for(i = 0; i < n; i++) { for(j = 0; j < n; j++) { bool found = h[i].find(j) != h[i].end(); ok &= (found == check_f0[i * n + j]); } } // compute sparsity pattern for H(x) = F_1^{(2)} (x) s[0].clear(); assert( s[0].empty() ); s[0].insert(1); h = f.ForSparseHes(r, s); // check values for(i = 0; i < n; i++) { for(j = 0; j < n; j++) { bool found = h[i].find(j) != h[i].end(); ok &= (found == check_f1[i * n + j]); } } return ok; } } // End empty namespace # include # include bool for_sparse_hes(void) { bool ok = true; for(size_t k = 0; k < 2; k++) { bool optimize = bool(k); // Run with Vector equal to four different cases // all of which are Simple Vectors with elements of type bool. ok &= BoolCases< CppAD::vector >(optimize); ok &= BoolCases< CppAD::vectorBool >(optimize); ok &= BoolCases< std::vector >(optimize); ok &= BoolCases< std::valarray >(optimize); // Run with Vector equal to two different cases both of which are // Simple Vectors with elements of type std::set typedef std::set set; ok &= SetCases< CppAD::vector >(optimize); ok &= SetCases< std::vector >(optimize); // Do not use valarray because its element access in the const case // returns a copy instead of a reference // ok &= SetCases< std::valarray >(optimize); } return ok; } // END C++ ================================================ FILE: example/sparse/for_sparse_jac.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin for_sparse_jac.cpp} Forward Mode Jacobian Sparsity: Example and Test ################################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end for_sparse_jac.cpp} */ // BEGIN C++ # include # include namespace { // ------------------------------------------------------------- // define the template function BoolCases template // vector class, elements of type bool bool BoolCases(void) { bool ok = true; using CppAD::AD; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) X(n); X[0] = 0.; X[1] = 1.; // declare independent variables and start recording CppAD::Independent(X); // range space vector size_t m = 3; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = X[0]; Y[1] = X[0] * X[1]; Y[2] = X[1]; // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // sparsity pattern for the identity matrix Vector r(n * n); size_t i, j; for(i = 0; i < n; i++) { for(j = 0; j < n; j++) r[ i * n + j ] = (i == j); } // sparsity pattern for F'(x) Vector s(m * n); s = f.ForSparseJac(n, r); // check values ok &= (s[ 0 * n + 0 ] == true); // Y[0] does depend on X[0] ok &= (s[ 0 * n + 1 ] == false); // Y[0] does not depend on X[1] ok &= (s[ 1 * n + 0 ] == true); // Y[1] does depend on X[0] ok &= (s[ 1 * n + 1 ] == true); // Y[1] does depend on X[1] ok &= (s[ 2 * n + 0 ] == false); // Y[2] does not depend on X[0] ok &= (s[ 2 * n + 1 ] == true); // Y[2] does depend on X[1] // check that values are stored ok &= (f.size_forward_bool() > 0); ok &= (f.size_forward_set() == 0); // sparsity pattern for F'(x)^T, note R is the identity, so R^T = R bool transpose = true; Vector st(n * m); st = f.ForSparseJac(n, r, transpose); // check values ok &= (st[ 0 * m + 0 ] == true); // Y[0] does depend on X[0] ok &= (st[ 1 * m + 0 ] == false); // Y[0] does not depend on X[1] ok &= (st[ 0 * m + 1 ] == true); // Y[1] does depend on X[0] ok &= (st[ 1 * m + 1 ] == true); // Y[1] does depend on X[1] ok &= (st[ 0 * m + 2 ] == false); // Y[2] does not depend on X[0] ok &= (st[ 1 * m + 2 ] == true); // Y[2] does depend on X[1] // check that values are stored ok &= (f.size_forward_bool() > 0); ok &= (f.size_forward_set() == 0); // free values from forward calculation f.size_forward_bool(0); ok &= (f.size_forward_bool() == 0); return ok; } // define the template function SetCases template // vector class, elements of type std::set bool SetCases(void) { bool ok = true; using CppAD::AD; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) X(n); X[0] = 0.; X[1] = 1.; // declare independent variables and start recording CppAD::Independent(X); // range space vector size_t m = 3; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = X[0]; Y[1] = X[0] * X[1]; Y[2] = X[1]; // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // sparsity pattern for the identity matrix Vector r(n); size_t i; for(i = 0; i < n; i++) { assert( r[i].empty() ); r[i].insert(i); } // sparsity pattern for F'(x) Vector s(m); s = f.ForSparseJac(n, r); // an iterator to a standard set std::set::iterator itr; bool found; // Y[0] does depend on X[0] found = s[0].find(0) != s[0].end(); ok &= ( found == true ); // Y[0] does not depend on X[1] found = s[0].find(1) != s[0].end(); ok &= ( found == false ); // Y[1] does depend on X[0] found = s[1].find(0) != s[1].end(); ok &= ( found == true ); // Y[1] does depend on X[1] found = s[1].find(1) != s[1].end(); ok &= ( found == true ); // Y[2] does not depend on X[0] found = s[2].find(0) != s[2].end(); ok &= ( found == false ); // Y[2] does depend on X[1] found = s[2].find(1) != s[2].end(); ok &= ( found == true ); // check that values are stored ok &= (f.size_forward_set() > 0); ok &= (f.size_forward_bool() == 0); // sparsity pattern for F'(x)^T bool transpose = true; Vector st(n); st = f.ForSparseJac(n, r, transpose); // Y[0] does depend on X[0] found = st[0].find(0) != st[0].end(); ok &= ( found == true ); // Y[0] does not depend on X[1] found = st[1].find(0) != st[1].end(); ok &= ( found == false ); // Y[1] does depend on X[0] found = st[0].find(1) != st[0].end(); ok &= ( found == true ); // Y[1] does depend on X[1] found = st[1].find(1) != st[1].end(); ok &= ( found == true ); // Y[2] does not depend on X[0] found = st[0].find(2) != st[0].end(); ok &= ( found == false ); // Y[2] does depend on X[1] found = st[1].find(2) != st[1].end(); ok &= ( found == true ); // check that values are stored ok &= (f.size_forward_set() > 0); ok &= (f.size_forward_bool() == 0); return ok; } } // End empty namespace # include # include bool ForSparseJac(void) { bool ok = true; // Run with Vector equal to four different cases // all of which are Simple Vectors with elements of type bool. ok &= BoolCases< CppAD::vectorBool >(); ok &= BoolCases< CppAD::vector >(); ok &= BoolCases< std::vector >(); ok &= BoolCases< std::valarray >(); // Run with Vector equal to two different cases both of which are // Simple Vectors with elements of type std::set typedef std::set set; ok &= SetCases< CppAD::vector >(); // ok &= SetCases< std::vector >(); // Do not use valarray because its element access in the const case // returns a copy instead of a reference // ok &= SetCases< std::valarray >(); return ok; } // END C++ ================================================ FILE: example/sparse/rc_sparsity.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin rc_sparsity.cpp} Preferred Sparsity Patterns: Row and Column Indices: Example and Test ##################################################################### Purpose ******* This example show how to use row and column index sparsity patterns :ref:`sparse_rc-name` to compute sparse Jacobians and Hessians. This became the preferred way to represent sparsity on :ref:`2017-02-09<2017@mm-dd@02-09>` . {xrst_literal // BEGIN C++ // END C++ } {xrst_end rc_sparsity.cpp} */ // BEGIN C++ # include namespace { using CppAD::sparse_rc; using CppAD::sparse_rcv; using CppAD::NearEqual; // typedef CPPAD_TESTVECTOR(bool) b_vector; typedef CPPAD_TESTVECTOR(size_t) s_vector; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR( CppAD::AD ) a_vector; // double eps99 = 99.0 * std::numeric_limits::epsilon(); // ----------------------------------------------------------------------- // function f(x) that we are computing sparse results for // ----------------------------------------------------------------------- a_vector fun(const a_vector& x) { size_t n = x.size(); a_vector ret(n + 1); for(size_t i = 0; i < n; i++) { size_t j = (i + 1) % n; ret[i] = x[i] * x[i] * x[j]; } ret[n] = 0.0; return ret; } // ----------------------------------------------------------------------- // Jacobian // ----------------------------------------------------------------------- bool check_jac( const d_vector& x , const sparse_rcv& subset ) { bool ok = true; size_t n = x.size(); // ok &= subset.nnz() == 2 * n; const s_vector& row( subset.row() ); const s_vector& col( subset.col() ); const d_vector& val( subset.val() ); s_vector row_major = subset.row_major(); for(size_t i = 0; i < n; i++) { size_t j = (i + 1) % n; size_t k = 2 * i; // ok &= row[ row_major[k] ] == i; ok &= row[ row_major[k+1] ] == i; // size_t ck = col[ row_major[k] ]; size_t ckp = col[ row_major[k+1] ]; double vk = val[ row_major[k] ]; double vkp = val[ row_major[k+1] ]; // // put diagonal element first if( j < i ) { std::swap(ck, ckp); std::swap(vk, vkp); } // diagonal element ok &= ck == i; ok &= NearEqual( vk, 2.0 * x[i] * x[j], eps99, eps99 ); // off diagonal element ok &= ckp == j; ok &= NearEqual( vkp, x[i] * x[i], eps99, eps99 ); } return ok; } // Use forward mode for Jacobian and sparsity pattern bool forward_jac(CppAD::ADFun& f) { bool ok = true; size_t n = f.Domain(); // // sparsity pattern for identity matrix sparse_rc pattern_in(n, n, n); for(size_t k = 0; k < n; k++) pattern_in.set(k, k, k); // // sparsity pattern for Jacobian bool transpose = false; bool dependency = false; bool internal_bool = false; sparse_rc pattern_out; f.for_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); // // compute entire Jacobian size_t group_max = 1; std::string coloring = "cppad"; sparse_rcv subset( pattern_out ); CppAD::sparse_jac_work work; d_vector x(n); for(size_t j = 0; j < n; j++) x[j] = double(j + 2); size_t n_sweep = f.sparse_jac_for( group_max, x, subset, pattern_out, coloring, work ); // // check Jacobian ok &= check_jac(x, subset); ok &= n_sweep == 2; // return ok; } // Use reverse mode for Jacobian and sparsity pattern bool reverse_jac(CppAD::ADFun& f) { bool ok = true; size_t n = f.Domain(); size_t m = f.Range(); // // sparsity pattern for identity matrix sparse_rc pattern_in(m, m, m); for(size_t k = 0; k < m; k++) pattern_in.set(k, k, k); // // sparsity pattern for Jacobian bool transpose = false; bool dependency = false; bool internal_bool = false; sparse_rc pattern_out; f.rev_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); // // compute entire Jacobian std::string coloring = "cppad"; sparse_rcv subset( pattern_out ); CppAD::sparse_jac_work work; d_vector x(n); for(size_t j = 0; j < n; j++) x[j] = double(j + 2); size_t n_sweep = f.sparse_jac_rev( x, subset, pattern_out, coloring, work ); // // check Jacobian ok &= check_jac(x, subset); ok &= n_sweep == 2; // return ok; } // ------------------------------------------------------------------------ // Hessian // ------------------------------------------------------------------------ bool check_hes( size_t i , const d_vector& x , const sparse_rcv& subset ) { bool ok = true; size_t n = x.size(); size_t j = (i + 1) % n; // ok &= subset.nnz() == 3; const s_vector& row( subset.row() ); const s_vector& col( subset.col() ); const d_vector& val( subset.val() ); s_vector row_major = subset.row_major(); // double v0 = val[ row_major[0] ]; double v1 = val[ row_major[1] ]; double v2 = val[ row_major[2] ]; if( j < i ) { ok &= row[ row_major[0] ] == j; ok &= col[ row_major[0] ] == i; ok &= NearEqual( v0, 2.0 * x[i], eps99, eps99 ); // ok &= row[ row_major[1] ] == i; ok &= col[ row_major[1] ] == j; ok &= NearEqual( v1, 2.0 * x[i], eps99, eps99 ); // ok &= row[ row_major[2] ] == i; ok &= col[ row_major[2] ] == i; ok &= NearEqual( v2, 2.0 * x[j], eps99, eps99 ); } else { ok &= row[ row_major[0] ] == i; ok &= col[ row_major[0] ] == i; ok &= NearEqual( v0, 2.0 * x[j], eps99, eps99 ); // ok &= row[ row_major[1] ] == i; ok &= col[ row_major[1] ] == j; ok &= NearEqual( v1, 2.0 * x[i], eps99, eps99 ); // ok &= row[ row_major[2] ] == j; ok &= col[ row_major[2] ] == i; ok &= NearEqual( v2, 2.0 * x[i], eps99, eps99 ); } return ok; } // Use forward mode for Hessian and sparsity pattern bool forward_hes(CppAD::ADFun& f) { bool ok = true; size_t n = f.Domain(); size_t m = f.Range(); // b_vector select_domain(n); for(size_t j = 0; j < n; j++) select_domain[j] = true; sparse_rc pattern_out; // for(size_t i = 0; i < m; i++) { // select i-th component of range b_vector select_range(m); d_vector w(m); for(size_t k = 0; k < m; k++) { select_range[k] = k == i; w[k] = 0.0; if( k == i ) w[k] = 1.0; } // bool internal_bool = false; f.for_hes_sparsity( select_domain, select_range, internal_bool, pattern_out ); // // compute Hessian for i-th component function std::string coloring = "cppad.symmetric"; sparse_rcv subset( pattern_out ); CppAD::sparse_hes_work work; d_vector x(n); for(size_t j = 0; j < n; j++) x[j] = double(j + 2); size_t n_sweep = f.sparse_hes( x, w, subset, pattern_out, coloring, work ); // // check Hessian if( i == n ) ok &= subset.nnz() == 0; else { ok &= check_hes(i, x, subset); ok &= n_sweep == 1; } } return ok; } // Use reverse mode for Hessian and sparsity pattern bool reverse_hes(CppAD::ADFun& f) { bool ok = true; size_t n = f.Domain(); size_t m = f.Range(); // // n by n identity matrix sparse_rc pattern_in(n, n, n); for(size_t j = 0; j < n; j++) pattern_in.set(j, j, j); // bool transpose = false; bool dependency = false; bool internal_bool = true; sparse_rc pattern_out; // f.for_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); // for(size_t i = 0; i < m; i++) { // select i-th component of range b_vector select_range(m); d_vector w(m); for(size_t k = 0; k < m; k++) { select_range[k] = k == i; w[k] = 0.0; if( k == i ) w[k] = 1.0; } // f.rev_hes_sparsity( select_range, transpose, internal_bool, pattern_out ); // // compute Hessian for i-th component function std::string coloring = "cppad.symmetric"; sparse_rcv subset( pattern_out ); CppAD::sparse_hes_work work; d_vector x(n); for(size_t j = 0; j < n; j++) x[j] = double(j + 2); size_t n_sweep = f.sparse_hes( x, w, subset, pattern_out, coloring, work ); // // check Hessian if( i == n ) ok &= subset.nnz() == 0; else { ok &= check_hes(i, x, subset); ok &= n_sweep == 1; } } return ok; } } // driver for all of the cases above bool rc_sparsity(void) { bool ok = true; // // record the function size_t n = 20; size_t m = n + 1; a_vector x(n), y(m); for(size_t j = 0; j < n; j++) x[j] = CppAD::AD(j+1); CppAD::Independent(x); y = fun(x); CppAD::ADFun f(x, y); // // run the example / tests ok &= forward_jac(f); ok &= reverse_jac(f); ok &= forward_hes(f); ok &= reverse_hes(f); // return ok; } // END C++ ================================================ FILE: example/sparse/rev_hes_sparsity.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin rev_hes_sparsity.cpp} Reverse Mode Hessian Sparsity: Example and Test ############################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end rev_hes_sparsity.cpp} */ // BEGIN C++ # include bool rev_hes_sparsity(void) { bool ok = true; using CppAD::AD; typedef CPPAD_TESTVECTOR(size_t) SizeVector; typedef CppAD::sparse_rc sparsity; // // domain space vector size_t n = 3; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.; ax[1] = 1.; ax[2] = 2.; // declare independent variables and start recording CppAD::Independent(ax); // range space vector size_t m = 2; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = sin( ax[2] ); ay[1] = ax[0] * ax[1]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // sparsity pattern for the identity matrix size_t nr = n; size_t nc = n; size_t nnz_in = n; sparsity pattern_in(nr, nc, nnz_in); for(size_t k = 0; k < nnz_in; k++) { size_t r = k; size_t c = k; pattern_in.set(k, r, c); } // compute sparsity pattern for J(x) = F'(x) bool transpose = false; bool dependency = false; bool internal_bool = false; sparsity pattern_out; f.for_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); // // compute sparsity pattern for H(x) = F_1''(x) CPPAD_TESTVECTOR(bool) select_range(m); select_range[0] = false; select_range[1] = true; f.rev_hes_sparsity( select_range, transpose, internal_bool, pattern_out ); size_t nnz = pattern_out.nnz(); ok &= nnz == 2; ok &= pattern_out.nr() == n; ok &= pattern_out.nc() == n; { // check results const SizeVector& row( pattern_out.row() ); const SizeVector& col( pattern_out.col() ); SizeVector row_major = pattern_out.row_major(); // ok &= row[ row_major[0] ] == 0 && col[ row_major[0] ] == 1; ok &= row[ row_major[1] ] == 1 && col[ row_major[1] ] == 0; } // // compute sparsity pattern for H(x) = F_0''(x) select_range[0] = true; select_range[1] = false; f.rev_hes_sparsity( select_range, transpose, internal_bool, pattern_out ); nnz = pattern_out.nnz(); ok &= nnz == 1; ok &= pattern_out.nr() == n; ok &= pattern_out.nc() == n; { // check results const SizeVector& row( pattern_out.row() ); const SizeVector& col( pattern_out.col() ); // ok &= row[0] == 2 && col[0] == 2; } return ok; } // END C++ ================================================ FILE: example/sparse/rev_jac_sparsity.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin rev_jac_sparsity.cpp} Reverse Mode Jacobian Sparsity: Example and Test ################################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end rev_jac_sparsity.cpp} */ // BEGIN C++ # include bool rev_jac_sparsity(void) { bool ok = true; using CppAD::AD; typedef CPPAD_TESTVECTOR(size_t) SizeVector; typedef CppAD::sparse_rc sparsity; // // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.; ax[1] = 1.; // declare independent variables and start recording CppAD::Independent(ax); // range space vector size_t m = 3; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = ax[0]; ay[1] = ax[0] * ax[1]; ay[2] = ax[1]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // sparsity pattern for the identity matrix size_t nr = m; size_t nc = m; size_t nnz_in = m; sparsity pattern_in(nr, nc, nnz_in); for(size_t k = 0; k < nnz_in; k++) { size_t r = k; size_t c = k; pattern_in.set(k, r, c); } // compute sparsite pattern for J(x) = F'(x) bool transpose = false; bool dependency = false; bool internal_bool = false; sparsity pattern_out; f.rev_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); size_t nnz = pattern_out.nnz(); ok &= nnz == 4; ok &= pattern_out.nr() == m; ok &= pattern_out.nc() == n; { // check results const SizeVector& row( pattern_out.row() ); const SizeVector& col( pattern_out.col() ); SizeVector col_major = pattern_out.col_major(); // ok &= row[ col_major[0] ] == 0 && col[ col_major[0] ] == 0; ok &= row[ col_major[1] ] == 1 && col[ col_major[1] ] == 0; ok &= row[ col_major[2] ] == 1 && col[ col_major[2] ] == 1; ok &= row[ col_major[3] ] == 2 && col[ col_major[3] ] == 1; } // note that the transpose of the identity is the identity transpose = true; internal_bool = true; f.rev_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); nnz = pattern_out.nnz(); ok &= nnz == 4; ok &= pattern_out.nr() == n; ok &= pattern_out.nc() == m; { // check results const SizeVector& row( pattern_out.row() ); const SizeVector& col( pattern_out.col() ); SizeVector row_major = pattern_out.row_major(); // ok &= col[ row_major[0] ] == 0 && row[ row_major[0] ] == 0; ok &= col[ row_major[1] ] == 1 && row[ row_major[1] ] == 0; ok &= col[ row_major[2] ] == 1 && row[ row_major[2] ] == 1; ok &= col[ row_major[3] ] == 2 && row[ row_major[3] ] == 1; } return ok; } // END C++ ================================================ FILE: example/sparse/rev_sparse_hes.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin rev_sparse_hes.cpp} Reverse Mode Hessian Sparsity: Example and Test ############################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end rev_sparse_hes.cpp} */ // BEGIN C++ # include namespace { // ------------------------------------------------------------- // expected sparsity pattern bool check_f0[] = { false, false, false, // partials w.r.t x0 and (x0, x1, x2) false, false, false, // partials w.r.t x1 and (x0, x1, x2) false, false, true // partials w.r.t x2 and (x0, x1, x2) }; bool check_f1[] = { false, true, false, // partials w.r.t x0 and (x0, x1, x2) true, false, false, // partials w.r.t x1 and (x0, x1, x2) false, false, false // partials w.r.t x2 and (x0, x1, x2) }; // define the template function BoolCases in empty namespace template // vector class, elements of type bool bool BoolCases(void) { bool ok = true; using CppAD::AD; // domain space vector size_t n = 3; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.; ax[1] = 1.; ax[2] = 2.; // declare independent variables and start recording CppAD::Independent(ax); // range space vector size_t m = 2; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = sin( ax[2] ); ay[1] = ax[0] * ax[1]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // sparsity pattern for the identity matrix Vector r(n * n); size_t i, j; for(i = 0; i < n; i++) { for(j = 0; j < n; j++) r[ i * n + j ] = (i == j); } // compute sparsity pattern for J(x) = F^{(1)} (x) f.ForSparseJac(n, r); // compute sparsity pattern for H(x) = F_0^{(2)} (x) Vector s(m); for(i = 0; i < m; i++) s[i] = false; s[0] = true; Vector h(n * n); h = f.RevSparseHes(n, s); // check values for(i = 0; i < n; i++) for(j = 0; j < n; j++) ok &= (h[ i * n + j ] == check_f0[ i * n + j ] ); // compute sparsity pattern for H(x) = F_1^{(2)} (x) for(i = 0; i < m; i++) s[i] = false; s[1] = true; h = f.RevSparseHes(n, s); // check values for(i = 0; i < n; i++) for(j = 0; j < n; j++) ok &= (h[ i * n + j ] == check_f1[ i * n + j ] ); // call that transposed the result bool transpose = true; h = f.RevSparseHes(n, s, transpose); // This h is symmetric, because R is symmetric, not really testing here for(i = 0; i < n; i++) for(j = 0; j < n; j++) ok &= (h[ j * n + i ] == check_f1[ i * n + j ] ); return ok; } // define the template function SetCases in empty namespace template // vector class, elements of type std::set bool SetCases(void) { bool ok = true; using CppAD::AD; // domain space vector size_t n = 3; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.; ax[1] = 1.; ax[2] = 2.; // declare independent variables and start recording CppAD::Independent(ax); // range space vector size_t m = 2; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = sin( ax[2] ); ay[1] = ax[0] * ax[1]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // sparsity pattern for the identity matrix Vector r(n); size_t i; for(i = 0; i < n; i++) { assert( r[i].empty() ); r[i].insert(i); } // compute sparsity pattern for J(x) = F^{(1)} (x) f.ForSparseJac(n, r); // compute sparsity pattern for H(x) = F_0^{(2)} (x) Vector s(1); assert( s[0].empty() ); s[0].insert(0); Vector h(n); h = f.RevSparseHes(n, s); // check values std::set::iterator itr; size_t j; for(i = 0; i < n; i++) { for(j = 0; j < n; j++) { bool found = h[i].find(j) != h[i].end(); ok &= (found == check_f0[i * n + j]); } } // compute sparsity pattern for H(x) = F_1^{(2)} (x) s[0].clear(); assert( s[0].empty() ); s[0].insert(1); h = f.RevSparseHes(n, s); // check values for(i = 0; i < n; i++) { for(j = 0; j < n; j++) { bool found = h[i].find(j) != h[i].end(); ok &= (found == check_f1[i * n + j]); } } // call that transposed the result bool transpose = true; h = f.RevSparseHes(n, s, transpose); // This h is symmetric, because R is symmetric, not really testing here for(i = 0; i < n; i++) { for(j = 0; j < n; j++) { bool found = h[j].find(i) != h[j].end(); ok &= (found == check_f1[i * n + j]); } } return ok; } } // End empty namespace # include # include bool rev_sparse_hes(void) { bool ok = true; // Run with Vector equal to four different cases // all of which are Simple Vectors with elements of type bool. ok &= BoolCases< CppAD::vector >(); ok &= BoolCases< CppAD::vectorBool >(); ok &= BoolCases< std::vector >(); ok &= BoolCases< std::valarray >(); // Run with Vector equal to two different cases both of which are // Simple Vectors with elements of type std::set typedef std::set set; ok &= SetCases< CppAD::vector >(); ok &= SetCases< std::vector >(); // Do not use valarray because its element access in the const case // returns a copy instead of a reference // ok &= SetCases< std::valarray >(); return ok; } // END C++ ================================================ FILE: example/sparse/rev_sparse_jac.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin rev_sparse_jac.cpp} Reverse Mode Jacobian Sparsity: Example and Test ################################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end rev_sparse_jac.cpp} */ // BEGIN C++ # include namespace { // ------------------------------------------------------------- // define the template function BoolCases template // vector class, elements of type bool bool BoolCases(void) { bool ok = true; using CppAD::AD; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.; ax[1] = 1.; // declare independent variables and start recording CppAD::Independent(ax); // range space vector size_t m = 3; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = ax[0]; ay[1] = ax[0] * ax[1]; ay[2] = ax[1]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // sparsity pattern for the identity matrix Vector r(m * m); size_t i, j; for(i = 0; i < m; i++) { for(j = 0; j < m; j++) r[ i * m + j ] = (i == j); } // sparsity pattern for F'(x) Vector s(m * n); s = f.RevSparseJac(m, r); // check values ok &= (s[ 0 * n + 0 ] == true); // y[0] does depend on x[0] ok &= (s[ 0 * n + 1 ] == false); // y[0] does not depend on x[1] ok &= (s[ 1 * n + 0 ] == true); // y[1] does depend on x[0] ok &= (s[ 1 * n + 1 ] == true); // y[1] does depend on x[1] ok &= (s[ 2 * n + 0 ] == false); // y[2] does not depend on x[0] ok &= (s[ 2 * n + 1 ] == true); // y[2] does depend on x[1] // sparsity pattern for F'(x)^T, note R is the identity, so R^T = R bool transpose = true; Vector st(n * m); st = f.RevSparseJac(m, r, transpose); // check values ok &= (st[ 0 * m + 0 ] == true); // y[0] does depend on x[0] ok &= (st[ 1 * m + 0 ] == false); // y[0] does not depend on x[1] ok &= (st[ 0 * m + 1 ] == true); // y[1] does depend on x[0] ok &= (st[ 1 * m + 1 ] == true); // y[1] does depend on x[1] ok &= (st[ 0 * m + 2 ] == false); // y[2] does not depend on x[0] ok &= (st[ 1 * m + 2 ] == true); // y[2] does depend on x[1] return ok; } // define the template function SetCases template // vector class, elements of type std::set bool SetCases(void) { bool ok = true; using CppAD::AD; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.; ax[1] = 1.; // declare independent variables and start recording CppAD::Independent(ax); // range space vector size_t m = 3; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = ax[0]; ay[1] = ax[0] * ax[1]; ay[2] = ax[1]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // sparsity pattern for the identity matrix Vector r(m); size_t i; for(i = 0; i < m; i++) { assert( r[i].empty() ); r[i].insert(i); } // sparsity pattern for F'(x) Vector s(m); s = f.RevSparseJac(m, r); // check values bool found; // y[0] does depend on x[0] found = s[0].find(0) != s[0].end(); ok &= (found == true); // y[0] does not depend on x[1] found = s[0].find(1) != s[0].end(); ok &= (found == false); // y[1] does depend on x[0] found = s[1].find(0) != s[1].end(); ok &= (found == true); // y[1] does depend on x[1] found = s[1].find(1) != s[1].end(); ok &= (found == true); // y[2] does not depend on x[0] found = s[2].find(0) != s[2].end(); ok &= (found == false); // y[2] does depend on x[1] found = s[2].find(1) != s[2].end(); ok &= (found == true); // sparsity pattern for F'(x)^T bool transpose = true; Vector st(n); st = f.RevSparseJac(m, r, transpose); // y[0] does depend on x[0] found = st[0].find(0) != st[0].end(); ok &= (found == true); // y[0] does not depend on x[1] found = st[1].find(0) != st[1].end(); ok &= (found == false); // y[1] does depend on x[0] found = st[0].find(1) != st[0].end(); ok &= (found == true); // y[1] does depend on x[1] found = st[1].find(1) != st[1].end(); ok &= (found == true); // y[2] does not depend on x[0] found = st[0].find(2) != st[0].end(); ok &= (found == false); // y[2] does depend on x[1] found = st[1].find(2) != st[1].end(); ok &= (found == true); return ok; } } // End empty namespace # include # include bool RevSparseJac(void) { bool ok = true; // Run with Vector equal to four different cases // all of which are Simple Vectors with elements of type bool. ok &= BoolCases< CppAD::vectorBool >(); ok &= BoolCases< CppAD::vector >(); ok &= BoolCases< std::vector >(); ok &= BoolCases< std::valarray >(); // Run with Vector equal to two different cases both of which are // Simple Vectors with elements of type std::set typedef std::set set; ok &= SetCases< CppAD::vector >(); ok &= SetCases< std::vector >(); // Do not use valarray because its element access in the const case // returns a copy instead of a reference // ok &= SetCases< std::valarray >(); return ok; } // END C++ ================================================ FILE: example/sparse/sparse.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sparse.cpp} Sparse AD Examples and Tests Driver ################################### Running These Tests ******************* After executing the :ref:`cmake-name` command form the :ref:`download@Distribution Directory`, you can build and run these tests with the commands:: cd build make check_example_sparse Note that your choice of :ref:`cmake@generator` may require using an different version of make; e.g., ``ninja`` . {xrst_literal // BEGIN C++ // END C++ } {xrst_end sparse.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ // CPPAD_HAS_* defines # include // for thread_alloc # include // test runner # include // BEGIN_SORT_THIS_LINE_PLUS_2 // external compiled tests extern bool ForSparseJac(void); extern bool RevSparseJac(void); extern bool colpack_hes(void); extern bool colpack_hessian(void); extern bool colpack_jac(void); extern bool colpack_jacobian(void); extern bool conj_grad(void); extern bool dependency(void); extern bool for_hes_sparsity(void); extern bool for_jac_sparsity(void); extern bool for_sparse_hes(void); extern bool rc_sparsity(void); extern bool rev_hes_sparsity(void); extern bool rev_jac_sparsity(void); extern bool rev_sparse_hes(void); extern bool sparse2eigen(void); extern bool sparse_hes(void); extern bool sparse_hessian(void); extern bool sparse_jac_for(void); extern bool sparse_jac_rev(void); extern bool sparse_jacobian(void); extern bool sparse_sub_hes(void); extern bool sparsity_sub(void); extern bool sub_sparse_hes(void); extern bool subgraph_hes2jac(void); extern bool subgraph_jac_rev(void); extern bool subgraph_reverse(void); extern bool subgraph_sparsity(void); // END_SORT_THIS_LINE_MINUS_1 // main program that runs all the tests int main(void) { std::string group = "example/sparse"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // BEGIN_SORT_THIS_LINE_PLUS_2 // external compiled tests Run( ForSparseJac, "ForSparseJac" ); Run( RevSparseJac, "RevSparseJac" ); Run( conj_grad, "conj_grad" ); Run( dependency, "dependency" ); Run( for_hes_sparsity, "for_hes_sparsity" ); Run( for_jac_sparsity, "for_jac_sparsity" ); Run( for_sparse_hes, "for_sparse_hes" ); Run( rc_sparsity, "rc_sparsity" ); Run( rev_hes_sparsity, "rev_hes_sparsity" ); Run( rev_jac_sparsity, "rev_jac_sparsity" ); Run( rev_sparse_hes, "rev_sparse_hes" ); Run( sparse_hes, "sparse_hes" ); Run( sparse_hessian, "sparse_hessian" ); Run( sparse_jac_for, "sparse_jac_for" ); Run( sparse_jac_rev, "sparse_jac_rev" ); Run( sparse_jacobian, "sparse_jacobian" ); Run( sparse_sub_hes, "sparse_sub_hes" ); Run( sparsity_sub, "sparsity_sub" ); Run( sub_sparse_hes, "sub_sparse_hes" ); Run( subgraph_hes2jac, "subgraph_hes2jac" ); Run( subgraph_jac_rev, "subgraph_jac_rev" ); Run( subgraph_reverse, "reverse_subgraph"); Run( subgraph_sparsity, "subgraph_sparsity" ); // END_SORT_THIS_LINE_MINUS_1 // # if CPPAD_HAS_COLPACK Run( colpack_jac, "colpack_jac" ); Run( colpack_jacobian, "colpack_jacobian" ); Run( colpack_hes, "colpack_hes" ); Run( colpack_hessian, "colpack_hessian" ); # endif # if CPPAD_HAS_EIGEN Run( sparse2eigen, "sparse2eigen" ); # endif // // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } // END C++ ================================================ FILE: example/sparse/sparse2eigen.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sparse2eigen.cpp} Converting CppAD Sparse Matrix to Eigen Format: Example and Test ################################################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end sparse2eigen.cpp} */ // BEGIN C++ # include bool sparse2eigen(void) { bool ok = true; // typedef CppAD::vector s_vector; typedef CppAD::vector d_vector; typedef CppAD::sparse_rc sparse_rc; typedef CppAD::sparse_rcv sparse_rcv; // // sparsity pattern size_t nr = 3; size_t nc = 4; size_t nnz = 2 * nr; sparse_rc pattern(nr, nc, nnz); for(size_t i = 0; i < nr; i++) { for(size_t j = i; j <= i + 1; j++) { size_t k = i + j; pattern.set(k, i, j); } } // // sparse matrix sparse_rcv source(pattern); for(size_t i = 0; i < nr; i++) { for(size_t j = i; j <= i + 1; j++) { size_t k = i + j; double v = double(k) / 2.0; source.set(k, v); } } // // example using row major order { Eigen::SparseMatrix destination; CppAD::sparse2eigen(source, destination); // typedef Eigen::SparseMatrix::InnerIterator iterator; // // check result const double* d_value = destination.valuePtr(); size_t k = 0; for(int i = 0; i < destination.outerSize(); ++i) { for(iterator itr(destination, i); itr; ++itr) { // check row ok &= itr.row() == i; // // check column if( k % 2 == 0 ) ok &= itr.col() == i; else ok &= itr.col() == (i+1); // // check value ok &= itr.value() == double( itr.row() + itr.col() ) / 2.0; ok &= itr.value() == d_value[k]; // ++k; } } ok &= k == nnz; } // // example using column major order { Eigen::SparseMatrix destination; CppAD::sparse2eigen(source, destination); // typedef Eigen::SparseMatrix::InnerIterator iterator; // // check result const double* d_value = destination.valuePtr(); size_t k = 0; for(int j = 0; j < destination.outerSize(); ++j) { for(iterator itr(destination, j); itr; ++itr) { // check column ok &= itr.col() == j; // // check row if( j == 0 ) { assert( k == 0 ); ok &= itr.row() == 0; } else if( k % 2 == 1 ) ok &= itr.row() == j - 1; else ok &= itr.row() == j; // // check value ok &= itr.value() == double( itr.row() + itr.col() ) / 2.0; ok &= itr.value() == d_value[k]; // ++k; } } ok &= k == nnz; } // return ok; } // END C++ ================================================ FILE: example/sparse/sparse_hes.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sparse_hes.cpp} Computing Sparse Hessian: Example and Test ########################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end sparse_hes.cpp} */ // BEGIN C++ # include bool sparse_hes(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; // typedef CPPAD_TESTVECTOR(AD) a_vector; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR(size_t) s_vector; typedef CPPAD_TESTVECTOR(bool) b_vector; // // domain space vector size_t n = 12; // must be greater than or equal 3; see n_sweep below a_vector a_x(n); for(size_t j = 0; j < n; j++) a_x[j] = AD (0); // // declare independent variables and starting recording CppAD::Independent(a_x); // // range space vector size_t m = 1; a_vector a_y(m); a_y[0] = a_x[0] * a_x[1]; for(size_t j = 0; j < n; j++) a_y[0] += a_x[j] * a_x[j] * a_x[j]; // // create f: x -> y and stop tape recording // (without executing zero order forward calculation) CppAD::ADFun f; f.Dependent(a_x, a_y); // // new value for the independent variable vector, and weighting vector d_vector w(m), x(n); for(size_t j = 0; j < n; j++) x[j] = double(j); w[0] = 1.0; // // vector used to check the value of the hessian d_vector check(n * n); size_t ij = 0 * n + 1; for(ij = 0; ij < n * n; ij++) check[ij] = 0.0; ij = 0 * n + 1; check[ij] = 1.0; ij = 1 * n + 0; check[ij] = 1.0 ; for(size_t j = 0; j < n; j++) { ij = j * n + j; check[ij] = 6.0 * x[j]; } // // compute Hessian sparsity pattern b_vector select_domain(n), select_range(m); for(size_t j = 0; j < n; j++) select_domain[j] = true; select_range[0] = true; // CppAD::sparse_rc hes_pattern; bool internal_bool = false; f.for_hes_sparsity( select_domain, select_range, internal_bool, hes_pattern ); // // compute entire sparse Hessian (really only need lower triangle) CppAD::sparse_rcv subset( hes_pattern ); CppAD::sparse_hes_work work; std::string coloring = "cppad.symmetric"; size_t n_sweep = f.sparse_hes(x, w, subset, hes_pattern, coloring, work); ok &= n_sweep == 2; // const s_vector row( subset.row() ); const s_vector col( subset.col() ); const d_vector val( subset.val() ); size_t nnz = subset.nnz(); ok &= nnz == n + 2; for(size_t k = 0; k < nnz; k++) { ij = row[k] * n + col[k]; ok &= val[k] == check[ij]; } return ok; } // END C++ ================================================ FILE: example/sparse/sparse_hessian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sparse_hessian.cpp} Sparse Hessian: Example and Test ################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end sparse_hessian.cpp} */ // BEGIN C++ # include bool sparse_hessian(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; size_t i, j, k, ell; typedef CPPAD_TESTVECTOR(AD) a_vector; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR(size_t) i_vector; typedef CPPAD_TESTVECTOR(bool) b_vector; typedef CPPAD_TESTVECTOR(std::set) s_vector; double eps = 10. * CppAD::numeric_limits::epsilon(); // domain space vector size_t n = 12; // must be greater than or equal 3; see n_sweep below a_vector a_x(n); for(j = 0; j < n; j++) a_x[j] = AD (0); // declare independent variables and starting recording CppAD::Independent(a_x); // range space vector size_t m = 1; a_vector a_y(m); a_y[0] = a_x[0]*a_x[1]; for(j = 0; j < n; j++) a_y[0] += a_x[j] * a_x[j] * a_x[j]; // create f: x -> y and stop tape recording // (without executing zero order forward calculation) CppAD::ADFun f; f.Dependent(a_x, a_y); // new value for the independent variable vector, and weighting vector d_vector w(m), x(n); for(j = 0; j < n; j++) x[j] = double(j); w[0] = 1.0; // vector used to check the value of the hessian d_vector check(n * n); for(ell = 0; ell < n * n; ell++) check[ell] = 0.0; ell = 0 * n + 1; check[ell] = 1.0; ell = 1 * n + 0; check[ell] = 1.0 ; for(j = 0; j < n; j++) { ell = j * n + j; check[ell] = 6.0 * x[j]; } // ------------------------------------------------------------------- // second derivative of y[0] w.r.t x d_vector hes(n * n); hes = f.SparseHessian(x, w); for(ell = 0; ell < n * n; ell++) ok &= NearEqual(w[0] * check[ell], hes[ell], eps, eps ); // -------------------------------------------------------------------- // example using vectors of bools to compute sparsity pattern for Hessian b_vector r_bool(n * n); for(i = 0; i < n; i++) { for(j = 0; j < n; j++) r_bool[i * n + j] = false; r_bool[i * n + i] = true; } f.ForSparseJac(n, r_bool); // b_vector s_bool(m); for(i = 0; i < m; i++) s_bool[i] = w[i] != 0; b_vector p_bool = f.RevSparseHes(n, s_bool); hes = f.SparseHessian(x, w, p_bool); for(ell = 0; ell < n * n; ell++) ok &= NearEqual(w[0] * check[ell], hes[ell], eps, eps ); // -------------------------------------------------------------------- // example using vectors of sets to compute sparsity pattern for Hessian s_vector r_set(n); for(i = 0; i < n; i++) r_set[i].insert(i); f.ForSparseJac(n, r_set); // s_vector s_set(m); for(i = 0; i < m; i++) if( w[i] != 0. ) s_set[0].insert(i); s_vector p_set = f.RevSparseHes(n, s_set); // example passing sparsity pattern to SparseHessian hes = f.SparseHessian(x, w, p_set); for(ell = 0; ell < n * n; ell++) ok &= NearEqual(w[0] * check[ell], hes[ell], eps, eps ); // -------------------------------------------------------------------- // use row and column indices to specify upper triangle of // non-zero elements of Hessian size_t K = n + 1; i_vector row(K), col(K); hes.resize(K); k = 0; for(j = 0; j < n; j++) { // diagonal of Hessian row[k] = j; col[k] = j; k++; } // only off diagonal non-zero element in upper triangle row[k] = 0; col[k] = 1; k++; ok &= k == K; CppAD::sparse_hessian_work work; // can use p_set or p_bool. size_t n_sweep = f.SparseHessian(x, w, p_set, row, col, hes, work); for(k = 0; k < K; k++) { ell = row[k] * n + col[k]; ok &= NearEqual(w[0] * check[ell], hes[k], eps, eps ); } ok &= n_sweep == 2; // now recompute at a different x and w (using work from previous call w[0] = 2.0; x[1] = 0.5; ell = 1 * n + 1; check[ell] = 6.0 * x[1]; s_vector not_used; n_sweep = f.SparseHessian(x, w, not_used, row, col, hes, work); for(k = 0; k < K; k++) { ell = row[k] * n + col[k]; ok &= NearEqual(w[0] * check[ell], hes[k], eps, eps ); } ok &= n_sweep == 2; return ok; } // END C++ ================================================ FILE: example/sparse/sparse_jac_for.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sparse_jac_for.cpp} Computing Sparse Jacobian Using Forward Mode: Example and Test ############################################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end sparse_jac_for.cpp} */ // BEGIN C++ # include bool sparse_jac_for(void) { bool ok = true; // using CppAD::AD; using CppAD::NearEqual; using CppAD::sparse_rc; using CppAD::sparse_rcv; // typedef CPPAD_TESTVECTOR(AD) a_vector; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR(size_t) s_vector; // // domain space vector size_t n = 3; a_vector a_x(n); for(size_t j = 0; j < n; j++) a_x[j] = AD (0); // // declare independent variables and starting recording CppAD::Independent(a_x); // size_t m = 4; a_vector a_y(m); a_y[0] = a_x[0] + a_x[2]; a_y[1] = a_x[0] + a_x[2]; a_y[2] = a_x[1] + a_x[2]; a_y[3] = a_x[1] + a_x[2] * a_x[2] / 2.; // // create f: x -> y and stop tape recording CppAD::ADFun f(a_x, a_y); // // new value for the independent variable vector d_vector x(n); for(size_t j = 0; j < n; j++) x[j] = double(j); /* [ 1 0 1 ] J(x) = [ 1 0 1 ] [ 0 1 1 ] [ 0 1 x_2 ] */ d_vector check(m * n); // // column-major order values of J(x) size_t nnz = 8; s_vector check_row(nnz), check_col(nnz); d_vector check_val(nnz); for(size_t k = 0; k < nnz; k++) { // check_val if( k < 7 ) check_val[k] = 1.0; else check_val[k] = x[2]; // // check_row and check_col check_row[k] = k; if( k < 2 ) check_col[k] = 0; else if( k < 4 ) check_col[k] = 1; else { check_col[k] = 2; check_row[k] = k - 4; } } // // n by n identity matrix sparsity sparse_rc pattern_in; pattern_in.resize(n, n, n); for(size_t k = 0; k < n; k++) pattern_in.set(k, k, k); // // sparsity for J(x) bool transpose = false; bool dependency = false; bool internal_bool = true; sparse_rc pattern_jac; f.for_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_jac ); // // compute entire forward mode Jacobian sparse_rcv subset( pattern_jac ); CppAD::sparse_jac_work work; std::string coloring = "cppad"; size_t group_max = 10; size_t n_color = f.sparse_jac_for( group_max, x, subset, pattern_jac, coloring, work ); ok &= n_color == 2; // const s_vector row( subset.row() ); const s_vector col( subset.col() ); const d_vector val( subset.val() ); s_vector col_major = subset.col_major(); ok &= subset.nnz() == nnz; for(size_t k = 0; k < nnz; k++) { ok &= row[ col_major[k] ] == check_row[k]; ok &= col[ col_major[k] ] == check_col[k]; ok &= val[ col_major[k] ] == check_val[k]; } // compute non-zero in row 3 only sparse_rc pattern_row3; pattern_row3.resize(m, n, 2); // nr = m, nc = n, nnz = 2 pattern_row3.set(0, 3, 1); // row[0] = 3, col[0] = 1 pattern_row3.set(1, 3, 2); // row[1] = 3, col[1] = 2 sparse_rcv subset_row3( pattern_row3 ); work.clear(); n_color = f.sparse_jac_for( group_max, x, subset_row3, pattern_jac, coloring, work ); ok &= n_color == 2; // const s_vector row_row3( subset_row3.row() ); const s_vector col_row3( subset_row3.col() ); const d_vector val_row3( subset_row3.val() ); ok &= subset_row3.nnz() == 2; // ok &= row_row3[0] == 3; ok &= col_row3[0] == 1; ok &= val_row3[0] == 1.0; // ok &= row_row3[1] == 3; ok &= col_row3[1] == 2; ok &= val_row3[1] == x[2]; // return ok; } // END C++ ================================================ FILE: example/sparse/sparse_jac_rev.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sparse_jac_rev.cpp} Computing Sparse Jacobian Using Reverse Mode: Example and Test ############################################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end sparse_jac_rev.cpp} */ // BEGIN C++ # include bool sparse_jac_rev(void) { bool ok = true; // using CppAD::AD; using CppAD::NearEqual; using CppAD::sparse_rc; using CppAD::sparse_rcv; // typedef CPPAD_TESTVECTOR(AD) a_vector; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR(size_t) s_vector; // // domain space vector size_t n = 4; a_vector a_x(n); for(size_t j = 0; j < n; j++) a_x[j] = AD (0); // // declare independent variables and starting recording CppAD::Independent(a_x); // size_t m = 3; a_vector a_y(m); a_y[0] = a_x[0] + a_x[1]; a_y[1] = a_x[2] + a_x[3]; a_y[2] = a_x[0] + a_x[1] + a_x[2] + a_x[3] * a_x[3] / 2.; // // create f: x -> y and stop tape recording CppAD::ADFun f(a_x, a_y); // // new value for the independent variable vector d_vector x(n); for(size_t j = 0; j < n; j++) x[j] = double(j); /* [ 1 1 0 0 ] J(x) = [ 0 0 1 1 ] [ 1 1 1 x_3] */ // // row-major order values of J(x) size_t nnz = 8; s_vector check_row(nnz), check_col(nnz); d_vector check_val(nnz); for(size_t k = 0; k < nnz; k++) { // check_val if( k < 7 ) check_val[k] = 1.0; else check_val[k] = x[3]; // // check_row and check_col check_col[k] = k; if( k < 2 ) check_row[k] = 0; else if( k < 4 ) check_row[k] = 1; else { check_row[k] = 2; check_col[k] = k - 4; } } // // m by m identity matrix sparsity sparse_rc pattern_in(m, m, m); for(size_t k = 0; k < m; k++) pattern_in.set(k, k, k); // // sparsity for J(x) bool transpose = false; bool dependency = false; bool internal_bool = true; sparse_rc pattern_jac; f.rev_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_jac ); // // compute entire reverse mode Jacobian sparse_rcv subset( pattern_jac ); CppAD::sparse_jac_work work; std::string coloring = "cppad"; size_t n_sweep = f.sparse_jac_rev(x, subset, pattern_jac, coloring, work); ok &= n_sweep == 2; // const s_vector row( subset.row() ); const s_vector col( subset.col() ); const d_vector val( subset.val() ); s_vector row_major = subset.row_major(); ok &= subset.nnz() == nnz; for(size_t k = 0; k < nnz; k++) { ok &= row[ row_major[k] ] == check_row[k]; ok &= col[ row_major[k] ] == check_col[k]; ok &= val[ row_major[k] ] == check_val[k]; } // // test using work stored by previous sparse_jac_rev sparse_rc pattern_not_used; std::string coloring_not_used; n_sweep = f.sparse_jac_rev(x, subset, pattern_jac, coloring, work); ok &= n_sweep == 2; for(size_t k = 0; k < nnz; k++) { ok &= row[ row_major[k] ] == check_row[k]; ok &= col[ row_major[k] ] == check_col[k]; ok &= val[ row_major[k] ] == check_val[k]; } // // compute non-zero in col 3 only, nr = m, nc = n, nnz = 2 sparse_rc pattern_col3(m, n, 2); pattern_col3.set(0, 1, 3); // row[0] = 1, col[0] = 3 pattern_col3.set(1, 2, 3); // row[1] = 2, col[1] = 3 sparse_rcv subset_col3( pattern_col3 ); work.clear(); n_sweep = f.sparse_jac_rev(x, subset_col3, pattern_jac, coloring, work); ok &= n_sweep == 2; // const s_vector row_col3( subset_col3.row() ); const s_vector col_col3( subset_col3.col() ); const d_vector val_col3( subset_col3.val() ); ok &= subset_col3.nnz() == 2; // ok &= row_col3[0] == 1; ok &= col_col3[0] == 3; ok &= val_col3[0] == 1.0; // ok &= row_col3[1] == 2; ok &= col_col3[1] == 3; ok &= val_col3[1] == x[3]; // return ok; } // END C++ ================================================ FILE: example/sparse/sparse_jacobian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sparse_jacobian.cpp} Sparse Jacobian: Example and Test ################################# {xrst_literal // BEGIN C++ // END C++ } {xrst_end sparse_jacobian.cpp} */ // BEGIN C++ # include namespace { // --------------------------------------------------------- bool reverse() { bool ok = true; using CppAD::AD; using CppAD::NearEqual; typedef CPPAD_TESTVECTOR(AD) a_vector; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR(size_t) i_vector; size_t i, j, k, ell; double eps = 10. * CppAD::numeric_limits::epsilon(); // domain space vector size_t n = 4; a_vector a_x(n); for(j = 0; j < n; j++) a_x[j] = AD (0); // declare independent variables and starting recording CppAD::Independent(a_x); size_t m = 3; a_vector a_y(m); a_y[0] = a_x[0] + a_x[1]; a_y[1] = a_x[2] + a_x[3]; a_y[2] = a_x[0] + a_x[1] + a_x[2] + a_x[3] * a_x[3] / 2.; // create f: x -> y and stop tape recording CppAD::ADFun f(a_x, a_y); // new value for the independent variable vector d_vector x(n); for(j = 0; j < n; j++) x[j] = double(j); // Jacobian of y without sparsity pattern d_vector jac(m * n); jac = f.SparseJacobian(x); /* [ 1 1 0 0 ] jac = [ 0 0 1 1 ] [ 1 1 1 x_3] */ d_vector check(m * n); check[0] = 1.; check[1] = 1.; check[2] = 0.; check[3] = 0.; check[4] = 0.; check[5] = 0.; check[6] = 1.; check[7] = 1.; check[8] = 1.; check[9] = 1.; check[10] = 1.; check[11] = x[3]; for(ell = 0; ell < size_t(check.size()); ell++) ok &= NearEqual(check[ell], jac[ell], eps, eps ); // using packed boolean sparsity patterns CppAD::vectorBool s_b(m * m), p_b(m * n); for(i = 0; i < m; i++) { for(ell = 0; ell < m; ell++) s_b[i * m + ell] = false; s_b[i * m + i] = true; } p_b = f.RevSparseJac(m, s_b); jac = f.SparseJacobian(x, p_b); for(ell = 0; ell < size_t(check.size()); ell++) ok &= NearEqual(check[ell], jac[ell], eps, eps ); // using vector of sets sparsity patterns std::vector< std::set > s_s(m), p_s(m); for(i = 0; i < m; i++) s_s[i].insert(i); p_s = f.RevSparseJac(m, s_s); jac = f.SparseJacobian(x, p_s); for(ell = 0; ell < size_t(check.size()); ell++) ok &= NearEqual(check[ell], jac[ell], eps, eps ); // using row and column indices to compute non-zero in rows 1 and 2 // (skip row 0). size_t K = 6; i_vector row(K), col(K); jac.resize(K); k = 0; for(j = 0; j < n; j++) { for(i = 1; i < m; i++) { ell = i * n + j; if( p_b[ell] ) { ok &= check[ell] != 0.; row[k] = i; col[k] = j; k++; } } } ok &= k == K; // empty work structure CppAD::sparse_jacobian_work work; // could use p_b size_t n_sweep = f.SparseJacobianReverse(x, p_s, row, col, jac, work); for(k = 0; k < K; k++) { ell = row[k] * n + col[k]; ok &= NearEqual(check[ell], jac[k], eps, eps); } ok &= n_sweep == 2; // now recompute at a different x value (using work from previous call) check[11] = x[3] = 10.; std::vector< std::set > not_used; n_sweep = f.SparseJacobianReverse(x, not_used, row, col, jac, work); for(k = 0; k < K; k++) { ell = row[k] * n + col[k]; ok &= NearEqual(check[ell], jac[k], eps, eps); } ok &= n_sweep == 2; return ok; } bool forward() { bool ok = true; using CppAD::AD; using CppAD::NearEqual; typedef CPPAD_TESTVECTOR(AD) a_vector; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR(size_t) i_vector; size_t i, j, k, ell; double eps = 10. * CppAD::numeric_limits::epsilon(); // domain space vector size_t n = 3; a_vector a_x(n); for(j = 0; j < n; j++) a_x[j] = AD (0); // declare independent variables and starting recording CppAD::Independent(a_x); size_t m = 4; a_vector a_y(m); a_y[0] = a_x[0] + a_x[2]; a_y[1] = a_x[0] + a_x[2]; a_y[2] = a_x[1] + a_x[2]; a_y[3] = a_x[1] + a_x[2] * a_x[2] / 2.; // create f: x -> y and stop tape recording CppAD::ADFun f(a_x, a_y); // new value for the independent variable vector d_vector x(n); for(j = 0; j < n; j++) x[j] = double(j); // Jacobian of y without sparsity pattern d_vector jac(m * n); jac = f.SparseJacobian(x); /* [ 1 0 1 ] jac = [ 1 0 1 ] [ 0 1 1 ] [ 0 1 x_2 ] */ d_vector check(m * n); check[0] = 1.; check[1] = 0.; check[2] = 1.; check[3] = 1.; check[4] = 0.; check[5] = 1.; check[6] = 0.; check[7] = 1.; check[8] = 1.; check[9] = 0.; check[10] = 1.; check[11] = x[2]; for(ell = 0; ell < size_t(check.size()); ell++) ok &= NearEqual(check[ell], jac[ell], eps, eps ); // test using packed boolean vectors for sparsity pattern CppAD::vectorBool r_b(n * n), p_b(m * n); for(j = 0; j < n; j++) { for(ell = 0; ell < n; ell++) r_b[j * n + ell] = false; r_b[j * n + j] = true; } p_b = f.ForSparseJac(n, r_b); jac = f.SparseJacobian(x, p_b); for(ell = 0; ell < size_t(check.size()); ell++) ok &= NearEqual(check[ell], jac[ell], eps, eps ); // test using vector of sets for sparsity pattern std::vector< std::set > r_s(n), p_s(m); for(j = 0; j < n; j++) r_s[j].insert(j); p_s = f.ForSparseJac(n, r_s); jac = f.SparseJacobian(x, p_s); for(ell = 0; ell < size_t(check.size()); ell++) ok &= NearEqual(check[ell], jac[ell], eps, eps ); // using row and column indices to compute non-zero elements excluding // row 0 and column 0. size_t K = 5; i_vector row(K), col(K); jac.resize(K); k = 0; for(i = 1; i < m; i++) { for(j = 1; j < n; j++) { ell = i * n + j; if( p_b[ell] ) { ok &= check[ell] != 0.; row[k] = i; col[k] = j; k++; } } } ok &= k == K; // empty work structure CppAD::sparse_jacobian_work work; // could use p_s size_t n_sweep = f.SparseJacobianForward(x, p_b, row, col, jac, work); for(k = 0; k < K; k++) { ell = row[k] * n + col[k]; ok &= NearEqual(check[ell], jac[k], eps, eps); } ok &= n_sweep == 2; // now recompute at a different x value (using work from previous call) check[11] = x[2] = 10.; n_sweep = f.SparseJacobianForward(x, p_s, row, col, jac, work); for(k = 0; k < K; k++) { ell = row[k] * n + col[k]; ok &= NearEqual(check[ell], jac[k], eps, eps); } ok &= n_sweep == 2; return ok; } } // End empty namespace bool sparse_jacobian(void) { bool ok = true; ok &= forward(); ok &= reverse(); return ok; } // END C++ ================================================ FILE: example/sparse/sparse_sub_hes.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sparse_sub_hes.cpp} Subset of a Sparse Hessian: Example and Test ############################################ Purpose ******* This example uses a :ref:`sparse_hessian@p@Column Subset` of the sparsity pattern to compute a subset of the Hessian. See Also ******** :ref:`sub_sparse_hes.cpp-name` {xrst_literal // BEGIN C++ // END C++ } {xrst_end sparse_sub_hes.cpp} */ // BEGIN C++ # include bool sparse_sub_hes(void) { bool ok = true; using CppAD::AD; typedef CPPAD_TESTVECTOR(size_t) SizeVector; typedef CPPAD_TESTVECTOR(double) DoubleVector; typedef CppAD::sparse_rc sparsity; // // domain space vector size_t n = 4; CPPAD_TESTVECTOR(AD) ax(n); for(size_t j = 0; j < n; j++) ax[j] = double(j); // declare independent variables and start recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = 0.0; for(size_t j = 0; j < n; j++) ay[0] += double(j+1) * ax[0] * ax[j]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // sparsity pattern for the identity matrix size_t nr = n; size_t nc = n; size_t nnz_in = n; sparsity pattern_in(nr, nc, nnz_in); for(size_t k = 0; k < nnz_in; k++) { size_t r = k; size_t c = k; pattern_in.set(k, r, c); } // compute sparsity pattern for J(x) = f'(x) bool transpose = false; bool dependency = false; bool internal_bool = false; sparsity pattern_out; f.for_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_out ); // // compute sparsity pattern for H(x) = f''(x) CPPAD_TESTVECTOR(bool) select_range(m); select_range[0] = true; CppAD::sparse_hes_work work; f.rev_hes_sparsity( select_range, transpose, internal_bool, pattern_out ); size_t nnz = pattern_out.nnz(); ok &= nnz == 7; ok &= pattern_out.nr() == n; ok &= pattern_out.nc() == n; { // check results const SizeVector& row( pattern_out.row() ); const SizeVector& col( pattern_out.col() ); SizeVector row_major = pattern_out.row_major(); // ok &= row[ row_major[0] ] == 0 && col[ row_major[0] ] == 0; ok &= row[ row_major[1] ] == 0 && col[ row_major[1] ] == 1; ok &= row[ row_major[2] ] == 0 && col[ row_major[2] ] == 2; ok &= row[ row_major[3] ] == 0 && col[ row_major[3] ] == 3; // ok &= row[ row_major[4] ] == 1 && col[ row_major[4] ] == 0; ok &= row[ row_major[5] ] == 2 && col[ row_major[5] ] == 0; ok &= row[ row_major[6] ] == 3 && col[ row_major[6] ] == 0; } // // Only interested in cross-terms. Since we are not computing rwo 0, // we do not need sparsity entries in row 0. CppAD::sparse_rc subset_pattern(n, n, 3); for(size_t k = 0; k < 3; k++) subset_pattern.set(k, k+1, 0); CppAD::sparse_rcv subset( subset_pattern ); // // argument and weight values for computation CPPAD_TESTVECTOR(double) x(n), w(m); for(size_t j = 0; j < n; j++) x[j] = double(n) / double(j+1); w[0] = 1.0; // std::string coloring = "cppad.general"; size_t n_sweep = f.sparse_hes( x, w, subset, subset_pattern, coloring, work ); ok &= n_sweep == 1; for(size_t k = 0; k < 3; k++) { size_t i = k + 1; ok &= subset.val()[k] == double(i + 1); } // // convert subset from lower triangular to upper triangular for(size_t k = 0; k < 3; k++) subset_pattern.set(k, 0, k+1); subset = CppAD::sparse_rcv( subset_pattern ); // // This will require more work because the Hessian is computed // column by column (not row by row). work.clear(); n_sweep = f.sparse_hes( x, w, subset, subset_pattern, coloring, work ); ok &= n_sweep == 3; // // but it will get the right answer for(size_t k = 0; k < 3; k++) { size_t i = k + 1; ok &= subset.val()[k] == double(i + 1); } return ok; } // END C++ ================================================ FILE: example/sparse/sparsity_sub.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sparsity_sub.cpp} Sparsity Patterns For a Subset of Variables: Example and Test ############################################################# See Also ******** :ref:`sparse_sub_hes.cpp-name` , :ref:`sub_sparse_hes.cpp-name` . ForSparseJac ************ The routine :ref:`ForSparseJac-name` is used to compute the sparsity for both the full Jacobian (see *s* ) and a subset of the Jacobian (see *s2* ). RevSparseHes ************ The routine :ref:`RevSparseHes-name` is used to compute both sparsity for both the full Hessian (see *h* ) and a subset of the Hessian (see *h2* ). {xrst_literal // BEGIN C++ // END C++ } {xrst_end sparsity_sub.cpp} */ // BEGIN C++ # include bool sparsity_sub(void) { // C++ source code bool ok = true; // using std::cout; using CppAD::vector; using CppAD::AD; using CppAD::vectorBool; size_t n = 4; size_t m = n-1; vector< AD > ax(n), ay(m); for(size_t j = 0; j < n; j++) ax[j] = double(j+1); CppAD::Independent(ax); for(size_t i = 0; i < m; i++) ay[i] = (ax[i+1] - ax[i]) * (ax[i+1] - ax[i]); CppAD::ADFun f(ax, ay); // Evaluate the full Jacobian sparsity pattern for f vectorBool r(n * n), s(m * n); for(size_t j = 0 ; j < n; j++) { for(size_t i = 0; i < n; i++) r[i * n + j] = (i == j); } s = f.ForSparseJac(n, r); // evaluate the sparsity for the Hessian of f_0 + ... + f_{m-1} vectorBool t(m), h(n * n); for(size_t i = 0; i < m; i++) t[i] = true; h = f.RevSparseHes(n, t); // evaluate the Jacobian sparsity pattern for first n/2 components of x size_t n2 = n / 2; vectorBool r2(n * n2), s2(m * n2); for(size_t j = 0 ; j < n2; j++) { for(size_t i = 0; i < n; i++) r2[i * n2 + j] = (i == j); } s2 = f.ForSparseJac(n2, r2); // evaluate the sparsity for the subset of Hessian of // f_0 + ... + f_{m-1} where first partial has only first n/2 components vectorBool h2(n2 * n); h2 = f.RevSparseHes(n2, t); // check sparsity pattern for Jacobian for(size_t i = 0; i < m; i++) { for(size_t j = 0; j < n2; j++) ok &= s2[i * n2 + j] == s[i * n + j]; } // check sparsity pattern for Hessian for(size_t i = 0; i < n2; i++) { for(size_t j = 0; j < n; j++) ok &= h2[i * n + j] == h[i * n + j]; } return ok; } // END C++ ================================================ FILE: example/sparse/sub_sparse_hes.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sub_sparse_hes.cpp} {xrst_spell nv } Computing Sparse Hessian for a Subset of Variables ################################################## Purpose ******* This example uses :ref:`multiple levels of AD` to compute the Hessian for a subset of the variables without having to compute the sparsity pattern for the entire function. See Also ******** :ref:`sparse_sub_hes.cpp-name` , :ref:`sparsity_sub.cpp-name` , Function ******** We consider the function :math:`f : \B{R}^{nu} \times \B{R}^{nv} \rightarrow \B{R}` defined by .. math:: f (u, v) = \left( \sum_{j=0}^{nu-1} u_j^3 \right) \left( \sum_{j=0}^{nv-1} v_j \right) Subset ****** Suppose that we are only interested computing the function .. math:: H(u, v) = \partial_u \partial_u f (u, v) where this Hessian is sparse. Example ******* The following code shows one way to compute this subset of the Hessian of :math:`f`. {xrst_literal // BEGIN C++ // END C++ } {xrst_end sub_sparse_hes.cpp} */ // BEGIN C++ # include namespace { using CppAD::vector; template Scalar f(const vector& u,const vector& v) { size_t i; Scalar sum_v = Scalar(0); for(i = 0; i < v.size(); i++) sum_v += v[i]; Scalar sum_cube_u = Scalar(0); for(i = 0; i < u.size(); i++) sum_cube_u += u[i] * u[i] * u[i] / 6.0; return sum_v * sum_cube_u; } } bool sub_sparse_hes(void) { bool ok = true; using CppAD::AD; typedef AD adouble; typedef AD a2double; typedef vector< std::set > pattern; double eps = 10. * std::numeric_limits::epsilon(); size_t i, j; // start recording with x = (u , v) size_t nu = 10; size_t nv = 5; size_t n = nu + nv; vector ax(n); for(j = 0; j < n; j++) ax[j] = adouble(j + 2); CppAD::Independent(ax); // extract u as independent variables vector a2u(nu); for(j = 0; j < nu; j++) a2u[j] = a2double(j + 2); CppAD::Independent(a2u); // extract v as parameters vector a2v(nv); for(j = 0; j < nv; j++) a2v[j] = ax[nu+j]; // record g(u) vector a2y(1); a2y[0] = f(a2u, a2v); CppAD::ADFun g; g.Dependent(a2u, a2y); // compue sparsity pattern for Hessian of g(u) pattern r(nu), s(1); for(j = 0; j < nu; j++) r[j].insert(j); g.ForSparseJac(nu, r); s[0].insert(0); pattern p = g.RevSparseHes(nu, s); // Row and column indices for non-zeros in lower triangle of Hessian vector row, col; for(i = 0; i < nu; i++) { std::set::const_iterator itr; for(itr = p[i].begin(); itr != p[i].end(); itr++) { j = *itr; if( j <= i ) { row.push_back(i); col.push_back(j); } } } size_t K = row.size(); CppAD::sparse_hessian_work work; vector au(nu), ahes(K), aw(1); aw[0] = 1.0; for(j = 0; j < nu; j++) au[j] = ax[j]; size_t n_sweep = g.SparseHessian(au, aw, p, row, col, ahes, work); // The Hessian w.r.t u is diagonal ok &= n_sweep == 1; // record H(u, v) = Hessian of f w.r.t u CppAD::ADFun H(ax, ahes); // remove unnecessary operations H.optimize(); // Now evaluate the Hessian at a particular value for u, v vector u(nu), v(nv), x(n); for(j = 0; j < n; j++) x[j] = double(j + 2); vector hes = H.Forward(0, x); // Now check the Hessian double sum_v = 0.0; for(j = 0; j < nv; j++) sum_v += x[nu + j]; for(size_t k = 0; k < K; k++) { i = row[k]; j = col[k]; ok &= i == j; double check = sum_v * x[i]; ok &= CppAD::NearEqual(hes[k], check, eps, eps); } return ok; } // END C++ ================================================ FILE: example/sparse/subgraph_hes2jac.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin subgraph_hes2jac.cpp} {xrst_spell subgraphs } Sparse Hessian Using Subgraphs and Jacobian: Example and Test ############################################################# {xrst_literal // BEGIN C++ // END C++ } {xrst_end subgraph_hes2jac.cpp} */ // BEGIN C++ # include bool subgraph_hes2jac(void) { bool ok = true; using CppAD::NearEqual; typedef CppAD::AD a_double; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR(a_double) a_vector; typedef CPPAD_TESTVECTOR(size_t) s_vector; typedef CPPAD_TESTVECTOR(bool) b_vector; typedef CppAD::sparse_rcv sparse_matrix; // double eps = 10. * CppAD::numeric_limits::epsilon(); // // double version of x size_t n = 12; d_vector x(n); for(size_t j = 0; j < n; j++) x[j] = double(j + 2); // // a_double version of x a_vector ax(n); for(size_t j = 0; j < n; j++) ax[j] = x[j]; // // declare independent variables and starting recording CppAD::Independent(ax); // // a_double version of y = f(x) = 5 * x0 * x1 + sum_j xj^3 size_t m = 1; a_vector ay(m); ay[0] = 5.0 * ax[0] * ax[1]; for(size_t j = 0; j < n; j++) ay[0] += ax[j] * ax[j] * ax[j]; // // create double version of f: x -> y and stop tape recording // (without executing zero order forward calculation) CppAD::ADFun f; f.Dependent(ax, ay); // // Optimize this function to reduce future computations. // Perhaps only one optimization at the end would be faster. f.optimize(); // // create a_double version of f CppAD::ADFun af = f.base2ad(); // // declare independent variables and start recording g(x) = f'(x) Independent(ax); // // Use one reverse mode pass to compute z = f'(x) a_vector aw(m), az(n); aw[0] = 1.0; af.Forward(0, ax); az = af.Reverse(1, aw); // // create double version of g : x -> f'(x) CppAD::ADFun g; g.Dependent(ax, az); ok &= g.size_random() == 0; // // Optimize this function to reduce future computations. // Perhaps no optimization would be faster. g.optimize(); // // compute f''(x) = g'(x) b_vector select_domain(n), select_range(n); for(size_t j = 0; j < n; ++j) { select_domain[j] = true; select_range[j] = true; } sparse_matrix hessian; g.subgraph_jac_rev(select_domain, select_range, x, hessian); // ------------------------------------------------------------------- // check number of non-zeros in the Hessian // (only x0 * x1 generates off diagonal terms) ok &= hessian.nnz() == n + 2; // for(size_t k = 0; k < hessian.nnz(); ++k) { size_t r = hessian.row()[k]; size_t c = hessian.col()[k]; double v = hessian.val()[k]; // if( r == c ) { // a diagonal element double check = 6.0 * x[r]; ok &= NearEqual(v, check, eps, eps); } else { // off diagonal element ok &= (r == 0 && c == 1) || (r == 1 && c == 0); double check = 5.0; ok &= NearEqual(v, check, eps, eps); } } ok &= g.size_random() > 0; g.clear_subgraph(); ok &= g.size_random() == 0; return ok; } // END C++ ================================================ FILE: example/sparse/subgraph_jac_rev.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin subgraph_jac_rev.cpp} Computing Sparse Jacobian Using Reverse Mode: Example and Test ############################################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end subgraph_jac_rev.cpp} */ // BEGIN C++ # include bool subgraph_jac_rev(void) { bool ok = true; // using CppAD::AD; using CppAD::NearEqual; using CppAD::sparse_rc; using CppAD::sparse_rcv; // typedef CPPAD_TESTVECTOR(AD) a_vector; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR(size_t) s_vector; typedef CPPAD_TESTVECTOR(bool) b_vector; // // domain space vector size_t n = 4; a_vector a_x(n); for(size_t j = 0; j < n; j++) a_x[j] = AD (0); // // declare independent variables and starting recording CppAD::Independent(a_x); // size_t m = 3; a_vector a_y(m); a_y[0] = a_x[0] + a_x[1]; a_y[1] = a_x[2] + a_x[3]; a_y[2] = a_x[0] + a_x[1] + a_x[2] + a_x[3] * a_x[3] / 2.; // // create f: x -> y and stop tape recording CppAD::ADFun f(a_x, a_y); ok &= f.size_random() == 0; // // new value for the independent variable vector d_vector x(n); for(size_t j = 0; j < n; j++) x[j] = double(j); /* [ 1 1 0 0 ] J(x) = [ 0 0 1 1 ] [ 1 1 1 x_3] */ // // row-major order values of J(x) size_t nnz = 8; s_vector check_row(nnz), check_col(nnz); d_vector check_val(nnz); for(size_t k = 0; k < nnz; k++) { // check_val if( k < 7 ) check_val[k] = 1.0; else check_val[k] = x[3]; // // check_row and check_col check_col[k] = k; if( k < 2 ) check_row[k] = 0; else if( k < 4 ) check_row[k] = 1; else { check_row[k] = 2; check_col[k] = k - 4; } } // // select all range components of domain and range b_vector select_domain(n), select_range(m); for(size_t j = 0; j < n; ++j) select_domain[j] = true; for(size_t i = 0; i < m; ++i) select_range[i] = true; // ----------------------------------------------------------------------- // Compute Jacobian using f.subgraph_jac_rev(x, subset) // ----------------------------------------------------------------------- // // get sparsity pattern bool transpose = false; sparse_rc pattern_jac; f.subgraph_sparsity( select_domain, select_range, transpose, pattern_jac ); // f.subgraph_jac_rev(x, subset) sparse_rcv subset( pattern_jac ); f.subgraph_jac_rev(x, subset); // // check result ok &= subset.nnz() == nnz; s_vector row_major = subset.row_major(); for(size_t k = 0; k < nnz; k++) { ok &= subset.row()[ row_major[k] ] == check_row[k]; ok &= subset.col()[ row_major[k] ] == check_col[k]; ok &= subset.val()[ row_major[k] ] == check_val[k]; } // ----------------------------------------------------------------------- // f.subgraph_jac_rev(select_domain, select_range, x, matrix_out) // ----------------------------------------------------------------------- sparse_rcv matrix_out; f.subgraph_jac_rev(select_domain, select_range, x, matrix_out); // // check result ok &= matrix_out.nnz() == nnz; row_major = matrix_out.row_major(); for(size_t k = 0; k < nnz; k++) { ok &= matrix_out.row()[ row_major[k] ] == check_row[k]; ok &= matrix_out.col()[ row_major[k] ] == check_col[k]; ok &= matrix_out.val()[ row_major[k] ] == check_val[k]; } // ok &= f.size_random() > 0; f.clear_subgraph(); ok &= f.size_random() == 0; return ok; } // END C++ ================================================ FILE: example/sparse/subgraph_reverse.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin subgraph_reverse.cpp} {xrst_spell subgraphs } Computing Reverse Mode on Subgraphs: Example and Test ##################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end subgraph_reverse.cpp} */ // BEGIN C++ # include bool subgraph_reverse(void) { bool ok = true; // using CppAD::AD; using CppAD::NearEqual; using CppAD::sparse_rc; using CppAD::sparse_rcv; // typedef CPPAD_TESTVECTOR(AD) a_vector; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR(bool) b_vector; typedef CPPAD_TESTVECTOR(size_t) s_vector; // double eps99 = 99.0 * std::numeric_limits::epsilon(); // // domain space vector size_t n = 4; a_vector a_x(n); for(size_t j = 0; j < n; j++) a_x[j] = AD (0); // // declare independent variables and starting recording CppAD::Independent(a_x); // size_t m = 3; a_vector a_y(m); a_y[0] = a_x[0] + a_x[1]; a_y[1] = a_x[2] + a_x[3]; a_y[2] = a_x[0] + a_x[1] + a_x[2] + a_x[3] * a_x[3] / 2.; // // create f: x -> y and stop tape recording CppAD::ADFun f(a_x, a_y); ok &= f.size_random() == 0; // // new value for the independent variable vector d_vector x(n); for(size_t j = 0; j < n; j++) x[j] = double(j); f.Forward(0, x); /* [ 1 1 0 0 ] J(x) = [ 0 0 1 1 ] [ 1 1 1 x_3] */ double J[] = { 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0 }; J[11] = x[3]; // // exclude x[0] from the calculations b_vector select_domain(n); select_domain[0] = false; for(size_t j = 1; j < n; j++) select_domain[j] = true; // // initialize for reverse mode derivatives computation on subgraphs f.subgraph_reverse(select_domain); // // compute the derivative for each range component for(size_t i = 0; i < m; i++) { d_vector dw; s_vector col; size_t q = 1; // derivative of one Taylor coefficient (zero order) f.subgraph_reverse(q, i, col, dw); // // check order in col for(size_t c = 1; c < size_t( col.size() ); c++) ok &= col[c] > col[c-1]; // // check that x[0] has been excluded by select_domain if( size_t( col.size() ) > 0 ) ok &= col[0] != 0; // // check derivatives for i-th row of J(x) // note that dw is only specified for j in col size_t c = 0; for(size_t j = 1; j < n; j++) { while( c < size_t( col.size() ) && col[c] < j ) ++c; if( c < size_t( col.size() ) && col[c] == j ) ok &= NearEqual(dw[j], J[i * n + j], eps99, eps99); else ok &= NearEqual(0.0, J[i * n + j], eps99, eps99); } } ok &= f.size_random() > 0; f.clear_subgraph(); ok &= f.size_random() == 0; return ok; } // END C++ ================================================ FILE: example/sparse/subgraph_sparsity.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin subgraph_sparsity.cpp} Subgraph Dependency Sparsity Patterns: Example and Test ####################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end subgraph_sparsity.cpp} */ // BEGIN C++ # include bool subgraph_sparsity(void) { bool ok = true; using CppAD::AD; typedef CPPAD_TESTVECTOR(size_t) SizeVector; typedef CppAD::sparse_rc sparsity; // // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.; ax[1] = 1.; // declare independent variables and start recording CppAD::Independent(ax); // range space vector size_t m = 3; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = ax[0]; ay[1] = ax[0] * ax[1]; ay[2] = ax[1]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); ok &= f.size_random() == 0; // compute sparsite pattern for F'(x) CPPAD_TESTVECTOR(bool) select_domain(n), select_range(m); for(size_t j = 0; j < n; j++) select_domain[j] = true; for(size_t i = 0; i < m; i++) select_range[i] = true; bool transpose = false; sparsity pattern_out; f.subgraph_sparsity(select_domain, select_range, transpose, pattern_out); // check sparsity pattern size_t nnz = pattern_out.nnz(); ok &= nnz == 4; ok &= pattern_out.nr() == m; ok &= pattern_out.nc() == n; { // check results const SizeVector& row( pattern_out.row() ); const SizeVector& col( pattern_out.col() ); SizeVector col_major = pattern_out.col_major(); // ok &= row[ col_major[0] ] == 0 && col[ col_major[0] ] == 0; ok &= row[ col_major[1] ] == 1 && col[ col_major[1] ] == 0; ok &= row[ col_major[2] ] == 1 && col[ col_major[2] ] == 1; ok &= row[ col_major[3] ] == 2 && col[ col_major[3] ] == 1; } // note that the transpose of the identity is the identity transpose = true; f.subgraph_sparsity(select_domain, select_range, transpose, pattern_out); // nnz = pattern_out.nnz(); ok &= nnz == 4; ok &= pattern_out.nr() == n; ok &= pattern_out.nc() == m; { // check results const SizeVector& row( pattern_out.row() ); const SizeVector& col( pattern_out.col() ); SizeVector row_major = pattern_out.row_major(); // ok &= col[ row_major[0] ] == 0 && row[ row_major[0] ] == 0; ok &= col[ row_major[1] ] == 1 && row[ row_major[1] ] == 0; ok &= col[ row_major[2] ] == 1 && row[ row_major[2] ] == 1; ok &= col[ row_major[3] ] == 2 && row[ row_major[3] ] == 1; } ok &= f.size_random() > 0; f.clear_subgraph(); ok &= f.size_random() == 0; return ok; } // END C++ ================================================ FILE: example/utility/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # IF( use_cplusplus_2017_ok ) SET(dll_lib "dll_lib.cpp") ELSE( use_cplusplus_2017_ok ) SET(dll_lib "") ENDIF( use_cplusplus_2017_ok ) # # BEGIN_SORT_THIS_LINE_PLUS_3 SET(source_list ${dll_lib} check_numeric_type.cpp check_simple_vector.cpp cppad_vector.cpp error_handler.cpp index_sort.cpp lu_factor.cpp lu_invert.cpp lu_solve.cpp nan.cpp near_equal.cpp ode_err_control.cpp ode_err_maxabs.cpp ode_gear.cpp ode_gear_control.cpp poly.cpp pow_int.cpp romberg_mul.cpp romberg_one.cpp rosen_34.cpp runge45_1.cpp runge_45.cpp set_union.cpp simple_vector.cpp sparse_rc.cpp sparse_rcv.cpp thread_alloc.cpp to_string.cpp utility.cpp vector_bool.cpp ) # END_SORT_THIS_LINE_MINUS_2 set_compile_flags( example_utility "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(example_utility EXCLUDE_FROM_ALL ${source_list}) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_utility ${cppad_lib} ${colpack_libs} ) # # check_example_utility add_check_executable(check_example utility) ================================================ FILE: example/utility/check_numeric_type.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin check_numeric_type.cpp} The CheckNumericType Function: Example and Test ############################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end check_numeric_type.cpp} */ // BEGIN C++ # include # include // Choosing a value between 1 and 10 selects a numeric class property to be // omitted and result in an error message being generated # define CppADMyTypeOmit 0 namespace { // Empty namespace // ------------------------------------------------------------------- class MyType { private: double d; public: // constructor from void MyType(void) : d(0.) { } // constructor from an int MyType(int d_) : d(d_) { } // copy constructor MyType(const MyType &x) { d = x.d; } // assignment operator void operator = (const MyType &x) { d = x.d; } // member function that converts to double double Double(void) const { return d; } # if CppADMyTypeOmit != 1 // unary plus MyType operator + (void) const { MyType x; x.d = d; return x; } # endif # if CppADMyTypeOmit != 2 // unary plus MyType operator - (void) const { MyType x; x.d = - d; return x; } # endif # if CppADMyTypeOmit != 3 // binary addition MyType operator + (const MyType &x) const { MyType y; y.d = d + x.d ; return y; } # endif # if CppADMyTypeOmit != 4 // binary subtraction MyType operator - (const MyType &x) const { MyType y; y.d = d - x.d ; return y; } # endif # if CppADMyTypeOmit != 5 // binary multiplication MyType operator * (const MyType &x) const { MyType y; y.d = d * x.d ; return y; } # endif # if CppADMyTypeOmit != 6 // binary division MyType operator / (const MyType &x) const { MyType y; y.d = d / x.d ; return y; } # endif # if CppADMyTypeOmit != 7 // compound assignment addition void operator += (const MyType &x) { d += x.d; } # endif # if CppADMyTypeOmit != 8 // compound assignment subtraction void operator -= (const MyType &x) { d -= x.d; } # endif # if CppADMyTypeOmit != 9 // compound assignment multiplication void operator *= (const MyType &x) { d *= x.d; } # endif # if CppADMyTypeOmit != 10 // compound assignment division void operator /= (const MyType &x) { d /= x.d; } # endif }; // ------------------------------------------------------------------- /* Solve: A[0] * x[0] + A[1] * x[1] = b[0] A[2] * x[0] + A[3] * x[1] = b[1] */ template void Solve(NumericType *A, NumericType *x, NumericType *b) { // make sure NumericType satisfies its conditions CppAD::CheckNumericType(); // copy b to x x[0] = b[0]; x[1] = b[1]; // copy A to work space NumericType W[4]; W[0] = A[0]; W[1] = A[1]; W[2] = A[2]; W[3] = A[3]; // divide first row by W(1,1) W[1] /= W[0]; x[0] /= W[0]; W[0] = NumericType(1); // subtract W(2,1) times first row from second row W[3] -= W[2] * W[1]; x[1] -= W[2] * x[0]; W[2] = NumericType(0); // divide second row by W(2, 2) x[1] /= W[3]; W[3] = NumericType(1); // use first row to solve for x[0] x[0] -= W[1] * x[1]; } } // End Empty namespace bool CheckNumericType(void) { bool ok = true; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); MyType A[4]; A[0] = MyType(1); A[1] = MyType(2); A[2] = MyType(3); A[3] = MyType(4); MyType b[2]; b[0] = MyType(1); b[1] = MyType(2); MyType x[2]; Solve(A, x, b); MyType sum; sum = A[0] * x[0] + A[1] * x[1]; ok &= NearEqual(sum.Double(), b[0].Double(), eps99, eps99); sum = A[2] * x[0] + A[3] * x[1]; ok &= NearEqual(sum.Double(), b[1].Double(), eps99, eps99); return ok; } // END C++ ================================================ FILE: example/utility/check_simple_vector.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin check_simple_vector.cpp} The CheckSimpleVector Function: Example and Test ################################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end check_simple_vector.cpp} */ // BEGIN C++ # include # include # include // Choosing a value between 1 and 9 selects a simple vector property to be // omitted and result in an error message being generated # define CppADMyVectorOmit 0 // ------------------------------------------------------------------------- // example class used for non-constant elements (different from Scalar) template class MyElement { private: Scalar *element; public: // element constructor MyElement(Scalar *e) { element = e; } // an example element assignment that returns void void operator = (const Scalar &s) { *element = s; } // conversion to Scalar operator Scalar() const { return *element; } }; // example simple vector class template class MyVector { private: size_t length; Scalar * data; public: # if CppADMyVectorOmit != 1 // type of the elements in the vector typedef Scalar value_type; # endif # if CppADMyVectorOmit != 2 // default constructor MyVector(void) : length(0) , data(0) { } # endif # if CppADMyVectorOmit != 3 // constructor with a specified size MyVector(size_t n) : length(n) { if( length == 0 ) data = 0; else data = new Scalar[length]; } # endif # if CppADMyVectorOmit != 4 // copy constructor MyVector(const MyVector &x) : length(x.length) { size_t i; if( length == 0 ) data = 0; else data = new Scalar[length]; for(i = 0; i < length; i++) data[i] = x.data[i]; } # endif # if CppADMyVectorOmit != 4 # if CppADMyVectorOmit != 7 // destructor (it is not safe to delete the pointer in cases 4 and 7) ~MyVector(void) { delete [] data; } # endif # endif # if CppADMyVectorOmit != 5 // size function size_t size(void) const { return length; } # endif # if CppADMyVectorOmit != 6 // resize function void resize(size_t n) { if( length > 0 ) delete [] data; length = n; if( length > 0 ) data = new Scalar[length]; else data = 0; } # endif # if CppADMyVectorOmit != 7 // assignment operator MyVector & operator=(const MyVector &x) { size_t i; for(i = 0; i < length; i++) data[i] = x.data[i]; return *this; } # endif # if CppADMyVectorOmit != 8 // non-constant element access MyElement operator[](size_t i) { return data + i; } # endif # if CppADMyVectorOmit != 9 // constant element access const Scalar & operator[](size_t i) const { return data[i]; } # endif }; // ------------------------------------------------------------------------- /* Compute r = a * v, where a is a scalar with same type as the elements of the Simple Vector v. This routine uses the CheckSimpleVector function to ensure that the types agree. */ namespace { // Empty namespace template Vector Sscal(const Scalar &a, const Vector &v) { // invoke CheckSimpleVector function CppAD::CheckSimpleVector(); size_t n = v.size(); Vector r(n); size_t i; for(i = 0; i < n; i++) r[i] = a * v[i]; return r; } } bool CheckSimpleVector(void) { bool ok = true; using CppAD::vector; // -------------------------------------------------------- // If you change double to float in the next statement, // CheckSimpleVector will generate an error message at compile time. double a = 3.; // -------------------------------------------------------- size_t n = 2; MyVector v(n); v[0] = 1.; v[1] = 2.; MyVector r = Sscal(a, v); ok &= (r[0] == 3.); ok &= (r[1] == 6.); return ok; } // END C++ ================================================ FILE: example/utility/cppad_vector.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppad_vector.cpp} CppAD::vector Template Class: Example and Test ############################################## Purpose ******* This is an example and test of the features of the :ref:`CppAD_vector-name` class that are not included in the :ref:`SimpleVector-name` concept. {xrst_literal // BEGIN C++ // END C++ } {xrst_end cppad_vector.cpp} */ // BEGIN C++ # include # include # include # include // sstream and string are used to test output operation # include # include namespace { void myhandler( bool known , int line , const char *file , const char *exp , const char *msg ) { // error handler must not return, so throw an exception throw std::string( file ); } } bool CppAD_vector(void) { bool ok = true; using CppAD::vector; // so can use vector instead of CppAD::vector typedef double Scalar; // change double to test other types // check Simple Vector specifications CppAD::CheckSimpleVector< Scalar, vector >(); // check constructor with size_t, with int, and with value size_t two_s = 2; int two_i = 2; Scalar value = 5.0; vector vec(2), other(two_s), another(two_i, value); ok &= another[0] == 5.0; ok &= another[1] == 5.0; // check resize with size_t and with int vec.resize(2); other.resize(two_s); another.resize(two_i); // assignment returns reference for use in other assignments another[0] = Scalar(1); another[1] = Scalar(2); vec = other = another; for(size_t i = 0; i < 2; ++i) { ok &= vec[i] == other[i]; ok &= vec[i] == another[i]; } // operator == ok &= vec == other; ok &= other == another; // initializer constructor vector yet_another = { 1.0, 3.0}; ok &= yet_another.size() == 2; ok &= yet_another[0] == 1.0; ok &= yet_another[1] == 3.0; // operator <=, >= ok &= vec <= yet_another; ok &= yet_another >= vec; // test of output std::string correct= "{ 1, 2 }"; std::string str; std::ostringstream buf; buf << vec; str = buf.str(); ok &= (str == correct); // swap other[0] = vec[0] + 1; vec.swap(other); ok &= vec[0] == other[0] + 1; // clear vec.clear(); ok &= vec.size() == 0; ok &= vec.capacity() == 0; // push_back scalar and changes in capacity size_t n = 100; size_t old_capacity = vec.capacity(); for(size_t i = 0; i < n; i++) { vec.push_back( Scalar(n - i) ); ok &= (i+1) == vec.size(); ok &= i < vec.capacity(); ok &= old_capacity == vec.capacity() || i == old_capacity; old_capacity = vec.capacity(); } for(size_t i = 0; i < n; i++) ok &= ( vec[i] == Scalar(n - i) ); // test of push_vector vec.push_vector(vec); ok &= (vec.size() == 2 * n); for(size_t i = 0; i < n; i++) { ok &= vec[i] == Scalar(n - i); ok &= vec[i + n] == Scalar(n - i); } // resize preserves elements when new size less than capacity ok &= n < vec.capacity(); vec.resize(n); for(size_t i = 0; i < n; i++) ok &= vec[i] == Scalar(n - i); // vector assignment OK no matter what target size was before assignment other[0] = vec[0] + 1; ok &= other.size() < vec.size(); other = vec; ok &= other.size() == vec.size(); for(size_t i = 0; i < vec.size(); i++) ok &= other[i] == vec[i]; // create a const vector equal to vec const vector cvec = vec; // sort of vec (will reverse order of elements for this case) std::sort(vec.begin(), vec.end()); for(size_t i = 0; i < n ; ++i) ok &= vec[i] == Scalar(i + 1); // use data pointer to sort using pointers instead of iterators std::sort(other.data(), other.data() + other.size()); for(size_t i = 0; i < n ; ++i) ok &= other[i] == Scalar(i + 1); // // test direct use of iterator and const_iterator typedef vector::iterator iterator; typedef vector::const_iterator const_iterator; iterator itr = vec.begin(); // increasing order const_iterator citr = cvec.end(); // decreasing order while( itr != vec.end() ) { --citr; ok &= *itr == *citr; ++itr; } // conversion from iterator to const_iterator citr = vec.begin(); ok &= *citr == vec[0]; // test use of [] operator with const_itr for(size_t i = 0; i < n; ++i) ok &= citr[i] == vec[i]; // test use of [] operator with iterator itr = vec.begin(); for(size_t i = 0; i < n; ++i) itr[i] = Scalar(i + 1); // Replace the default CppAD error handler with myhandler (defined above). // This replacement is in effect until info drops out of scope. CppAD::ErrorHandler info(myhandler); # ifndef NDEBUG // ----------------------------------------------------------------------- // check that iterator access out of range generates an error ok &= *itr == Scalar(1); // this access OK bool detected_error = false; try { vec.clear(); // The iterator knows that the vector has changed and that // this access is no longer valid *itr; } catch(const std::string& file) { // This location for the error is not part of user API and may change size_t pos = file.find("/cppad_vector_itr.hpp"); ok &= pos != std::string::npos; detected_error = true; } ok &= detected_error; // ----------------------------------------------------------------------- # endif return ok; } // END C++ ================================================ FILE: example/utility/dll_lib.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin dll_lib.cpp} {xrst_spell msvc } dll_lib: Example and Test ######################### options ******* The following subsection of this example sets :ref:`create_dll_lib@options` that are different from the default options: {xrst_literal // BEGIN_OPTIONS // END_OPTIONS } Restrictions ************ This example is only built and run under the following conditions: #. The :ref:`cmake-name` command determines that the C compiler is gcc, clang, or msvc. #. The :ref:`cmake-name` command determines it is OK to use C++17 features. #. The :ref:`cmake@cppad_link_flags` do not contain ``-m32`` . Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end dll_lib.cpp} */ // BEGIN C++ # include # include # include # include # include # include # if _WIN32 # define DIR_SEP '\\' # define DLL_EXT ".dll" # else # define DIR_SEP '/' # define DLL_EXT ".so" # endif // CALL_CONVENTION, CALL_IMPORT # ifdef _MSC_VER # define CALL_CONVENTION __cdecl # define CALL_IMPORT __declspec(dllimport) # else # define CALL_CONVENTION # define CALL_IMPORT # endif namespace { extern "C" typedef CALL_IMPORT int (CALL_CONVENTION *function_ptr)(int x, int y); } bool dll_lib(void) { bool ok = true; // // add_source std::string add_source = "int add(int x, int y)\n" "{ return x + y; }\n" ; // // dll_entry_source std::string dll_entry_source = "extern int add(int x, int y);" # ifdef _MSC_VER "__declspec(dllexport) int _cdecl dll_entry(int x, int y)\n" # else "int dll_entry(int x, int y)\n" # endif "{ return add(x, y);}" ; // // temp_dir std::string temp_dir = std::filesystem::temp_directory_path().string(); if( temp_dir.back() != DIR_SEP ) temp_dir += DIR_SEP; // // ofs std::ofstream ofs; // // add_file std::string add_file = temp_dir + "add.c"; ofs.open(add_file.c_str(), std::ofstream::out); ofs << add_source; ofs.close(); // // dll_entry_file std::string dll_entry_file = temp_dir + "dll_entry.c"; ofs.open(dll_entry_file.c_str(), std::ofstream::out); ofs << dll_entry_source; ofs.close(); // // BEGIN_OPTIONS // Example using options that are different from the default options std::map< std::string, std::string > options; # ifdef _MSC_VER options["compile"] = "cl /EHs /EHc /c /TC /O2"; # elif CPPAD_LINK_FLAGS_HAS_M32 options["compile"] = "gcc -c -fPIC -O2 -m32"; # else options["compile"] = "gcc -c -fPIC -O2"; # endif // END_OPTIONS // // dll_file std::vector< std::string > csrc_files(2); csrc_files[0] = add_file; csrc_files[1] = dll_entry_file; std::string dll_file = temp_dir + "dll_entry" + DLL_EXT; CppAD::create_dll_lib(dll_file, csrc_files, options); // // dll_linker std::string err_msg; CppAD::link_dll_lib dll_linker(dll_file, err_msg); if( err_msg != "" ) { std::cerr << "dll_lib error: " << err_msg << "\n"; return false; } // // dll_entry void* vptr = dll_linker("dll_entry", err_msg); if( err_msg != "" ) { std::cerr << "dll_lib error: " << err_msg << "\n"; return false; } function_ptr dll_entry = reinterpret_cast(vptr); // // z = dll_entry(x, y) int x = 1, y = 2; int z = dll_entry(x, y); // // ok ok &= z == (x + y); // return ok; } // END C++ ================================================ FILE: example/utility/error_handler.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin error_handler.cpp} Replacing The CppAD Error Handler: Example and Test ################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end error_handler.cpp} */ // BEGIN C++ # include # include namespace { void myhandler( bool known , int line , const char *file , const char *exp , const char *msg ) { // error handler must not return, so throw an exception throw line; } } bool ErrorHandler(void) { using CppAD::ErrorHandler; int lineMinusFive = 0; // replace the default CppAD error handler ErrorHandler info(myhandler); // set ok to false unless catch block is executed bool ok = false; // use try / catch because handler throws an exception try { // set the static variable Line to next source code line lineMinusFive = __LINE__; // can call myhandler anywhere that ErrorHandler is defined ErrorHandler::Call( true , // reason for the error is known __LINE__ , // current source code line number __FILE__ , // current source code file name "1 > 0" , // an intentional error condition "Testing ErrorHandler" // reason for error ); } catch ( int line ) { // check value of the line number that was passed to handler ok = (line == lineMinusFive + 5); } // info drops out of scope and the default CppAD error handler // is restored when this routine returns. return ok; } // END C++ ================================================ FILE: example/utility/index_sort.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin index_sort.cpp} Index Sort: Example and Test ############################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end index_sort.cpp} */ // BEGIN C++ # include # include # include # include namespace{ // class that uses < to compare a pair of size_t values class Key { public: size_t first_; size_t second_; // Key(void) { } // Key(size_t first, size_t second) : first_(first), second_(second) { } // bool operator<(const Key& other) const { if( first_ == other.first_ ) return second_ < other.second_; return first_ < other.first_; } }; template bool vector_case(void) { bool ok = true; size_t i, j; size_t first[] = { 4, 4, 3, 3, 2, 2, 1, 1}; size_t second[] = { 0, 1, 0, 1, 0, 1, 0, 1}; size_t size = sizeof(first) / sizeof(first[0]); KeyVector keys(size); for(i = 0; i < size; i++) keys[i] = Key(first[i], second[i]); SizeVector ind(size); CppAD::index_sort(keys, ind); // check that all the indices are different for(i = 0; i < size; i++) { for(j = 0; j < size; j++) ok &= (i == j) || (ind[i] != ind[j]); } // check for increasing order for(i = 0; i < size-1; i++) { if( first[ ind[i] ] == first[ ind[i+1] ] ) ok &= second[ ind[i] ] <= second[ ind[i+1] ]; else ok &= first[ ind[i] ] < first[ ind[i+1] ]; } return ok; } } bool index_sort(void) { bool ok = true; // some example simple vector template classes ok &= vector_case< std::vector, std::valarray >(); ok &= vector_case< std::valarray, CppAD::vector >(); ok &= vector_case< CppAD::vector, std::vector >(); return ok; } // END C++ ================================================ FILE: example/utility/lu_factor.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin lu_factor.cpp} LuFactor: Example and Test ########################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end lu_factor.cpp} */ // BEGIN C++ # include // for rand function # include // for CppAD::LuFactor # include // for CppAD::NearEqual # include // for CppAD::vector bool LuFactor(void) { bool ok = true; # ifndef _MSC_VER using std::rand; using std::srand; # endif using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); size_t n = 5; // number rows in A double rand_max = double(RAND_MAX); // maximum rand value double sum; // element of L * U double pij; // element of permuted A size_t i, j, k; // temporary indices // A is an n by n matrix CppAD::vector A(n*n), LU(n*n), L(n*n), U(n*n); // set A equal to an n by n random matrix for(i = 0; i < n; i++) for(j = 0; j < n; j++) A[i * n + j] = rand() / rand_max; // pivot vectors CppAD::vector ip(n); CppAD::vector jp(n); // factor the matrix A LU = A; CppAD::LuFactor(ip, jp, LU); // check that ip and jp are permutations of the indices 0, ... , n-1 for(i = 0; i < n; i++) { ok &= (ip[i] < n); ok &= (jp[i] < n); for(j = 0; j < n; j++) { if( i != j ) { ok &= (ip[i] != ip[j]); ok &= (jp[i] != jp[j]); } } } // Extract L from LU for(i = 0; i < n; i++) { // elements along and below the diagonal for(j = 0; j <= i; j++) L[i * n + j] = LU[ ip[i] * n + jp[j] ]; // elements above the diagonal for(j = i+1; j < n; j++) L[i * n + j] = 0.; } // Extract U from LU for(i = 0; i < n; i++) { // elements below the diagonal for(j = 0; j < i; j++) U[i * n + j] = 0.; // elements along the diagonal U[i * n + i] = 1.; // elements above the diagonal for(j = i+1; j < n; j++) U[i * n + j] = LU[ ip[i] * n + jp[j] ]; } // Compute L * U for(i = 0; i < n; i++) { for(j = 0; j < n; j++) { // compute element (i,j) entry in L * U sum = 0.; for(k = 0; k < n; k++) sum += L[i * n + k] * U[k * n + j]; // element (i,j) in permuted version of A pij = A[ ip[i] * n + jp[j] ]; // compare ok &= NearEqual(pij, sum, eps99, eps99); } } return ok; } // END C++ ================================================ FILE: example/utility/lu_invert.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin lu_invert.cpp} LuInvert: Example and Test ########################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end lu_invert.cpp} */ // BEGIN C++ # include // for rand function # include // for CppAD::LuInvert # include // for CppAD::NearEqual # include // for CppAD::vector bool LuInvert(void) { bool ok = true; # ifndef _MSC_VER using std::rand; using std::srand; # endif double eps200 = 200.0 * std::numeric_limits::epsilon(); size_t n = 7; // number rows in A size_t m = 3; // number columns in B double rand_max = double(RAND_MAX); // maximum rand value double sum; // element of L * U size_t i, j, k; // temporary indices // dimension matrices CppAD::vector A(n*n), X(n*m), B(n*m), LU(n*n), L(n*n), U(n*n); // seed the random number generator srand(123); // pivot vectors CppAD::vector ip(n); CppAD::vector jp(n); // set pivot vectors for(i = 0; i < n; i++) { ip[i] = (i + 2) % n; // ip = 2 , 3, ... , n-1, 0, 1 jp[i] = (n + 2 - i) % n; // jp = 2 , 1, n-1, n-2, ... , 3 } // chose L, a random lower triangular matrix for(i = 0; i < n; i++) { for(j = 0; j <= i; j++) L [i * n + j] = rand() / rand_max; for(j = i+1; j < n; j++) L [i * n + j] = 0.; } // chose U, a random upper triangular matrix with ones on diagonal for(i = 0; i < n; i++) { for(j = 0; j < i; j++) U [i * n + j] = 0.; U[ i * n + i ] = 1.; for(j = i+1; j < n; j++) U [i * n + j] = rand() / rand_max; } // chose X, a random matrix for(i = 0; i < n; i++) { for(k = 0; k < m; k++) X[i * m + k] = rand() / rand_max; } // set LU to a permuted combination of both L and U for(i = 0; i < n; i++) { for(j = 0; j <= i; j++) LU [ ip[i] * n + jp[j] ] = L[i * n + j]; for(j = i+1; j < n; j++) LU [ ip[i] * n + jp[j] ] = U[i * n + j]; } // set A to a permuted version of L * U for(i = 0; i < n; i++) { for(j = 0; j < n; j++) { // compute (i,j) entry in permuted matrix sum = 0.; for(k = 0; k < n; k++) sum += L[i * n + k] * U[k * n + j]; A[ ip[i] * n + jp[j] ] = sum; } } // set B to A * X for(i = 0; i < n; i++) { for(k = 0; k < m; k++) { // compute (i,k) entry of B sum = 0.; for(j = 0; j < n; j++) sum += A[i * n + j] * X[j * m + k]; B[i * m + k] = sum; } } // solve for X CppAD::LuInvert(ip, jp, LU, B); // check result for(i = 0; i < n; i++) { for(k = 0; k < m; k++) { ok &= CppAD::NearEqual( X[i * m + k], B[i * m + k], eps200, eps200 ); } } return ok; } // END C++ ================================================ FILE: example/utility/lu_solve.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin lu_solve.cpp} LuSolve With Complex Arguments: Example and Test ################################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end lu_solve.cpp} */ // BEGIN C++ # include // for CppAD::LuSolve # include // for CppAD::NearEqual # include // for CppAD::vector # include // for std::complex typedef std::complex Complex; // define the Complex type bool LuSolve(void) { bool ok = true; using namespace CppAD; size_t n = 3; // number rows in A and B size_t m = 2; // number columns in B, X and S // A is an n by n matrix, B, X, and S are n by m matrices CppAD::vector A(n * n), B(n * m), X(n * m) , S(n * m); Complex logdet; // log of determinant of A int signdet; // zero if A is singular Complex det; // determinant of A size_t i, j, k; // some temporary indices // set A equal to the n by n Hilbert Matrix for(i = 0; i < n; i++) for(j = 0; j < n; j++) A[i * n + j] = 1. / (double) (i + j + 1); // set S to the solution of the equation we will solve for(j = 0; j < n; j++) for(k = 0; k < m; k++) S[ j * m + k ] = Complex(double(j), double(j + k)); // set B = A * S size_t ik; Complex sum; for(k = 0; k < m; k++) { for(i = 0; i < n; i++) { sum = 0.; for(j = 0; j < n; j++) sum += A[i * n + j] * S[j * m + k]; B[i * m + k] = sum; } } // solve the equation A * X = B and compute determinant of A signdet = CppAD::LuSolve(n, m, A, B, X, logdet); det = Complex( signdet ) * exp( logdet ); double cond = 4.62963e-4; // condition number of A when n = 3 double determinant = 1. / 2160.; // determinant of A when n = 3 double delta = 1e-14 / cond; // accuracy expected in X // check determinant ok &= CppAD::NearEqual(det, determinant, delta, delta); // check solution for(ik = 0; ik < n * m; ik++) ok &= CppAD::NearEqual(X[ik], S[ik], delta, delta); return ok; } // END C++ ================================================ FILE: example/utility/nan.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin nan.cpp} nan: Example and Test ##################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end nan.cpp} */ // BEGIN C++ # include # include # include bool nan(void) { bool ok = true; // get a nan double double_zero = 0.; double double_nan = std::numeric_limits::quiet_NaN(); // create a simple vector with no nans std::vector v(2); v[0] = double_zero; v[1] = double_zero; // check that zero is not nan ok &= ! CppAD::isnan(double_zero); ok &= ! CppAD::hasnan(v); // check that nan is a nan v[1] = double_nan; ok &= CppAD::isnan(double_nan); ok &= CppAD::hasnan(v); // check that nan is not equal to itself ok &= (double_nan != double_nan); return ok; } // END C++ ================================================ FILE: example/utility/near_equal.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin near_equal.cpp} NearEqual Function: Example and Test #################################### File Name ********* This file is called ``near_equal.cpp`` instead of ``NearEqual.cpp`` to avoid a name conflict with ``../lib/NearEqual.cpp`` in the corresponding Microsoft project file. {xrst_literal // BEGIN C++ // END C++ } {xrst_end near_equal.cpp} */ // BEGIN C++ # include # include bool Near_Equal(void) { bool ok = true; typedef std::complex Complex; using CppAD::NearEqual; // double double x = 1.00000; double y = 1.00001; double a = .00003; double r = .00003; double zero = 0.; double inf = 1. / zero; double nan = 0. / zero; ok &= NearEqual(x, y, zero, a); ok &= NearEqual(x, y, r, zero); ok &= NearEqual(x, y, r, a); ok &= ! NearEqual(x, y, r / 10., a / 10.); ok &= ! NearEqual(inf, inf, r, a); ok &= ! NearEqual(-inf, -inf, r, a); ok &= ! NearEqual(nan, nan, r, a); // complex Complex X(x, x / 2.); Complex Y(y, y / 2.); Complex Inf(inf, zero); Complex Nan(zero, nan); ok &= NearEqual(X, Y, zero, a); ok &= NearEqual(X, Y, r, zero); ok &= NearEqual(X, Y, r, a); ok &= ! NearEqual(X, Y, r / 10., a / 10.); ok &= ! NearEqual(Inf, Inf, r, a); ok &= ! NearEqual(-Inf, -inf, r, a); ok &= ! NearEqual(Nan, Nan, r, a); return ok; } // END C++ ================================================ FILE: example/utility/ode_err_control.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ode_err_control.cpp} OdeErrControl: Example and Test ############################### Define :math:`X : \B{R} \rightarrow \B{R}^2` by .. math:: :nowrap: \begin{eqnarray} X_0 (0) & = & 1 \\ X_1 (0) & = & 0 \\ X_0^{(1)} (t) & = & - \alpha X_0 (t) \\ X_1^{(1)} (t) & = & 1 / X_0 (t) \end{eqnarray} It follows that .. math:: :nowrap: \begin{eqnarray} X_0 (t) & = & \exp ( - \alpha t ) \\ X_1 (t) & = & [ \exp( \alpha t ) - 1 ] / \alpha \end{eqnarray} This example tests OdeErrControl using the relations above. Nan *** Note that :math:`X_0 (t) > 0` for all :math:`t` and that the ODE goes through a singularity between :math:`X_0 (t) > 0` and :math:`X_0 (t) < 0`. If :math:`X_0 (t) < 0`, we return ``nan`` in order to inform ``OdeErrControl`` that its is taking to large a step. {xrst_literal // BEGIN C++ // END C++ } {xrst_end ode_err_control.cpp} */ // BEGIN C++ # include // for quiet_NaN # include // for size_t # include // for exp # include // CppAD::OdeErrControl # include // CppAD::NearEqual # include // CppAD::vector # include // CppAD::Runge45 namespace { // -------------------------------------------------------------- class Fun { private: const double alpha_; public: // constructor Fun(double alpha) : alpha_(alpha) { } // set f = x'(t) void Ode( const double &t, const CppAD::vector &x, CppAD::vector &f) { f[0] = - alpha_ * x[0]; f[1] = 1. / x[0]; // case where ODE does not make sense if( x[0] < 0. ) f[1] = std::numeric_limits::quiet_NaN(); } }; // -------------------------------------------------------------- class Method { private: Fun F; public: // constructor Method(double alpha) : F(alpha) { } void step( double ta, double tb, CppAD::vector &xa , CppAD::vector &xb , CppAD::vector &eb ) { xb = CppAD::Runge45(F, 1, ta, tb, xa, eb); } size_t order(void) { return 4; } }; } bool OdeErrControl(void) { bool ok = true; // initial return value double alpha = 10.; Method method(alpha); CppAD::vector xi(2); xi[0] = 1.; xi[1] = 0.; CppAD::vector eabs(2); eabs[0] = 1e-4; eabs[1] = 1e-4; // inputs double ti = 0.; double tf = 1.; double smin = 1e-4; double smax = 1.; double scur = 1.; double erel = 0.; // outputs CppAD::vector ef(2); CppAD::vector xf(2); CppAD::vector maxabs(2); size_t nstep; xf = OdeErrControl(method, ti, tf, xi, smin, smax, scur, eabs, erel, ef, maxabs, nstep); double x0 = exp(-alpha*tf); ok &= CppAD::NearEqual(x0, xf[0], 1e-4, 1e-4); ok &= CppAD::NearEqual(0., ef[0], 1e-4, 1e-4); double x1 = (exp(alpha*tf) - 1) / alpha; ok &= CppAD::NearEqual(x1, xf[1], 1e-4, 1e-4); ok &= CppAD::NearEqual(0., ef[1], 1e-4, 1e-4); return ok; } // END C++ ================================================ FILE: example/utility/ode_err_maxabs.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ode_err_maxabs.cpp} OdeErrControl: Example and Test Using Maxabs Argument ##################################################### Define :math:`X : \B{R} \rightarrow \B{R}^2` by .. math:: :nowrap: \begin{eqnarray} X_0 (t) & = & - \exp ( - w_0 t ) \\ X_1 (t) & = & \frac{w_0}{w_1 - w_0} [ \exp ( - w_0 t ) - \exp( - w_1 t )] \end{eqnarray} It follows that :math:`X_0 (0) = 1`, :math:`X_1 (0) = 0` and .. math:: :nowrap: \begin{eqnarray} X_0^{(1)} (t) & = & - w_0 X_0 (t) \\ X_1^{(1)} (t) & = & + w_0 X_0 (t) - w_1 X_1 (t) \end{eqnarray} Note that :math:`X_1 (0)` is zero an if :math:`w_0 t` is large, :math:`X_0 (t)` is near zero. This example tests OdeErrControl using the *maxabs* argument. {xrst_literal // BEGIN C++ // END C++ } {xrst_end ode_err_maxabs.cpp} */ // BEGIN C++ # include // for size_t # include // for exp # include // CppAD::OdeErrControl # include // CppAD::NearEqual # include // CppAD::vector # include // CppAD::Runge45 namespace { // -------------------------------------------------------------- class Fun { private: CppAD::vector w; public: // constructor Fun(const CppAD::vector &w_) : w(w_) { } // set f = x'(t) void Ode( const double &t, const CppAD::vector &x, CppAD::vector &f) { f[0] = - w[0] * x[0]; f[1] = + w[0] * x[0] - w[1] * x[1]; } }; // -------------------------------------------------------------- class Method { private: Fun F; public: // constructor Method(const CppAD::vector &w_) : F(w_) { } void step( double ta, double tb, CppAD::vector &xa , CppAD::vector &xb , CppAD::vector &eb ) { xb = CppAD::Runge45(F, 1, ta, tb, xa, eb); } size_t order(void) { return 4; } }; } bool OdeErrMaxabs(void) { bool ok = true; // initial return value CppAD::vector w(2); w[0] = 10.; w[1] = 1.; Method method(w); CppAD::vector xi(2); xi[0] = 1.; xi[1] = 0.; CppAD::vector eabs(2); eabs[0] = 0.; eabs[1] = 0.; CppAD::vector ef(2); CppAD::vector xf(2); CppAD::vector maxabs(2); double ti = 0.; double tf = 1.; double smin = .5; double smax = 1.; double scur = .5; double erel = 1e-4; bool accurate = false; while( ! accurate ) { xf = OdeErrControl(method, ti, tf, xi, smin, smax, scur, eabs, erel, ef, maxabs); accurate = true; size_t i; for(i = 0; i < 2; i++) accurate &= ef[i] <= erel * maxabs[i]; if( ! accurate ) smin = smin / 2; } double x0 = exp(-w[0]*tf); ok &= CppAD::NearEqual(x0, xf[0], erel, 0.); ok &= CppAD::NearEqual(0., ef[0], erel, erel); double x1 = w[0] * (exp(-w[0]*tf) - exp(-w[1]*tf))/(w[1] - w[0]); ok &= CppAD::NearEqual(x1, xf[1], erel, 0.); ok &= CppAD::NearEqual(0., ef[1], erel, erel); return ok; } // END C++ ================================================ FILE: example/utility/ode_gear.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ode_gear.cpp} {xrst_spell rclr } OdeGear: Example and Test ######################### Define :math:`x : \B{R} \rightarrow \B{R}^n` by .. math:: x_i (t) = t^{i+1} for :math:`i = 1 , \ldots , n-1`. It follows that .. math:: \begin{array}{rclr} x_i(0) & = & 0 & {\rm for \; all \;} i \\ x_i ' (t) & = & 1 & {\rm if \;} i = 0 \\ x_i '(t) & = & (i+1) t^i = (i+1) x_{i-1} (t) & {\rm if \;} i > 0 \end{array} The example tests OdeGear using the relations above: {xrst_literal // BEGIN C++ // END C++ } {xrst_end ode_gear.cpp} */ // BEGIN C++ # include # include // For automatic differentiation namespace { class Fun { public: // constructor Fun(bool use_x_) : use_x(use_x_) { } // compute f(t, x) both for double and AD template void Ode( const Scalar &t, const CPPAD_TESTVECTOR(Scalar) &x, CPPAD_TESTVECTOR(Scalar) &f) { size_t n = x.size(); Scalar ti(1); f[0] = Scalar(1); size_t i; for(i = 1; i < n; i++) { ti *= t; // convert int(size_t) to avoid warning // on _MSC_VER systems if( use_x ) f[i] = int(i+1) * x[i-1]; else f[i] = int(i+1) * ti; } } void Ode_dep( const double &t, const CPPAD_TESTVECTOR(double) &x, CPPAD_TESTVECTOR(double) &f_x) { using namespace CppAD; size_t n = x.size(); CPPAD_TESTVECTOR(AD) T(1); CPPAD_TESTVECTOR(AD) X(n); CPPAD_TESTVECTOR(AD) F(n); // set argument values T[0] = t; size_t i, j; for(i = 0; i < n; i++) X[i] = x[i]; // declare independent variables Independent(X); // compute f(t, x) this->Ode(T[0], X, F); // define AD function object ADFun fun(X, F); // compute partial of f w.r.t x CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) df(n); for(j = 0; j < n; j++) dx[j] = 0.; for(j = 0; j < n; j++) { dx[j] = 1.; df = fun.Forward(1, dx); for(i = 0; i < n; i++) f_x [i * n + j] = df[i]; dx[j] = 0.; } } private: const bool use_x; }; } bool OdeGear(void) { bool ok = true; // initial return value size_t i, j; // temporary indices double eps99 = 99.0 * std::numeric_limits::epsilon(); size_t m = 4; // index of next value in X size_t n = m; // number of components in x(t) // vector of times CPPAD_TESTVECTOR(double) T(m+1); double step = .1; T[0] = 0.; for(j = 1; j <= m; j++) { T[j] = T[j-1] + step; step = 2. * step; } // initial values for x( T[m-j] ) CPPAD_TESTVECTOR(double) X((m+1) * n); for(j = 0; j < m; j++) { double ti = T[j]; for(i = 0; i < n; i++) { X[ j * n + i ] = ti; ti *= T[j]; } } // error bound CPPAD_TESTVECTOR(double) e(n); size_t use_x; for( use_x = 0; use_x < 2; use_x++) { // function object depends on value of use_x Fun F(use_x > 0); // compute OdeGear approximation for x( T[m] ) CppAD::OdeGear(F, m, n, T, X, e); double check = T[m]; for(i = 0; i < n; i++) { // method is exact up to order m and x[i] = t^{i+1} if( i + 1 <= m ) ok &= CppAD::NearEqual( X[m * n + i], check, eps99, eps99 ); // error bound should be zero up to order m-1 if( i + 1 < m ) ok &= CppAD::NearEqual( e[i], 0., eps99, eps99 ); // check value for next i check *= T[m]; } } return ok; } // END C++ ================================================ FILE: example/utility/ode_gear_control.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ode_gear_control.cpp} OdeGearControl: Example and Test ################################ Define :math:`X : \B{R} \rightarrow \B{R}^2` by .. math:: :nowrap: \begin{eqnarray} X_0 (t) & = & - \exp ( - w_0 t ) \\ X_1 (t) & = & \frac{w_0}{w_1 - w_0} [ \exp ( - w_0 t ) - \exp( - w_1 t )] \end{eqnarray} It follows that :math:`X_0 (0) = 1`, :math:`X_1 (0) = 0` and .. math:: :nowrap: \begin{eqnarray} X_0^{(1)} (t) & = & - w_0 X_0 (t) \\ X_1^{(1)} (t) & = & + w_0 X_0 (t) - w_1 X_1 (t) \end{eqnarray} The example tests OdeGearControl using the relations above: {xrst_literal // BEGIN C++ // END C++ } {xrst_end ode_gear_control.cpp} */ // BEGIN C++ # include # include // CppAD::OdeGearControl namespace { // -------------------------------------------------------------- class Fun { private: CPPAD_TESTVECTOR(double) w; public: // constructor Fun(const CPPAD_TESTVECTOR(double) &w_) : w(w_) { } // set f = x'(t) template void Ode( const Scalar &t, const CPPAD_TESTVECTOR(Scalar) &x, CPPAD_TESTVECTOR(Scalar) &f) { f[0] = - w[0] * x[0]; f[1] = + w[0] * x[0] - w[1] * x[1]; } void Ode_dep( const double &t, const CPPAD_TESTVECTOR(double) &x, CPPAD_TESTVECTOR(double) &f_x) { using namespace CppAD; size_t n = x.size(); CPPAD_TESTVECTOR(AD) T(1); CPPAD_TESTVECTOR(AD) X(n); CPPAD_TESTVECTOR(AD) F(n); // set argument values T[0] = t; size_t i, j; for(i = 0; i < n; i++) X[i] = x[i]; // declare independent variables Independent(X); // compute f(t, x) this->Ode(T[0], X, F); // define AD function object ADFun fun(X, F); // compute partial of f w.r.t x CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) df(n); for(j = 0; j < n; j++) dx[j] = 0.; for(j = 0; j < n; j++) { dx[j] = 1.; df = fun.Forward(1, dx); for(i = 0; i < n; i++) f_x [i * n + j] = df[i]; dx[j] = 0.; } } }; } bool OdeGearControl(void) { bool ok = true; // initial return value using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); CPPAD_TESTVECTOR(double) w(2); w[0] = 10.; w[1] = 1.; Fun F(w); CPPAD_TESTVECTOR(double) xi(2); xi[0] = 1.; xi[1] = 0.; CPPAD_TESTVECTOR(double) eabs(2); eabs[0] = 1e-4; eabs[1] = 1e-4; // return values CPPAD_TESTVECTOR(double) ef(2); CPPAD_TESTVECTOR(double) maxabs(2); CPPAD_TESTVECTOR(double) xf(2); size_t nstep; // input values size_t M = 5; double ti = 0.; double tf = 1.; double smin = 1e-8; double smax = 1.; double sini = eps99; double erel = 0.; xf = CppAD::OdeGearControl(F, M, ti, tf, xi, smin, smax, sini, eabs, erel, ef, maxabs, nstep); double x0 = exp(-w[0]*tf); ok &= NearEqual(x0, xf[0], 1e-4, 1e-4); ok &= NearEqual(0., ef[0], 1e-4, 1e-4); double x1 = w[0] * (exp(-w[0]*tf) - exp(-w[1]*tf))/(w[1] - w[0]); ok &= NearEqual(x1, xf[1], 1e-4, 1e-4); ok &= NearEqual(0., ef[1], 1e-4, 1e-4); return ok; } // END C++ ================================================ FILE: example/utility/poly.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin poly.cpp} Polynomial Evaluation: Example and Test ####################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end poly.cpp} */ // BEGIN C++ # include # include bool poly(void) { bool ok = true; // degree of the polynomial size_t deg = 3; // set the polynomial coefficients CPPAD_TESTVECTOR(double) a(deg + 1); size_t i; for(i = 0; i <= deg; i++) a[i] = 1.; // evaluate this polynomial size_t k = 0; double z = 2.; double p = CppAD::Poly(k, a, z); ok &= (p == 1. + z + z*z + z*z*z); // evaluate derivative k = 1; p = CppAD::Poly(k, a, z); ok &= (p == 1 + 2.*z + 3.*z*z); return ok; } // END C++ ================================================ FILE: example/utility/pow_int.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin pow_int.cpp} The Pow Integer Exponent: Example and Test ########################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end pow_int.cpp} */ // BEGIN C++ # include # include bool pow_int(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // declare independent variables and start tape recording size_t n = 1; double x0 = -0.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; CppAD::Independent(x); // dependent variable vector size_t m = 7; CPPAD_TESTVECTOR(AD) y(m); for(size_t i = 0; i < m; i++) y[i] = CppAD::pow(x[0], int(i) - 3); // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check value double check; for(size_t i = 0; i < m; i++) { check = std::pow(x0, double(i) - 3.0); ok &= NearEqual(y[i] , check, eps99 , eps99); } // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); for(size_t i = 0; i < m; i++) { check = (double(i) - 3.0) * std::pow(x0, double(i) - 4.0); ok &= NearEqual(dy[i] , check, eps99 , eps99); } // reverse computation of derivative of y[i] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); for(size_t i = 0; i < m; i++) w[i] = 0.; for(size_t i = 0; i < m; i++) { w[i] = 1.; dw = f.Reverse(1, w); check = (double(i) - 3.0) * std::pow(x0, double(i) - 4.0); ok &= NearEqual(dw[0] , check, eps99 , eps99); w[i] = 0.; } return ok; } // END C++ ================================================ FILE: example/utility/romberg_mul.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin romberg_mul.cpp} Multi-Dimensional Romberg Integration: Example and Test ####################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end romberg_mul.cpp} */ // BEGIN C++ # include # include # include namespace { class TestFun { private: const CppAD::vector deg; public: // constructor TestFun(const CppAD::vector deg_) : deg(deg_) { } // function F(x) = x[0]^deg[0] * x[1]^deg[1] double operator () (const CppAD::vector &x) { size_t i; double f = 1; for(i = 0; i < deg[0]; i++) f *= x[0]; for(i = 0; i < deg[1]; i++) f *= x[1]; return f; } }; } bool RombergMul(void) { bool ok = true; size_t i; size_t k; CppAD::vector deg(2); deg[0] = 5; deg[1] = 3; TestFun F(deg); CppAD::RombergMul< TestFun , CppAD::vector, CppAD::vector, 2 > RombergMulTest; // arguments to RombergMul CppAD::vector a(2); CppAD::vector b(2); CppAD::vector n(2); CppAD::vector p(2); for(i = 0; i < 2; i++) { a[i] = 0.; b[i] = 1.; } n[0] = 4; n[1] = 3; double r, e; // int_a1^b1 dx1 int_a0^b0 F(x0,x1) dx0 // = [ b0^(deg[0]+1) - a0^(deg[0]+1) ] / (deg[0]+1) // * [ b1^(deg[1]+1) - a1^(deg[1]+1) ] / (deg[1]+1) double bpow = 1.; double apow = 1.; for(i = 0; i <= deg[0]; i++) { bpow *= b[0]; apow *= a[0]; } double check = (bpow - apow) / double(deg[0]+1); bpow = 1.; apow = 1.; for(i = 0; i <= deg[1]; i++) { bpow *= b[1]; apow *= a[1]; } check *= (bpow - apow) / double(deg[1]+1); double step = (b[1] - a[1]) / exp(log(2.)*double(n[1]-1)); double spow = 1; for(k = 0; k <= n[1]; k++) { spow = spow * step * step; double bnd = 3 * double(deg[1] + 1) * spow; for(i = 0; i < 2; i++) p[i] = k; r = RombergMulTest(F, a, b, n, p, e); ok &= e < bnd; ok &= CppAD::NearEqual(check, r, 0., e); } return ok; } // END C++ ================================================ FILE: example/utility/romberg_one.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin romberg_one.cpp} One Dimensional Romberg Integration: Example and Test ##################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end romberg_one.cpp} */ // BEGIN C++ # include # include # include namespace { class Fun { private: const size_t degree; public: // constructor Fun(size_t degree_) : degree(degree_) { } // function F(x) = x^degree template Type operator () (const Type &x) { size_t i; Type f = 1; for(i = 0; i < degree; i++) f *= x; return f; } }; } bool RombergOne(void) { bool ok = true; size_t i; size_t degree = 4; Fun F(degree); // arguments to RombergOne double a = 0.; double b = 1.; size_t n = 4; size_t p; double r, e; // int_a^b F(x) dx = [ b^(degree+1) - a^(degree+1) ] / (degree+1) double bpow = 1.; double apow = 1.; for(i = 0; i <= degree; i++) { bpow *= b; apow *= a; } double check = (bpow - apow) / double(degree+1); // step size corresponding to r double step = (b - a) / exp(log(2.)*double(n-1)); // step size corresponding to error estimate step *= 2.; // step size raised to a power double spow = 1; for(p = 0; p < n; p++) { spow = spow * step * step; r = CppAD::RombergOne(F, a, b, n, p, e); ok &= e < double(degree+1) * spow; ok &= CppAD::NearEqual(check, r, 0., e); } return ok; } // END C++ ================================================ FILE: example/utility/rosen_34.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin rosen_34.cpp} {xrst_spell rclr } rosen_34: Example and Test ########################## Define :math:`X : \B{R} \rightarrow \B{R}^n` by .. math:: X_i (t) = t^{i+1} for :math:`i = 1 , \ldots , n-1`. It follows that .. math:: \begin{array}{rclr} X_i(0) & = & 0 & {\rm for \; all \;} i \\ X_i ' (t) & = & 1 & {\rm if \;} i = 0 \\ X_i '(t) & = & (i+1) t^i = (i+1) X_{i-1} (t) & {\rm if \;} i > 0 \end{array} The example tests Rosen34 using the relations above: Operation Sequence ****************** The :ref:`rosen34-name` method for solving ODE's requires the inversion of a system of linear equations. This indices used for pivoting may change with different values for :math:`t` and :math:`x`. This example checks the comparison operators. If some of the comparisons change, it makes a new recording of the function with the pivots for the current :math:`t` and :math:`x`. Note that one could skip this step and always use the same pivot. This would not be as numerically stable, but it would still solve the equations (so long as none of the pivot elements are zero). {xrst_literal // BEGIN C++ // END C++ } {xrst_end rosen_34.cpp} */ // BEGIN C++ # include // For automatic differentiation namespace { class Fun { private: const bool use_x_; CppAD::ADFun ode_ind_; CppAD::ADFun ode_dep_; public: // constructor Fun(bool use_x) : use_x_(use_x) { } // compute f(t, x) both for double and AD template void Ode( const Scalar &t, const CPPAD_TESTVECTOR(Scalar) &x, CPPAD_TESTVECTOR(Scalar) &f) { size_t n = x.size(); Scalar ti(1); f[0] = Scalar(1); for(size_t i = 1; i < n; i++) { ti *= t; if( use_x_ ) f[i] = Scalar(i+1) * x[i-1]; else f[i] = Scalar(i+1) * ti; } } // compute partial of f(t, x) w.r.t. t using AD void Ode_ind( const double &t, const CPPAD_TESTVECTOR(double) &x, CPPAD_TESTVECTOR(double) &f_t) { using namespace CppAD; size_t n = x.size(); bool ode_ind_defined = ode_ind_.size_var() != 0; // CPPAD_TESTVECTOR(double) t_vec(1); t_vec[0] = t; // bool retape = true; if( ode_ind_defined ) { // check if any comparison operators have a different result ode_ind_.new_dynamic(x); ode_ind_.Forward(0, t_vec); retape = ode_ind_.compare_change_number() > 0; } if( retape ) { // record function that evaluates f(t, x) // with t as independent variable and x as dynamcic parameter CPPAD_TESTVECTOR(AD) at(1); CPPAD_TESTVECTOR(AD) ax(n); CPPAD_TESTVECTOR(AD) af(n); // set argument values at[0] = t; size_t i; for(i = 0; i < n; i++) ax[i] = x[i]; // declare independent variables and dynamic parameters size_t abort_op_index = 0; bool record_compare = false; Independent(at, abort_op_index, record_compare, ax); // compute f(t, x) this->Ode(at[0], ax, af); // define AD function object ode_ind_.Dependent(at, af); // store result in ode_ind_ so can be re-used assert( ode_ind_.size_var() != 0 ); } // special case where new_dynamic not yet set if( ! ode_ind_defined ) ode_ind_.new_dynamic(x); // compute partial of f w.r.t t f_t = ode_ind_.Jacobian(t_vec); // partial f(t, x) w.r.t. t } // compute partial of f(t, x) w.r.t. x using AD void Ode_dep( const double &t, const CPPAD_TESTVECTOR(double) &x, CPPAD_TESTVECTOR(double) &f_x) { using namespace CppAD; size_t n = x.size(); bool ode_dep_defined = ode_dep_.size_var() != 0; // CPPAD_TESTVECTOR(double) t_vec(1), dx(n), df(n); t_vec[0] = t; // bool retape = true; if( ode_dep_defined ) { // check if any comparison operators have a different result ode_dep_.new_dynamic(t_vec); ode_dep_.Forward(0, x); retape = ode_dep_.compare_change_number() > 0; } if( retape ) { // record function that evaluates f(t, x) // with x as independent variable and t as dynamcic parameter CPPAD_TESTVECTOR(AD) at(1); CPPAD_TESTVECTOR(AD) ax(n); CPPAD_TESTVECTOR(AD) af(n); // set argument values at[0] = t; for(size_t i = 0; i < n; i++) ax[i] = x[i]; // declare independent variables size_t abort_op_index = 0; bool record_compare = false; Independent(ax, abort_op_index, record_compare, at); // compute f(t, x) this->Ode(at[0], ax, af); // define AD function object ode_dep_.Dependent(ax, af); // store result in ode_dep_ so can be re-used assert( ode_ind_.size_var() != 0 ); } // special case where new_dynamic not yet set if( ! ode_dep_defined ) ode_dep_.new_dynamic(t_vec); // compute partial of f w.r.t x f_x = ode_dep_.Jacobian(x); // partial f(t, x) w.r.t. x } }; } bool rosen_34(void) { bool ok = true; // initial return value using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); size_t n = 4; // number components in X(t) and order of method size_t M = 2; // number of Rosen34 steps in [ti, tf] double ti = 0.; // initial time double tf = 2.; // final time // xi = X(0) CPPAD_TESTVECTOR(double) xi(n); for(size_t i = 0; i 0); // compute Rosen34 approximation for X(tf) CPPAD_TESTVECTOR(double) xf(n), e(n); xf = CppAD::Rosen34(F, M, ti, tf, xi, e); double check = tf; for(size_t i = 0; i < n; i++) { // check that error is always positive ok &= (e[i] >= 0.); // 4th order method is exact for i < 4 if( i < 4 ) ok &= NearEqual(xf[i], check, eps99, eps99); // 3rd order method is exact for i < 3 if( i < 3 ) ok &= (e[i] <= eps99); // check value for next i check *= tf; } } return ok; } // END C++ ================================================ FILE: example/utility/runge45_1.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin runge45_1.cpp} {xrst_spell rclr } Runge45: Example and Test ######################### Define :math:`X : \B{R} \rightarrow \B{R}^n` by .. math:: X_i (t) = t^{i+1} for :math:`i = 1 , \ldots , n-1`. It follows that .. math:: \begin{array}{rclr} X_i(0) & = & 0 & {\rm for \; all \;} i \\ X_i ' (t) & = & 1 & {\rm if \;} i = 0 \\ X_i '(t) & = & (i+1) t^i = (i+1) X_{i-1} (t) & {\rm if \;} i > 0 \end{array} The example tests Runge45 using the relations above: {xrst_literal // BEGIN C++ // END C++ } {xrst_end runge45_1.cpp} */ // BEGIN C++ # include // for size_t # include // for CppAD::NearEqual # include // for CppAD::vector # include // for CppAD::Runge45 // Runge45 requires fabs to be defined (not std::fabs) // defines this for doubles, but runge_45.hpp does not. # include // for fabs without std in front namespace { class Fun { public: // constructor Fun(bool use_x_) : use_x(use_x_) { } // set f = x'(t) void Ode( const double &t, const CppAD::vector &x, CppAD::vector &f) { size_t n = x.size(); double ti = 1.; f[0] = 1.; size_t i; for(i = 1; i < n; i++) { ti *= t; if( use_x ) f[i] = double(i+1) * x[i-1]; else f[i] = double(i+1) * ti; } } private: const bool use_x; }; } bool runge_45_1(void) { bool ok = true; // initial return value size_t i; // temporary indices using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); size_t n = 5; // number components in X(t) and order of method size_t M = 2; // number of Runge45 steps in [ti, tf] double ti = 0.; // initial time double tf = 2.; // final time // xi = X(0) CppAD::vector xi(n); for(i = 0; i 0); // compute Runge45 approximation for X(tf) CppAD::vector xf(n), e(n); xf = CppAD::Runge45(F, M, ti, tf, xi, e); double check = tf; for(i = 0; i < n; i++) { // check that error is always positive ok &= (e[i] >= 0.); // 5th order method is exact for i < 5 if( i < 5 ) ok &= NearEqual(xf[i], check, eps99, eps99); // 4th order method is exact for i < 4 if( i < 4 ) ok &= (e[i] <= eps99); // check value for next i check *= tf; } } return ok; } // END C++ ================================================ FILE: example/utility/runge_45.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin runge_45.cpp} Runge45: Example and Test ######################### Define :math:`X : \B{R} \times \B{R} \rightarrow \B{R}^n` by .. math:: X_j (b, t) = b \left( \sum_{k=0}^j t^k / k ! \right) for :math:`j = 0 , \ldots , n-1`. It follows that .. math:: :nowrap: \begin{eqnarray} X_j (b, 0) & = & b \\ \partial_t X_j (b, t) & = & b \left( \sum_{k=0}^{j-1} t^k / k ! \right) \\ \partial_t X_j (b, t) & = & \left\{ \begin{array}{ll} 0 & {\rm if} \; j = 0 \\ X_{j-1} (b, t) & {\rm otherwise} \end{array} \right. \end{eqnarray} For a fixed :math:`t_f`, we can use :ref:`Runge45-name` to define :math:`f : \B{R} \rightarrow \B{R}^n` as an approximation for :math:`f(b) = X(b, t_f )`. We can then compute :math:`f^{(1)} (b)` which is an approximation for .. math:: \partial_b X(b, t_f ) = \sum_{k=0}^j t_f^k / k ! {xrst_literal // BEGIN C++ // END C++ } {xrst_end runge_45.cpp} */ // BEGIN C++ # include // for size_t # include // for machine epsilon # include // for all of CppAD namespace { template class Fun { public: // constructor Fun(void) { } // set return value to X'(t) void Ode( const Scalar &t, const CPPAD_TESTVECTOR(Scalar) &x, CPPAD_TESTVECTOR(Scalar) &f) { size_t n = x.size(); f[0] = 0.; for(size_t k = 1; k < n; k++) f[k] = x[k-1]; } }; } bool runge_45(void) { typedef CppAD::AD Scalar; using CppAD::NearEqual; bool ok = true; // initial return value size_t j; // temporary indices size_t n = 5; // number components in X(t) and order of method size_t M = 2; // number of Runge45 steps in [ti, tf] Scalar ad_ti = 0.; // initial time Scalar ad_tf = 2.; // final time // value of independent variable at which to record operations CPPAD_TESTVECTOR(Scalar) ad_b(1); ad_b[0] = 1.; // declare b to be the independent variable Independent(ad_b); // object to evaluate ODE Fun ad_F; // xi = X(0) CPPAD_TESTVECTOR(Scalar) ad_xi(n); for(j = 0; j < n; j++) ad_xi[j] = ad_b[0]; // compute Runge45 approximation for X(tf) CPPAD_TESTVECTOR(Scalar) ad_xf(n), ad_e(n); ad_xf = CppAD::Runge45(ad_F, M, ad_ti, ad_tf, ad_xi, ad_e); // stop recording and use it to create f : b -> xf CppAD::ADFun f(ad_b, ad_xf); // evaluate f(b) CPPAD_TESTVECTOR(double) b(1); CPPAD_TESTVECTOR(double) xf(n); b[0] = 1.; xf = f.Forward(0, b); // check that f(b) = X(b, tf) double tf = Value(ad_tf); double term = 1; double sum = 0; double eps = 10. * CppAD::numeric_limits::epsilon(); for(j = 0; j < n; j++) { sum += term; ok &= NearEqual(xf[j], b[0] * sum, eps, eps); term *= tf; term /= double(j+1); } // evaluate f'(b) CPPAD_TESTVECTOR(double) d_xf(n); d_xf = f.Jacobian(b); // check that f'(b) = partial of X(b, tf) w.r.t b term = 1; sum = 0; for(j = 0; j < n; j++) { sum += term; ok &= NearEqual(d_xf[j], sum, eps, eps); term *= tf; term /= double(j+1); } return ok; } // END C++ ================================================ FILE: example/utility/set_union.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin set_union.cpp} Set Union: Example and Test ########################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end set_union.cpp} */ // BEGIN C++ # include bool set_union(void) { bool ok = true; // create empty sets std::set left, right, result; // set left = {1, 2} left.insert(1); left.insert(2); // set right = {2, 3} right.insert(2); right.insert(3); // set result = {1, 2} U {2, 3} result = CppAD::set_union(left, right); // expected result size_t check_vec[] = {1, 2, 3}; std::set check_set(check_vec, check_vec + 3); // check result ok &= result == check_set; return ok; } // END C++ ================================================ FILE: example/utility/simple_vector.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin simple_vector.cpp} Simple Vector Template Class: Example and Test ############################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end simple_vector.cpp} -------------------------------------------------------------------------- */ // BEGIN C++ # include // std::cout and std::endl # include // std::vector # include // std::valarray # include // CppAD::vector # include // CppAD::CheckSimpleVector namespace { template bool Ok(void) { // type corresponding to elements of Vector typedef typename Vector::value_type Scalar; bool ok = true; // initialize testing flag Vector x; // use the default constructor ok &= (x.size() == 0); // test size for an empty vector Vector y(2); // use the sizing constructor ok &= (y.size() == 2); // size for an vector with elements // non-const access to the elements of y size_t i; for(i = 0; i < 2; i++) y[i] = Scalar(i); const Vector z(y); // copy constructor x.resize(2); // resize x = z; // vector assignment // use the const access to the elements of x // and test the values of elements of x, y, z for(i = 0; i < 2; i++) { ok &= (x[i] == Scalar(i)); ok &= (y[i] == Scalar(i)); ok &= (z[i] == Scalar(i)); } return ok; } } bool SimpleVector (void) { bool ok = true; // use routine above to check these cases ok &= Ok< std::vector >(); ok &= Ok< std::valarray >(); ok &= Ok< CppAD::vector >(); # ifndef _MSC_VER // Avoid Microsoft following compiler warning: 'size_t' : // forcing value to bool 'true' or 'false' (performance warning) ok &= Ok< std::vector >(); ok &= Ok< CppAD::vector >(); # endif // use CheckSimpleVector for more extensive testing CppAD::CheckSimpleVector >(); CppAD::CheckSimpleVector >(); CppAD::CheckSimpleVector >(); CppAD::CheckSimpleVector >(); CppAD::CheckSimpleVector >(); return ok; } // END C++ ================================================ FILE: example/utility/sparse_rc.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sparse_rc.cpp} sparse_rc: Example and Test ########################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end sparse_rc.cpp} */ // BEGIN C++ # include # include bool sparse_rc(void) { bool ok = true; typedef std::vector SizeVector; // 3 by 3 identity matrix size_t nr = 3; size_t nc = 3; size_t nnz = 3; CppAD::sparse_rc pattern(nr, nc, nnz); for(size_t k = 0; k < nnz; k++) pattern.set(k, k, k); // row and column vectors corresponding to pattern const SizeVector& row = pattern.row(); const SizeVector& col = pattern.row(); // check pattern ok &= pattern.nnz() == nnz; ok &= pattern.nr() == nr; ok &= pattern.nc() == nc; for(size_t k = 0; k < nnz; k++) { ok &= row[k] == k; ok &= col[k] == k; } // change to sparsity pattern for a 5 by 5 diagonal matrix // using push_back instead of set nr = 5; nc = 5; pattern.resize(nr, nc, 0); for(size_t k = 0; k < nr; k++) { size_t r = nr - k - 1; // reverse or row-major order size_t c = nr - k - 1; pattern.push_back(r, c); } nnz = pattern.nnz(); // check nnz, row major by value row major by reference, row and column ok &= nnz == nr; SizeVector row_major_value = pattern.row_major(); const SizeVector& row_major_reference = pattern.get_row_major(); for(size_t k = 0; k < nnz; k++) { ok &= row[ row_major_value[k] ] == k; ok &= col[ row_major_value[k] ] == k; } ok &= row_major_reference.size() == 0; // set_row_major pattern.set_row_major(); ok &= row_major_reference.size() == nnz; for(size_t k = 0; k < nnz; ++k) ok &= row_major_reference[k] == row_major_value[k]; // create an empty pattern CppAD::sparse_rc other; ok &= other.nnz() == 0; ok &= other.nr() == 0; ok &= other.nc() == 0; // now swap other with pattern pattern.swap(other); ok &= pattern.nnz() == 0; ok &= pattern.nr() == 0; ok &= pattern.nc() == 0; for(size_t k = 0; k < nnz; k++) { ok &= other.row()[k] == nnz - k - 1; ok &= other.col()[k] == nnz - k - 1; } // now use the assignment statement pattern = other; // set_col_major const SizeVector& col_major_reference( pattern.get_col_major() ); ok &= col_major_reference.size() == 0; pattern.set_col_major(); ok &= col_major_reference.size() == nnz; // // check equality ok &= pattern == other; // // Change pattern so it no longer corresponds to a diagonal matrix pattern.set(nnz-1, 0, 1); // // check equality ok &= ! (pattern == other); // return ok; } // END C++ ================================================ FILE: example/utility/sparse_rcv.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sparse_rcv.cpp} sparse_rcv: Example and Test ############################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end sparse_rcv.cpp} */ // BEGIN C++ # include # include bool sparse_rcv(void) { bool ok = true; typedef std::vector SizeVector; typedef std::vector ValueVector; // sparsity pattern for a 5 by 5 diagonal matrix size_t nr = 5; size_t nc = 5; size_t nnz = 5; CppAD::sparse_rc pattern(nr, nc, nnz); for(size_t k = 0; k < nnz; k++) { size_t r = nnz - k - 1; // reverse or column-major order size_t c = nnz - k - 1; pattern.set(k, r, c); } // sparse matrix CppAD::sparse_rcv matrix(pattern); for(size_t k = 0; k < nnz; k++) { double v = double(k); matrix.set(nnz - k - 1, v); } // row, column, and value vectors const SizeVector& row( matrix.row() ); const SizeVector& col( matrix.row() ); const ValueVector& val( matrix.val() ); SizeVector col_major = matrix.col_major(); // check row, column, and value for(size_t k = 0; k < nnz; k++) { ok &= row[ col_major[k] ] == k; ok &= col[ col_major[k] ] == k; ok &= val[ col_major[k] ] == double(k); } // pat const CppAD::sparse_rc pat( matrix.pat() ); ok &= nnz == pat.nnz(); ok &= nr == pat.nr(); ok &= nc == pat.nc(); for(size_t k = 0; k < nnz; k++) { ok &= row[k] == pat.row()[k]; ok &= col[k] == pat.col()[k]; } // create an empty matrix CppAD::sparse_rcv other; ok &= other.nnz() == 0; ok &= other.nr() == 0; ok &= other.nc() == 0; // now swap other with matrix matrix.swap(other); ok &= matrix.nnz() == 0; ok &= matrix.nr() == 0; ok &= matrix.nc() == 0; for(size_t k = 0; k < nnz; k++) { ok &= other.row()[ col_major[k] ] == k; ok &= other.col()[ col_major[k] ] == k; ok &= other.val()[ col_major[k] ] == double(k); } // now use the assignment statement matrix = other; ok &= other.nr() == matrix.nr(); ok &= other.nc() == matrix.nc(); ok &= other.nnz() == matrix.nnz(); for(size_t k = 0; k < nnz; k++) { ok &= matrix.row()[k] == other.row()[k]; ok &= matrix.col()[k] == other.col()[k]; ok &= matrix.val()[k] == other.val()[k]; } // now use the copy constructor CppAD::sparse_rcv copy(matrix); ok &= copy.nr() == matrix.nr(); ok &= copy.nc() == matrix.nc(); ok &= copy.nnz() == matrix.nnz(); for(size_t k = 0; k < nnz; k++) { ok &= matrix.row()[k] == copy.row()[k]; ok &= matrix.col()[k] == copy.col()[k]; ok &= matrix.val()[k] == copy.val()[k]; } return ok; } // END C++ ================================================ FILE: example/utility/thread_alloc.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin thread_alloc.cpp} Fast Multi-Threading Memory Allocator: Example and Test ####################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end thread_alloc.cpp} */ // BEGIN C++ # include # include # include namespace { // Begin empty namespace bool raw_allocate(void) { bool ok = true; using CppAD::thread_alloc; size_t thread; // check that no memory is initilaly inuse ok &= thread_alloc::free_all(); // amount of static memory used by thread zero size_t static_inuse = 0; // repeatedly allocate enough memory for at least two size_t values. size_t min_size_t = 2; size_t min_bytes = min_size_t * sizeof(size_t); size_t n_outer = 10; size_t n_inner = 5; for(size_t i = 0; i < n_outer; i++) { // Do not use CppAD::vector here because its use of thread_alloc // complicates the inuse and available results. std::vector v_ptr(n_inner); // cap_bytes will be set by get_memory size_t cap_bytes = 0; // set here to avoid MSC warning for(size_t j = 0; j < n_inner; j++) { // allocate enough memory for min_size_t size_t objects v_ptr[j] = thread_alloc::get_memory(min_bytes, cap_bytes); size_t* ptr = reinterpret_cast(v_ptr[j]); // determine the number of size_t values we have obtained size_t cap_size_t = cap_bytes / sizeof(size_t); ok &= min_size_t <= cap_size_t; // use placement new to call the size_t copy constructor for(size_t k = 0; k < cap_size_t; k++) new(ptr + k) size_t(i + j + k); // check that the constructor worked for(size_t k = 0; k < cap_size_t; k++) ok &= ptr[k] == (i + j + k); } // check that n_inner * cap_bytes are inuse and none are available thread = thread_alloc::thread_num(); ok &= thread_alloc::inuse(thread) == n_inner*cap_bytes + static_inuse; ok &= thread_alloc::available(thread) == 0; // return the memrory to thread_alloc for(size_t j = 0; j < n_inner; j++) thread_alloc::return_memory(v_ptr[j]); // check that now n_inner * cap_bytes are now available // and none are in use ok &= thread_alloc::inuse(thread) == static_inuse; ok &= thread_alloc::available(thread) == n_inner * cap_bytes; } thread_alloc::free_available(thread); // check that the tests have not held onto memory ok &= thread_alloc::free_all(); return ok; } class my_char { public: char ch_ ; my_char(void) : ch_(' ') { } my_char(const my_char& my_ch) : ch_(my_ch.ch_) { } }; bool type_allocate(void) { bool ok = true; using CppAD::thread_alloc; size_t i; // check initial memory values size_t thread = thread_alloc::thread_num(); ok &= thread == 0; ok &= thread_alloc::free_all(); size_t static_inuse = 0; // initial allocation of an array size_t size_min = 3; size_t size_one; my_char *array_one = thread_alloc::create_array(size_min, size_one); // check the values and change them to null 'x' for(i = 0; i < size_one; i++) { ok &= array_one[i].ch_ == ' '; array_one[i].ch_ = 'x'; } // now create a longer array size_t size_two; my_char *array_two = thread_alloc::create_array(2 * size_min, size_two); // check the values in array one for(i = 0; i < size_one; i++) ok &= array_one[i].ch_ == 'x'; // check the values in array two for(i = 0; i < size_two; i++) ok &= array_two[i].ch_ == ' '; // check the amount of inuse and available memory // (an extra size_t value is used for each memory block). size_t check = static_inuse + sizeof(my_char)*(size_one + size_two); ok &= thread_alloc::inuse(thread) - check < sizeof(my_char); ok &= thread_alloc::available(thread) == 0; // delete the arrays thread_alloc::delete_array(array_one); thread_alloc::delete_array(array_two); ok &= thread_alloc::inuse(thread) == static_inuse; check = sizeof(my_char)*(size_one + size_two); ok &= thread_alloc::available(thread) - check < sizeof(my_char); // free the memory for use by this thread thread_alloc::free_available(thread); // check that the tests have not held onto memory ok &= thread_alloc::free_all(); return ok; } } // End empty namespace bool check_alignment(void) { bool ok = true; using CppAD::thread_alloc; // number of binary digits in a size_t value size_t n_digit = std::numeric_limits::digits; // must be a multiple of 8 ok &= (n_digit % 8) == 0; // number of bytes in a size_t value size_t n_byte = n_digit / 8; // check raw allocation ------------------------------------------------- size_t min_bytes = 1; size_t cap_bytes; void* v_ptr = thread_alloc::get_memory(min_bytes, cap_bytes); // convert to a size_t value size_t v_size_t = reinterpret_cast(v_ptr); // check that it is aligned ok &= (v_size_t % n_byte) == 0; // return memory to available pool thread_alloc::return_memory(v_ptr); // check array allocation ---------------------------------------------- size_t size_min = 1; size_t size_out; my_char *array_ptr = thread_alloc::create_array(size_min, size_out); // convert to a size_t value size_t array_size_t = reinterpret_cast(array_ptr); // check that it is aligned ok &= (array_size_t % n_byte) == 0; // return memory to available pool thread_alloc::delete_array(array_ptr); return ok; } bool thread_alloc(void) { bool ok = true; using CppAD::thread_alloc; // check that there is only on thread ok &= thread_alloc::num_threads() == 1; // so thread number must be zero ok &= thread_alloc::thread_num() == 0; // and we are in sequential execution mode ok &= thread_alloc::in_parallel() == false; // Instruct thread_alloc to hold onto memory. This makes memory // allocation faster (especially when there are multiple threads). thread_alloc::hold_memory(true); // run raw allocation tests ok &= raw_allocate(); // run typed allocation tests ok &= type_allocate(); // check alignment ok &= check_alignment(); // return allocator to its default mode thread_alloc::hold_memory(false); return ok; } // END C++ ================================================ FILE: example/utility/to_string.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin to_string.cpp} to_string: Example and Test ########################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end to_string.cpp} */ // BEGIN C++ // Examples with fundamental types # include namespace { template Integer string2signed(const std::string& s) { Integer result = 0; size_t index = 0; if( s[0] == '-' ) { ++index; while( index < s.size() ) result = Integer(10) * result - Integer(s[index++] - '0' ); } else { while( index < s.size() ) result = Integer(10) * result + Integer(s[index++] - '0' ); } return result; } template Integer string2unsigned(const std::string& s) { Integer result = 0; size_t index = 0; while( index < s.size() ) result = Integer(10 * result + Integer(s[index++]) - '0'); return result; } template bool signed_integer(void) { bool ok = true; // Integer max = std::numeric_limits::max(); std::string s = CppAD::to_string(max); Integer check = string2signed(s); ok &= max == check; // Integer min = std::numeric_limits::min(); s = CppAD::to_string(min); check = string2signed(s); ok &= min == check; // return ok; } template bool unsigned_integer(void) { bool ok = true; // Integer max = std::numeric_limits::max(); std::string s = CppAD::to_string(max); Integer check = string2unsigned(s); ok &= max == check; ok &= std::numeric_limits::min() == 0; // return ok; } template bool floating(void) { bool ok = true; Float eps = std::numeric_limits::epsilon(); Float pi = Float( 4.0 * std::atan(1.0) ); // std::string s = CppAD::to_string( pi ); Float check = Float( std::atof( s.c_str() ) ); ok &= std::fabs( check / pi - 1.0 ) <= 2.0 * eps; // return ok; } } // Examples with AD types # include namespace { template bool ad_floating(void) { bool ok = true; Base eps = std::numeric_limits::epsilon(); Base pi = Base( 4.0 * std::atan(1.0) ); // std::string s = CppAD::to_string( CppAD::AD( pi ) ); Base check = Base( std::atof( s.c_str() ) ); ok &= fabs( check / pi - Base(1.0) ) <= Base( 2.0 ) * eps; // return ok; } } // Test driver bool to_string(void) { bool ok = true; ok &= unsigned_integer(); ok &= signed_integer(); // ok &= unsigned_integer(); ok &= signed_integer(); ok &= unsigned_integer(); ok &= signed_integer(); // ok &= floating(); ok &= floating(); ok &= floating(); // ok &= ad_floating(); ok &= ad_floating(); // return ok; } // END C++ ================================================ FILE: example/utility/utility.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin utility.cpp} CppAD Utilities Examples and Tests Driver ######################################### Running These Tests ******************* After executing the :ref:`cmake-name` command form the :ref:`download@Distribution Directory`, you can build and run these tests with the commands:: cd build make check_example_utility Note that your choice of :ref:`cmake@generator` may require using an different version of make; e.g., ``ninja`` . {xrst_literal // BEGIN C++ // END C++ } {xrst_end utility.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ // for thread_alloc # include // test runner # include // BEGIN_SORT_THIS_LINE_PLUS_1 extern bool CheckNumericType(void); extern bool CheckSimpleVector(void); extern bool CppAD_vector(void); extern bool ErrorHandler(void); extern bool LuFactor(void); extern bool LuInvert(void); extern bool LuSolve(void); extern bool Near_Equal(void); extern bool OdeErrControl(void); extern bool OdeErrMaxabs(void); extern bool OdeGear(void); extern bool OdeGearControl(void); extern bool RombergMul(void); extern bool RombergOne(void); extern bool SimpleVector(void); extern bool dll_lib(void); extern bool index_sort(void); extern bool nan(void); extern bool poly(void); extern bool pow_int(void); extern bool rosen_34(void); extern bool runge_45(void); extern bool runge_45_1(void); extern bool set_union(void); extern bool sparse_rc(void); extern bool sparse_rcv(void); extern bool thread_alloc(void); extern bool to_string(void); extern bool vectorBool(void); // END_SORT_THIS_LINE_MINUS_1 // main program that runs all the tests int main(void) { std::string group = "example/utility"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // BEGIN_SORT_THIS_LINE_PLUS_1 Run( CheckNumericType, "CheckNumericType" ); Run( CheckSimpleVector, "CheckSimpleVector" ); Run( CppAD_vector, "CppAD_vector" ); Run( ErrorHandler, "ErrorHandler" ); Run( LuFactor, "LuFactor" ); Run( LuInvert, "LuInvert" ); Run( LuSolve, "LuSolve" ); Run( Near_Equal, "Near_Equal" ); Run( OdeErrControl, "OdeErrControl" ); Run( OdeErrMaxabs, "OdeErrMaxabs" ); Run( OdeGear, "OdeGear" ); Run( OdeGearControl, "OdeGearControl" ); Run( RombergMul, "RombergMul" ); Run( RombergOne, "RombergOne" ); Run( SimpleVector, "SimpleVector" ); Run( index_sort, "index_sort" ); Run( nan, "nan" ); Run( poly, "poly" ); Run( pow_int, "pow_int" ); Run( rosen_34, "rosen_34" ); Run( runge_45, "runge_45" ); Run( runge_45_1, "runge_45_1" ); Run( set_union, "set_union" ); Run( sparse_rc, "sparse_rc" ); Run( sparse_rcv, "sparse_rcv" ); Run( thread_alloc, "thread_alloc" ); Run( to_string, "to_string" ); Run( vectorBool, "vectorBool" ); // END_SORT_THIS_LINE_MINUS_1 // # if CPPAD_C_COMPILER_GNU_FLAGS || CPPAD_C_COMPILER_MSVC_FLAGS # if CPPAD_USE_CPLUSPLUS_2017 && ! CPPAD_LINK_FLAGS_HAS_M32 Run( dll_lib, "dll_lib" ); # endif # endif // // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } // END C++ ================================================ FILE: example/utility/vector_bool.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin vector_bool.cpp} CppAD::vectorBool Class: Example and Test ######################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end vector_bool.cpp} */ // BEGIN C++ # include # include # include // sstream and string are used to test output operation # include bool vectorBool(void) { bool ok = true; using CppAD::vectorBool; // is that boolvector is // a simple vector class with elements of type bool CppAD::CheckSimpleVector< bool, vectorBool >(); vectorBool x; // default constructor ok &= (x.size() == 0); // resize using size_t or int size_t two_s = 2; int two_i = 2; x.resize( two_s ); x.resize( two_i ); ok &= (x.size() == 2); // set element values using size_t and int size_t zero_s = 0; int one_i = 1; x[zero_s] = false; x[one_i] = true; vectorBool y(2); // sizing constructor ok &= (y.size() == 2); // swap, get element values using size_t and int y.swap(x); ok &= y[zero_s] == false; ok &= y[one_i] == true; const vectorBool z(y); // copy constructor and const element access ok &= (z.size() == 2); ok &= ( (z[0] == false) && (z[1] == true) ); x[0] = true; // modify, assignment changes x ok &= (x[0] == true); // resize x to zero and check that vector assignment works for both // size zero and matching sizes x.resize(0); ok &= (x.size() == 0); ok &= (y.size() == z.size()); // x = y = z; ok &= ( (x[0] == false) && (x[1] == true) ); ok &= ( (y[0] == false) && (y[1] == true) ); ok &= ( (z[0] == false) && (z[1] == true) ); // test of push_vector y.push_vector(z); ok &= y.size() == 4; ok &= ( (y[0] == false) && (y[1] == true) ); ok &= ( (y[2] == false) && (y[3] == true) ); y[1] = false; // element assignment to another element x[0] = y[1]; ok &= (x[0] == false); // test of output std::string correct= "01"; std::string str; std::ostringstream buf; buf << z; str = buf.str(); ok &= (str == correct); // test resize(0), capacity, and clear size_t i = x.capacity(); ok &= i > 0; x.resize(0); ok &= i == x.capacity(); x.clear(); ok &= 0 == x.capacity(); // test of push_back element for(i = 0; i < 100; i++) x.push_back( (i % 3) != 0 ); ok &= (x.size() == 100); for(i = 0; i < 100; i++) ok &= ( x[i] == ((i % 3) != 0) ); // initializer list constructor vectorBool w = { true, false, false, true }; ok &= w.size() == 4; ok &= w[0] == true; ok &= w[1] == false; ok &= w[2] == false; ok &= w[3] == true; return ok; } // END C++ ================================================ FILE: example/valvector/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the example/valvector directory tests # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list valvector.cpp ad_join.cpp ad_split.cpp ad_sum.cpp assign.cpp azmul.cpp base_require.cpp binary_op.cpp compare_op.cpp compound_op.cpp condexp.cpp ctor.cpp element.cpp get_started.cpp llsq_obj.cpp output.cpp pow.cpp resize.cpp size.cpp sum.cpp unary_math.cpp unary_op.cpp ) # END_SORT_THIS_LINE_MINUS_2 # set_compile_flags( example_valvector "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(example_valvector EXCLUDE_FROM_ALL ${source_list}) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(example_valvector ${cppad_lib} ${colpack_libs} ) # check_example_valvector add_check_executable(check_example valvector) ================================================ FILE: example/valvector/ad_join.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // --------------------------------------------------------------------------- /* {xrst_begin valvector_ad_join.cpp} Example and Test of Joining a valvector ####################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector_ad_join.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include # include bool ad_join(void) { // ok bool ok = true; // // sparsity_type typedef CppAD::sparse_rc< CPPAD_TESTVECTOR(size_t) > sparsity_type; // // scalar_type typedef valvector::scalar_type scalar_type; // // ad_valvector typedef CppAD::AD ad_valvector; // // ajoin valvector_ad_join ajoin; // // n, m size_t n = 10; size_t m = 1; // // ax CPPAD_TESTVECTOR( ad_valvector ) ax(n); for(size_t j = 0; j < n; ++j) ax[j] = valvector(j); CppAD::Independent(ax); // // ay ad_valvector ay; ajoin(ax, ay); // ad_valvector ay_sq = ay * ay; // // f CPPAD_TESTVECTOR( ad_valvector ) az(m); az[0] = ay_sq; CppAD::ADFun f(ax, az); // // x CPPAD_TESTVECTOR( valvector ) x(n); for(size_t j = 0; j < n; ++j) x[j][0] = scalar_type(j + 1); // // a CPPAD_TESTVECTOR( valvector ) z(m); z = f.Forward(0, x); // // ok for(size_t j = 0; j < n; ++j) ok &= z[0][j] == x[j][0] * x[j][0]; // // dw CPPAD_TESTVECTOR( valvector ) w(m), dw(n); w[0][0] = 1.0; dw = f.Reverse(1, w); // // ok for(size_t j = 0; j < n; ++j) ok &= dw[0][j] == scalar_type(2) * x[0][j]; // // jac_pattern sparsity_type identity_pattern(n, n, n); for(size_t k = 0; k < n; ++k) identity_pattern.set(k, k, k); bool transpose = false; bool dependency = false; bool internal_bool = false; sparsity_type jac_pattern; f.for_jac_sparsity( identity_pattern, transpose, dependency, internal_bool, jac_pattern ); // // ok ok &= jac_pattern.nnz() == n; ok &= jac_pattern.nr() == m; ok &= jac_pattern.nc() == n; { CPPAD_TESTVECTOR(size_t) col_major = jac_pattern.col_major(); const CPPAD_TESTVECTOR(size_t)& row = jac_pattern.row(); const CPPAD_TESTVECTOR(size_t)& col = jac_pattern.col(); for(size_t k = 0; k < n; ++k) { ok &= row[ col_major[k] ] == 0; ok &= col[ col_major[k] ] == k; } } // // hes_pattern internal_bool = false; CPPAD_TESTVECTOR(bool) select_domain(n); CPPAD_TESTVECTOR(bool) select_range(m); select_range[0] = true; for(size_t j = 0; j < n; ++j) select_domain[j] = true; sparsity_type hes_pattern; f.for_hes_sparsity ( select_domain , select_range , internal_bool , hes_pattern ); // // ok // The sparsity calculation does not distinguish between different elements // of a valvector so the sparsity pattern fills in (this is not efficient). ok &= hes_pattern.nnz() == n * n; ok &= hes_pattern.nr() == n; ok &= hes_pattern.nc() == n; { CPPAD_TESTVECTOR(size_t) col_major = hes_pattern.col_major(); const CPPAD_TESTVECTOR(size_t)& row = hes_pattern.row(); const CPPAD_TESTVECTOR(size_t)& col = hes_pattern.col(); size_t k = 0; for(size_t j = 0; j < n; ++j) { for(size_t i = 0; i < n; ++i) { ok &= row[ col_major[k] ] == i; ok &= col[ col_major[k] ] == j; ++k; } } } // // af typedef CppAD::ADFun ad_fun_type; ad_fun_type af = ad_fun_type( f.base2ad() ); // // g CPPAD_TESTVECTOR( ad_valvector ) adx(n), adz(m); for(size_t j = 0; j < n; ++j) adx[j] = valvector( 1.0 ); CppAD::Independent(ax); af.Forward(0, ax); adz = af.Forward(1, adx); CppAD::ADFun g(ax, adz); // // ok CPPAD_TESTVECTOR( valvector ) dz(m); dz = g.Forward(0, x); for(size_t j = 0; j < n; ++j) ok &= dz[0][j] == 2.0 * x[j][0]; // // h CPPAD_TESTVECTOR( ad_valvector ) aw(m), adw(n); aw[0] = valvector( 1.0 ); CppAD::Independent(ax); af.Forward(0, ax); adw = af.Reverse(1, aw); CppAD::ADFun h(ax, adw); // // ok dw = h.Forward(0, x); for(size_t j = 0; j < n; ++j) ok &= dw[j][0] == 2.0 * x[j][0]; // // ok f.optimize(); z = f.Forward(0, x); for(size_t j = 0; j < n; ++j) ok &= z[0][j] == x[j][0] * x[j][0]; // return ok; } // END C++ ================================================ FILE: example/valvector/ad_split.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // --------------------------------------------------------------------------- /* {xrst_begin valvector_ad_split.cpp} Example and Test of Splitting a valvector ######################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector_ad_split.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include # include bool ad_split(void) { // ok bool ok = true; // // sparsity_type typedef CppAD::sparse_rc< CPPAD_TESTVECTOR(size_t) > sparsity_type; // // scalar_type typedef valvector::scalar_type scalar_type; // // ad_valvector typedef CppAD::AD ad_valvector; // // asplit valvector_ad_split asplit; // // n, m size_t n = 1; size_t m = 10; // // ax CPPAD_TESTVECTOR( ad_valvector ) ax(n); ax[0] = valvector(0); CppAD::Independent(ax); // // ax_sq ad_valvector ax_sq = ax[0] * ax[0]; // // f CPPAD_TESTVECTOR( ad_valvector ) ay(m); asplit(ax_sq, ay); CppAD::ADFun f(ax, ay); // // x CPPAD_TESTVECTOR( valvector ) x(n); x[0].resize(m); for(size_t i = 0; i < m; ++i) x[0][i] = scalar_type(i + 1); // // y CPPAD_TESTVECTOR( valvector ) y(m); y = f.Forward(0, x); // // ok for(size_t i = 0; i < m; ++i) ok &= y[i][0] == x[0][i] * x[0][i]; // // dw CPPAD_TESTVECTOR( valvector ) w(m), dw(n); for(size_t i = 0; i < m; ++i) w[i][0] = 1.0; dw = f.Reverse(1, w); // // ok for(size_t i = 0; i < m; ++i) ok &= dw[0][i] == scalar_type(2) * x[0][i]; // // jac_pattern sparsity_type identity_pattern(n, n, n); identity_pattern.set(0, 0, 0); bool transpose = false; bool dependency = false; bool internal_bool = false; sparsity_type jac_pattern; f.for_jac_sparsity( identity_pattern, transpose, dependency, internal_bool, jac_pattern ); // // ok ok &= jac_pattern.nnz() == m; ok &= jac_pattern.nr() == m; ok &= jac_pattern.nc() == n; CPPAD_TESTVECTOR(size_t) col_major = jac_pattern.col_major(); for(size_t k = 0; k < m; ++k) { ok &= jac_pattern.row()[k] == k; ok &= jac_pattern.col()[k] == 0; } // // hes_pattern internal_bool = false; CPPAD_TESTVECTOR(bool) select_domain(n); CPPAD_TESTVECTOR(bool) select_range(m); select_domain[0] = true; for(size_t i = 0; i < m; ++i) select_range[i] = false; select_range[0] = true; sparsity_type hes_pattern; f.for_hes_sparsity ( select_domain , select_range , internal_bool , hes_pattern ); // // ok ok &= hes_pattern.nnz() == 1; ok &= hes_pattern.nr() == n; ok &= hes_pattern.nc() == n; ok &= hes_pattern.row()[0] == 0; ok &= hes_pattern.col()[0] == 0; // // af typedef CppAD::ADFun ad_fun_type; ad_fun_type af = ad_fun_type( f.base2ad() ); // // g CPPAD_TESTVECTOR( ad_valvector ) adx(n), ady(m); adx[0] = valvector( 1.0 ); CppAD::Independent(ax); af.Forward(0, ax); ady = af.Forward(1, adx); CppAD::ADFun g(ax, ady); // // ok CPPAD_TESTVECTOR( valvector ) dy(m); dy = g.Forward(0, x); for(size_t i = 0; i < m; ++i) ok &= dy[i][0] == 2.0 * x[0][i]; // // h CPPAD_TESTVECTOR( ad_valvector ) aw(m), adw(n); for(size_t i = 0; i < m; ++i) aw[i] = valvector( 1.0 ); CppAD::Independent(ax); af.Forward(0, ax); adw = af.Reverse(1, aw); CppAD::ADFun h(ax, adw); // // ok dw = h.Forward(0, x); for(size_t i = 0; i < m; ++i) ok &= dw[0][i] == 2.0 * x[0][i]; // // ok f.optimize(); y = f.Forward(0, x); for(size_t i = 0; i < m; ++i) ok &= y[i][0] == x[0][i] * x[0][i]; // return ok; } // END C++ ================================================ FILE: example/valvector/ad_sum.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // --------------------------------------------------------------------------- /* {xrst_begin valvector_ad_sum.cpp} Example and Test of Summing a valvector ####################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector_ad_sum.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include # include bool ad_sum(void) { // ok bool ok = true; // // sparsity_type typedef CppAD::sparse_rc< CPPAD_TESTVECTOR(size_t) > sparsity_type; // // scalar_type typedef valvector::scalar_type scalar_type; // // ad_valvector typedef CppAD::AD ad_valvector; // // asum valvector_ad_sum asum; // // n, m, p size_t n = 1; size_t m = 1; size_t p = 10; // // ax CPPAD_TESTVECTOR( ad_valvector ) ax(n); ax[0] = valvector(1); CppAD::Independent(ax); // // ax_sq ad_valvector ax_sq = ax[0] * ax[0]; // // ay CPPAD_TESTVECTOR( ad_valvector ) ay(m); asum(ax_sq, ay[0]); // // f CppAD::ADFun f(ax, ay); // // x CPPAD_TESTVECTOR( valvector ) x(n); x[0].resize(p); for(size_t k = 0; k < p; ++k) x[0][k] = scalar_type(k + 1); // // y CPPAD_TESTVECTOR( valvector ) y(m); y = f.Forward(0, x); // // ok scalar_type check(0); for(size_t k = 0; k < p; ++k) check += x[0][k] * x[0][k]; ok &= check == y[0][0]; // // dw CPPAD_TESTVECTOR( valvector ) w(m), dw(n); w[0][0] = 1.0; dw = f.Reverse(1, w); // // ok for(size_t k = 0; k < p; ++k) ok &= dw[0][k] == scalar_type(2) * x[0][k]; // // jac_pattern sparsity_type identity_pattern(n, n, n); for(size_t k = 0; k < n; ++k) identity_pattern.set(k, k, k); bool transpose = false; bool dependency = false; bool internal_bool = false; sparsity_type jac_pattern; f.for_jac_sparsity( identity_pattern, transpose, dependency, internal_bool, jac_pattern ); // // ok ok &= jac_pattern.nnz() == 1; ok &= jac_pattern.nr() == 1; ok &= jac_pattern.nc() == 1; ok &= jac_pattern.row()[0] == 0; ok &= jac_pattern.col()[0] == 0; // // hes_pattern internal_bool = false; CPPAD_TESTVECTOR(bool) select_domain(n); CPPAD_TESTVECTOR(bool) select_range(m); select_range[0] = true; select_domain[0] = true; sparsity_type hes_pattern; f.for_hes_sparsity ( select_domain , select_range , internal_bool , hes_pattern ); // // ok ok &= hes_pattern.nnz() == n * n; ok &= hes_pattern.nr() == n; ok &= hes_pattern.nc() == n; ok &= hes_pattern.row()[0] == 0; ok &= hes_pattern.col()[0] == 0; // // af typedef CppAD::ADFun ad_fun_type; ad_fun_type af = ad_fun_type( f.base2ad() ); // // g CPPAD_TESTVECTOR( ad_valvector ) adx(n), ady(m); adx[0] = valvector( 1.0 ); CppAD::Independent(ax); af.Forward(0, ax); ady = af.Forward(1, adx); CppAD::ADFun g(ax, ady); // // ok CPPAD_TESTVECTOR( valvector ) dy(m); dy = g.Forward(0, x); check = scalar_type(0); for(size_t k = 0; k < p; ++k) check += 2.0 * x[0][k]; ok &= dy[0][0] == check; // // h CPPAD_TESTVECTOR( ad_valvector ) aw(m), adw(n); aw[0] = valvector( 1.0 ); CppAD::Independent(ax); af.Forward(0, ax); adw = af.Reverse(1, aw); CppAD::ADFun h(ax, adw); // // ok dw = h.Forward(0, x); for(size_t k = 0; k < p; ++k) ok &= dw[0][k] == 2.0 * x[0][k]; // // ok f.optimize(); y = f.Forward(0, x); check = scalar_type(0); for(size_t k = 0; k < p; ++k) check += x[0][k] * x[0][k]; ok &= y[0][0] == check; // return ok; } // END C++ ================================================ FILE: example/valvector/assign.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin valvector_assign.cpp} Example and Test of valvector Assignment Operator ################################################# {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector_assign.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include // bool assign(void) { // scalar_type typedef valvector::scalar_type scalar_type; // // // ok bool ok = true; // // v, ok valvector v; ok &= v.size() == 1; // // w valvector w = valvector( {2, 3, 4} ); ok &= w.size() == 3; // // v v = w; v[0] = scalar_type(1); // // ok ok &= v.size() == w.size(); ok &= w[0] == scalar_type(2); ok &= v[0] == scalar_type(1); for(size_t i = 1; i < w.size(); ++i) ok &= v[i] == w[i]; // return ok; } // END C++ ================================================ FILE: example/valvector/azmul.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin valvector_azmul.cpp} Example and Test of valvector azmul Function ############################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector_azmul.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include # include # include # include // bool azmul(void) { typedef valvector::scalar_type scalar_type; // // ok bool ok = true; // // nan scalar_type nan = CppAD::numeric_limits::quiet_NaN(); // // x, y, z valvector x = valvector( {0, 1, 2} ); valvector y = valvector( {nan, nan, 5} );; valvector z = CppAD::azmul(x, y); // // ok ok &= z[0] == scalar_type(0); ok &= CppAD::isnan( z[1] ); ok &= z[2] == x[2] * y[2]; // return ok; } // END C++ ================================================ FILE: example/valvector/base_require.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // --------------------------------------------------------------------------- /* {xrst_begin valvector_base_require.cpp} Example and Test of valvector Base Requirements ############################################### Scope ***** This only covers that valvector :ref:`valvector_base_require@Features Implemented Here` . Source Code *********** {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector_base_require.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include // // base_require bool base_require(void) { // ok bool ok = true; // // x, y valvector x, y; // // ok x = valvector(2.5); int integer_x = CppAD::Integer(x); ok &= integer_x == 2; // // ok valvector nan = CppAD::numeric_limits::quiet_NaN(); ok &= CppAD::isnan( nan ); // // ok x = valvector( {1, 2} ); std::string string_x = CppAD::to_string( x ); std::string s; for(auto itr = string_x.begin(); itr != string_x.end(); ++itr) if( *itr != ' ') s.push_back( *itr ); ok &= s == "{1,2}"; // // ok x = valvector( {1, 2} ); y = valvector( {1.0, 2.0} ); ok &= CppAD::EqualOpSeq(x, y); // // ok ok &= CppAD::IdenticalCon(x); ok &= ! CppAD::IdenticalZero(x); ok &= ! CppAD::IdenticalOne(x); ok &= CppAD::IdenticalEqualCon(x, y); // x = valvector( {0.0, 0.0} ); ok &= CppAD::IdenticalZero(x); // return ok; } // END C++ ================================================ FILE: example/valvector/binary_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin valvector_binary_op.cpp} Example and Test of valvector Binary Operators ############################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector_binary_op.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include // bool binary_op(void) { // // ok bool ok = true; // // u, v, w valvector u( {4, 6, 8} ); valvector v(2); valvector w; // // w_size size_t w_size = std::max( u.size(), v.size() ); // // ok w = u + v; ok &= w.size() == w_size; for(size_t i = 0; i < w_size; ++i) ok &= w[i] == u[i] + v[i]; // // ok w = u - v; ok &= w.size() == w_size; for(size_t i = 0; i < w_size; ++i) ok &= w[i] == u[i] - v[i]; // // ok w = u * v; ok &= w.size() == w_size; for(size_t i = 0; i < w_size; ++i) ok &= w[i] == u[i] * v[i]; // // ok w = u / v; ok &= w.size() == w_size; for(size_t i = 0; i < w_size; ++i) ok &= w[i] == u[i] / v[i]; // return ok; } // END C++ ================================================ FILE: example/valvector/compare_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin valvector_compare_op.cpp} Example and Test of valvector Compare Operators ############################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector_compare_op.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include // bool compare_op(void) { typedef valvector::scalar_type scalar_type; // // ok bool ok = true; // // u, v valvector u( {4, 6, 8} ); valvector v( {4.0, 6.0, 8.0} ); // // ok ok &= u == v; u[0] = u[0] + scalar_type(1); ok &= u != v; // // ok ok &= valvector( 2.0 ) < valvector( 3.0 ); ok &= valvector( 4.0 ) > valvector( 3.0 ); // return ok; } // END C++ ================================================ FILE: example/valvector/compound_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin valvector_compound_op.cpp} Example and Test of valvector Compound Assignment Operators ########################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector_compound_op.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include // bool compound_op(void) { // // ok bool ok = true; // // u, v, w valvector u(2); valvector v( {4, 6, 8} ); valvector w; ok &= u.size() == 1; ok &= v.size() == 3; ok &= w.size() == 1; // // ok w = v; w += u; ok &= w.size() == 3; for(size_t i = 0; i < w.size(); ++i) ok &= w[i] == v[i] + u[i]; // // ok w = v; w -= u; ok &= w.size() == 3; for(size_t i = 0; i < w.size(); ++i) ok &= w[i] == v[i] - u[i]; // // ok w = v; w *= u; ok &= w.size() == 3; for(size_t i = 0; i < w.size(); ++i) ok &= w[i] == v[i] * u[i]; // // ok w = v; w /= u; ok &= w.size() == 3; for(size_t i = 0; i < w.size(); ++i) ok &= w[i] == v[i] / u[i]; // return ok; } // END C++ ================================================ FILE: example/valvector/condexp.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin valvector_condexp.cpp} Example and Test of valvector Conditional Expressions ##################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector_condexp.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include # include # include # include // bool condexp(void) { // // ok bool ok = true; // // left, right, if_true, if_false valvector left = valvector( {0, 1, 2, 3, 4} ); valvector right = valvector( 2 ); valvector if_true = left; valvector if_false = right; // // result valvector result = CppAD::CondExpGe(left, right, if_true, if_false); // // ok for(size_t i = 0; i < result.size(); ++i) ok &= result[i] == std::max( left[i], right[i] ); // return ok; } // END C++ ================================================ FILE: example/valvector/ctor.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin valvector_ctor.cpp} Example and Test of valvector Constructors ########################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector_ctor.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include // bool ctor(void) { // scalar_type typedef valvector::scalar_type scalar_type; // // ok bool ok = true; // // ok valvector default_ctor; ok &= default_ctor.size() == 1; // // ok valvector scalar_ctor( scalar_type(1) ); ok &= scalar_ctor.size() == 1; ok &= scalar_ctor[0] == scalar_type(1); // // ok valvector int_ctor( int(1) ); ok &= int_ctor.size() == 1; ok &= int_ctor[0] == scalar_type(1); // // ok valvector long_int_ctor( (long int)(2) ); ok &= long_int_ctor.size() == 1; ok &= long_int_ctor[0] == scalar_type(2); // // ok valvector double_ctor( double(3) ); ok &= double_ctor.size() == 1; ok &= double_ctor[0] == scalar_type(3); // // ok valvector long_double_ctor( (long double)(4) ); ok &= long_double_ctor.size() == 1; ok &= long_double_ctor[0] == scalar_type(4); // // ok valvector size_t_ctor( size_t(5) ); ok &= size_t_ctor.size() == 1; ok &= size_t_ctor[0] == scalar_type(5); // // ok // Test implicit conversion of list elements to scalar_type valvector list_ctor( { 6, 7.0 } ); ok &= list_ctor.size() == 2; ok &= list_ctor[0] == scalar_type(6); ok &= list_ctor[1] == scalar_type(7); // return ok; } // END C++ ================================================ FILE: example/valvector/element.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin valvector_element.cpp} Example and Test of valvector Element Access ############################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector_element.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include // bool element(void) { // scalar_type typedef valvector::scalar_type scalar_type; // // // ok bool ok = true; // // v, ok valvector v(3); ok &= v.size() == 1; for(size_t i = 0; i <= 10; ++i) v[i] = scalar_type(i); ok &= v[0] == scalar_type(10); ok &= v[1] == scalar_type(10); // // v v.resize(3); // // ok ok &= v.size() == 3; for(size_t i = 0; i < v.size(); ++i) v[i] = scalar_type(i); for(size_t i = 0; i < v.size(); ++i) ok &= v[i] == scalar_type(i); // return ok; } // END C++ ================================================ FILE: example/valvector/get_started.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // --------------------------------------------------------------------------- /* {xrst_begin valvector_get_started.cpp} Getting Started Using valvector as a CppAD Base Class ##################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector_get_started.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include # include bool get_started(void) { // ok bool ok = true; // // ad_valvector typedef CppAD::AD ad_valvector; // // ax CPPAD_TESTVECTOR( ad_valvector ) ax(2); ax[0] = valvector( {1.0} ); ax[1] = valvector( {2.0} ); CppAD::Independent(ax); // // f CPPAD_TESTVECTOR( ad_valvector ) ay(1); ay[0] = ax[0] * ax[1]; CppAD::ADFun f(ax, ay); // // x CPPAD_TESTVECTOR( valvector ) x(2); x[0] = valvector( {1.0, 2.0, 3.0} ); x[1] = valvector( {4.0, 3.0, 2.0} ); // // y CPPAD_TESTVECTOR( valvector ) y(1); y = f.Forward(0, x); // // ok valvector check; check = valvector( {4.0, 6.0, 6.0} ); ok &= y[0] == check; // // dw CPPAD_TESTVECTOR( valvector ) w(1), dw(2); w[0] = valvector( 1.0 ); dw = f.Reverse(1, w); // // ok ok &= dw[0] == x[1]; ok &= dw[1] == x[0]; // return ok; } // END C++ ================================================ FILE: example/valvector/llsq_obj.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // --------------------------------------------------------------------------- /* {xrst_begin valvector_llsq_obj.cpp} Using valvector to Represent Linear Least Squares Objective ########################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector_llsq_obj.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include # include # include bool llsq_obj(void) { // ok bool ok = true; // // scalar_type typedef valvector::scalar_type scalar_type; // // eps99 scalar_type eps99 = CppAD::numeric_limits::epsilon(); eps99 *= scalar_type(99); // // ad_valvector typedef CppAD::AD ad_valvector; // // asum valvector_ad_sum asum; // // n_data // number of data points in the fit size_t n_data = 100; // // time, data // argument and data values in the function being fit valvector time, data; if( n_data == 1 ) { time[0] = 0.0; data[0] = 0.0; } else { time.resize(n_data); data.resize(n_data); // for(size_t i = 0; i < n_data; ++i) { time[i] = -1.0 + scalar_type(2 * i) / scalar_type(n_data - 1); if( time[i] < 0.0 ) data[i] = -1.0; else if( 0.0 < time[i] ) data[i] = +1.0; else data[i] = 0.0; } } // data if( n_data % 2 == 1 ) { // in case time[n_data/2] was not exactly zero due to roundoff data[n_data / 2] = 0.0; } // // nx // number of coefficients in the function being fit size_t nx = 3; // // ax // coefficients in the function being fit CPPAD_TESTVECTOR( ad_valvector ) ax(nx); for(size_t j = 0; j < nx; ++j) ax[j] = valvector(0.0); CppAD::Independent(ax); // // amodel valvector time_j(1.0); ad_valvector amodel(0.0); for(size_t j = 0; j < nx; ++j) { amodel += time_j * ax[j]; time_j *= time; } // // aobj ad_valvector ares = data - amodel; ad_valvector asq = ares * ares; ad_valvector aobj; asum(asq, aobj); // // ay CPPAD_TESTVECTOR( ad_valvector ) ay(1); ay[0] = aobj; CppAD::ADFun f(ax, ay); // // x CPPAD_TESTVECTOR( valvector ) x(nx); for(size_t j = 0; j < nx; ++j) x[j][0] = 1.0; // // y CPPAD_TESTVECTOR( valvector ) y(1); y = f.Forward(0, x); // // res CPPAD_TESTVECTOR( scalar_type ) res(n_data); for(size_t i = 0; i < n_data; ++i) { scalar_type tj = 1.0; scalar_type model = 0.0; for(size_t j = 0; j < nx; ++j) { model += tj * x[j][0]; tj *= time[i]; } res[i] = data[i] - model; } // // ok scalar_type check = 0.0; for(size_t i = 0; i < n_data; ++i) check += res[i] * res[i]; ok &= CppAD::NearEqual(y[0][0], check, eps99, eps99); // // dw CPPAD_TESTVECTOR( valvector ) w(1), dw(nx); w[0][0] = 1.0; dw = f.Reverse(1, w); // // // dres_sq CPPAD_TESTVECTOR( scalar_type ) dres_sq(nx); for(size_t j = 0; j < nx; ++j) dres_sq[j] = 0.0; for(size_t i = 0; i < n_data; ++i) { scalar_type tj = 1.0; for(size_t j = 0; j < nx; ++j) { dres_sq[j] += - 2.0 * res[i] * tj; tj *= time[i]; } } // // ok // reverse mode does not know that x[j] had size one and represents // a valvector of size n_data and with x[j][k] constant w.r.t k. for(size_t j = 0; j < nx; ++j) { /* std::cout << "j = " << j; std::cout << ", dw[j] = " << dw[j]; std::cout << ", dres_sq[j] = " << dres_sq[j] << "\n"; */ ok &= dw[j].size() == n_data; ok &= CppAD::NearEqual(dw[j].sum(), dres_sq[j], eps99, eps99); } return ok; } // END C++ ================================================ FILE: example/valvector/output.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin valvector_output.cpp} Example and Test of Outputting a valvector ########################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector_output.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include # include # include // bool output(void) { // // ok bool ok = true; // // x valvector x( {4, 6, 8} ); // // s std::stringstream ss; ss << x; std::string s = ss.str(); // // q std::string q; for(auto itr = s.begin(); itr != s.end(); ++itr) if( *itr != ' ') q.push_back( *itr ); // ok &= q == "{4,6,8}"; // return ok; } // END C++ ================================================ FILE: example/valvector/pow.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin valvector_pow.cpp} Example and Test of valvector pow Function ########################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector_pow.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include # include # include # include // bool pow(void) { typedef valvector::scalar_type scalar_type; // // ok bool ok = true; // // eps99 scalar_type eps99 = CppAD::numeric_limits::epsilon(); eps99 *= scalar_type(99); // // x, y, z valvector x = valvector( {2, 3, 4} ); valvector y = valvector( {4, 3, 2} );; valvector z = CppAD::pow(x, y); // // ok ok &= x.size() == 3; for(size_t i = 0; i < x.size(); ++i) { scalar_type check = pow( x[i], y[i] ); ok &= CppAD::NearEqual( z[i], check, eps99, eps99); } return ok; } // END C++ ================================================ FILE: example/valvector/resize.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin valvector_resize.cpp} Example and Test of valvector Resize #################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector_resize.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include // bool resize(void) { // scalar_type typedef valvector::scalar_type scalar_type; // // // ok bool ok = true; // // v, ok valvector v; ok &= v.size() == 1; // // v v.resize(3); for(size_t i = 0; i < v.size(); ++i) v[i] = scalar_type(i); // // ok ok &= v.size() == 3; for(size_t i = 0; i < v.size(); ++i) ok &= v[i] == scalar_type(i); // return ok; } // END C++ ================================================ FILE: example/valvector/size.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin valvector_size.cpp} Example and Test of valvector Size ################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector_size.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include // bool size(void) { // scalar_type typedef valvector::scalar_type scalar_type; // // // ok bool ok = true; // // v, ok valvector v(3); ok &= v.size() == 1; for(size_t i = 0; i < 10; ++i) ok &= v[i] == scalar_type(3); // // v v.resize(3); // // ok ok &= v.size() == 3; for(size_t i = 0; i < v.size(); ++i) v[i] = scalar_type(i); for(size_t i = 0; i < v.size(); ++i) ok &= v[i] == scalar_type(i); // return ok; } // END C++ ================================================ FILE: example/valvector/sum.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin valvector_sum.cpp} Example and Test of valvector Size ################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector_sum.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include // bool sum(void) { // scalar_type typedef valvector::scalar_type scalar_type; // // // ok bool ok = true; // // v valvector v( {1.0, 2.0, 3.0} ); // // ok scalar_type sum = v.sum(); ok &= sum == scalar_type(6.0); // return ok; } // END C++ ================================================ FILE: example/valvector/unary_math.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin valvector_unary_math.cpp} Example and Test of valvector Unary Math Functions ################################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector_unary_math.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include # include # include # include // bool math_unary(void) { typedef valvector::scalar_type scalar_type; // // ok bool ok = true; // // eps99 scalar_type eps99 = CppAD::numeric_limits::epsilon(); eps99 *= scalar_type(99); // // u, v, ok valvector u = valvector( {2, 3, 4} ); valvector v; ok &= u.size() == 3; // // ok v = CppAD::sqrt(u); for(size_t i = 0; i < u.size(); ++i) ok &= CppAD::NearEqual( v[i], sqrt(u[i]), eps99, eps99); // return ok; } // END C++ ================================================ FILE: example/valvector/unary_op.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin valvector_unary_op.cpp} Example and Test of valvector Unary Operators ############################################# {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector_unary_op.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include // bool unary_op(void) { // // ok bool ok = true; // // v valvector v( {-1, +1} ); // // ok valvector w = +v; for(size_t i = 0; i < v.size(); ++i) ok &= w[i] == v[i]; // // ok valvector u = -v; for(size_t i = 0; i < v.size(); ++i) ok &= u[i] == - v[i]; // return ok; } // END C++ ================================================ FILE: example/valvector/valvector.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin valvector.cpp} valvector Examples and Tests Driver ################################### Running These Tests ******************* After executing the :ref:`cmake-name` command form the :ref:`download@Distribution Directory`, you can build and run these tests with the commands:: cd build make check_example_valvector Note that your choice of :ref:`cmake@generator` may require using an different version of make; e.g., ``ninja`` . {xrst_literal // BEGIN C++ // END C++ } {xrst_end valvector.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ // CPPAD_HAS_* defines # include // system include files used for I/O # include // for thread_alloc # include // test runner # include // BEGIN_SORT_THIS_LINE_PLUS_1 extern bool ad_join(void); extern bool ad_split(void); extern bool ad_sum(void); extern bool assign(void); extern bool azmul(void); extern bool base_require(void); extern bool binary_op(void); extern bool compare_op(void); extern bool compound_op(void); extern bool condexp(void); extern bool ctor(void); extern bool element(void); extern bool get_started(void); extern bool llsq_obj(void); extern bool math_unary(void); extern bool output(void); extern bool pow(void); extern bool resize(void); extern bool size(void); extern bool sum(void); extern bool unary_op(void); // END_SORT_THIS_LINE_MINUS_1 // main program that runs all the tests int main(void) { bool ok = true; // std::string group = "example/valvector"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // BEGIN_SORT_THIS_LINE_PLUS_1 Run( ad_join, "ad_join" ); Run( ad_split, "ad_split" ); Run( ad_sum, "ad_sum" ); Run( assign, "assign" ); Run( azmul, "azmul" ); Run( base_require, "base_require" ); Run( binary_op, "binary_op" ); Run( compare_op, "compare_op" ); Run( compound_op, "compound_op" ); Run( condexp, "condexp" ); Run( ctor, "ctor" ); Run( element, "element" ); Run( get_started, "get_started" ); Run( llsq_obj, "llsq_obj" ); Run( math_unary, "math_unary" ); Run( output, "output" ); Run( pow, "pow" ); Run( resize, "resize" ); Run( size, "size" ); Run( sum, "sum" ); Run( unary_op, "unary_op" ); // END_SORT_THIS_LINE_MINUS_1 // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end ok = Run.summary(memory_ok); // return static_cast( ! ok ); } // END C++ ================================================ FILE: include/cppad/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- # Configure the CppAD include file directory # ----------------------------------------------------------------------------- # check_match MACRO(check_match match_variable match_constant output_variable) STRING(COMPARE EQUAL ${${match_variable}} ${match_constant} match_flag ) IF( match_flag ) SET(${output_variable} 1) ELSE( match_flag ) SET(${output_variable} 0) ENDIF( match_flag ) print_variable(${output_variable}) ENDMACRO(check_match) # ----------------------------------------------------------------------------- # CMAKE_REQUIRED_name SET(CMAKE_REQUIRED_DEFINITIONS "") SET(CMAKE_REQUIRED_FLAGS "") SET(CMAKE_REQUIRED_INCLUDES "") SET(CMAKE_REQUIRED_LIBRARIES "") # ----------------------------------------------------------------------------- # # cppad_c_compiler_cmd # is the command used to run the C compiler. STRING(REGEX REPLACE ".*[/\]" "" cppad_c_compiler_cmd "${CMAKE_C_COMPILER}") print_variable(cppad_c_compiler_cmd) # # c_compiler_msvc_clang STRING(REGEX MATCH ".*[/\]clang-cl.exe" match "${CMAKE_C_COMPILER}" ) IF( "${match}" STREQUAL "${CMAKE_C_COMPILER}" ) SET(c_compiler_msvc_clang 1) ELSE( ) SET(c_compiler_msvc_clang 0) ENDIF( ) print_variable(c_compiler_msvc_clang) # # cmake_compiler_id_lower STRING(TOLOWER "${CMAKE_C_COMPILER_ID}" cmake_compiler_id_lower) # # cppad_c_compiler_msvc_flags STRING(FIND "${cmake_compiler_id_lower}" msvc index) IF( NOT "${index}" EQUAL "-1" ) SET(cppad_c_compiler_msvc_flags 1) ELSEIF( ${c_compiler_msvc_clang} ) SET(cppad_c_compiler_msvc_flags 1) # STRING(FIND "${cmake_compiler_id_lower}" clang index) IF( "${index}" EQUAL "-1" ) MESSAGE(FATAL_ERROR "Looks like MSVC compatible clang-cl.ext C compiler but" "CMAKE_C_COMPILER_ID = ${CMAKE_C_COMPILER_ID}" ) ENDIF( ) ELSE( ) SET(cppad_c_compiler_msvc_flags 0) ENDIF( ) print_variable( cppad_c_compiler_msvc_flags ) # # cppad_c_compiler_gnu_flags IF( ${c_compiler_msvc_clang} ) SET(cppad_c_compiler_gnu_flags 0) ELSE( ) STRING(FIND "${cmake_compiler_id_lower}" clang clang_index) STRING(FIND "${cmake_compiler_id_lower}" gnu gnu_index) IF( "${clang_index}" EQUAL "-1" AND "${gnu_index}" EQUAL "-1" ) SET(cppad_c_compiler_gnu_flags 0) ELSE( ) SET(cppad_c_compiler_gnu_flags 1) ENDIF( ) ENDIF( ) print_variable( cppad_c_compiler_gnu_flags ) # ----------------------------------------------------------------------------- # compiler_has_conversion_warn SET( clang_or_gnu 0 ) IF( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" ) SET(clang_or_gnu 1) ENDIF( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" ) IF( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" ) SET(clang_or_gnu 1) ENDIF( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" ) IF( clang_or_gnu ) SET(CMAKE_REQUIRED_FLAGS "${cppad_cxx_flags} -Wfloat-conversion -Wconversion -Werror" ) # SET(source "int main(void) { return 0; }") compile_source_test( ${cmake_defined_ok} "${source}" compiler_has_conversion_warn ) # SET(CMAKE_REQUIRED_FLAGS "") ELSE( clang_or_gnu ) SET( compiler_has_conversion_warn 0 ) ENDIF( clang_or_gnu ) # ----------------------------------------------------------------------------- # cppad_boostvector, cppad_cppadvector, cppad_eigenvector, cppad_stdvector # check_match(cppad_testvector boost cppad_boostvector) check_match(cppad_testvector cppad cppad_cppadvector) check_match(cppad_testvector eigen cppad_eigenvector) check_match(cppad_testvector std cppad_stdvector) IF( NOT cppad_boostvector ) IF( NOT cppad_cppadvector ) IF( NOT cppad_eigenvector ) IF( NOT cppad_stdvector ) MESSAGE(FATAL_ERROR "cppad_testvector not one of following: boost, cppad, eigen, std." "This should have been found earlier, please report this as a bug." ) ENDIF( NOT cppad_stdvector ) ENDIF( NOT cppad_eigenvector ) ENDIF( NOT cppad_cppadvector ) ENDIF( NOT cppad_boostvector ) # IF( cppad_boostvector ) # FIND_PACKAGE(Boost) done by ../CMakeLists.txt IF( NOT Boost_FOUND ) MESSAGE(FATAL_ERROR "cppad_testvector == boost but cannot find boost include files" ) ENDIF( NOT Boost_FOUND ) ENDIF( cppad_boostvector ) # IF( cppad_eigenvector ) IF( NOT cppad_has_eigen ) MESSAGE(FATAL_ERROR "cppad_testvector == eigen but Eigen was not found or c++14 not supported" ) ENDIF( NOT cppad_has_eigen ) ENDIF( cppad_eigenvector ) # ----------------------------------------------------------------------------- # cppad_has_gettimeofday # SET(source " # include int main(void) { struct timeval time; gettimeofday(&time, 0); return 0; }" ) compile_source_test(${cmake_defined_ok} "${source}" cppad_has_gettimeofday) # ----------------------------------------------------------------------------- # Warn user if the following types are signed: # cppad_tape_addr_type, cppad_tape_id_type FOREACH(cmake_var cppad_tape_id_type cppad_tape_addr_type ) SET(source " # include # include int main(void) { static_assert( ! std::numeric_limits<${${cmake_var}}>::is_signed , \"${cmake_var} is a signed type\" ); return 0; } " ) compile_source_test( ${cmake_defined_ok} "${source}" ${cmake_var}_is_unsigned ) IF( NOT ${${cmake_var}_is_unsigned} ) MESSAGE(STATUS "Warning: using a signed type for ${cmake_var} is for CppAD developers only !" ) ENDIF( NOT ${${cmake_var}_is_unsigned} ) ENDFOREACH( cmake_var ) # ----------------------------------------------------------------------------- # cppad_has_mkstemp # SET(source " # include # if _MSC_VER # include # else # include # endif int main(void) { char pattern[] = \"/tmp/fileXXXXXX\"; int fd = mkstemp(pattern); return 0; } " ) compile_source_test(${cmake_defined_ok} "${source}" cppad_has_mkstemp ) # ----------------------------------------------------------------------------- # cppad_has_tmpname_s # SET(source " # include int main(void) { char filename[L_tmpnam_s ]; if( tmpnam_s(filename, L_tmpnam_s ) != 0 ) return 1; return 0; } " ) compile_source_test(${cmake_defined_ok} "${source}" cppad_has_tmpnam_s ) # ----------------------------------------------------------------------------- # cppad_is_same_unsigned_int_size_t # SET(source " # include # include int main(void) { static_assert( std::is_same::value , \"message not needed with C++17 or later\" ); return 0; } " ) compile_source_test( ${cmake_defined_ok} "${source}" cppad_is_same_unsigned_int_size_t ) # ----------------------------------------------------------------------------- # cppad_is_same_tape_addr_type_size_t # SET(source " # include # include int main(void) { static_assert( std::is_same<${cppad_tape_addr_type}, size_t>::value , \"message not needed with C++17 or later\" ); return 0; } " ) compile_source_test( ${cmake_defined_ok} "${source}" cppad_is_same_tape_addr_type_size_t ) # ----------------------------------------------------------------------------- # cppad_padding_block_t # # See block_t in include/cppad/utility/thread_alloc.hpp before changing this. SET(source " # include int main(void) { class block_t { size_t extra_; size_t tc_index_; void* next_; }; static_assert( sizeof(block_t) % sizeof(double) == 0 , \"message not needed with C++17 or later\" ); return 0; } " ) compile_source_test( ${cmake_defined_ok} "${source}" first_block_t_mod_double_zero ) IF( ${first_block_t_mod_double_zero} ) SET(cppad_padding_block_t "") ELSE() SET(cppad_padding_block_t "void* note_used_;") ENDIF() SET(source " # include int main(void) { class block_t { size_t extra_; size_t tc_index_; void* next_; ${cppad_padding_block_t} }; static_assert( sizeof(block_t) % sizeof(double) == 0 , \"message not needed with C++17 or later\" ); return 0; } " ) compile_source_test( ${cmake_defined_ok} "${source}" second_block_t_mod_double_zero ) assert( second_block_t_mod_double_zero ) # ----------------------------------------------------------------------------- # configure.hpp CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/configure.hpp.in ${CMAKE_CURRENT_SOURCE_DIR}/configure.hpp ) # ----------------------------------------------------------------------------- ================================================ FILE: include/cppad/base_require.hpp ================================================ # ifndef CPPAD_BASE_REQUIRE_HPP # define CPPAD_BASE_REQUIRE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin base_require} {xrst_spell ostream } AD Requirements for a CppAD Base Type ########################################### Syntax ****** | ``# include `` Purpose ******* This section lists the requirements for the type *Base* so that the type ``AD`` < *Base* > can be used. API Warning *********** Defining a CppAD *Base* type is an advanced use of CppAD. This part of the CppAD API changes with time. The most common change is adding more requirements. Search for ``base_require`` in the current :ref:`whats_new-name` section for these changes. Standard Base Types ******************* In the case where *Base* is ``float`` , ``double`` , ``std::complex`` , ``std::complex`` , or ``AD`` < *Other* > , these requirements are provided by including the file ``cppad/cppad.hpp`` . In the documentation, The notation :math:`\B{R}` denotes the field corresponding to the base type. Multiplication must be commutative for this field, but it need not be the reals; e.g., the complex numbers. Include Order ************* If you are linking a non-standard base type to CppAD, you must first include the file ``cppad/base_require.hpp`` , then provide the specifications below, and then include the file ``cppad/cppad.hpp`` . Numeric Type ************ The type *Base* must support all the operations for a :ref:`NumericType-name` . Output Operator *************** The type *Base* must support the syntax *os* << *x* where *os* is an ``std::ostream&`` and *x* is a ``const base_alloc&`` . For example, see :ref:`base_alloc` . Integer ******* The type *Base* must support the syntax *i* = ``CppAD::Integer`` ( *x* ) which converts *x* to an ``int`` . The argument *x* has prototype ``const`` *Base* & *x* and the return value *i* has prototype ``int`` *i* Suggestion ========== In many cases, the *Base* version of the ``Integer`` function can be defined by | ``namespace CppAD`` { | |tab| ``inline int Integer`` ( ``const`` *Base* & ``x`` ) | |tab| { ``return static_cast`` ( ``x`` ); } | } For example, see :ref:`base_float` and :ref:`base_alloc` . Absolute Zero, azmul ******************** The type *Base* must support the syntax *z* = ``azmul`` ( *x* , *y* ) see; :ref:`azmul-name` . The following preprocessor macro invocation suffices (for most *Base* types): | ``namespace CppAD`` { | |tab| ``CPPAD_AZMUL`` ( *Base* ) | } where the macro is defined by {xrst_spell_off} {xrst_code cpp} */ # define CPPAD_AZMUL(Base) \ inline Base azmul(const Base& x, const Base& y) \ { Base zero(0.0); \ if( x == zero ) \ return zero; \ return x * y; \ } /* {xrst_code} {xrst_spell_on} Contents ******** {xrst_toc_table xrst/base_require/base_member.xrst include/cppad/core/base_cond_exp.hpp xrst/base_require/base_identical.xrst xrst/base_require/base_ordered.xrst include/cppad/core/base_std_math.hpp include/cppad/core/base_limits.hpp include/cppad/core/base_to_string.hpp include/cppad/core/base_hash.hpp xrst/base_require/base_example.xrst } {xrst_end base_require} */ // definitions that must come before base implementations # include # include # include # include // grouping documentation by feature # include # include # include # include # include // must define template class numeric_limits before the base cases # include # include // deprecated // base cases that come with CppAD # include # include # include // deprecated base type # include # endif ================================================ FILE: include/cppad/configure.hpp.in ================================================ # ifndef CPPAD_CONFIGURE_HPP # define CPPAD_CONFIGURE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- /*! {xrst_begin configure.hpp dev} {xrst_spell adolc cmd colpack gettimeofday ipopt mkstemp noexcept nullptr pragmas tmpnam unreferenced yyyy yyyymmdd } Preprocessor Symbols Set By CMake Command ######################################### CPPAD_LINK_FLAGS_HAS_M32 ************************ is -m32 in the :ref:`cmake@cppad_link_flags` . {xrst_code hpp} */ # define CPPAD_LINK_FLAGS_HAS_M32 @cppad_link_flags_has_m32@ /* {xrst_code} CPPAD_COMPILER_HAS_CONVERSION_WARN ********************************** is the compiler a variant of g++ and has conversion warnings {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_COMPILER_HAS_CONVERSION_WARN @compiler_has_conversion_warn@ /* {xrst_code} {xrst_spell_on} CPPAD_DISABLE_SOME_MICROSOFT_COMPILER_WARNINGS ********************************************** This macro is only used to document the pragmas that disables the follow warnings: C4100 ===== unreferenced formal parameter. C4127 ===== conditional expression is constant. C4723 ===== The second operand in a divide operation evaluated to zero at compile time. {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_DISABLE_SOME_MICROSOFT_COMPILER_WARNINGS 1 # if _MSC_VER # pragma warning( disable : 4100 ) # pragma warning( disable : 4127 ) # pragma warning( disable : 4723 ) # endif # undef CPPAD_DISABLE_SOME_MICROSOFT_COMPILER_WARNINGS /* {xrst_code} {xrst_spell_on} CPPAD_DEBUG_AND_RELEASE *********************** Starting with 2023-12-24, this flag is set by the cmake command; see :ref:`cmake@cppad_debug_and_release` . Before then, one would add -D CPPAD_DEBUG_AND_RELEASE when compiling CppAD code. {xrst_code hpp} */ # define CPPAD_DEBUG_AND_RELEASE @cppad_debug_and_release_01@ /* {xrst_code} CPPAD_USE_CPLUSPLUS_2011 ************************ Deprecated 2020-12-03: Is it OK to use C++11 features. This is always 1 (for true). {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_USE_CPLUSPLUS_2011 1 /* {xrst_code} {xrst_spell_on} CPPAD_USE_CPLUSPLUS_2017 ************************ Deprecated 2020-12-03: Is it OK for CppAD use C++17 features. {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_USE_CPLUSPLUS_2017 @use_cplusplus_2017_ok@ /* {xrst_code} {xrst_spell_on} CPPAD_PACKAGE_STRING ******************** cppad-yyyymmdd as a C string where yyyy is year, mm is month, and dd is day. {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_PACKAGE_STRING "cppad-@cppad_version@" /* {xrst_code} {xrst_spell_on} CPPAD_HAS_ADOLC *************** Was include_adolc=true on the cmake command line. {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_HAS_ADOLC @cppad_has_adolc@ /* {xrst_code} {xrst_spell_on} CPPAD_HAS_COLPACK ***************** Was a colpack_prefix specified on the cmake command line. {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_HAS_COLPACK @cppad_has_colpack@ /* {xrst_code} {xrst_spell_on} CPPAD_HAS_EIGEN *************** Was Eigen found and c++14 is supported. {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_HAS_EIGEN @cppad_has_eigen@ /* {xrst_code} {xrst_spell_on} CPPAD_HAS_IPOPT *************** Was include_ipopt=true on the cmake command line. {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_HAS_IPOPT @cppad_has_ipopt@ /* {xrst_code} {xrst_spell_on} CPPAD_DEPRECATED **************** This symbol is not currently being used. {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_DEPRECATED @cppad_deprecated_01@ /* {xrst_code} {xrst_spell_on} CPPAD_BOOSTVECTOR ***************** If this symbol is one, and _MSC_VER is not defined, we are using boost vector for CPPAD_TESTVECTOR. It this symbol is zero, we are not using boost vector for CPPAD_TESTVECTOR. {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_BOOSTVECTOR @cppad_boostvector@ /* {xrst_code} {xrst_spell_on} CPPAD_CPPADVECTOR ***************** If this symbol is one, we are using CppAD vector for CPPAD_TESTVECTOR. It this symbol is zero, we are not using CppAD vector for CPPAD_TESTVECTOR. {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_CPPADVECTOR @cppad_cppadvector@ /* {xrst_code} {xrst_spell_on} CPPAD_STDVECTOR *************** If this symbol is one, we are using standard vector for CPPAD_TESTVECTOR. It this symbol is zero, we are not using standard vector for CPPAD_TESTVECTOR. {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_STDVECTOR @cppad_stdvector@ /* {xrst_code} {xrst_spell_on} CPPAD_EIGENVECTOR ***************** If this symbol is one, we are using Eigen vector for CPPAD_TESTVECTOR. If this symbol is zero, we are not using Eigen vector for CPPAD_TESTVECTOR. {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_EIGENVECTOR @cppad_eigenvector@ /* {xrst_code} {xrst_spell_on} CPPAD_HAS_GETTIMEOFDAY ********************** If this symbol is one, and _MSC_VER is not defined, this system supports the gettimeofday function. Otherwise, this symbol should be zero. {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_HAS_GETTIMEOFDAY @cppad_has_gettimeofday@ /* {xrst_code} {xrst_spell_on} CPPAD_TAPE_ADDR_TYPE ******************** Is the type used to store address on the tape. If it is not size_t, then {xrst_code cpp} sizeof(CPPAD_TAPE_ADDR_TYPE) < sizeof( size_t ) {xrst_code} can be used to conserve memory. This type must support std::numeric_limits, the <= operator, and conversion to size_t. Make sure that the type chosen returns true for is_pod in pod_vector.hpp. This type is later defined as addr_t in the CppAD namespace. {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_TAPE_ADDR_TYPE @cppad_tape_addr_type@ /* {xrst_code} {xrst_spell_on} CPPAD_IS_SAME_TAPE_ADDR_TYPE_SIZE_T *********************************** Is size_t the type the same as CPPAD_TAPE_ADDR_TYPE. {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_IS_SAME_TAPE_ADDR_TYPE_SIZE_T \ @cppad_is_same_tape_addr_type_size_t@ /* {xrst_code} {xrst_spell_off} CPPAD_TAPE_ID_TYPE ****************** Is the type used to store tape identifiers. If it is not size_t, then {xrst_code cpp} sizeof(CPPAD_TAPE_ID_TYPE) < sizeof( size_t ) {xrst_code} can be used to conserve memory. This type must support std::numeric_limits, the <= operator, and conversion to size_t. Make sure that the type chosen returns true for is_pod in pod_vector.hpp. This type is later defined as tape_id_t in the CppAD namespace. {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_TAPE_ID_TYPE @cppad_tape_id_type@ /* {xrst_code} {xrst_spell_on} CPPAD_MAX_NUM_THREADS ********************* Specifies the maximum number of threads that CppAD can support (must be greater than or equal four). The user may define CPPAD_MAX_NUM_THREADS before including any of the CppAD header files. If it is not yet defined, {xrst_spell_off} {xrst_code hpp} */ # ifndef CPPAD_MAX_NUM_THREADS # define CPPAD_MAX_NUM_THREADS @cppad_max_num_threads@ # endif /* {xrst_code} {xrst_spell_on} CPPAD_HAS_MKSTEMP ***************** if true, mkstemp works in C++ on this system. {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_HAS_MKSTEMP @cppad_has_mkstemp@ /* {xrst_code} {xrst_spell_on} CPPAD_HAS_TMPNAM_S ****************** If true, tmpnam_s works in C++ on this system. {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_HAS_TMPNAM_S @cppad_has_tmpnam_s@ /* {xrst_code} {xrst_spell_on} CPPAD_NULL ********** Deprecated 2020-12-03: This preprocessor symbol was used for a null pointer before c++11. Replace it by ``nullptr`` . CPPAD_NOEXCEPT ************** Deprecated 2020-12-03: This preprocessor symbol was used for no exception before c++11, replace it by ``noexcept`` . CPPAD_NDEBUG_NOEXCEPT ===================== This preprocessor symbol is ``noexcept`` when ``NDEBUG`` is defined. Otherwise it is empty. CPPAD_C_COMPILER_CMD ******************** This is the command that runs the C compiler as a C string; i.e., surrounded by double quotes. It can be used to run the C compiler; e.g. see for :ref:`create_dll_lib-name` . {xrst_code hpp} */ # define CPPAD_C_COMPILER_CMD "@cppad_c_compiler_cmd@" /* {xrst_code} CPPAD_C_COMPILER_GNU_FLAGS ************************** If true, the C compiler uses the same flags as ``gcc`` {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_C_COMPILER_GNU_FLAGS @cppad_c_compiler_gnu_flags@ /* {xrst_code} {xrst_spell_on} CPPAD_C_COMPILER_MSVC_FLAGS *************************** If true, the C compiler uses the same flags as ``cl`` {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_C_COMPILER_MSVC_FLAGS @cppad_c_compiler_msvc_flags@ /* {xrst_code} {xrst_spell_on} CPPAD_IS_SAME_UNSIGNED_INT_SIZE_T ********************************* If true, ``unsigned int`` and ``size_t`` are the same type {xrst_code hpp} */ # define CPPAD_IS_SAME_UNSIGNED_INT_SIZE_T @cppad_is_same_unsigned_int_size_t@ /* {xrst_code} CPPAD_PADDING_BLOCK_T ********************* Is a string used to define an object that pads the block_t structure so that its size is a multiple of the size of a double. {xrst_code hpp} */ # define CPPAD_PADDING_BLOCK_T @cppad_padding_block_t@ /* {xrst_code} {xrst_end configure.hpp} */ // ------------------------------------------------- # define CPPAD_NULL nullptr # define CPPAD_NOEXCEPT noexcept // # ifdef NDEBUG # define CPPAD_NDEBUG_NOEXCEPT noexcept # else # define CPPAD_NDEBUG_NOEXCEPT # endif // ------------------------------------------------- # endif ================================================ FILE: include/cppad/core/abort_recording.hpp ================================================ # ifndef CPPAD_CORE_ABORT_RECORDING_HPP # define CPPAD_CORE_ABORT_RECORDING_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin abort_recording} Abort Recording of an Operation Sequence ######################################## Syntax ****** | ``AD`` < *Base* >:: ``abort_recording`` () Purpose ******* Sometimes it is necessary to abort the recording of an operation sequence that started with a call of the form ``Independent`` ( *x* ) If such a recording is currently in progress, ``abort_recording`` will stop the recording and delete the corresponding information. Otherwise, ``abort_recording`` has no effect. {xrst_toc_hidden example/general/abort_recording.cpp } Example ******* The file :ref:`abort_recording.cpp-name` contains an example and test of this operation. {xrst_end abort_recording} ---------------------------------------------------------------------------- */ namespace CppAD { template void AD::abort_recording(void) { local::ADTape* tape = AD::tape_ptr(); if( tape != nullptr ) AD::tape_manage(delete_tape_manage); } } # endif ================================================ FILE: include/cppad/core/abs.hpp ================================================ # ifndef CPPAD_CORE_ABS_HPP # define CPPAD_CORE_ABS_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* ------------------------------------------------------------------------------- {xrst_begin abs} {xrst_spell faq rl } AD Absolute Value Functions: abs, fabs ###################################### Syntax ****** | *y* = ``abs`` ( *x* ) | *y* = ``fabs`` ( *x* ) x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Atomic ****** In the case where *x* is an AD type, this is an :ref:`atomic operation` . Complex Types ************* The functions ``abs`` and *fabs* are not defined for the base types ``std::complex`` or ``std::complex`` because the complex ``abs`` function is not complex differentiable (see :ref:`complex types faq` ). Derivative ********** CppAD defines the derivative of the ``abs`` function is the :ref:`sign-name` function; i.e., .. math:: {\rm abs}^{(1)} ( x ) = {\rm sign} (x ) = \left\{ \begin{array}{rl} +1 & {\rm if} \; x > 0 \\ 0 & {\rm if} \; x = 0 \\ -1 & {\rm if} \; x < 0 \end{array} \right. The result for *x* == 0 used to be a directional derivative. Example ******* {xrst_toc_hidden example/general/fabs.cpp } The file :ref:`fabs.cpp-name` contains an example and test of this function. {xrst_end abs} ------------------------------------------------------------------------------- */ // BEGIN CppAD namespace namespace CppAD { template AD AD::abs_me (void) const { AD result; result.value_ = abs(value_); CPPAD_ASSERT_UNKNOWN( Parameter(result) ); // check if there is a recording in progress local::ADTape* tape = AD::tape_ptr(); if( tape == nullptr ) return result; // check if operand is a constant parameter if( tape_id_ != tape->id_ ) return result; if(ad_type_ == dynamic_enum) { // dynamic parameter argument result.taddr_ = tape->Rec_.put_dyn_par( result.value_, local::abs_dyn, taddr_ ); result.tape_id_ = tape_id_; result.ad_type_ = dynamic_enum; } else { // variable argument CPPAD_ASSERT_UNKNOWN( local::NumRes(local::AbsOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::AbsOp) == 1 ); // corresponding operand address tape->Rec_.PutArg(taddr_); // put operator in the tape result.taddr_ = tape->Rec_.PutOp(local::AbsOp); // make result a variable result.tape_id_ = tape_id_; result.ad_type_ = variable_enum; } return result; } template AD abs(const AD &x) { return x.abs_me(); } template AD abs(const VecAD_reference &x) { return x.ADBase().abs_me(); } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/abs_normal_fun.hpp ================================================ # ifndef CPPAD_CORE_ABS_NORMAL_FUN_HPP # define CPPAD_CORE_ABS_NORMAL_FUN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin abs_normal_fun} Create An Abs-normal Representation of a Function ################################################# Syntax ****** | *f* . ``abs_normal_fun`` ( *g* , *a* ) f * The object *f* has prototype ``const ADFun`` < *Base* >& *f* It represents a function :math:`f : \B{R}^n \rightarrow \B{R}^m`. We assume that the only non-smooth terms in the representation are absolute value functions and use :math:`s \in \B{Z}_+` to represent the number of these terms. n = We use *n* to denote the dimension of the domain space for *f* . m = We use *m* to denote the dimension of the range space for *f* . s = We use *s* to denote the number of absolute value terms in *f* . a * The object *a* has prototype ``ADFun`` < *Base* > *a* The initial function representation in *a* is lost. Upon return it represents the result of the absolute terms :math:`a : \B{R}^n \rightarrow \B{R}^s`; see :math:`a(x)` defined below. Note that *a* is constructed by copying *f* and then changing the dependent variables. There may be many calculations in this representation that are not necessary and can be removed using *a* . ``optimize`` () This optimization is not done automatically by ``abs_normal_fun`` because it may take a significant amount of time. zeta ==== Let :math:`\zeta_0 ( x )` denote the argument for the first absolute value term in :math:`f(x)`, :math:`\zeta_1 ( x , |\zeta_0 (x)| )` for the second term, and so on. a(x) ==== For :math:`i = 0 , \ldots , {s-1}` define .. math:: a_i (x) = | \zeta_i ( x , a_0 (x) , \ldots , a_{i-1} (x ) ) | This defines :math:`a : \B{R}^n \rightarrow \B{R}^s`. g * The object *g* has prototype ``ADFun`` < *Base* > *g* The initial function representation in *g* is lost. Upon return it represents the smooth function :math:`g : \B{R}^{n + s} \rightarrow \B{R}^{m + s}` is defined by .. math:: g( x , u ) = \left[ \begin{array}{c} y(x, u) \\ z(x, u) \end{array} \right] were :math:`y(x, u)` and :math:`z(x, u)` are defined below. z(x, u) ======= Define the smooth function :math:`z : \B{R}^{n + s} \rightarrow \B{R}^s` by .. math:: z_i ( x , u ) = \zeta_i ( x , u_0 , \ldots , u_{i-1} ) Note that the partial of :math:`z_i` with respect to :math:`u_j` is zero for :math:`j \geq i`. y(x, u) ======= There is a smooth function :math:`y : \B{R}^{n + s} \rightarrow \B{R}^m` such that :math:`y( x , u ) = f(x)` whenever :math:`u = a(x)`. Affine Approximation ******************** We define the affine approximations .. math:: :nowrap: \begin{eqnarray} y[ \hat{x} ]( x , u ) & = & y ( \hat{x}, a( \hat{x} ) ) + \partial_x y ( \hat{x}, a( \hat{x} ) ) ( x - \hat{x} ) + \partial_u y ( \hat{x}, a( \hat{x} ) ) ( u - a( \hat{x} ) ) \\ z[ \hat{x} ]( x , u ) & = & z ( \hat{x}, a( \hat{x} ) ) + \partial_x z ( \hat{x}, a( \hat{x} ) ) ( x - \hat{x} ) + \partial_u z ( \hat{x}, a( \hat{x} ) ) ( u - a( \hat{x} ) ) \end{eqnarray} It follows that .. math:: :nowrap: \begin{eqnarray} y( x , u ) & = & y[ \hat{x} ]( x , u ) + o ( x - \hat{x}, u - a( \hat{x} ) ) \\ z( x , u ) & = & z[ \hat{x} ]( x , u ) + o ( x - \hat{x}, u - a( \hat{x} ) ) \end{eqnarray} Abs-normal Approximation ************************ Approximating a(x) ================== The function :math:`a(x)` is not smooth, but it is equal to :math:`| z(x, u) |` when :math:`u = a(x)`. Furthermore .. math:: z[ \hat{x} ]( x , u ) = z ( \hat{x}, a( \hat{x} ) ) + \partial_x z ( \hat{x}, a( \hat{x} ) ) ( x - \hat{x} ) + \partial_u z ( \hat{x}, a( \hat{x} ) ) ( u - a( \hat{x} ) ) The partial of :math:`z_i` with respect to :math:`u_j` is zero for :math:`j \geq i`. It follows that .. math:: z_i [ \hat{x} ]( x , u ) = z_i ( \hat{x}, a( \hat{x} ) ) + \partial_x z_i ( \hat{x}, a( \hat{x} ) ) ( x - \hat{x} ) + \sum_{j < i} \partial_{u(j)} z_i ( \hat{x}, a( \hat{x} ) ) ( u_j - a_j ( \hat{x} ) ) Considering the case :math:`i = 0` we define .. math:: a_0 [ \hat{x} ]( x ) = | z_0 [ \hat{x} ]( x , u ) | = \left| z_0 ( \hat{x}, a( \hat{x} ) ) + \partial_x z_0 ( \hat{x}, a( \hat{x} ) ) ( x - \hat{x} ) \right| It follows that .. math:: a_0 (x) = a_0 [ \hat{x} ]( x ) + o ( x - \hat{x} ) In general, we define :math:`a_i [ \hat{x} ]` using :math:`a_j [ \hat{x} ]` for :math:`j < i` as follows: .. math:: a_i [ \hat{x} ]( x ) = \left | z_i ( \hat{x}, a( \hat{x} ) ) + \partial_x z_i ( \hat{x}, a( \hat{x} ) ) ( x - \hat{x} ) + \sum_{j < i} \partial_{u(j)} z_i ( \hat{x}, a( \hat{x} ) ) ( a_j [ \hat{x} ] ( x ) - a_j ( \hat{x} ) ) \right| It follows that .. math:: a (x) = a[ \hat{x} ]( x ) + o ( x - \hat{x} ) Note that in the case where :math:`z(x, u)` and :math:`y(x, u)` are affine, .. math:: a[ \hat{x} ]( x ) = a( x ) Approximating f(x) ================== .. math:: f(x) = y ( x , a(x ) ) = y [ \hat{x} ] ( x , a[ \hat{x} ] ( x ) ) + o( x - \hat{x} ) Correspondence to Literature **************************** Using the notation :math:`Z = \partial_x z(\hat{x}, \hat{u})`, :math:`L = \partial_u z(\hat{x}, \hat{u})`, :math:`J = \partial_x y(\hat{x}, \hat{u})`, :math:`Y = \partial_u y(\hat{x}, \hat{u})`, the approximation for :math:`z` and :math:`y` are .. math:: :nowrap: \begin{eqnarray} z[ \hat{x} ]( x , u ) & = & z ( \hat{x}, a( \hat{x} ) ) + Z ( x - \hat{x} ) + L ( u - a( \hat{x} ) ) \\ y[ \hat{x} ]( x , u ) & = & y ( \hat{x}, a( \hat{x} ) ) + J ( x - \hat{x} ) + Y ( u - a( \hat{x} ) ) \end{eqnarray} Moving the terms with :math:`\hat{x}` together, we have .. math:: :nowrap: \begin{eqnarray} z[ \hat{x} ]( x , u ) & = & z ( \hat{x}, a( \hat{x} ) ) - Z \hat{x} - L a( \hat{x} ) + Z x + L u \\ y[ \hat{x} ]( x , u ) & = & y ( \hat{x}, a( \hat{x} ) ) - J \hat{x} - Y a( \hat{x} ) + J x + Y u \end{eqnarray} Using the notation :math:`c = z ( \hat{x}, \hat{u} ) - Z \hat{x} - L \hat{u}`, :math:`b = y ( \hat{x}, \hat{u} ) - J \hat{x} - Y \hat{u}`, we have .. math:: :nowrap: \begin{eqnarray} z[ \hat{x} ]( x , u ) & = & c + Z x + L u \\ y[ \hat{x} ]( x , u ) & = & b + J x + Y u \end{eqnarray} Considering the affine case, where the approximations are exact, and choosing :math:`u = a(x) = |z(x, u)|`, we obtain .. math:: :nowrap: \begin{eqnarray} z( x , a(x ) ) & = & c + Z x + L |z( x , a(x ) )| \\ y( x , a(x ) ) & = & b + J x + Y |z( x , a(x ) )| \end{eqnarray} This is Equation (2) of the :ref:`example_abs_normal@Reference` . {xrst_toc_hidden example/abs_normal/abs_normal.xrst } Example ******* The file :ref:`abs_get_started.cpp-name` contains an example and test using this operation. The section :ref:`example_abs_normal-name` has a links to all the abs normal examples. {xrst_end abs_normal_fun} ------------------------------------------------------------------------------- */ /*! file abs_normal_fun.hpp Create an abs-normal representation of a function */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! Create an abs-normal representation of an ADFun object. \tparam Base base type for this abs-normal form and for the function being represented; i.e., f. \param f is the function that this object will represent in abs-normal form. This is effectively const except that the play back state play_ is used. */ # ifndef NDEBUG # define CPPAD_J_PAR_EQUAL_REC j_par = (size_t) rec # else # define CPPAD_J_PAR_EQUAL_REC rec # endif template void ADFun::abs_normal_fun(ADFun& g, ADFun& a) const { using namespace local; // ----------------------------------------------------------------------- // Forward sweep to determine number of absolute value operations in f // ----------------------------------------------------------------------- // The argument and result index in f for each absolute value operator CppAD::vector f_abs_arg; CppAD::vector f_abs_res; // op_code_var op; // this operator const addr_t* arg = nullptr; // arguments for this operator size_t i_var; // variable index for this operator local::play::const_sequential_iterator itr = play_.begin(); itr.op_info(op, arg, i_var); CPPAD_ASSERT_UNKNOWN( op == BeginOp ); // bool more_operators = true; while( op != EndOp ) { // next op (++itr).op_info(op, arg, i_var); switch( op ) { // absolute value operator case AbsOp: CPPAD_ASSERT_NARG_NRES(op, 1, 1); f_abs_arg.push_back( arg[0] ); f_abs_res.push_back( i_var ); break; default: break; } } // ------------------------------------------------------------------------ // Forward sweep to create new recording // ------------------------------------------------------------------------ // dynamic parameter information in player const pod_vector& par_is_dyn( play_.par_is_dyn() ); const pod_vector& dyn_par_op( play_.dyn_par_op() ); const pod_vector& dyn_par_arg( play_.dyn_par_arg() ); // // recorder for new operation sequence recorder rec; // // number of parameters in both operation sequences size_t num_par = play_.num_par_all(); // // number of independent dynamic parameters size_t n_dyn_independent = play_.num_dynamic_par(); rec.set_n_dyn_independent(n_dyn_independent); // // set all parameter to be exactly the same in rec as in play size_t i_dyn = 0; // dynamic parameter index size_t i_arg = 0; // dynamic parameter operator argument index for(size_t i_par = 0; i_par < num_par; ++i_par) { # ifndef NDEBUG size_t j_par = 0; # endif // value of this parameter Base par = play_.par_one(i_par); if( ! par_is_dyn[i_par] ) CPPAD_J_PAR_EQUAL_REC.put_con_par(par); else { // operator for this dynamic parameter op_code_dyn op_dyn = op_code_dyn( dyn_par_op[i_dyn] ); CPPAD_ASSERT_KNOWN( op_dyn != local::atom_dyn, "abs_normal_fun: not yet implemented for " "atomic dynamic parameter functions" ); // // number of arguments for this dynamic parameter size_t n_arg = num_arg_dyn(op_dyn); // switch(n_arg) { case 0: CPPAD_J_PAR_EQUAL_REC.put_dyn_par(par, op_dyn); break; case 1: CPPAD_J_PAR_EQUAL_REC.put_dyn_par(par, op_dyn, dyn_par_arg[i_arg + 0] ); break; case 2: CPPAD_J_PAR_EQUAL_REC.put_dyn_par(par, op_dyn, dyn_par_arg[i_arg + 0] , dyn_par_arg[i_arg + 1] ); break; case 5: CPPAD_J_PAR_EQUAL_REC.put_dyn_cond_exp(par, CompareOp( dyn_par_arg[i_arg + 0] ) , dyn_par_arg[i_arg + 1] , dyn_par_arg[i_arg + 2] , dyn_par_arg[i_arg + 3] , dyn_par_arg[i_arg + 4] ); break; default: CPPAD_ASSERT_UNKNOWN(false); } ++i_dyn; i_arg += n_arg; } CPPAD_ASSERT_UNKNOWN( j_par == i_par ); } // // number of variables in both operation sequences // (the AbsOp operators are replace by InvOp operators) const size_t num_var = play_.num_var(); // // mapping from old variable index to new variable index CPPAD_ASSERT_UNKNOWN( size_t( (std::numeric_limits::max)() ) >= num_var ); CppAD::vector f2g_var(num_var); for(i_var = 0; i_var < num_var; i_var++) f2g_var[i_var] = addr_t( num_var ); // invalid (should not be used) // // record the independent variables in f itr = play_.begin(); itr.op_info(op, arg, i_var); CPPAD_ASSERT_UNKNOWN( op == BeginOp ); more_operators = true; while( more_operators ) { switch( op ) { // phantom variable case BeginOp: CPPAD_ASSERT_NARG_NRES(op, 1, 1); CPPAD_ASSERT_UNKNOWN( arg[0] == 0 ); rec.PutArg(0); f2g_var[i_var] = rec.PutOp(op); break; // independent variables case InvOp: CPPAD_ASSERT_NARG_NRES(op, 0, 1); f2g_var[i_var] = rec.PutOp(op); break; // end of independent variables default: more_operators = false; break; } if( more_operators ) (++itr).op_info(op, arg, i_var); } // add one for the phantom variable CPPAD_ASSERT_UNKNOWN( 1 + Domain() == i_var ); // // record the independent variables corresponding AbsOp results size_t index_abs; for(index_abs = 0; index_abs < f_abs_res.size(); index_abs++) f2g_var[ f_abs_res[index_abs] ] = rec.PutOp(InvOp); // // used to hold new argument vector addr_t new_arg[6]; // // now loop through the rest of the more_operators = true; index_abs = 0; while( more_operators ) { addr_t mask; // temporary used in some switch cases switch( op ) { // check setting of f_abs_arg and f_abs_res; case AbsOp: CPPAD_ASSERT_NARG_NRES(op, 1, 1); CPPAD_ASSERT_UNKNOWN( f_abs_arg[index_abs] == arg[0] ); CPPAD_ASSERT_UNKNOWN( f_abs_res[index_abs] == i_var ); CPPAD_ASSERT_UNKNOWN( f2g_var[i_var] > 0 ); ++index_abs; break; // These operators come at beginning of take and are handled above case InvOp: CPPAD_ASSERT_UNKNOWN(false); break; // --------------------------------------------------------------- // Unary operators, argument a parameter, one result case ParOp: CPPAD_ASSERT_NARG_NRES(op, 1, 1); new_arg[0] = arg[0]; // parameter rec.PutArg( new_arg[0] ); f2g_var[i_var] = rec.PutOp(op); break; // -------------------------------------------------------------- // Unary operators, argument a variable, one result // (excluding the absolute value operator AbsOp) case AcosOp: case AcoshOp: case AsinOp: case AsinhOp: case AtanOp: case AtanhOp: case CosOp: case CoshOp: case ExpOp: case Expm1Op: case LogOp: case Log1pOp: case NegOp: case SignOp: case SinOp: case SinhOp: case SqrtOp: case TanOp: case TanhOp: // some of these operators have an auxiliary result; e.g., // sine and cosine are computed togeather. CPPAD_ASSERT_UNKNOWN( NumArg(op) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(op) == 1 || NumRes(op) == 2 ); CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[0] ] ) < num_var ); new_arg[0] = f2g_var[ arg[0] ]; rec.PutArg( new_arg[0] ); f2g_var[i_var] = rec.PutOp( op ); break; case ErfOp: case ErfcOp: CPPAD_ASSERT_NARG_NRES(op, 3, 5); CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[0] ] ) < num_var ); // Error function is a special case // second argument is always the parameter 0 // third argument is always the parameter 2 / sqrt(pi) rec.PutArg( arg[1] ); // parameter rec.PutArg( arg[2] ); // parameter f2g_var[i_var] = rec.PutOp(op); break; // -------------------------------------------------------------- // Binary operators, left variable, right parameter, one result case SubvpOp: case DivvpOp: case PowvpOp: case ZmulvpOp: CPPAD_ASSERT_NARG_NRES(op, 2, 1); CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[0] ] ) < num_var ); new_arg[0] = f2g_var[ arg[0] ]; new_arg[1] = arg[1]; // parameter rec.PutArg( new_arg[0], new_arg[1] ); f2g_var[i_var] = rec.PutOp(op); break; // --------------------------------------------------- // Binary operators, left index, right variable, one result case DisOp: CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[1] ] ) < num_var ); new_arg[0] = arg[0]; new_arg[1] = f2g_var[ arg[1] ]; rec.PutArg( new_arg[0], new_arg[1] ); f2g_var[i_var] = rec.PutOp(op); break; // -------------------------------------------------------------- // Binary operators, left parameter, right variable, one result case AddpvOp: case SubpvOp: case MulpvOp: case DivpvOp: case PowpvOp: case ZmulpvOp: # ifndef NDEBUG if( op == PowpvOp ) { CPPAD_ASSERT_NARG_NRES(op, 2, 3); } else { CPPAD_ASSERT_NARG_NRES(op, 2, 1); } # endif CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[1] ] ) < num_var ); new_arg[0] = arg[0]; // parameter new_arg[1] = f2g_var[ arg[1] ]; rec.PutArg( new_arg[0], new_arg[1] ); f2g_var[i_var] = rec.PutOp(op); break; // -------------------------------------------------------------- // Binary operators, left and right variables, one result case AddvvOp: case SubvvOp: case MulvvOp: case DivvvOp: case PowvvOp: case ZmulvvOp: # ifndef NDEBUG if( op == PowvvOp ) { CPPAD_ASSERT_NARG_NRES(op, 2, 3); } else { CPPAD_ASSERT_NARG_NRES(op, 2, 1); } # endif CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[0] ] ) < num_var ); CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[1] ] ) < num_var ); new_arg[0] = f2g_var[ arg[0] ]; new_arg[1] = f2g_var[ arg[1] ]; rec.PutArg( new_arg[0], new_arg[1] ); f2g_var[i_var] = rec.PutOp(op); break; // --------------------------------------------------- // Conditional expression operators case CExpOp: CPPAD_ASSERT_NARG_NRES(op, 6, 1); new_arg[0] = arg[0]; new_arg[1] = arg[1]; mask = 1; for(size_t i = 2; i < 6; i++) { if( arg[1] & mask ) { CPPAD_ASSERT_UNKNOWN( size_t(f2g_var[arg[i]]) < num_var ); new_arg[i] = f2g_var[ arg[i] ]; } else new_arg[i] = arg[i]; // parameter mask = mask << 1; } rec.PutArg( new_arg[0] , new_arg[1] , new_arg[2] , new_arg[3] , new_arg[4] , new_arg[5] ); f2g_var[i_var] = rec.PutOp(op); break; // -------------------------------------------------- // Operators with no arguments and no results case EndOp: CPPAD_ASSERT_NARG_NRES(op, 0, 0); rec.PutOp(op); more_operators = false; break; // --------------------------------------------------- // Operations with two arguments and no results case LepvOp: case LtpvOp: case EqpvOp: case NepvOp: CPPAD_ASSERT_NARG_NRES(op, 2, 0); new_arg[0] = arg[0]; // parameter new_arg[1] = f2g_var[ arg[1] ]; rec.PutArg(new_arg[0], new_arg[1]); rec.PutOp(op); break; // case LevpOp: case LtvpOp: CPPAD_ASSERT_NARG_NRES(op, 2, 0); new_arg[0] = f2g_var[ arg[0] ]; new_arg[1] = arg[1]; // parameter rec.PutArg(new_arg[0], new_arg[1]); rec.PutOp(op); break; // case LevvOp: case LtvvOp: case EqvvOp: case NevvOp: CPPAD_ASSERT_NARG_NRES(op, 2, 0); new_arg[0] = f2g_var[ arg[0] ]; new_arg[1] = f2g_var[ arg[1] ]; rec.PutArg(new_arg[0], new_arg[1]); rec.PutOp(op); break; // --------------------------------------------------- // print forward operator case PriOp: CPPAD_ASSERT_NARG_NRES(op, 5, 0); // // arg[0] new_arg[0] = arg[0]; // // arg[1] if( arg[0] & 1 ) { CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[1] ] ) < num_var ); new_arg[1] = f2g_var[ arg[1] ]; } else { new_arg[1] = arg[1]; // parameter } // // arg[3] if( arg[0] & 2 ) { CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[3] ] ) < num_var ); new_arg[3] = f2g_var[ arg[3] ]; } else { new_arg[3] = arg[3]; // parameter } new_arg[2] = rec.PutTxt( play_.GetTxt(size_t(arg[2])) ); new_arg[4] = rec.PutTxt( play_.GetTxt(size_t(arg[4])) ); // rec.PutArg( new_arg[0] , new_arg[1] , new_arg[2] , new_arg[3] , new_arg[4] ); // no result rec.PutOp(op); break; // --------------------------------------------------- // VecAD operators // Load using a parameter index case LdpOp: CPPAD_ASSERT_NARG_NRES(op, 3, 1); new_arg[0] = arg[0]; new_arg[1] = arg[1]; // parameter new_arg[2] = arg[2]; rec.PutArg( new_arg[0], new_arg[1], new_arg[2] ); f2g_var[i_var] = rec.PutLoadOp(op); break; // Load using a variable index case LdvOp: CPPAD_ASSERT_NARG_NRES(op, 3, 1); CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[1] ] ) < num_var ); new_arg[0] = arg[0]; new_arg[1] = f2g_var[ arg[1] ]; new_arg[2] = arg[2]; rec.PutArg( new_arg[0], new_arg[1], new_arg[2] ); f2g_var[i_var] = rec.PutLoadOp(op); break; // Store a parameter using a parameter index case StppOp: CPPAD_ASSERT_NARG_NRES(op, 3, 0); new_arg[0] = arg[0]; new_arg[1] = arg[1]; // parameter new_arg[2] = arg[2]; // parameter rec.PutArg( new_arg[0], new_arg[1], new_arg[2] ); rec.PutOp(op); break; // Store a parameter using a variable index case StvpOp: CPPAD_ASSERT_NARG_NRES(op, 3, 0); CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[1] ] ) < num_var ); new_arg[0] = arg[0]; new_arg[1] = f2g_var[ arg[1] ]; new_arg[2] = arg[2]; // parameter rec.PutArg( new_arg[0], new_arg[1], new_arg[2] ); rec.PutOp(op); break; // Store a variable using a parameter index case StpvOp: CPPAD_ASSERT_NARG_NRES(op, 3, 0); CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[2] ] ) < num_var ); new_arg[0] = arg[0]; new_arg[1] = arg[1]; // parameter new_arg[2] = f2g_var[ arg[2] ]; rec.PutArg( new_arg[0], new_arg[1], new_arg[2] ); rec.PutOp(op); break; // Store a variable using a variable index case StvvOp: CPPAD_ASSERT_NARG_NRES(op, 3, 0); CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[1] ] ) < num_var ); CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[2] ] ) < num_var ); new_arg[0] = arg[0]; new_arg[1] = f2g_var[ arg[1] ]; new_arg[2] = f2g_var[ arg[2] ]; rec.PutArg( new_arg[0], new_arg[1], new_arg[2] ); break; // ----------------------------------------------------------- // atomic function call operators case AFunOp: CPPAD_ASSERT_NARG_NRES(op, 4, 0); // atom_index, atom_old, atom_n, atom_m rec.PutArg(arg[0], arg[1], arg[2], arg[3]); rec.PutOp(AFunOp); break; case FunapOp: CPPAD_ASSERT_NARG_NRES(op, 1, 0); new_arg[0] = arg[0]; // parameter rec.PutArg(new_arg[0]); rec.PutOp(FunapOp); break; case FunavOp: CPPAD_ASSERT_NARG_NRES(op, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[arg[0]] ) < num_var ); new_arg[0] = f2g_var[ arg[0] ]; rec.PutArg(new_arg[0]); rec.PutOp(FunavOp); break; case FunrpOp: CPPAD_ASSERT_NARG_NRES(op, 1, 0); new_arg[0] = arg[0]; // parameter rec.PutArg(new_arg[0]); rec.PutOp(FunrpOp); break; case FunrvOp: CPPAD_ASSERT_NARG_NRES(op, 0, 1); f2g_var[i_var] = rec.PutOp(FunrvOp); break; // --------------------------------------------------- // all cases should be handled above default: CPPAD_ASSERT_UNKNOWN(false); } if( more_operators ) (++itr).op_info(op, arg, i_var); } // Check a few expected results CPPAD_ASSERT_UNKNOWN( rec.num_var_op() == play_.num_var_op() ); CPPAD_ASSERT_UNKNOWN( rec.num_var() == play_.num_var() ); CPPAD_ASSERT_UNKNOWN( rec.num_var_load() == play_.num_var_load() ); // ----------------------------------------------------------------------- // Use rec to create the function g // ----------------------------------------------------------------------- // number of variables in the recording g.num_var_tape_ = rec.num_var(); // dimension cskip_op vector to number of operators g.cskip_op_.resize( rec.num_var_op() ); // independent variables in g: (x, u) size_t s = f_abs_res.size(); size_t n = Domain(); g.ind_taddr_.resize(n + s); // (x, u) for(size_t j = 0; j < n; j++) { g.ind_taddr_[j] = size_t( f2g_var[ ind_taddr_[j] ] ); CPPAD_ASSERT_UNKNOWN( g.ind_taddr_[j] == j + 1 ); } for(size_t j = 0; j < s; j++) { g.ind_taddr_[n + j] = size_t( f2g_var[ f_abs_res[j] ] ); CPPAD_ASSERT_UNKNOWN( g.ind_taddr_[n + j] == n + j + 1 ); } // dependent variable in g: (y, z) CPPAD_ASSERT_UNKNOWN( s == f_abs_arg.size() ); size_t m = Range(); g.dep_taddr_.resize(m + s); for(size_t i = 0; i < m; i++) { g.dep_taddr_[i] = size_t( f2g_var[ dep_taddr_[i] ] ); CPPAD_ASSERT_UNKNOWN( g.dep_taddr_[i] < num_var ); } for(size_t i = 0; i < s; i++) { g.dep_taddr_[m + i] = size_t( f2g_var[ f_abs_arg[i] ] ); CPPAD_ASSERT_UNKNOWN( g.dep_taddr_[m + i] < num_var ); } // which dependent variables are parameters g.dep_parameter_.resize(m + s); for(size_t i = 0; i < m; i++) g.dep_parameter_[i] = dep_parameter_[i]; for(size_t i = 0; i < s; i++) g.dep_parameter_[m + i] = false; // free memory allocated for sparse Jacobian calculation // (the results are no longer valid) g.for_jac_sparse_pack_.resize(0, 0); g.for_jac_sparse_set_.resize(0, 0); // free taylor coefficient memory g.taylor_.clear(); g.num_order_taylor_ = 0; g.cap_order_taylor_ = 0; // Transferring the recording swaps its vectors so do this last // replace the recording in g (this ADFun object) g.play_.get_recording(rec, n + s); // resize subgraph_info_ g.subgraph_info_.resize( g.ind_taddr_.size(), // n_ind g.dep_taddr_.size(), // n_dep g.play_.num_var_op(), // n_op g.play_.num_var() // n_var ); // ------------------------------------------------------------------------ // Create the function a // ------------------------------------------------------------------------ // start with a copy of f a = *this; // dependent variables in a(x) CPPAD_ASSERT_UNKNOWN( s == f_abs_arg.size() ); a.dep_taddr_.resize(s); for(size_t i = 0; i < s; i++) { a.dep_taddr_[i] = f_abs_res[i]; CPPAD_ASSERT_UNKNOWN( a.dep_taddr_[i] < num_var ); } // free memory allocated for sparse Jacobian calculation // (the results are no longer valid) a.for_jac_sparse_pack_.resize(0, 0); a.for_jac_sparse_set_.resize(0, 0); // free taylor coefficient memory a.taylor_.clear(); a.num_order_taylor_ = 0; a.cap_order_taylor_ = 0; } // preprocessor symbols that are local to this file # undef CPPAD_J_PAR_EQUAL_REC } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/ad.hpp ================================================ # ifndef CPPAD_CORE_AD_HPP # define CPPAD_CORE_AD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- // simple AD operations that must be defined for AD as well as base class # include # include // define the template classes that are used by the AD template class # include # include # include # include # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // tape_manage_enum typedef enum { new_tape_manage, delete_tape_manage } tape_manage_enum; template class AD { private : // ----------------------------------------------------------------------- // Base type value for this object Base value_; // // tape for this object tape_id_t tape_id_; // // tape address for this object // (when tape_id is current tape for AD) addr_t taddr_; // // sub-type for this object // (when tape_id is current tape for AD) ad_type_enum ad_type_; // ----------------------------------------------------------------------- // enable use of AD in parallel mode template friend void parallel_ad(void); // template friend functions where template parameter is not bound template friend void Independent( ADVector& x , size_t abort_op_index , bool record_compare , ADVector& dynamic ); // one argument functions friend bool Constant (const AD &u); friend bool Constant (const VecAD &u); // friend bool Dynamic (const AD &u); friend bool Dynamic (const VecAD &u); // friend bool Parameter (const AD &u); friend bool Parameter (const VecAD &u); // friend bool Variable (const AD &u); friend bool Variable (const VecAD &u); // friend int Integer (const AD &u); friend AD Var2Par (const AD &u); // friend unsigned short hash_code (const AD &u); // // power function friend AD pow (const AD &x, const AD &y); // azmul function friend AD azmul (const AD &x, const AD &y); // order determining functions, see ordered.hpp friend bool GreaterThanZero (const AD &x); friend bool GreaterThanOrZero (const AD &x); friend bool LessThanZero (const AD &x); friend bool LessThanOrZero (const AD &x); friend bool abs_geq (const AD& x, const AD& y); // The identical property functions, see identical.hpp friend bool IdenticalCon (const AD &x); friend bool IdenticalZero (const AD &x); friend bool IdenticalOne (const AD &x); friend bool IdenticalEqualCon (const AD &x, const AD &y); // EqualOpSeq function friend bool EqualOpSeq (const AD &u, const AD &v); // NearEqual function friend bool NearEqual ( const AD &x, const AD &y, const Base &r, const Base &a); friend bool NearEqual ( const Base &x, const AD &y, const Base &r, const Base &a); friend bool NearEqual ( const AD &x, const Base &y, const Base &r, const Base &a); // CondExp function friend AD CondExpOp ( enum CompareOp cop , const AD &left , const AD &right , const AD &trueCase , const AD &falseCase ); // classes friend class local::ADTape; friend class local::dyn_recorder; friend class local::recorder; friend class ADFun; friend class atomic_base; friend class atomic_three; friend class atomic_four; friend class discrete; friend class VecAD; friend class VecAD_reference; // arithematic binary operators friend AD operator + (const AD &left, const AD &right); friend AD operator - (const AD &left, const AD &right); friend AD operator * (const AD &left, const AD &right); friend AD operator / (const AD &left, const AD &right); // comparison operators friend bool operator < (const AD &left, const AD &right); friend bool operator <= (const AD &left, const AD &right); friend bool operator > (const AD &left, const AD &right); friend bool operator >= (const AD &left, const AD &right); friend bool operator == (const AD &left, const AD &right); friend bool operator != (const AD &left, const AD &right); // input operator friend std::istream& operator >> (std::istream &is, AD &x); // output operations friend std::ostream& operator << (std::ostream &os, const AD &x); friend void PrintFor ( const AD& flag , const char* before , const AD& var , const char* after ); public: // type of value typedef Base value_type; // implicit default constructor AD(void); // destructor ~AD(void) { } // use default implicit copy constructor // AD(const AD &x); # ifdef CPPAD_FOR_TMB // TMB would rather have implicit construction from double, // CppAD uses default constructor and assignment to double instead. AD(const double &d); # else // implicit construction from base type AD(const Base &b); # endif // implicit constructor from VecAD::reference AD(const VecAD_reference &x); // explicit construction from some other type (deprecated) template explicit AD(const T &t); // conversion from AD to Base type friend Base Value (const AD &x); // use default assignment operator // AD& operator=(const AD &x); // assignment from base type AD& operator=(const Base &b); // assignment from VecAD::reference AD& operator=(const VecAD_reference &x); // assignment from some other type template AD& operator=(const T &right); // compound assignment operators AD& operator += (const AD &right); AD& operator -= (const AD &right); AD& operator *= (const AD &right); AD& operator /= (const AD &right); // unary operators AD operator +(void) const; AD operator -(void) const; // interface so these functions need not be friends AD abs_me(void) const; AD acos_me(void) const; AD asin_me(void) const; AD atan_me(void) const; AD cos_me(void) const; AD cosh_me(void) const; AD exp_me(void) const; AD fabs_me(void) const; AD log_me(void) const; AD sin_me(void) const; AD sign_me(void) const; AD sinh_me(void) const; AD sqrt_me(void) const; AD tan_me(void) const; AD tanh_me(void) const; AD asinh_me(void) const; AD acosh_me(void) const; AD atanh_me(void) const; AD erf_me(bool complemnet) const; AD expm1_me(void) const; AD log1p_me(void) const; // ---------------------------------------------------------- // static public member functions // abort current AD recording static void abort_recording(void); // set the maximum number of OpenMP threads (deprecated) static void omp_max_thread(size_t number); // These functions declared public so can be accessed by user through // a macro interface and are not intended for direct use. // The macro interface is documented in bool_fun.hpp. // Developer documentation for these functions is in bool_fun.hpp static bool UnaryBool( bool FunName(const Base &x), const AD &x ); static bool BinaryBool( bool FunName(const Base &x, const Base &y), const AD &x , const AD &y ); private: // ----------------------------------------------------------------- // Make this parameter a new variable void make_variable(tape_id_t id, addr_t taddr) { CPPAD_ASSERT_UNKNOWN( Parameter(*this) ); // currently a par CPPAD_ASSERT_UNKNOWN( taddr > 0 ); // sure valid taddr tape_id_ = id; taddr_ = taddr; ad_type_ = variable_enum; } // ----------------------------------------------------------------- // Make this parameter a new dynamic void make_dynamic(tape_id_t id, addr_t taddr) { CPPAD_ASSERT_UNKNOWN( Parameter(*this) ); // currently a par CPPAD_ASSERT_UNKNOWN( taddr > 0 ); // sure valid taddr tape_id_ = id; taddr_ = taddr; ad_type_ = dynamic_enum; } // --------------------------------------------------------------- // tape linking functions // // not static local::ADTape* tape_this(void) const; // // static static tape_id_t* tape_id_ptr(size_t thread); static local::ADTape** tape_handle(size_t thread); static local::ADTape* tape_manage(tape_manage_enum job); static local::ADTape* tape_ptr(void); static local::ADTape* tape_ptr(tape_id_t tape_id); }; // --------------------------------------------------------------------------- } // END_CPPAD_NAMESPACE // tape linking private functions # include // operations that expect the AD template class to be defined # endif ================================================ FILE: include/cppad/core/ad_assign.hpp ================================================ # ifndef CPPAD_CORE_AD_ASSIGN_HPP # define CPPAD_CORE_AD_ASSIGN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* ------------------------------------------------------------------------------ {xrst_begin ad_assign} AD Assignment Operator ###################### Syntax ****** | *y* = *x* Purpose ******* Assigns the value in *x* to the object *y* . In either case, x * The argument *x* has prototype ``const`` *Type* & *x* where *Type* is ``VecAD`` < *Base* >:: ``reference`` , ``AD`` < *Base* > , *Base* , or any type that has an implicit constructor of the form *Base* ( *x* ) . y * The target *y* has prototype ``AD`` < *Base* > *y* Example ******* {xrst_toc_hidden example/general/ad_assign.cpp } The file :ref:`ad_assign.cpp-name` contain examples and tests of these operations. It test returns true if it succeeds and false otherwise. {xrst_end ad_assign} ------------------------------------------------------------------------------ */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file ad_assign.hpp AD constructors and and copy operations. */ /*! \page AD_default_assign Use default assignment operator because they may be optimized better than the code below: \code template AD& AD::operator=(const AD &right) { value_ = right.value_; tape_id_ = right.tape_id_; taddr_ = right.taddr_; ad_type_ = right.ad_type_; return *this; } \endcode */ /*! Assignment to Base type value. \tparam Base Base type for this AD object. \param b is the Base type value being assignment to this AD object. The tape identifier will be an invalid tape identifier, so this object is initially a parameter. */ template AD& AD::operator=(const Base &b) { value_ = b; tape_id_ = 0; // CPPAD_ASSERT_UNKNOWN( ! ( Variable(*this) || Dynamic(*this) ) ); return *this; } /*! Assignment to an ADVec element drops the vector information. \tparam Base Base type for this AD object. */ template AD& AD::operator=(const VecAD_reference &x) { *this = x.ADBase(); CPPAD_ASSERT_UNKNOWN( ! Dynamic(*this) ); return *this; } /*! Assignment from any other type, converts to Base type, and then uses assignment from Base type. \tparam Base Base type for this AD object. \tparam T is the the type that is being assigned to AD. There must be an assignment for Base from Type. \param t is the object that is being assigned to an AD object. */ template template AD& AD::operator=(const T &t) { *this = Base(t); CPPAD_ASSERT_UNKNOWN( ! ( Variable(*this) || Dynamic(*this) ) ); return *this; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/ad_binary.hpp ================================================ # ifndef CPPAD_CORE_AD_BINARY_HPP # define CPPAD_CORE_AD_BINARY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* ------------------------------------------------------------------------------- {xrst_begin ad_binary} {xrst_spell div } AD Binary Arithmetic Operators ############################## Syntax ****** | *z* = *x* *Op* *y* Purpose ******* Performs arithmetic operations where either *x* or *y* has type ``AD`` < *Base* > or :ref:`VecAD@VecAD\::reference` . Op ** The operator *Op* is one of the following .. csv-table:: :widths: auto **Op**,**Meaning** ``+``,*z* is *x* plus *y* ``-``,*z* is *x* minus *y* ``*``,*z* is *x* times *y* ``/``,*z* is *x* divided by *y* Base **** The type *Base* is determined by the operand that has type ``AD`` < *Base* > or ``VecAD`` < *Base* >:: ``reference`` . x * The operand *x* has the following prototype ``const`` *Type* & *x* where *Type* is ``VecAD`` < *Base* >:: ``reference`` , ``AD`` < *Base* > , *Base* , or ``double`` . y * The operand *y* has the following prototype ``const`` *Type* & *y* where *Type* is ``VecAD`` < *Base* >:: ``reference`` , ``AD`` < *Base* > , *Base* , or ``double`` . z * The result *z* has the following prototype *Type* *z* where *Type* is ``AD`` < *Base* > . Operation Sequence ****************** This is an :ref:`atomic_base` :ref:`glossary@AD of Base` operation and hence it is part of the current AD of *Base* :ref:`operation sequence` . {xrst_toc_hidden example/general/add.cpp example/general/sub.cpp example/general/mul.cpp example/general/div.cpp } Zero Special Cases ****************** Suppose that an AD *value* is :ref:`identically zero ` . Then the following results will be identically zero no matter what *other* is: value * other, other * value, value / other. This may be unexpected when *other* is nan (or when it is zero in the division case). This is closely related to the :ref:`azmul-name` function. Example ******* The following files contain examples and tests of these functions. Each test returns true if it succeeds and false otherwise. .. csv-table:: :widths: auto add.cpp,:ref:`add.cpp-title` sub.cpp,:ref:`sub.cpp-title` mul.cpp,:ref:`mul.cpp-title` div.cpp,:ref:`div.cpp-title` Derivative ********** If :math:`f` and :math:`g` are :ref:`Base functions` Addition ======== .. math:: \D{[ f(x) + g(x) ]}{x} = \D{f(x)}{x} + \D{g(x)}{x} Subtraction =========== .. math:: \D{[ f(x) - g(x) ]}{x} = \D{f(x)}{x} - \D{g(x)}{x} Multiplication ============== .. math:: \D{[ f(x) * g(x) ]}{x} = g(x) * \D{f(x)}{x} + f(x) * \D{g(x)}{x} Division ======== .. math:: \D{[ f(x) / g(x) ]}{x} = [1/g(x)] * \D{f(x)}{x} - [f(x)/g(x)^2] * \D{g(x)}{x} {xrst_end ad_binary} ----------------------------------------------------------------------------- */ # include # include # include # include # endif ================================================ FILE: include/cppad/core/ad_ctor.hpp ================================================ # ifndef CPPAD_CORE_AD_CTOR_HPP # define CPPAD_CORE_AD_CTOR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* ------------------------------------------------------------------------------ {xrst_begin ad_ctor} AD Constructors ############### Syntax ****** | ``AD`` < *Base* > *ay* () | ``AD`` < *Base* > *ay* ( *x* ) Purpose ******* creates a new ``AD`` < *Base* > object *ay* and initializes it as a equal to *x* . x * implicit ======== There is an implicit constructor where *x* has prototype ``const VecAD`` < *Base* >& *x* There also is an implicit constructor where *x* has prototype ``const`` *Base* & *x* In this case, *ay* is a :ref:`constant parameter` explicit ======== There is an explicit constructor where *x* has prototype ``const`` *Type* & *x* for any type that has an explicit constructor of the form *Base* ( *x* ) . In this case, *ay* is a :ref:`constant parameter` ay ** The target *ay* has prototype ``AD`` < *Base* > *ay* Example ******* {xrst_toc_hidden example/general/ad_ctor.cpp } The files :ref:`ad_ctor.cpp-name` contain examples and tests of these operations. It test returns true if it succeeds and false otherwise. {xrst_end ad_ctor} ------------------------------------------------------------------------------ */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file ad_ctor.hpp AD constructors and and copy operations. */ /*! \page AD_default_ctor Use default copy constructor because they may be optimized better than the code below: \code template AD::AD(const AD &x) { value_ = x.value_; tape_id_ = x.tape_id_; taddr_ = x.taddr_; ad_type_ = x.ad_type_; return; } \endcode */ /*! Default Constructor. \tparam Base Base type for this AD object. */ template AD::AD(void) : value_() , tape_id_(0) , taddr_(0) , ad_type_(constant_enum) { } // -------------------------------------------------------------------------- # ifdef CPPAD_FOR_TMB /*! Constructor from double. \param d is value corresponding to this AD object. The tape identifier will be an invalid tape identifier, so this object is initially a parameter. \par CPPAD_FOR_TMB This constructor is defined when CPPAD_FOR_TMB is defined. */ template AD::AD(const double &d) : value_( Base(d) ) , tape_id_(0) , taddr_(0) , ad_type_(constant_enum) { // check that this is a parameter CPPAD_ASSERT_UNKNOWN( Parameter(*this) ); } // -------------------------------------------------------------------------- # else // -------------------------------------------------------------------------- /*! Constructor from Base type. \tparam Base Base type for this AD object. \param b is the Base type value corresponding to this AD object. The tape identifier will be an invalid tape identifier, so this object is initially a parameter. \par CPPAD_FOR_TMB This constructor is defined when CPPAD_FOR_TMB is not defined. */ template AD::AD(const Base &b) : value_(b) , tape_id_(0) , taddr_(0) , ad_type_(constant_enum) { // check that this is a parameter CPPAD_ASSERT_UNKNOWN( Parameter(*this) ); } # endif // -------------------------------------------------------------------------- /*! Constructor from an ADVec element drops the vector information. \tparam Base Base type for this AD object. */ template AD::AD(const VecAD_reference &x) { *this = x.ADBase(); } /*! Constructor from any other type, converts to Base type, and uses constructor from Base type. \tparam Base Base type for this AD object. \tparam T is the the type that is being converted to AD. There must be a constructor for Base from Type. \param t is the object that is being converted from T to AD. */ template template AD::AD(const T &t) : value_( Base(t) ) , tape_id_(0) , taddr_(0) , ad_type_(constant_enum) { } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/ad_fun.hpp ================================================ # ifndef CPPAD_CORE_AD_FUN_HPP # define CPPAD_CORE_AD_FUN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ADFun} ADFun Objects ############# Purpose ******* An AD of *Base* :ref:`operation sequence` is stored in an ``ADFun`` object by its :ref:`fun_construct-name` . The ``ADFun`` object can then be used to calculate function values, derivative values, and other values related to the corresponding function. Contents ******** {xrst_toc_table include/cppad/core/ad_fun.xrst include/cppad/core/optimize.hpp include/cppad/core/fun_check.hpp include/cppad/core/check_for_nan.hpp include/cppad/core/to_csrc.hpp } {xrst_end ADFun} */ # include # include # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file ad_fun.hpp File used to define the ADFun class. */ /*! Class used to hold function objects \tparam Base A function object has a recording of AD operations. It does it calculations using Base operations. */ template class ADFun { // ADFun must be a friend of ADFun< AD > for base2ad to work. template friend class ADFun; private: // ------------------------------------------------------------ // Private member variables // ------------------------------------------------------------ /// name of this function (so far only json operations use this value) std::string function_name_; /// Did the previous optimization exceed the collision limit bool exceed_collision_limit_; /// Has this ADFun object been optimized bool has_been_optimized_; /// Check for nan's and report message to user (default value is true). bool check_for_nan_; /// If zero, ignoring comparison operators. Otherwise is the /// compare change count at which to store the operator index. size_t compare_change_count_; /// If compare_change_count_ is zero, compare_change_number_ is also zero. /// Otherwise, it is set to the number of comparison operations that had a /// different result during the subsequent zero order forward. size_t compare_change_number_; /// If compare_change_count is zero, compare_change_op_index_ is also /// zero. Otherwise it is the operator index for the comparison operator //// that corresponded to the number changing from count-1 to count. size_t compare_change_op_index_; /// number of orders stored in taylor_ size_t num_order_taylor_; /// maximum number of orders that will fit in taylor_ size_t cap_order_taylor_; /// number of directions stored in taylor_ size_t num_direction_taylor_; /// number of variables in the recording (play_) size_t num_var_tape_; /// tape address for the independent variables local::pod_vector ind_taddr_; /// tape address and parameter flag for the dependent variables local::pod_vector dep_taddr_; /// which dependent variables are actually parameters local::pod_vector dep_parameter_; /// which operations can be conditionally skipped /// Set during forward pass of order zero local::pod_vector cskip_op_; /// Variable on the tape corresponding to each vecad load operation /// (if zero, the operation corresponds to a parameter). local::pod_vector load_op2var_; /// results of the forward mode calculations local::pod_vector_maybe taylor_; /// used for subgraph reverse mode calculations. /// Declared here to avoid reallocation for each call to subgraph_reverse. /// Not in subgraph_info_ because it depends on Base. local::pod_vector_maybe subgraph_partial_; /// the operation sequence corresponding to this object local::player play_; /// subgraph information for this object local::subgraph::subgraph_info subgraph_info_; /// Packed results of the forward mode Jacobian sparsity calculations. /// for_jac_sparse_pack_.n_set() != 0 implies other sparsity results /// are empty local::sparse::pack_setvec for_jac_sparse_pack_; /// Set results of the forward mode Jacobian sparsity calculations /// for_jac_sparse_set_.n_set() != 0 implies for_sparse_pack_ is empty. local::sparse::list_setvec for_jac_sparse_set_; // ------------------------------------------------------------ // Private member functions // ------------------------------------------------------------ /// change the operation sequence corresponding to this object template void Dependent(local::ADTape *tape, const ADvector &y); // vector of bool version of ForSparseJac // (doxygen in cppad/core/for_sparse_jac.hpp) template void ForSparseJacCase( bool set_type , bool transpose , bool dependency, size_t q , const SetVector& r , SetVector& s ); // vector of std::set version of ForSparseJac // (doxygen in cppad/core/for_sparse_jac.hpp) template void ForSparseJacCase( const std::set& set_type , bool transpose , bool dependency, size_t q , const SetVector& r , SetVector& s ); // vector of bool version of RevSparseJac // (doxygen in cppad/core/rev_sparse_jac.hpp) template void RevSparseJacCase( bool set_type , bool transpose , bool dependency, size_t p , const SetVector& s , SetVector& r ); // vector of std::set version of RevSparseJac // (doxygen in cppad/core/rev_sparse_jac.hpp) template void RevSparseJacCase( const std::set& set_type , bool transpose , bool dependency, size_t p , const SetVector& s , SetVector& r ); // vector of bool version of ForSparseHes // (doxygen in cppad/core/for_sparse_hes.hpp) template void ForSparseHesCase( bool set_type , const SetVector& r , const SetVector& s , SetVector& h ); // vector of std::set version of ForSparseHes // (doxygen in cppad/core/for_sparse_hes.hpp) template void ForSparseHesCase( const std::set& set_type , const SetVector& r , const SetVector& s , SetVector& h ); // vector of bool version of RevSparseHes // (doxygen in cppad/core/rev_sparse_hes.hpp) template void RevSparseHesCase( bool set_type , bool transpose , size_t q , const SetVector& s , SetVector& h ); // vector of std::set version of RevSparseHes // (doxygen in cppad/core/rev_sparse_hes.hpp) template void RevSparseHesCase( const std::set& set_type , bool transpose , size_t q , const SetVector& s , SetVector& h ); // Forward mode version of SparseJacobian // (doxygen in cppad/core/sparse_jacobian.hpp) template size_t SparseJacobianFor( const BaseVector& x , SetVector& p_transpose , const SizeVector& row , const SizeVector& col , BaseVector& jac , sparse_jacobian_work& work ); // Reverse mode version of SparseJacobian // (doxygen in cppad/core/sparse_jacobian.hpp) template size_t SparseJacobianRev( const BaseVector& x , SetVector& p , const SizeVector& row , const SizeVector& col , BaseVector& jac , sparse_jacobian_work& work ); // combined sparse_list and sparse_pack version of SparseHessian // (doxygen in cppad/core/sparse_hessian.hpp) template size_t SparseHessianCompute( const BaseVector& x , const BaseVector& w , SetVector& sparsity , const SizeVector& row , const SizeVector& col , BaseVector& hes , sparse_hessian_work& work ); public: /// default constructor ADFun(void); /// copy constructor ADFun(const ADFun& g) = delete; // assignment operator // (doxygen in cppad/core/fun_construct.hpp) void operator=(const ADFun& f); // swap void swap(ADFun& f); // move semenatics copy ADFun(ADFun&& f); // move semantics assignment void operator=(ADFun&& f); // create from Json or C++ AD graph void from_json(const std::string& json); void from_graph(const cpp_graph& graph_obj); void from_graph( const cpp_graph& graph_obj , const vector& dyn2var , const vector& var2dyn ); // convert function to a // C++ graph, Json graph, C source code void to_graph(cpp_graph& graph_obj); std::string to_json(void); void to_csrc(std::ostream& os, const std::string& type); // // value graph routines void fun2val( local::val_graph::tape_t& val_tape ); void val2fun( const local::val_graph::tape_t& val_tape , const CppAD::local::val_graph::Vector& dyn_ind , const CppAD::local::val_graph::Vector& var_ind , const CppAD::vectorBool& use_val ); void val2fun( const local::val_graph::tape_t& val_tape , const CppAD::local::val_graph::Vector& dyn_ind , const CppAD::local::val_graph::Vector& var_ind ); void val_optimize(const std::string& options); // create ADFun< AD > from this ADFun // (doxygen in cppad/core/base2ad.hpp) ADFun< AD, RecBase > base2ad(void) const; /// sequence constructor template ADFun(const ADvector &x, const ADvector &y); /// destructor ~ADFun(void); /// set check_for_nan void check_for_nan(bool value); /// get check_for_nan bool check_for_nan(void) const; /// assign a new operation sequence template void Dependent(const ADvector &x, const ADvector &y); /// new_dynamic user API template void new_dynamic(const BaseVector& dynamic); /// forward mode user API, one order multiple directions. template BaseVector Forward(size_t q, size_t r, const BaseVector& x); /// forward mode user API, multiple orders one direction. template BaseVector Forward( size_t q, const BaseVector& xq, std::ostream& s = std::cout ); /// reverse mode sweep template BaseVector Reverse(size_t p, const BaseVector &v); // forward Jacobian sparsity pattern // (doxygen in cppad/core/for_sparse_jac.hpp) template SetVector ForSparseJac( size_t q, const SetVector &r, bool transpose = false, bool dependency = false ); // reverse Jacobian sparsity pattern // (doxygen in cppad/core/rev_sparse_jac.hpp) template SetVector RevSparseJac( size_t q, const SetVector &s, bool transpose = false, bool dependency = false ); // subgraph_reverse: select domain // (doxygen in cppad/core/subgraph_reverse.hpp) template void subgraph_reverse( const BoolVector& select_domain ); // subgraph_reverse: compute derivative // (doxygen in cppad/core/subgraph_reverse.hpp) template void subgraph_reverse_helper( size_t q , size_t ell , SizeVector& col , BaseVector& dw ); // subgraph_reverse: compute derivative // (doxygen in cppad/core/subgraph_reverse.hpp) template void subgraph_reverse( size_t q , size_t ell , SizeVector& col , BaseVector& dw ); // subgraph_jac_rev: compute Jacobian // (doxygen in cppad/core/subgraph_jac_rev.hpp) template void subgraph_jac_rev( const BaseVector& x , sparse_rcv& subset ); // subgraph_jac_rev: compute Jacobian // (doxygen missing in cppad/core/subgraph_jac_rev.hpp) template void subgraph_jac_rev( const BoolVector& select_domain , const BoolVector& select_range , const BaseVector& x , sparse_rcv& matrix_out ); // compute sparse Jacobian using forward mode // (doxygen in cppad/core/sparse_jac.hpp) template size_t sparse_jac_for( size_t group_max , const BaseVector& x , sparse_rcv& subset , const sparse_rc& pattern , const std::string& coloring , sparse_jac_work& work ); // compute sparse Jacobian using reverse mode // (doxygen in cppad/core/sparse_jac.hpp) template size_t sparse_jac_rev( const BaseVector& x , sparse_rcv& subset , const sparse_rc& pattern , const std::string& coloring , sparse_jac_work& work ); // compute sparse Hessian // (doxygen in cppad/core/sparse_hes.hpp) template size_t sparse_hes( const BaseVector& x , const BaseVector& w , sparse_rcv& subset , const sparse_rc& pattern , const std::string& coloring , sparse_hes_work& work ); // compute sparsity pattern using subgraphs // (doxygen in cppad/core/subgraph_sparsity.hpp) template void subgraph_sparsity( const BoolVector& select_domain , const BoolVector& select_range , bool transpose , sparse_rc& pattern_out ); // forward mode Jacobian sparsity pattern // (doxygen in cppad/core/for_jac_sparsity.hpp) template void for_jac_sparsity( const sparse_rc& pattern_in , bool transpose , bool dependency , bool internal_bool , sparse_rc& pattern_out ); // reverse mode Jacobian sparsity pattern // (doxygen in cppad/core/for_jac_sparsity.hpp) template void rev_jac_sparsity( const sparse_rc& pattern_in , bool transpose , bool dependency , bool internal_bool , sparse_rc& pattern_out ); // reverse mode Hessian sparsity pattern // (doxygen in cppad/core/rev_hes_sparsity.hpp) template void rev_hes_sparsity( const BoolVector& select_range , bool transpose , bool internal_bool , sparse_rc& pattern_out ); // forward mode Hessian sparsity pattern // (doxygen in cppad/core/for_hes_sparsity.hpp) template void for_hes_sparsity( const BoolVector& select_domain , const BoolVector& select_range , bool internal_bool , sparse_rc& pattern_out ); // forward mode Hessian sparsity pattern // (see doxygen in cppad/core/for_sparse_hes.hpp) template SetVector ForSparseHes( const SetVector &r, const SetVector &s ); // internal set sparsity version of ForSparseHes // (used by checkpoint functions only) void ForSparseHesCheckpoint( vector& r , vector& s , local::sparse::list_setvec& h ); // reverse mode Hessian sparsity pattern // (see doxygen in cppad/core/rev_sparse_hes.hpp) template SetVector RevSparseHes( size_t q, const SetVector &s, bool transpose = false ); // internal set sparsity version of RevSparseHes // (doxygen in cppad/core/rev_sparse_hes.hpp) // (used by checkpoint functions only) void RevSparseHesCheckpoint( size_t q , vector& s , bool transpose , local::sparse::list_setvec& h ); // internal set sparsity version of RevSparseJac // (doxygen in cppad/core/rev_sparse_jac.hpp) // (used by checkpoint functions only) void RevSparseJacCheckpoint( size_t q , const local::sparse::list_setvec& r , bool transpose , bool dependency , local::sparse::list_setvec& s ); // internal set sparsity version of RevSparseJac // (doxygen in cppad/core/for_sparse_jac.hpp) // (used by checkpoint functions only) void ForSparseJacCheckpoint( size_t q , const local::sparse::list_setvec& r , bool transpose , bool dependency , local::sparse::list_setvec& s ); /// did previous optimization exceed the collision limit bool exceed_collision_limit(void) const { return exceed_collision_limit_; } /// amount of memory used for boolean Jacobain sparsity pattern size_t size_forward_bool(void) const { return for_jac_sparse_pack_.memory(); } /// free memory used for Jacobain sparsity pattern void size_forward_bool(size_t zero) { CPPAD_ASSERT_KNOWN( zero == 0, "size_forward_bool: argument not equal to zero" ); for_jac_sparse_pack_.resize(0, 0); } /// amount of memory used for vector of set Jacobain sparsity pattern size_t size_forward_set(void) const { return for_jac_sparse_set_.memory(); } /// free memory used for Jacobain sparsity pattern void size_forward_set(size_t zero) { CPPAD_ASSERT_KNOWN( zero == 0, "size_forward_bool: argument not equal to zero" ); for_jac_sparse_set_.resize(0, 0); } /// number of operators in the operation sequence size_t size_op(void) const { return play_.num_var_op(); } /// number of operator arguments in the operation sequence size_t size_op_arg(void) const { return play_.num_var_arg(); } /// amount of memory required for the operation sequence size_t size_op_seq(void) const { return play_.size_op_seq(); } /// amount of memory currently allocated for random access /// of the operation sequence size_t size_random(void) const { return play_.size_random(); } /// number of parameters in the operation sequence size_t size_par(void) const { return play_.num_par_all(); } /// number of independent dynamic parameters size_t size_dyn_ind(void) const { return play_.n_dyn_independent(); } /// number of dynamic parameters size_t size_dyn_par(void) const { return play_.num_dynamic_par(); } /// number of dynamic parameters arguments size_t size_dyn_arg(void) const { return play_.num_dynamic_arg(); } /// number taylor coefficient orders calculated size_t size_order(void) const { return num_order_taylor_; } /// number taylor coefficient directions calculated size_t size_direction(void) const { return num_direction_taylor_; } /// number of characters in the operation sequence size_t size_text(void) const { return play_.num_var_text(); } /// number of variables in operation sequence size_t size_var(void) const { return num_var_tape_; } /// number of VecAD indices in the operation sequence size_t size_VecAD(void) const { return play_.num_var_vec_ind(); } /// set number of orders currently allocated (user API) void capacity_order(size_t c); /// set number of orders and directions currently allocated void capacity_order(size_t c, size_t r); /// number of variables in conditional expressions that can be skipped size_t number_skip(void); /// number of independent variables size_t Domain(void) const { return ind_taddr_.size(); } /// number of dependent variables size_t Range(void) const { return dep_taddr_.size(); } /// set and get function name void function_name_set(const std::string& function_name) { function_name_ = function_name; } std::string function_name_get(void) { return function_name_; } /// is variable a parameter bool Parameter(size_t i) { CPPAD_ASSERT_KNOWN( i < dep_taddr_.size(), "Argument to Parameter is >= dimension of range space" ); return dep_parameter_[i]; } /// Deprecated: number of comparison operations that changed /// for the previous zero order forward (than when function was recorded) size_t CompareChange(void) const { return compare_change_number_; } /// count as which to store operator index void compare_change_count(size_t count) { compare_change_count_ = count; compare_change_number_ = 0; compare_change_op_index_ = 0; } /// number of comparison operations that changed size_t compare_change_number(void) const { return compare_change_number_; } /// operator index for the count-th comparison change size_t compare_change_op_index(void) const { if( has_been_optimized_ ) return 0; return compare_change_op_index_; } /// calculate entire Jacobian template BaseVector Jacobian(const BaseVector &x); /// calculate Hessian for one component of f template BaseVector Hessian(const BaseVector &x, const BaseVector &w); template BaseVector Hessian(const BaseVector &x, size_t i); /// forward mode calculation of partial w.r.t one domain component template BaseVector ForOne( const BaseVector &x , size_t j ); /// reverse mode calculation of derivative of one range component template BaseVector RevOne( const BaseVector &x , size_t i ); /// forward mode calculation of a subset of second order partials template BaseVector ForTwo( const BaseVector &x , const SizeVector_t &J , const SizeVector_t &K ); /// reverse mode calculation of a subset of second order partials template BaseVector RevTwo( const BaseVector &x , const SizeVector_t &I , const SizeVector_t &J ); /// calculate sparse Jacobians template BaseVector SparseJacobian( const BaseVector &x ); template BaseVector SparseJacobian( const BaseVector &x , const SetVector &p ); template size_t SparseJacobianForward( const BaseVector& x , const SetVector& p , const SizeVector& r , const SizeVector& c , BaseVector& jac , sparse_jacobian_work& work ); template size_t SparseJacobianReverse( const BaseVector& x , const SetVector& p , const SizeVector& r , const SizeVector& c , BaseVector& jac , sparse_jacobian_work& work ); /// calculate sparse Hessians template BaseVector SparseHessian( const BaseVector& x , const BaseVector& w ); template BaseVector SparseHessian( const BaseVector& x , const BaseVector& w , const BoolVector& p ); template size_t SparseHessian( const BaseVector& x , const BaseVector& w , const SetVector& p , const SizeVector& r , const SizeVector& c , BaseVector& hes , sparse_hessian_work& work ); // Optimize the tape // (see doxygen documentation in optimize.hpp) void optimize( const std::string& options = "" ); // create abs-normal representation of the function f(x) void abs_normal_fun( ADFun& g, ADFun& a ) const; // clear all subgraph information void clear_subgraph(void); // ------------------- Deprecated ----------------------------- /// deprecated: assign a new operation sequence template void Dependent(const ADvector &y); /// Deprecated: number of variables in operation sequence size_t Size(void) const { return num_var_tape_; } /// Deprecated: # taylor_ coefficients currently stored /// (per variable,direction) size_t Order(void) const { return num_order_taylor_ - 1; } /// Deprecated: amount of memory for this object /// Note that an approximation is used for the std::set memory size_t Memory(void) const { size_t pervar = cap_order_taylor_ * sizeof(Base) + for_jac_sparse_pack_.memory() + for_jac_sparse_set_.memory(); size_t total = num_var_tape_ * pervar; total += play_.size_op_seq(); total += play_.size_random(); total += subgraph_info_.memory(); return total; } /// Deprecated: # taylor_ coefficient orderss stored /// (per variable,direction) size_t taylor_size(void) const { return num_order_taylor_; } /// Deprecated: Does this AD operation sequence use /// VecAD::reference operands bool use_VecAD(void) const { return play_.num_var_vec_ind() > 0; } /// Deprecated: # taylor_ coefficient orders calculated /// (per variable,direction) size_t size_taylor(void) const { return num_order_taylor_; } /// Deprecated: set number of orders currently allocated /// (per variable,direction) void capacity_taylor(size_t per_var); }; // --------------------------------------------------------------------------- } // END_CPPAD_NAMESPACE // non-user interfaces # include # include # include # include # include # include # include # include # include # include // user interfaces # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include // 2DO: move to core directory # include # endif ================================================ FILE: include/cppad/core/ad_fun.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin record_adfun} Create an ADFun Object by Recording an Operation Sequence ######################################################### Contents ******** {xrst_toc_table include/cppad/core/independent/user.xrst include/cppad/core/fun_construct.hpp include/cppad/core/dependent.hpp include/cppad/core/abort_recording.hpp include/cppad/core/fun_property.xrst include/cppad/core/function_name.xrst } {xrst_end record_adfun} ------------------------------------------------------------------------------- {xrst_begin other_adfun} {xrst_spell dyn } Other Ways to Create an ADFun Object #################################### Contents ******** {xrst_toc_table include/cppad/core/base2ad.hpp include/cppad/core/graph/json_ad_graph.xrst include/cppad/core/graph/cpp_ad_graph.xrst include/cppad/core/abs_normal_fun.hpp } See Also ******** .. csv-table:: :widths: auto switch_var_dyn.cpp,:ref:`switch_var_dyn.cpp-title` {xrst_end other_adfun} ------------------------------------------------------------------------------- {xrst_begin drivers} First and Second Order Derivatives: Easy Drivers ################################################ Contents ******** {xrst_toc_table include/cppad/core/jacobian.hpp include/cppad/core/hessian.hpp include/cppad/core/for_one.hpp include/cppad/core/rev_one.hpp include/cppad/core/for_two.hpp include/cppad/core/rev_two.hpp } {xrst_end drivers} ------------------------------------------------------------------------------- {xrst_begin Forward} Forward Mode ############ Contents ******** {xrst_toc_table include/cppad/core/new_dynamic.hpp include/cppad/core/forward/forward_zero.xrst include/cppad/core/forward/forward_one.xrst include/cppad/core/forward/forward_two.xrst include/cppad/core/forward/forward_order.xrst include/cppad/core/forward/forward_dir.xrst include/cppad/core/forward/size_order.xrst include/cppad/core/forward/compare_change.xrst include/cppad/core/capacity_order.hpp include/cppad/core/num_skip.hpp } {xrst_end Forward} ------------------------------------------------------------------------------- {xrst_begin Reverse} {xrst_spell xq } Reverse Mode ############ Multiple Directions ******************* Reverse mode after :ref:`Forward(q, r, xq)` with number of directions *r* != 1 is not yet supported. There is one exception, :ref:`reverse_one-name` is allowed because there is only one zero order forward direction. After such an operation, only the zero order forward results are retained (the higher order forward results are lost). Contents ******** {xrst_toc_table xrst/reverse/reverse_one.xrst xrst/reverse/reverse_two.xrst xrst/reverse/reverse_any.xrst include/cppad/core/subgraph_reverse.hpp } {xrst_end Reverse} ------------------------------------------------------------------------------- {xrst_begin sparsity_pattern} {xrst_spell subgraph } Calculating Sparsity Patterns ############################# {xrst_toc_hidden include/cppad/core/for_jac_sparsity.hpp include/cppad/core/rev_jac_sparsity.hpp include/cppad/core/for_hes_sparsity.hpp include/cppad/core/rev_hes_sparsity.hpp include/cppad/core/subgraph_sparsity.hpp example/sparse/dependency.cpp example/sparse/rc_sparsity.cpp include/cppad/core/for_sparse_jac.hpp include/cppad/core/rev_sparse_jac.hpp include/cppad/core/rev_sparse_hes.hpp include/cppad/core/for_sparse_hes.hpp } Preferred Sparsity Pattern Calculations *************************************** .. csv-table:: :widths: auto for_jac_sparsity,:ref:`for_jac_sparsity-title` rev_jac_sparsity,:ref:`rev_jac_sparsity-title` for_hes_sparsity,:ref:`for_hes_sparsity-title` rev_hes_sparsity,:ref:`rev_hes_sparsity-title` subgraph_sparsity,:ref:`subgraph_sparsity-title` Old Sparsity Pattern Calculations ********************************* .. csv-table:: :widths: auto ForSparseJac,:ref:`ForSparseJac-title` RevSparseJac,:ref:`RevSparseJac-title` ForSparseHes,:ref:`ForSparseHes-title` RevSparseHes,:ref:`RevSparseHes-title` {xrst_end sparsity_pattern} ------------------------------------------------------------------------------- {xrst_begin sparse_derivative} {xrst_spell subgraph } Calculating Sparse Derivatives ############################## {xrst_toc_hidden include/cppad/core/sparse_jac.hpp include/cppad/core/sparse_jacobian.hpp include/cppad/core/sparse_hes.hpp include/cppad/core/sparse_hessian.hpp include/cppad/core/subgraph_jac_rev.hpp } Preferred Sparsity Patterns *************************** .. csv-table:: :widths: auto sparse_jac,:ref:`sparse_jac-title` sparse_hes,:ref:`sparse_hes-title` subgraph_jac_rev,:ref:`subgraph_jac_rev-title` Old Sparsity Patterns ********************* .. csv-table:: :widths: auto sparse_jacobian,:ref:`sparse_jacobian-title` sparse_hessian,:ref:`sparse_hessian-title` {xrst_end sparse_derivative} ------------------------------------------------------------------------------- ================================================ FILE: include/cppad/core/ad_io.hpp ================================================ # ifndef CPPAD_CORE_AD_IO_HPP # define CPPAD_CORE_AD_IO_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ad_input} {xrst_spell istream } AD Input Stream Operator ######################## Syntax ****** | *is* >> *x* Purpose ******* Sets *x* to a :ref:`glossary@Parameter` with value *b* corresponding to *is* >> *b* where *b* is a *Base* object. It is assumed that this *Base* input operation returns a reference to *is* . is ** The operand *is* has prototype ``std::istream&`` *is* x * The operand *x* has one of the following prototypes ``AD`` < *Base* >& *x* Result ****** The result of this operation can be used as a reference to *is* . For example, if the operand *y* has prototype ``AD`` < *Base* > *y* then the syntax *is* >> *x* >> *y* will first read the *Base* value of *x* from *is* , and then read the *Base* value to *y* . Operation Sequence ****************** The result of this operation is not an :ref:`glossary@AD of Base` object. Thus it will not be recorded as part of an AD of *Base* :ref:`operation sequence` . Example ******* {xrst_toc_hidden example/general/ad_input.cpp } The file :ref:`ad_input.cpp-name` contains an example and test of this operation. {xrst_end ad_input} ------------------------------------------------------------------------------ {xrst_begin ad_output} {xrst_spell ostream } AD Output Stream Operator ######################### Syntax ****** *os* << *x* See Also ******** :ref:`PrintFor-name` Purpose ******* Writes the *Base* value, corresponding to *x* , to the output stream *os* . Assumption ********** If *b* is a *Base* object, *os* << *b* returns a reference to *os* . os ** The operand *os* has prototype ``std::ostream&`` *os* x * The operand *x* has one of the following prototypes | |tab| ``const AD`` < *Base* >& *x* | |tab| ``const VecAD`` < *Base* >:: ``reference&`` *x* Result ****** The result of this operation can be used as a reference to *os* . For example, if the operand *y* has prototype ``AD`` < *Base* > *y* then the syntax *os* << *x* << *y* will output the value corresponding to *x* followed by the value corresponding to *y* . Operation Sequence ****************** The result of this operation is not an :ref:`glossary@AD of Base` object. Thus it will not be recorded as part of an AD of *Base* :ref:`operation sequence` . Example ******* {xrst_toc_hidden example/general/ad_output.cpp } The file :ref:`ad_output.cpp-name` contains an example and test of this operation. {xrst_end ad_output} ------------------------------------------------------------------------------ */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file ad_io.hpp AD input and output stream operators. */ // --------------------------------------------------------------------------- /*! Read an AD object from an input stream. \tparam Base Base type for the AD object. \param is [in,out] Is the input stream from which that value is read. \param x [out] is the object that is being set to a value. Upone return, x.value_ is read from the input stream and x.tape_is_ is zero; i.e., x is a parameter. */ template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION std::istream& operator >> (std::istream& is, AD& x) { // like assignment to a base type value x.tape_id_ = 0; CPPAD_ASSERT_UNKNOWN( Parameter(x) ); return (is >> x.value_); } // --------------------------------------------------------------------------- /*! Write an AD object to an output stream. \tparam Base Base type for the AD object. \param os [in,out] Is the output stream to which that value is written. \param x is the object that is being written to the output stream. This is equivalent to writing x.value_ to the output stream. */ template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION std::ostream& operator << (std::ostream &os, const AD &x) { return (os << x.value_); } // --------------------------------------------------------------------------- /*! Write a VecAD_reference object to an output stream. \tparam Base Base type for the VecAD_reference object. \param os [in,out] Is the output stream to which that value is written. \param x is the element of the VecAD object that is being written to the output stream. This is equivalent to writing the corresponding Base value to the stream. */ template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION std::ostream& operator << (std::ostream &os, const VecAD_reference &x) { return (os << x.ADBase()); } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/ad_to_string.hpp ================================================ # ifndef CPPAD_CORE_AD_TO_STRING_HPP # define CPPAD_CORE_AD_TO_STRING_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ad_to_string} Convert An AD or Base Type to String #################################### Syntax ****** | *s* = ``to_string`` ( *value* ) . See Also ******** :ref:`to_string-name` , :ref:`base_to_string-name` value ***** The argument *value* has prototype | |tab| ``const AD`` < *Base* >& *value* | |tab| ``const`` *Base* & *value* where *Base* is a type that supports the :ref:`base_to_string-name` type requirement. s * The return value has prototype ``std::string`` *s* and contains a representation of the specified *value* . If *value* is an AD type, the result has the same precision as for the *Base* type. Example ******* The file :ref:`to_string.cpp-name` includes an example and test of ``to_string`` with AD types. {xrst_end ad_to_string} */ # include # include namespace CppAD { // Template definition is in cppad/utility/to_string.hpp. // Partial specialzation for AD types template struct to_string_struct< CppAD::AD > { std::string operator()(const CppAD::AD& value) { to_string_struct ts; return ts( Value( Var2Par( value ) ) ); } }; } # endif ================================================ FILE: include/cppad/core/ad_type.hpp ================================================ # ifndef CPPAD_CORE_AD_TYPE_HPP # define CPPAD_CORE_AD_TYPE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # ifdef NDEBUG # define CPPAD_ASSERT_AD_TYPE(ad_obj) # else # define CPPAD_ASSERT_AD_TYPE(ad_obj) \ switch(ad_obj.ad_type_) \ { case constant_enum: \ CPPAD_ASSERT_UNKNOWN( ad_obj.tape_id_ == 0 ); \ break; \ \ case dynamic_enum: \ case variable_enum: \ break; \ \ default: \ CPPAD_ASSERT_UNKNOWN(false); \ } \ CPPAD_ASSERT_UNKNOWN( \ ad_obj.tape_id_ == 0 || \ ad_obj.ad_type_ == dynamic_enum || \ ad_obj.ad_type_ == variable_enum \ ); # endif namespace CppAD { // BEGIN TYPEDEF typedef enum { identical_zero_enum, // identically zero constant_enum, // constant parameter dynamic_enum, // dynamic parameter variable_enum, // variable number_ad_type_enum // number of valid values for type_ad_enum } ad_type_enum; // END TYPEDEF // BEGIN IS_POD namespace local { template <> inline bool is_pod(void) { return true; } } // END IS_POD } /* {xrst_begin ad_type_enum dev} {xrst_spell typedef } Type of AD an Object #################### typedef ******* This typedef is in the ``CppAD`` namespace: {xrst_literal // BEGIN TYPEDEF // END TYPEDEF } is_pod ****** The following informs :ref:`is_pod-name` that this is plain old data. {xrst_literal // BEGIN IS_POD // END IS_POD } Atomic Function *************** Only some of the values are valid for the user atomic function API; see :ref:`atomic_three` and :ref:`atomic_four` . ASSERT_AD_TYPE ************** If *ad_obj* is an ``AD`` < *Base* > object, the syntax ``CPPAD_ASSERT_AD_TYPE`` ( *ad_obj* ) check that *ad_obj* satisfies the following conditions: #. *ad_obj* . ``ad_type_`` is one of the following: ``constant_enum`` , ``dynamic_enum`` , ``variable_enum`` . #. *ad_obj* . ``ad_type_`` is ``constant_enum`` , then *ad_obj* . ``tape_id_`` == 0 . {xrst_end ad_type_enum} */ # endif ================================================ FILE: include/cppad/core/ad_valued.hpp ================================================ # ifndef CPPAD_CORE_AD_VALUED_HPP # define CPPAD_CORE_AD_VALUED_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ADValued} AD Valued Operations and Functions ################################## {xrst_comment atomic.omh includes atomic_two.hpp} Contents ******** {xrst_toc_table include/cppad/core/arithmetic.hpp include/cppad/core/standard_math.hpp include/cppad/core/cond_exp.hpp include/cppad/core/discrete/user.xrst include/cppad/core/numeric_limits.hpp include/cppad/core/atomic/atomic.xrst } {xrst_end ADValued} */ // include MathOther.h after CondExp.h because some MathOther.h routines use // CondExp.h and CondExp.h is not sufficiently declared in Declare.h # include # include # include # include # include # include # include # include # include # include # include # include # endif ================================================ FILE: include/cppad/core/add.hpp ================================================ # ifndef CPPAD_CORE_ADD_HPP # define CPPAD_CORE_ADD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN CppAD namespace namespace CppAD { template AD operator + (const AD &left , const AD &right) { // compute the Base part of this AD object AD result; result.value_ = left.value_ + right.value_; CPPAD_ASSERT_UNKNOWN( Parameter(result) ); // check if there is a recording in progress local::ADTape* tape = AD::tape_ptr(); if( tape == nullptr ) return result; tape_id_t tape_id = tape->id_; // tape_id cannot match the default value for tape_id_; i.e., 0 CPPAD_ASSERT_UNKNOWN( tape_id > 0 ); // check if left and right tapes match bool match_left = left.tape_id_ == tape_id; bool match_right = right.tape_id_ == tape_id; // check if left and right are dynamic parameters bool dyn_left = match_left & (left.ad_type_ == dynamic_enum); bool dyn_right = match_right & (right.ad_type_ == dynamic_enum); // check if left and right are variables bool var_left = match_left & (left.ad_type_ != dynamic_enum); bool var_right = match_right & (right.ad_type_ != dynamic_enum); CPPAD_ASSERT_KNOWN( left.tape_id_ == right.tape_id_ || ! match_left || ! match_right , "Add: AD variables or dynamic parameters on different threads." ); if( var_left ) { if( var_right ) { // result = variable + variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::AddvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::AddvvOp) == 2 ); // put operand addresses in tape tape->Rec_.PutArg(left.taddr_, right.taddr_); // put operator in the tape result.taddr_ = tape->Rec_.PutOp(local::AddvvOp); // make result a variable result.tape_id_ = tape_id; result.ad_type_ = variable_enum; } else if( (! dyn_right) && IdenticalZero(right.value_) ) { // result = variable + 0 result.make_variable(left.tape_id_, left.taddr_); } else { // result = variable + parameter // = parameter + variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::AddpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::AddpvOp) == 2 ); // put operand addresses in tape addr_t p = right.taddr_; if( ! dyn_right ) p = tape->Rec_.put_con_par(right.value_); tape->Rec_.PutArg(p, left.taddr_); // put operator in the tape result.taddr_ = tape->Rec_.PutOp(local::AddpvOp); // make result a variable result.tape_id_ = tape_id; result.ad_type_ = variable_enum; } } else if( var_right ) { if( (! dyn_left) && IdenticalZero(left.value_) ) { // result = 0 + variable result.make_variable(right.tape_id_, right.taddr_); } else { // result = parameter + variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::AddpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::AddpvOp) == 2 ); // put operand addresses in tape addr_t p = left.taddr_; if( ! dyn_left ) p = tape->Rec_.put_con_par(left.value_); tape->Rec_.PutArg(p, right.taddr_); // put operator in the tape result.taddr_ = tape->Rec_.PutOp(local::AddpvOp); // make result a variable result.tape_id_ = tape_id; result.ad_type_ = variable_enum; } } else if( dyn_left | dyn_right ) { if( (! dyn_left) && IdenticalZero(left.value_) ) { result.make_dynamic(right.tape_id_, right.taddr_); } else if( (! dyn_right) && IdenticalZero(right.value_) ) { result.make_dynamic(left.tape_id_, left.taddr_); } else { addr_t arg0 = left.taddr_; addr_t arg1 = right.taddr_; if( ! dyn_left ) arg0 = tape->Rec_.put_con_par(left.value_); if( ! dyn_right ) arg1 = tape->Rec_.put_con_par(right.value_); // // parameters with a dynamic parameter result result.taddr_ = tape->Rec_.put_dyn_par( result.value_, local::add_dyn, arg0, arg1 ); result.tape_id_ = tape_id; result.ad_type_ = dynamic_enum; } } return result; } // convert other cases into the case above CPPAD_FOLD_AD_VALUED_BINARY_OPERATOR(+) } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/add_eq.hpp ================================================ # ifndef CPPAD_CORE_ADD_EQ_HPP # define CPPAD_CORE_ADD_EQ_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN CppAD namespace namespace CppAD { template AD& AD::operator += (const AD &right) { // compute the Base part Base left; left = value_; value_ += right.value_; // check if there is a recording in progress local::ADTape* tape = AD::tape_ptr(); if( tape == nullptr ) return *this; tape_id_t tape_id = tape->id_; // tape_id cannot match the default value for tape_id_; i.e., 0 CPPAD_ASSERT_UNKNOWN( tape_id > 0 ); // check if left and right tapes match bool match_left = tape_id_ == tape_id; bool match_right = right.tape_id_ == tape_id; // check if left and right are dynamic parameters bool dyn_left = match_left & (ad_type_ == dynamic_enum); bool dyn_right = match_right & (right.ad_type_ == dynamic_enum); // check if left and right are variables bool var_left = match_left & (ad_type_ != dynamic_enum); bool var_right = match_right & (right.ad_type_ != dynamic_enum); CPPAD_ASSERT_KNOWN( tape_id_ == right.tape_id_ || ! match_left || ! match_right , "+= : AD variables or dynamic parameters on different threads." ); if( var_left ) { if( var_right ) { // this = variable + variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::AddvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::AddvvOp) == 2 ); // put operand addresses in tape tape->Rec_.PutArg(taddr_, right.taddr_); // put operator in the tape taddr_ = tape->Rec_.PutOp(local::AddvvOp); // check that this is a variable CPPAD_ASSERT_UNKNOWN( tape_id_ == tape_id ); CPPAD_ASSERT_UNKNOWN( ad_type_ == variable_enum); } else if( dyn_right | (! IdenticalZero(right.value_) ) ) { // this = variable + parameter // = parameter + variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::AddpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::AddpvOp) == 2 ); // put operand addresses in tape addr_t p = right.taddr_; if( ! dyn_right ) p = tape->Rec_.put_con_par(right.value_); tape->Rec_.PutArg(p, taddr_); // put operator in the tape taddr_ = tape->Rec_.PutOp(local::AddpvOp); // check that this is a variable CPPAD_ASSERT_UNKNOWN( tape_id_ == tape_id ); CPPAD_ASSERT_UNKNOWN( ad_type_ == variable_enum); } } else if( var_right ) { if( (! dyn_left) && IdenticalZero(left) ) { // this = 0 + right make_variable(right.tape_id_, right.taddr_); } else { // this = parameter + variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::AddpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::AddpvOp) == 2 ); // put operand addresses in tape addr_t p = taddr_; if( ! dyn_left ) p = tape->Rec_.put_con_par(left); tape->Rec_.PutArg(p, right.taddr_); // put operator in the tape taddr_ = tape->Rec_.PutOp(local::AddpvOp); // make this a variable tape_id_ = tape_id; ad_type_ = variable_enum; } } else if( dyn_left | dyn_right ) { if( (! dyn_right) && IdenticalZero(right.value_) ) { // this is left += 0, so do nothing } else if( (! dyn_left) && IdenticalZero(left)) { // this is 0 += right make_dynamic(right.tape_id_, right.taddr_); } else { addr_t arg0 = taddr_; addr_t arg1 = right.taddr_; if( ! dyn_left ) arg0 = tape->Rec_.put_con_par(left); if( ! dyn_right ) arg1 = tape->Rec_.put_con_par(right.value_); // // parameters with a dynamic parameter results taddr_ = tape->Rec_.put_dyn_par( value_, local::add_dyn, arg0, arg1 ); tape_id_ = tape_id; ad_type_ = dynamic_enum; } } return *this; } CPPAD_FOLD_ASSIGNMENT_OPERATOR(+=) } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/arithmetic.hpp ================================================ # ifndef CPPAD_CORE_ARITHMETIC_HPP # define CPPAD_CORE_ARITHMETIC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* ------------------------------------------------------------------------------- {xrst_begin Arithmetic} AD Arithmetic Operators and Compound Assignments ################################################ Contents ******** {xrst_toc_table include/cppad/core/unary_plus.hpp include/cppad/core/unary_minus.hpp include/cppad/core/ad_binary.hpp include/cppad/core/compound_assign.hpp } {xrst_end Arithmetic} ------------------------------------------------------------------------------- */ # include # include # include # include # endif ================================================ FILE: include/cppad/core/atan2.hpp ================================================ # ifndef CPPAD_CORE_ATAN2_HPP # define CPPAD_CORE_ATAN2_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* ------------------------------------------------------------------------------- {xrst_begin atan2} AD Two Argument Inverse Tangent Function ######################################## Syntax ****** | *theta* = ``atan2`` ( *y* , *x* ) Purpose ******* Determines an angle :math:`\theta \in [ - \pi , + \pi ]` such that .. math:: :nowrap: \begin{eqnarray} \sin ( \theta ) & = & y / \sqrt{ x^2 + y^2 } \\ \cos ( \theta ) & = & x / \sqrt{ x^2 + y^2 } \end{eqnarray} y * The argument *y* has one of the following prototypes | |tab| ``const AD`` < *Base* > & *y* | |tab| ``const VecAD`` < *Base* >:: ``reference &`` *y* x * The argument *x* has one of the following prototypes | |tab| ``const AD`` < *Base* > & *x* | |tab| ``const VecAD`` < *Base* >:: ``reference &`` *x* theta ***** The result *theta* has prototype ``AD`` < *Base* > *theta* Operation Sequence ****************** The AD of *Base* operation sequence used to calculate *theta* is :ref:`glossary@Operation@Independent` of *x* and *y* . Example ******* {xrst_toc_hidden example/general/atan2.cpp } The file :ref:`atan2.cpp-name` contains an example and test of this function. {xrst_end atan2} ------------------------------------------------------------------------------- */ namespace CppAD { // BEGIN CppAD namespace inline float atan2(float x, float y) { return std::atan2(x, y); } inline double atan2(double x, double y) { return std::atan2(x, y); } // The code below is used as an example by the CondExp documentation. // BEGIN CondExp template AD atan2 (const AD &y, const AD &x) { // // zero, pi2, pi AD zero(0.); AD pi2(2. * atan(1.)); AD pi(2. * pi2); // // abs_x, abs_y // Not using fabs because its derivative is zero at zero AD abs_x = CondExpGe(x, zero, x, -x); AD abs_y = CondExpGe(y, zero, y, -y); // // first // This is the result for first quadrant: x >= 0 , y >= 0 AD alpha = atan(abs_y / abs_x); AD beta = pi2 - atan(abs_x / abs_y); AD first = CondExpGt(abs_x, abs_y, alpha, beta); // // second // This is the result for second quadrant: x <= 0 , y >= 0 AD second = pi - first; // // third // This is the result for third quadrant: x <= 0 , y <= 0 AD third = - pi + first; // // fourth // This is the result for fourth quadrant: x >= 0 , y <= 0 AD fourth = - first; // // alpha // This is the result for x >= 0 alpha = CondExpGe(y, zero, first, fourth); // // beta // This is the result for x <= 0 beta = CondExpGe(y, zero, second, third); // // AD result = CondExpGe(x, zero, alpha, beta); return result; } // END CondExp template AD atan2 (const VecAD_reference &y, const AD &x) { return atan2( y.ADBase() , x ); } template AD atan2 (const AD &y, const VecAD_reference &x) { return atan2( y , x.ADBase() ); } template AD atan2 (const VecAD_reference &y, const VecAD_reference &x) { return atan2( y.ADBase() , x.ADBase() ); } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/atomic/atomic.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin atomic} Atomic AD Functions ################### Contents ******** {xrst_toc_table include/cppad/core/atomic/four/atomic.xrst include/cppad/core/atomic/three/atomic.xrst include/cppad/core/chkpoint_two/chkpoint_two.hpp } Deprecated Atomic Function ************************** :ref:`atomic_one-name` , :ref:`atomic_two-name` , :ref:`chkpoint_one-name` . {xrst_end atomic} ================================================ FILE: include/cppad/core/atomic/four/atomic.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_FOUR_ATOMIC_HPP # define CPPAD_CORE_ATOMIC_FOUR_ATOMIC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_define} {xrst_spell ctor } Defining Atomic Functions: Fourth Generation ############################################ Syntax ****** Define Class ============ | ``class`` *atomic_user* : ``public CppAD::atomic_four<`` *Base* > { | |tab| ... | }; Constructor =========== *atomic_user* *afun* ( *ctor_arg_list* ) Call ==== | *afun* ( *ax* , *ay* ) | *afun* ( *call_id* , *ax* , *ay* ) Callbacks ========= | *ok* = *afun* . ``for_type`` ( *call_id* , | |tab| *type_x* , *type_y* | ) | *ok* = *afun* . ``forward`` ( *call_id* , | |tab| *select_y* , *order_low* , *order_up* , *taylor_x* , *taylor_y* | ) | *ok* = *afun* . ``reverse`` ( *call_id* , | |tab| *select_x* , *order_up* , *taylor_x* , *taylor_y* , *partial_x* , *partial_y* | ) | *ok* = *afun* . ``jac_sparsity`` ( *call_id* , | |tab| *dependency* , *ident_zero_x* , *select_x* *select_y* , *pattern_out* | ) | *ok* = *afun* . ``hes_sparsity`` ( *call_id* , | |tab| *ident_zero_x* , *select_x* , *select_y* , *pattern_out* | ) | *ok* = *afun* . ``rev_depend`` ( *call_id* , | |tab| *ident_zero_x* , *depend_x* , *depend_y* | ) See Also ******** :ref:`chkpoint_two-name` , :ref:`atomic_three-name` Purpose ******* Speed ===== In some cases, it is possible to compute derivatives of a function .. math:: y = g(x) \; {\rm where} \; g : \B{R}^n \rightarrow \B{R}^m more efficiently than by coding it using ``AD`` < *Base* > :ref:`glossary@Operation@Atomic` operations and letting CppAD do the rest. The class ``atomic_four`` < ``Base`` > is used to create a new atomic operation corresponding to a function :math:`g(x)` where the user specifies how to compute the derivatives and sparsity patterns for :math:`g(x)`. Reduce Memory ============= If the function :math:`g(x)` is used many times during the recording of an :ref:`ADFun-name` object, an atomic version of :math:`g(x)` removes the need for repeated copies of the corresponding ``AD`` < *Base* > operations and variables in the recording. Virtual Functions ***************** The :ref:`callback functions` are implemented by defining the virtual functions in the *atomic_user* class. These functions compute derivatives, sparsity patterns, and dependency relations. Each virtual function has a default implementation that returns *ok* == ``false`` . The :ref:`for_type` and :ref:`forward` function (for the case *order_up* == 0 ) are used by an atomic function :ref:`atomic_four_define@Syntax@Call` . Hence, they are required for one to use an atomic function. Other functions and orders are only required if they are used for your calculations. For example, *forward* for the case *order_up* == 2 can just return *ok* == ``false`` unless you require forward mode calculation of second derivatives. Contents ******** {xrst_toc_table include/cppad/core/atomic/four/ctor.hpp include/cppad/core/atomic/four/call.hpp include/cppad/core/atomic/four/for_type.hpp include/cppad/core/atomic/four/forward.hpp include/cppad/core/atomic/four/reverse.hpp include/cppad/core/atomic/four/jac_sparsity.hpp include/cppad/core/atomic/four/hes_sparsity.hpp include/cppad/core/atomic/four/rev_depend.hpp } {xrst_end atomic_four_define} ------------------------------------------------------------------------------- */ # include # include # include # include # include # include # include # include // needed before one can use in_parallel # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic_four.hpp Base class for atomic function operations. */ template class atomic_four { // =================================================================== private: // ------------------------------------------------------ // constants // /// index of this object in local::atomic_index /// (set by constructor and not changed; i.e., effectively const) size_t index_; // // ----------------------------------------------------- // /// temporary work space used by call member functions, declared here // to avoid memory allocation/deallocation for each call struct work_struct { vector type_x; vector type_y; // vector taylor_x; vector taylor_y; // vector< AD > ataylor_x; vector< AD > ataylor_y; // vector select_y; }; // Use pointers, to avoid false sharing between threads. // Not using: vector work_; // so that deprecated atomic examples do not result in a memory leak. work_struct* work_[CPPAD_MAX_NUM_THREADS]; // ----------------------------------------------------- public: // // atomic_index // Needed by val_graph and not documented. Perhaps should be in // include/cppad/core/atomic/four/devel/devel.xrst size_t atomic_index(void) const { return index_; } // ===================================================================== // In User API // ===================================================================== // // --------------------------------------------------------------------- // constructors atomic_four(void); atomic_four(const std::string& name); // ------------------------------------------------------------------------ template void operator()( size_t call_id , const ADVector& ax , ADVector& ay ); template void operator()( const ADVector& ax , ADVector& ay ); // ------------------------------------------------------------------------ // for_type virtual bool for_type( size_t call_id , const vector& type_x , vector& type_y ); // ------------------------------------------------------------------------ // forward virtual bool forward( size_t call_id , const vector& select_y , size_t order_low , size_t order_up , const vector& taylor_x , vector& taylor_y ); virtual bool forward( size_t call_id , const vector& select_y , size_t order_low , size_t order_up , const vector< AD >& ataylor_x , vector< AD >& ataylor_y ); // ------------------------------------------------------------------------ // reverse virtual bool reverse( size_t call_id , const vector& select_x , size_t order_up , const vector& taylor_x , const vector& taylor_y , vector& partial_x , const vector& partial_y ); virtual bool reverse( size_t call_id , const vector& select_x , size_t order_up , const vector< AD >& ataylor_x , const vector< AD >& ataylor_y , vector< AD >& apartial_x , const vector< AD >& apartial_y ); // ------------------------------------------------------------ // jac_sparsity virtual bool jac_sparsity( size_t call_id , bool dependency , const vector& ident_zero_x , const vector& select_x , const vector& select_y , sparse_rc< vector >& pattern_out ); template bool for_jac_sparsity( size_t call_id , bool dependency , const vector& ident_zero_x , const vector& x_index , const vector& y_index , InternalSparsity& var_sparsity ); template bool rev_jac_sparsity( size_t call_id , bool dependency , const vector& ident_zero_x , const vector& x_index , const vector& y_index , InternalSparsity& var_sparsity ); // deprecated version of this callback virtual bool jac_sparsity( size_t call_id , bool dependency , const vector& select_x , const vector& select_y , sparse_rc< vector >& pattern_out ); // ------------------------------------------------------------ // hes_sparsity virtual bool hes_sparsity( size_t call_id , const vector& ident_zero_x , const vector& select_x , const vector& select_y , sparse_rc< vector >& pattern_out ); template bool for_hes_sparsity( size_t call_id , const vector& ident_zero_x , const vector& x_index , const vector& y_index , size_t np1 , size_t numvar , const InternalSparsity& rev_jac_sparsity , InternalSparsity& for_sparsity ); template bool rev_hes_sparsity( size_t call_id , const vector& ident_zero_x , const vector& x_index , const vector& y_index , const InternalSparsity& for_jac_pattern , bool* rev_jac_flag , InternalSparsity& hes_sparsity ); // deprecated version of this callback virtual bool hes_sparsity( size_t call_id , const vector& select_x , const vector& select_y , sparse_rc< vector >& pattern_out ); // ------------------------------------------------------------------------ // rev_depend virtual bool rev_depend( size_t call_id , const vector& ident_zero_x , vector& depend_x , const vector& depend_y ); // deprecated version of this callback virtual bool rev_depend( size_t call_id , vector& depend_x , const vector& depend_y ); // ===================================================================== // Not in User API // ===================================================================== /// Name corresponding to a atomic_four object const std::string atomic_name(void) const { bool set_null = false; size_t type = 0; // set to avoid warning std::string name; void* v_ptr = nullptr; // set to avoid warning local::atomic_index(set_null, index_, type, &name, v_ptr); CPPAD_ASSERT_UNKNOWN( type == 4 ); return name; } /// destructor informs CppAD that this atomic function with this index /// has dropped out of scope by setting its pointer to null virtual ~atomic_four(void) { // change object pointer to null, but leave name for error reporting bool set_null = true; size_t type = 0; // set to avoid warning std::string* name = nullptr; void* v_ptr = nullptr; // set to avoid warning local::atomic_index(set_null, index_, type, name, v_ptr); CPPAD_ASSERT_UNKNOWN( type == 4 ); // // free temporary work memory for(size_t thread = 0; thread < CPPAD_MAX_NUM_THREADS; thread++) free_work(thread); } /// allocates work_ for a specified thread void allocate_work(size_t thread) { if( work_[thread] == nullptr ) { // allocate the raw memory size_t min_bytes = sizeof(work_struct); size_t num_bytes; void* v_ptr = thread_alloc::get_memory(min_bytes, num_bytes); // save in work_ work_[thread] = reinterpret_cast( v_ptr ); // call constructor new( work_[thread] ) work_struct; } return; } /// frees work_ for a specified thread void free_work(size_t thread) { if( work_[thread] != nullptr ) { // call destructor work_[thread]->~work_struct(); // return memory to available pool for this thread thread_alloc::return_memory( reinterpret_cast(work_[thread]) ); // mark this thread as not allocated work_[thread] = nullptr; } return; } /// atomic_four function object corresponding to a certain index static atomic_four* class_object(size_t index) { bool set_null = false; size_t type = 0; // set to avoid warning std::string* name = nullptr; void* v_ptr = nullptr; // set to avoid warning local::atomic_index(set_null, index, type, name, v_ptr); CPPAD_ASSERT_UNKNOWN( type == 4 ); return reinterpret_cast( v_ptr ); } /// atomic_four function name corresponding to a certain index static const std::string class_name(size_t index) { bool set_null = false; size_t type = 0; // set to avoid warning std::string name; void* v_ptr = nullptr; // set to avoid warning local::atomic_index(set_null, index, type, &name, v_ptr); CPPAD_ASSERT_UNKNOWN( type == 4 ); return name; } /*! Set value of id (used by deprecated atomic_one class) This function is called just before calling any of the virtual function and has the corresponding id of the corresponding virtual call. */ virtual void set_old(size_t id) { } // --------------------------------------------------------------------------- }; } // END_CPPAD_NAMESPACE // member functions # include # include # include # include # include # include # include # include # endif ================================================ FILE: include/cppad/core/atomic/four/atomic.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin atomic_four} Atomic AD Functions: Fourth Generation ###################################### Contents ******** {xrst_toc_table include/cppad/core/atomic/four/atomic.hpp example/atomic_four/atomic_four.xrst } {xrst_end atomic_four} ================================================ FILE: include/cppad/core/atomic/four/call.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_FOUR_CALL_HPP # define CPPAD_CORE_ATOMIC_FOUR_CALL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_call} Calling an Atomic Function ########################## Syntax ****** | *afun* ( *ax* , *ay* ) | *ay* = *afun* ( *call_id* , *ax* , *ay* ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } Purpose ******* Given *ax* , this call computes the corresponding value of *ay* . If ``AD`` < *Base* > operations are being recorded, it enters the computation as an atomic operation in the recording; see :ref:`Independent@Start Recording` . Base **** This is the :ref:`atomic_four_ctor@atomic_four@Base` in the *afun* constructor. It is also the *Base* type of the elements of *ax* and *ay* in the atomic function call. To be specific, the elements of *ax* and *ay* have type ``AD`` < ``Base`` > . ADVector ******** The type *ADVector* must be a :ref:`simple vector class` with elements of type ``AD`` < *Base* > . afun **** is a :ref:`atomic_four_ctor@atomic_user` object and this *afun* function call is implemented by the :ref:`atomic_four_ctor@atomic_four` class. ax ** This vector is ``const`` and passed by reference and its size determines *n* . It specifies vector :math:`x \in \B{R}^n` at which an ``AD`` < *Base* > version of :math:`y = g(x)` is to be evaluated. ay ** This vector is passed by reference and its size determines *m* . The input values of its elements are not specified (must not matter). Upon return, it is an ``AD`` < *Base* > version of :math:`y = g(x)`. call_id ******* This optional argument has default value zero. It can be used to specify additional information about this call to *afun* . For example, it could specify the index in vector of structures in the *afun* object where the actual information is placed. for_type ******** The :ref:`for_type` routine will be called once, for each call to an atomic function, before any other callbacks corresponding to the atomic function call. This enables you to store, during the ``for_type`` routine, the values in :ref:`atomic_four_for_type@type_x` and or :ref:`atomic_four_for_type@type_y` corresponding to this atomic function call. Restriction =========== The value of *call_id* must be less than or equal ``std::numeric_limits<`` *cppad_tape_id_type* >:: ``max`` () see :ref:`cmake@cppad_tape_id_type` . {xrst_end atomic_four_call} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN_PROTOTYPE template template void atomic_four::operator()( size_t call_id , const ADVector& ax , ADVector& ay ) // END_PROTOTYPE { size_t n = ax.size(); size_t m = ay.size(); # ifndef NDEBUG bool ok = true; std::string msg = "atomic_four: call " + atomic_name() + " "; if( (n == 0) || (m == 0) ) { msg += "ax.size() or ay.size() is zero"; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # endif // // type_x, type_y, taylor_x, taylor_y, select_y size_t thread = thread_alloc::thread_num(); allocate_work(thread); vector& type_x = work_[thread]->type_x; vector& type_y = work_[thread]->type_y; vector& taylor_x = work_[thread]->taylor_x; vector& taylor_y = work_[thread]->taylor_y; vector& select_y = work_[thread]->select_y; type_x.resize(n); taylor_x.resize(n); type_y.resize(m); taylor_y.resize(m); select_y.resize(m); // // tape_id, tape, taylor_x, type_x tape_id_t tape_id = 0; local::ADTape* tape = nullptr; for(size_t j = 0; j < n; j++) { taylor_x[j] = ax[j].value_; if( IdenticalZero( ax[j] ) ) type_x[j] = identical_zero_enum; else if( Constant( ax[j] ) ) type_x[j] = constant_enum; else { type_x[j] = ax[j].ad_type_; if( tape_id == 0 ) { tape = ax[j].tape_this(); tape_id = ax[j].tape_id_; CPPAD_ASSERT_UNKNOWN( tape != nullptr ); } # ifndef NDEBUG if( Dynamic( ax[j] ) ) { CPPAD_ASSERT_UNKNOWN( type_x[j] == dynamic_enum ); } else { CPPAD_ASSERT_UNKNOWN( Variable( ax[j] ) ); CPPAD_ASSERT_UNKNOWN( type_x[j] == variable_enum ); } if( tape_id != ax[j].tape_id_ ) { msg += atomic_name() + ": ax contains non-constant values from different threads."; CPPAD_ASSERT_KNOWN(false, msg.c_str()); } # endif } } // Use zero order forward mode to compute all the components of y for(size_t i = 0; i < m; ++i) select_y[i] = true; size_t order_low = 0; size_t order_up = 0; # ifdef NDEBUG for_type( call_id, type_x, type_y ); forward( call_id, select_y, order_low, order_up, taylor_x, taylor_y ); # else ok &= for_type( call_id, type_x, type_y ); ok &= forward( call_id, select_y, order_low, order_up, taylor_x, taylor_y ); if( ! ok ) { msg += atomic_name() + ": ok is false for " "type or zero order forward mode calculation."; CPPAD_ASSERT_KNOWN(false, msg.c_str()); } # endif bool record_dynamic = false; bool record_variable = false; // // set ay to be vector of constant parameters with correct value for(size_t i = 0; i < m; i++) { // pass back values ay[i].value_ = taylor_y[i]; // initialize entire vector as constants ay[i].tape_id_ = 0; ay[i].taddr_ = 0; // we need to record this operation if // any of the elements of ay are dynamics or variables, record_dynamic |= type_y[i] == dynamic_enum; record_variable |= type_y[i] == variable_enum; } # ifndef NDEBUG if( (record_dynamic || record_variable) && tape == nullptr ) { msg += "all elements of x are constants but y contains a non-constant"; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # endif if( record_dynamic) { tape->Rec_.put_dyn_atomic( tape_id, index_, call_id, type_x, type_y, ax, ay ); } // case where result contains a variable if( record_variable ) { tape->Rec_.put_var_atomic( tape_id, index_, call_id, type_x, type_y, ax, ay ); } # ifndef NDEBUG for(size_t i = 0; i < m; ++i) switch( type_y[i] ) { // case identical_zero_enum: case constant_enum: CPPAD_ASSERT_UNKNOWN( Constant( ay[i] ) ); break; // case dynamic_enum: CPPAD_ASSERT_UNKNOWN( Dynamic( ay[i] ) ); break; // case variable_enum: CPPAD_ASSERT_UNKNOWN( Variable( ay[i] ) ); break; // default: CPPAD_ASSERT_KNOWN( false, "atomic_four: for_type: type_y[i]: is not a valid type" ); break; } # endif return; } template template void atomic_four::operator()( const ADVector& ax , ADVector& ay ) { size_t call_id = 0; (*this)(call_id, ax, ay); } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/four/ctor.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_FOUR_CTOR_HPP # define CPPAD_CORE_ATOMIC_FOUR_CTOR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_ctor} Atomic Function Constructor ########################### Syntax ****** | ``class`` *atomic_user* : ``public CppAD::atomic_four<`` *Base* > { | ``public:`` | |tab| *atomic_user* ( *ctor_arg_list* ) : ``CppAD::atomic_four<`` *Base* >( *name* ) | |tab| ... | }; | *atomic_user afun* ( *ctor_arg_list* ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } atomic_user *********** ctor_arg_list ============= Is a list of arguments for the *atomic_user* constructor. afun ==== The object *afun* must stay in scope for as long as the corresponding atomic function is used. This includes use by any :ref:`ADFun\` object that has this *atomic_user* operation in its :ref:`operation sequence` . Implementation ============== The user defined *atomic_user* class is a publicly derived class of ``atomic_four`` < *Base* > . It should be declared as follows: | |tab| ``class`` *atomic_user* : ``public CppAD::atomic_four<`` *Base* > { | |tab| ``public:`` | |tab| |tab| *atomic_user* ( *ctor_arg_list* ) : ``atomic_four`` < *Base* >( *name* ) | |tab| ... | |tab| }; where ... denotes the rest of the implementation of the derived class. This includes completing the constructor and all the virtual functions that have their ``atomic_four`` implementations replaced by *atomic_user* implementations. atomic_four *********** Restrictions ============ The ``atomic_four`` constructor and destructor cannot be called in :ref:`parallel` mode. Base ==== The template parameter determines the :ref:`atomic_four_call@Base` type for this ``AD`` < *Base* > atomic operation. name ==== This ``atomic_four`` constructor argument has the following prototype ``const std::string&`` *name* It is the name for this atomic function and is used for error reporting. The suggested value for *name* is *afun* or *atomic_user* , i.e., the name of the corresponding atomic object or class. Example ******* The following is an example constructor definition taken from :ref:`atomic_four_norm_sq.cpp-name` : {xrst_literal example/atomic_four/norm_sq.cpp // BEGIN CONSTRUCTOR // END CONSTRUCTOR } {xrst_end atomic_four_ctor} ------------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE // atomic_four() template atomic_four::atomic_four(void) { CPPAD_ASSERT_KNOWN(false, "Attempt to use the atomic_four default constructor" ); } // atomic_four(name) // BEGIN_PROTOTYPE template atomic_four::atomic_four(const std::string& name ) // END_PROTOTYPE { CPPAD_ASSERT_KNOWN( ! thread_alloc::in_parallel() , "atomic_four: constructor cannot be called in parallel mode." ); // // index_ bool set_null = false; size_t index = 0; size_t type = 4; std::string copy_name = name; void* copy_this = reinterpret_cast( this ); index_ = local::atomic_index( set_null, index, type, ©_name, copy_this ); // // work_ for(size_t thread = 0; thread < CPPAD_MAX_NUM_THREADS; thread++) work_[thread] = nullptr; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/four/devel/devel.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin atomic_four_devel dev} atomic_four Developer Documentation ################################### Contents ******** {xrst_toc_table include/cppad/core/atomic/four/devel/jac_sparsity.hpp include/cppad/core/atomic/four/devel/hes_sparsity.hpp } {xrst_end atomic_four_devel} ================================================ FILE: include/cppad/core/atomic/four/devel/hes_sparsity.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_FOUR_DEVEL_HES_SPARSITY_HPP # define CPPAD_CORE_ATOMIC_FOUR_DEVEL_HES_SPARSITY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { // BEGIN_CPPAD_NAMESPACE /* ----------------------------------------------------------------------------- {xrst_begin atomic_four_for_hes_sparsity dev} {xrst_spell numvar } Link from Forward Hessian Sparsity Sweep to atomic_four Callback ################################################################ Prototype ********* {xrst_literal // BEGIN_FOR_HES_SPARSITY // END_FOR_HES_SPARSITY } InternalSparsity **************** Is the used internally for sparsity calculations; i.e., sparse_pack or sparse_list. call_id [in] ************ see :ref:`atomic_four_call@call_id` . ident_zero_x ************ This argument has size equal to the number of arguments to this atomic function; i.e. the size of *ax* . If *ident_zero_x* [ *j* ] is true, the argument *ax* [ *j* ] is a constant parameter that is identically zero. x_index ******* is the variable index, on the tape, for the arguments to this function. This size of x_index is n, the number of arguments to this function. The index zero is used for parameters. y_index ******* is the variable index, on the tape, for the results for this function. This size of y_index is m, the number of results for this function. The index zero is used for parameters. np1 *** is the number of components of x plus one; i.e. *n* + 1 . numvar ****** is the total number of variables in the tape; i.e., *play* ``->num_var`` () . for_sparsity ************ The sparsity patterns with index zero and index *np1* are empty (because they correspond to parameters). On Input ======== On input, for j = 0, ... , n-1, the forward Jacobian sparsity for the *j*-th argument to the atomic function is the sparsity pattern with index *np1* + *x_index* [ *j* ] . In addition, the sparsity pattern with index *j+1*-th contains the non-zero cross partial indices where *j+1*-th is the other index. This Hessian does not include the atomic operation. On Output ========= On output, for i = 0, ... , m-1, the forward Jacobian sparsity for the *i*-th result of the atomic function is the sparsity pattern with index *np1* + *y_index* [ *i* ] . In addition, the sparsity pattern with index *j+1*-th contains the non-zero cross partial indices where *j+1*-th is the other index. This Hessian includes the atomic operation. rev_jac_pattern *************** On input, for i = 0, ... , m-1, the sparsity pattern with index y_index[i], is the reverse Jacobian sparsity for the i-th result to this atomic function. This shows which components of the result affect the function we are computing the Hessian of. hes_sparsity_for **************** This is the sparsity pattern for the Hessian. On input, the non-linear terms in the atomic function have not been included. Upon return, they have been included. {xrst_end atomic_four_for_hes_sparsity} */ // BEGIN_FOR_HES_SPARSITY template template bool atomic_four::for_hes_sparsity( size_t call_id , const vector& ident_zero_x , const vector& x_index , const vector& y_index , size_t np1 , size_t numvar , const InternalSparsity& rev_jac_pattern , InternalSparsity& for_sparsity ) // END_FOR_HES_SPARSITY { typedef typename InternalSparsity::const_iterator const_iterator; // CPPAD_ASSERT_UNKNOWN( rev_jac_pattern.end() == 1 ); CPPAD_ASSERT_UNKNOWN( for_sparsity.end() == np1 ); CPPAD_ASSERT_UNKNOWN( for_sparsity.n_set() == np1 + numvar ); CPPAD_ASSERT_UNKNOWN( for_sparsity.number_elements(0) == 0 ); CPPAD_ASSERT_UNKNOWN( for_sparsity.number_elements(np1) == 0 ); // size_t n = x_index.size(); size_t m = y_index.size(); // // select_x vector select_x(n); for(size_t j = 0; j < n; j++) { // check if should compute pattern w.r.t x[j] select_x[j] = for_sparsity.number_elements(np1 + x_index[j]) > 0; } // // bool select_y vector select_y(m); for(size_t i = 0; i < m; i++) { // check if we should include y[i] select_y[i] = rev_jac_pattern.number_elements(y_index[i]) > 0; } // ------------------------------------------------------------------------ // call user's version of atomic function for Jacobian sparse_rc< vector > pattern_out; bool dependency = false; bool ok = jac_sparsity( call_id, dependency, ident_zero_x, select_x, select_y, pattern_out ); if(! ok) ok = jac_sparsity( call_id, dependency, select_x, select_y, pattern_out ); if( ! ok ) return false; // // transfer sparsity patterns from pattern_out to var_sparsity size_t nnz = pattern_out.nnz(); const vector& row( pattern_out.row() ); const vector& col( pattern_out.col() ); for(size_t k = 0; k < nnz; ++k) { size_t i = row[k]; size_t j = col[k]; CPPAD_ASSERT_KNOWN( select_y[i] && select_x[j], "atomic: jac_sparsity: pattern_out not in " "select_x or select_y range" ); const_iterator itr(for_sparsity, np1 + x_index[j]); size_t ell = *itr; while( ell < np1 ) { for_sparsity.post_element(np1 + y_index[i], ell ); ell = *(++itr); } } for(size_t i = 0; i < m; ++i) for_sparsity.process_post( np1 + y_index[i] ); // ------------------------------------------------------------------------ // call user's version of atomic function for Hessian ok = hes_sparsity( call_id, ident_zero_x, select_x, select_y, pattern_out ); if(! ok ) ok = hes_sparsity( call_id, select_x, select_y, pattern_out ); if( ! ok ) return ok; // // add new elements to Hessian sparisty in calling routine nnz = pattern_out.nnz(); for(size_t k = 0; k < nnz; ++k) { size_t r = row[k]; size_t c = col[k]; CPPAD_ASSERT_KNOWN( select_x[r] && select_x[c], "atomic: hes_sparsity: pattern_out not in select_x range" ); const_iterator itr_1(for_sparsity, np1 + x_index[r]); size_t v1 = *itr_1; while( v1 < np1 ) { for_sparsity.binary_union( v1, v1, np1 + x_index[c], for_sparsity ); v1 = *(++itr_1); } // no need to add same elements twice if( c != r ) { const_iterator itr_2(for_sparsity, np1 + x_index[c]); size_t v2 = *itr_2; while( v2 < np1 ) { for_sparsity.binary_union( v2, v2, np1 + x_index[r], for_sparsity ); v2 = *(++itr_2); } } } return ok; } /* {xrst_begin atomic_four_rev_hes_sparsity dev} Link from Reverse Hessian Sparsity Sweep to atomic_four Callback ################################################################ Prototype ********* {xrst_literal // BEGIN_REV_HES_SPARSITY // END_REV_HES_SPARSITY } InternalSparsity **************** Is the used internally for sparsity calculations; i.e., sparse_pack or sparse_list. call_id [in] ************ see :ref:`atomic_four_call@call_id` . ident_zero_x ************ This argument has size equal to the number of arguments to this atomic function; i.e. the size of *ax* . If *ident_zero_x* [ *j* ] is true, the argument *ax* [ *j* ] is a constant parameter that is identically zero. x_index ******* is the variable index, on the tape, for the arguments to this function. This size of x_index is n, the number of arguments to this function. The index zero is used for parameters. y_index ******* is the variable index, on the tape, for the results for this function. This size of y_index is m, the number of results for this function. The index zero is used for parameters. for_jac_pattern *************** On input, for j = 0, ... , n-1, the sparsity pattern with index x_index[j], is the forward Jacobian pattern for the j-th argument to this atomic function. rev_jac_flag ************ On input, for i = 0, ... , m-1, rev_jac_flag[ y_index[i] ] is true if the function we are computing the Hessian of has possibly non-zero Jacobian w.r.t variable y_index[i]. On output, for j = 0, ... , n, rev_jac_flag[ x_index[j] ] is set to true if the variable with index x_index[j] has possible non-zero Jacobian with respect to one of the true y_index[i] cases. Otherwise, rev_jac_flag [ x_index[j] ] is not changed. hes_sparsity_rev **************** Is the reverse mode sparsity pattern for the Hessian. On input, the non-linear terms in the atomic function have not been included. Upon return, they have been included. {xrst_end atomic_four_rev_hes_sparsity} */ // BEGIN_REV_HES_SPARSITY template template bool atomic_four::rev_hes_sparsity( size_t call_id , const vector& ident_zero_x , const vector& x_index , const vector& y_index , const InternalSparsity& for_jac_pattern , bool* rev_jac_flag , InternalSparsity& hes_sparsity_rev ) // END_REV_HES_SPARSITY { CPPAD_ASSERT_UNKNOWN( for_jac_pattern.number_elements(0) == 0 ); CPPAD_ASSERT_UNKNOWN( ! rev_jac_flag[0] ); // size_t n = x_index.size(); size_t m = y_index.size(); // // select_x vector select_x(n); for(size_t j = 0; j < n; j++) select_x[j] = for_jac_pattern.number_elements( x_index[j] ) > 0; // // select_y vector select_y(m); for(size_t i = 0; i < m; i++) select_y[i] = rev_jac_flag[ y_index[i] ]; // // call atomic function for Jacobain sparsity bool dependency = false; sparse_rc< vector > pattern_jac; bool ok = jac_sparsity( call_id, dependency, ident_zero_x, select_x, select_y, pattern_jac ); if(! ok) ok = jac_sparsity( call_id, dependency, select_x, select_y, pattern_jac ); const vector& row_jac( pattern_jac.row() ); const vector& col_jac( pattern_jac.col() ); size_t nnz_jac = pattern_jac.nnz(); if( ! ok ) return ok; // // call atomic function for Hessian sparsity sparse_rc< vector > pattern_hes; ok = hes_sparsity( call_id, ident_zero_x, select_x, select_y, pattern_hes ); if( ! ok ) ok = hes_sparsity( call_id, select_x, select_y, pattern_hes ); if( ! ok ) return ok; // // row_hes, col_hes, nnz_hes const vector& row_hes( pattern_hes.row() ); const vector& col_hes( pattern_hes.col() ); size_t nnz_hes = pattern_hes.nnz(); // // propagate Hessian sparsity through the Jacobian for(size_t k = 0; k < nnz_jac; ++k) { size_t i = row_jac[k]; size_t j = col_jac[k]; CPPAD_ASSERT_KNOWN( select_y[i] && select_x[j] , "atomic: jac_sparsity: pattern_out not in " "select_x or select_y range" ); // from y_index[i] to x_index[j] hes_sparsity_rev.binary_union( x_index[j], x_index[j], y_index[i], hes_sparsity_rev ); } // // propagate rev_jac_flag through the Jacobian // (seems OK to exclude variables with zero forward jacobian) for(size_t k = 0; k < nnz_jac; ++k) { size_t j = col_jac[k]; rev_jac_flag[ x_index[j] ] = true; } // // new hessian sparsity terms between y and x for(size_t k = 0; k < nnz_hes; ++k) { size_t r = row_hes[k]; size_t c = col_hes[k]; CPPAD_ASSERT_KNOWN( select_x[r] && select_x[c] , "atomic: hes_sparsity: pattern_out not in select_x range" ); hes_sparsity_rev.binary_union( x_index[r], x_index[r], x_index[c], for_jac_pattern ); hes_sparsity_rev.binary_union( x_index[c], x_index[c], x_index[r], for_jac_pattern ); } return ok; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/four/devel/jac_sparsity.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_FOUR_DEVEL_JAC_SPARSITY_HPP # define CPPAD_CORE_ATOMIC_FOUR_DEVEL_JAC_SPARSITY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { // BEGIN_CPPAD_NAMESPACE /* ------------------------------------------------------------------------------ {xrst_begin atomic_four_for_jac_sparsity dev} Link from Forward Jacobian Sparsity Sweep to atomic_four Callback ################################################################# Prototype ********* {xrst_literal // BEGIN_FOR_JAC_SPARSITY // END_FOR_JAC_SPARSITY } InternalSparsity **************** is the type used for internal sparsity calculations; i.e., sparse_pack or sparse_list. call_id ******* see :ref:`atomic_four_call@call_id` . dependency ********** if true, calculate dependency pattern, otherwise calculate sparsity pattern. ident_zero_x ************ This argument has size equal to the number of arguments to this atomic function; i.e. the size of *ax* . If *ident_zero_x* [ *j* ] is true, the argument *ax* [ *j* ] is a constant parameter that is identically zero. x_index ******* is the variable index, on the tape, for the arguments to this atomic function. This size of x_index is, the number of arguments to this atomic function. The index zero is used for parameters. y_index ******* is the variable index, on the tape, for the results for this atomic function. This size of y_index is m, the number of results for this atomic function. The index zero is used for parameters. var_sparsity ************ On input, for j = 0, ... , n-1, the sparsity pattern with index x_index[j], is the sparsity for the j-th argument to this atomic function. On output, for i = 0, ... , m-1, the sparsity pattern with index y_index[i], is the sparsity for the i-th result for this atomic function. Return Value ************ is true if the computation succeeds. {xrst_end atomic_four_for_jac_sparsity} */ // BEGIN_FOR_JAC_SPARSITY template template bool atomic_four::for_jac_sparsity( size_t call_id , bool dependency , const vector& ident_zero_x , const vector& x_index , const vector& y_index , InternalSparsity& var_sparsity ) // END_FOR_JAC_SPARSITY { typedef typename InternalSparsity::const_iterator iterator; // number of arguments and results for this atomic function size_t n = x_index.size(); size_t m = y_index.size(); // select_y vector select_y(m); for(size_t i = 0; i < m; ++i) select_y[i] = y_index[i] != 0; // determine select_x vector select_x(n); for(size_t j = 0; j < n; ++j) { if( x_index[j] == 0 ) select_x[j] = false; else { // check if x_j depends on any previous variable iterator itr(var_sparsity, x_index[j]); size_t ell = *itr; select_x[j] = ell < var_sparsity.end(); } } sparse_rc< vector > pattern_out; bool ok = jac_sparsity( call_id, dependency, ident_zero_x, select_x, select_y, pattern_out ); if( ! ok ) ok = jac_sparsity( call_id, dependency, select_x, select_y, pattern_out ); if( ! ok ) return false; // // transfer sparsity patterns from pattern_out to var_sparsity size_t nnz = pattern_out.nnz(); const vector& row( pattern_out.row() ); const vector& col( pattern_out.col() ); for(size_t k = 0; k < nnz; ++k) { size_t i = row[k]; size_t j = col[k]; CPPAD_ASSERT_KNOWN( select_y[i] && select_x[j], "atomic: jac_sparsity: pattern_out not in " "select_x or select_y range" ); iterator itr(var_sparsity, x_index[j]); size_t ell = *itr; while( ell < var_sparsity.end() ) { var_sparsity.post_element( y_index[i], ell ); ell = *(++itr); } } for(size_t i = 0; i < m; ++i) var_sparsity.process_post( y_index[i] ); // return true; } /* ------------------------------------------------------------------------------ {xrst_begin atomic_four_rev_jac_sparsity dev} Link from Reverse Jacobian Sparsity Sweep to atomic_four Callback ################################################################# Prototype ********* {xrst_literal // BEGIN_REV_JAC_SPARSITY // END_REV_JAC_SPARSITY } InternalSparsity **************** Is the type used for internal sparsity calculations; i.e., sparse_pack or sparse_list. call_id ******* see :ref:`atomic_four_call@call_id` dependency ********** if true, calculate dependency pattern, otherwise calculate sparsity pattern. ident_zero_x ************ This argument has size equal to the number of arguments to this atomic function; i.e. the size of *ax* . If *ident_zero_x* [ *j* ] is true, the argument *ax* [ *j* ] is a constant parameter that is identically zero. x_index ******* is the variable index, on the tape, for the arguments to this atomic function. This size of x_index is n, the number of arguments to this atomic function. The index zero is used for parameters. y_index ******* is the variable index, on the tape, for the results for this atomic function. This size of y_index is m, the number of results for this atomic function. The index zero is used for parameters. var_sparsity ************ We are given a sparsity pattern an outer function G(y, x) and compute the pattern for an inner function H(x), which is the outer functions with the components of y treated as functions of x; i.e. H(x) = G( Y(x), x). y_index ======= On input, for i = 0, ... , m-1, the sparsity pattern with index y_index[i], is the sparsity of the outer function with respect to the i-th result for this atomic function. x_index ======= On input, for j = 0, ... , n-1, the sparsity pattern with index x_index[j], is the sparsity for the outer function with respect to the j-th argument to this atomic function. On output, for j = 0, ... , n-1, the sparsity pattern with index x_index[j], is the sparsity for the inner function with respect to the j-th argument to this atomic function. Return Value ************ is true if the computation succeeds. {xrst_end atomic_four_rev_jac_sparsity} */ // BEGIN_REV_JAC_SPARSITY template template bool atomic_four::rev_jac_sparsity( size_t call_id , bool dependency , const vector& ident_zero_x , const vector& x_index , const vector& y_index , InternalSparsity& var_sparsity ) // END_REV_JAC_SPARSITY { typedef typename InternalSparsity::const_iterator iterator; // number of arguments and results for this atomic function size_t n = x_index.size(); size_t m = y_index.size(); // selection vectors vector select_x(n), select_y(m); // select_x for(size_t j = 0; j < n; ++j) select_x[j] = x_index[j] != 0; // determine select_y for(size_t i = 0; i < m; ++i) { if( y_index[i] == 0 ) select_y[i] = false; else { // check if y_i has sparsity is non-empty iterator itr(var_sparsity, y_index[i]); size_t ell = *itr; select_y[i] = ell < var_sparsity.end(); } } sparse_rc< vector > pattern_out; bool ok = jac_sparsity( call_id, dependency, ident_zero_x, select_x, select_y, pattern_out ); if( ! ok ) ok = jac_sparsity( call_id, dependency, select_x, select_y, pattern_out ); if( ! ok ) return false; // // transfer sparsity patterns from pattern_out to var_sparsity size_t nnz = pattern_out.nnz(); const vector& row( pattern_out.row() ); const vector& col( pattern_out.col() ); for(size_t k = 0; k < nnz; ++k) { size_t i = row[k]; size_t j = col[k]; CPPAD_ASSERT_KNOWN( select_y[i] && select_x[j], "atomic: jac_sparsity: pattern_out not in " "select_x or select_y range" ); iterator itr(var_sparsity, y_index[i]); size_t ell = *itr; while( ell < var_sparsity.end() ) { var_sparsity.post_element( x_index[j], ell ); ell = *(++itr); } } for(size_t j = 0; j < n; ++j) var_sparsity.process_post( x_index[j] ); // return true; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/four/for_type.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_FOUR_FOR_TYPE_HPP # define CPPAD_CORE_ATOMIC_FOUR_FOR_TYPE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_for_type} Atomic Function Forward Type Calculation ######################################## Syntax ****** | *ok* = *afun* . ``for_type`` ( *call_id* , *type_x* , *type_y* ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } Dependency Analysis ******************* This calculation is sometimes referred to as a forward dependency analysis. Usage ***** This syntax and prototype are used a :ref:`call` to an atomic function. Implementation ************** This virtual function must be defined by the :ref:`atomic_four_ctor@atomic_user` derived class. vector ****** is the :ref:`CppAD_vector-name` template class. Base **** See :ref:`atomic_four_call@Base` . call_id ******* See :ref:`atomic_four_call@call_id` . ad_type ******* The type ``CppAD::ad_type_enum`` is used to specify if an AD object is a :ref:`constant parameter` :ref:`dynamic parameter` or :ref:`glossary@Variable` . It has the following possible values: .. csv-table:: :widths: auto *ad_type_enum*,Meaning ``identical_zero_enum``,identically zero ``constant_enum``,constant parameter ``dynamic_enum``,dynamic parameter ``variable_enum``,variable In addition, ``identical_zero_enum < constant_enum < dynamic_enum < variable_enum`` A value that is identically zero is also a constant parameter. In CppAD, multiplication of a variable by a value that is identically zero is sometimes treated like :ref:`azmul-title`. This avoids having to record the operation. type_x ****** This vector has size equal to the number of arguments in the atomic function call; i.e., the size of :ref:`atomic_four_call@ax` which we denote by *n* . For *j* =0,..., *n* ``-1`` , *type_x* [ *j* ] is the type of *ax* [ *j* ] . type_y ****** This vector has size equal to the number of results in the atomic function call; i.e., the size of :ref:`atomic_four_call@ay` which we denote by *m* . The input values of the elements of *type_y* are not specified (must not matter). Upon return, for :math:`i = 0 , \ldots , m-1`, *type_y* [ *i* ] is set to one of the following values: #. It is ``identical_zero_enum`` if *ay* [ *i* ] is :ref:`identically zero` . #. It is ``constant_enum`` if *ay* [ *i* ] only depends on the arguments that are constants. #. It is ``dynamic_enum`` if *ay* [ *i* ] depends on a dynamic parameter and does not depend on any variables. #. It is ``variable_enum`` if *ay* [ *i* ] depends on a variable. ok ** If this calculation succeeded, *ok* is true. Otherwise, it is false. Example ******* The following is an example ``for_type`` definition taken from :ref:`atomic_four_norm_sq.cpp-name` : {xrst_literal example/atomic_four/norm_sq.cpp // BEGIN FOR_TYPE // END FOR_TYPE } {xrst_end atomic_four_for_type} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN_PROTOTYPE template bool atomic_four::for_type( size_t call_id , const vector& type_x , vector& type_y ) // END_PROTOTYPE { return false; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/four/forward.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_FOUR_FORWARD_HPP # define CPPAD_CORE_ATOMIC_FOUR_FORWARD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_forward} {xrst_spell ataylor } Atomic Function Forward Mode ############################ Syntax ****** Base ==== | *ok* = *afun* . ``forward`` ( | |tab| *call_id* , *select_y* , | |tab| *order_low* , *order_up* , *type_x* , *taylor_x* , *taylor_y* | ) AD ======== | *ok* = *afun* . ``forward`` ( | |tab| *call_id* , *select_y* , | |tab| *order_low* , *order_up* , *type_x* , *ataylor_x* , *ataylor_y* | ) Prototype ********* Base ==== {xrst_literal // BEGIN_PROTOTYPE_BASE // END_PROTOTYPE_BASE } AD ======== {xrst_literal // BEGIN_PROTOTYPE_AD_BASE // END_PROTOTYPE_AD_BASE } Base **** see :ref:`atomic_four_call@Base` . vector ****** is the :ref:`CppAD_vector-name` template class. Usage ***** Base ==== The *Base* syntax and prototype are used by a :ref:`call` to the atomic function *afun* . They are also used by *f* . ``Forward`` and *f* . ``new_dynamic`` where *f* has prototype ``ADFun`` < *Base* > *f* and *afun* is used during the recording of *f* . AD ======== The ``AD`` < *Base* > syntax and prototype are used by *af* . ``Forward`` and *af* . ``new_dynamic`` where *af* has prototype ``ADFun< AD<`` *Base* > , *Base* > *af* and *afun* is used in a function *af* , created from *f* using :ref:`base2ad-name` . Implementation ************** The *taylor_x* , *taylor_y* version of this function must be defined by the :ref:`atomic_four_ctor@atomic_user` class. It can return *ok* == ``false`` (and not compute anything) for values of *order_up* that are greater than those used by your :ref:`Forward-name` mode calculations. Order zero must be implemented. call_id ******* See :ref:`atomic_four_call@call_id` . select_y ******** This argument has size equal to the number of results to this atomic function; i.e. the size of :ref:`atomic_four_call@ay` . It specifies which components of *y* the corresponding Taylor coefficients must be computed. order_low ********* This argument specifies the lowest order Taylor coefficient that we are computing. p = We sometimes use the notation *p* = *order_low* below. order_up ******** This argument is the highest order Taylor coefficient that we are computing ( *order_low* <= *order_up* ). q = We use the notation *q* = *order_up* + 1 below. This is the number of Taylor coefficients for each component of *x* and *y* . taylor_x ******** The size of *taylor_x* is *q* * *n* . For :math:`j = 0 , \ldots , n-1` and :math:`k = 0 , \ldots , q-1`, we use the Taylor coefficient notation .. math:: :nowrap: \begin{eqnarray} x_j^k & = & \R{taylor\_x} [ j * q + k ] \\ X_j (t) & = & x_j^0 + x_j^1 t^1 + \cdots + x_j^{q-1} t^{q-1} \end{eqnarray} Note that superscripts represent an index for :math:`x_j^k` and an exponent for :math:`t^k`. Also note that the Taylor coefficients for :math:`X(t)` correspond to the derivatives of :math:`X(t)` at :math:`t = 0` in the following way: .. math:: x_j^k = \frac{1}{ k ! } X_j^{(k)} (0) parameters ========== If the *j*-th component of *x* is a parameter, *type_x* [ *j* ] < ``CppAD::variable_enum`` In this case, for *k* > 0 , *taylor_x* [ *j* * *q* + *k* ] == 0 ataylor_x ********* The specifications for *ataylor_x* is the same as for *taylor_x* (only the type of *ataylor_x* is different). taylor_y ******** The size of *taylor_y* is *q* * *m* . Upon return, For :math:`i = 0 , \ldots , m-1` and :math:`k = 0 , \ldots , q-1`, if *select_y* [ *i* ] is true, .. math:: :nowrap: \begin{eqnarray} Y_i (t) & = & g_i [ X(t) ] \\ Y_i (t) & = & y_i^0 + y_i^1 t^1 + \cdots + y_i^{q-1} t^{q-1} + o( t^{q-1} ) \\ \R{taylor\_y} [ i * q + k ] & = & y_i^k \end{eqnarray} where :math:`o( t^{q-1} ) / t^{q-1} \rightarrow 0` as :math:`t \rightarrow 0`. Note that superscripts represent an index for :math:`y_j^k` and an exponent for :math:`t^k`. Also note that the Taylor coefficients for :math:`Y(t)` correspond to the derivatives of :math:`Y(t)` at :math:`t = 0` in the following way: .. math:: y_j^k = \frac{1}{ k ! } Y_j^{(k)} (0) If :math:`p > 0`, for :math:`i = 0 , \ldots , m-1` and :math:`k = 0 , \ldots , p-1`, the input of *taylor_y* satisfies .. math:: \R{taylor\_y} [ i * q + k ] = y_i^k These values do not need to be recalculated and can be used during the computation of the higher order coefficients. ataylor_y ********* The specifications for *ataylor_y* is the same as for *taylor_y* (only the type of *ataylor_y* is different). ok ** If this calculation succeeded, *ok* is true. Otherwise, it is false. Discussion ********** For example, suppose that *order_up* == 2 , and you know how to compute the function :math:`g(x)`, its first derivative :math:`g^{(1)} (x)`, and it component wise Hessian :math:`g_i^{(2)} (x)`. Then you can compute *taylor_x* using the following formulas: .. math:: :nowrap: \begin{eqnarray} y_i^0 & = & Y(0) = g_i ( x^0 ) \\ y_i^1 & = & Y^{(1)} ( 0 ) = g_i^{(1)} ( x^0 ) X^{(1)} ( 0 ) = g_i^{(1)} ( x^0 ) x^1 \\ y_i^2 & = & \frac{1}{2 !} Y^{(2)} (0) \\ & = & \frac{1}{2} X^{(1)} (0)^\R{T} g_i^{(2)} ( x^0 ) X^{(1)} ( 0 ) + \frac{1}{2} g_i^{(1)} ( x^0 ) X^{(2)} ( 0 ) \\ & = & \frac{1}{2} (x^1)^\R{T} g_i^{(2)} ( x^0 ) x^1 + g_i^{(1)} ( x^0 ) x^2 \end{eqnarray} For :math:`i = 0 , \ldots , m-1`, and :math:`k = 0 , 1 , 2`, .. math:: \R{taylor\_y} [ i * q + k ] = y_i^k Example ******* The following is an example ``forward`` definition taken from :ref:`atomic_four_norm_sq.cpp-name` : {xrst_literal example/atomic_four/norm_sq.cpp // BEGIN FORWARD // END FORWARD } {xrst_end atomic_four_forward} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN_PROTOTYPE_BASE template bool atomic_four::forward( size_t call_id , const vector& select_y , size_t order_low , size_t order_up , const vector& taylor_x , vector& taylor_y ) // END_PROTOTYPE_BASE { return false; } // BEGIN_PROTOTYPE_AD_BASE template bool atomic_four::forward( size_t call_id , const vector& select_y , size_t order_low , size_t order_up , const vector< AD >& ataylor_x , vector< AD >& ataylor_y ) // END_PROTOTYPE_AD_BASE { return false; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/four/hes_sparsity.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_FOUR_HES_SPARSITY_HPP # define CPPAD_CORE_ATOMIC_FOUR_HES_SPARSITY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include /* {xrst_begin atomic_four_hes_sparsity} Atomic Function Hessian Sparsity Patterns ######################################### Syntax ****** Preferred ========= | *ok* = *afun* . ``hes_sparsity`` ( *call_id* , | |tab| *ident_zero_x* , *select_x* , *select_y* , *pattern_out* | ) Deprecated 2022-05-16 ===================== | *ok* = *afun* . ``hes_sparsity`` ( *call_id* , | |tab| *select_x* , *select_y* , *pattern_out* | ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } Implementation ************** This function must be defined if :ref:`atomic_four_ctor@atomic_user@afun` is used to define an :ref:`ADFun-name` object *f* , and Hessian sparsity patterns are computed for *f* . Base **** See :ref:`atomic_four_call@Base` . vector ****** is the :ref:`CppAD_vector-name` template class. call_id ******* See :ref:`atomic_four_call@call_id` . ident_zero_x ************ This can sometimes be used to create more efficient sparsity patterns. If you do not see a way to do this, you can just ignore it. This argument has size equal to the number of arguments to this atomic function; i.e. the size of *ax* . If *ident_zero_x* [ *j* ] is true, the argument *ax* [ *j* ] is a constant parameter that is identically zero. An identically zero value times any other value can be treated as being identically zero. select_x ******** This argument has size equal to the number of arguments to this atomic function; i.e. the size of *ax* . It specifies which domain components are included in the calculation of *pattern_out* . If *select_x* [ *j* ] is false, then there will be no indices *k* such that either of the following hold: | |tab| *pattern_out* . ``row`` ()[ *k* ] == *j* | |tab| *pattern_out* . ``col`` ()[ *k* ] == *j* . select_y ******** This argument has size equal to the number of results to this atomic function; i.e. the size of *ay* . It specifies which range component functions :math:`g_i (x)` are included in of *pattern_out* . pattern_out *********** This input value of *pattern_out* does not matter. Upon return it is the union, with respect to *i* such that *select_y* [ *i* ] is true, of the sparsity pattern for Hessian of :math:`g_i (x)`. To be specific, there are non-negative indices *r* , *c* , and *k* such that | |tab| *pattern_out* . ``row`` ()[ *k* ] == *r* | |tab| *pattern_out* . ``col`` ()[ *k* ] == *c* if and only if there exists an index *i* such that, *select_y* [ *i* ] is true, *select_x* [ *r* ] is true, *select_x* [ *c* ] is true, and .. math:: \partial_{x(r)} \partial_{x(c)} g_i(x) is possibly non-zero. Note that the sparsity pattern should be symmetric. ok ** If this calculation succeeded, *ok* is true. Otherwise it is false. Example ******* The following is an example ``hes_sparsity`` definition taken from :ref:`atomic_four_norm_sq.cpp-name` : {xrst_literal example/atomic_four/norm_sq.cpp // BEGIN HES_SPARSITY // END HES_SPARSITY } {xrst_end atomic_four_hes_sparsity} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN_PROTOTYPE template bool atomic_four::hes_sparsity( size_t call_id , const vector& ident_zero_x , const vector& select_x , const vector& select_y , sparse_rc< vector >& pattern_out ) // END_PROTOTYPE { return false; } // // deprecated version template bool atomic_four::hes_sparsity( size_t call_id , const vector& select_x , const vector& select_y , sparse_rc< vector >& pattern_out ) { return false; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/four/jac_sparsity.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_FOUR_JAC_SPARSITY_HPP # define CPPAD_CORE_ATOMIC_FOUR_JAC_SPARSITY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include /* {xrst_begin atomic_four_jac_sparsity} Atomic Function Jacobian Sparsity Patterns ########################################## Syntax ****** | You can define one or the other of the following callbacks, | but you should not define both. Preferred ========= | *ok* = *afun* . ``jac_sparsity`` ( *call_id* , | |tab| *dependency* , *ident_zero_x* , *select_x* , *select_y* , *pattern_out* | ) Deprecated 2022-05-10 ===================== | *ok* = *afun* . ``jac_sparsity`` ( | |tab| *dependency* , *call_id* , *select_x* , *select_y* , *pattern_out* | ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } Implementation ************** This function must be defined if :ref:`atomic_four_ctor@atomic_user@afun` is used to define an :ref:`ADFun-name` object *f* , and Jacobian sparsity patterns are computed for *f* . (Computing Hessian sparsity patterns requires Jacobian sparsity patterns.) Base **** See :ref:`atomic_four_call@Base` . vector ****** is the :ref:`CppAD_vector-name` template class. call_id ******* See :ref:`atomic_four_call@call_id` . dependency ********** If *dependency* is true, then *pattern_out* is a :ref:`dependency.cpp@Dependency Pattern` for this atomic function. Otherwise it is a :ref:`glossary@Sparsity Pattern` for the derivative of the atomic function. ident_zero_x ************ This can sometimes be used to create more efficient sparsity patterns. If you do not see a way to do this, you can just ignore it. This argument has size equal to the number of arguments to this atomic function; i.e. the size of *ax* . If *ident_zero_x* [ *j* ] is true, the argument *ax* [ *j* ] is a constant parameter that is identically zero. An identically zero value times any other value can be treated as being identically zero. select_x ******** This argument has size equal to the number of arguments to this atomic function; i.e. the size of *ax* . It specifies which domain components are included in the calculation of *pattern_out* . If *select_x* [ *j* ] is false, then there will be no indices *k* such that *pattern_out* . ``col`` ()[ *k* ] == *j* . If *select_x* [ *j* ] is true, the argument *ax* [ *j* ] is a variable and *ident_zero_x* [ *j* ] will be false. select_y ******** This argument has size equal to the number of results to this atomic function; i.e. the size of *ay* . It specifies which range components are included in the calculation of *pattern_out* . If *select_y* [ *i* ] is false, then there will be no indices *k* such that *pattern_out* . ``row`` ()[ *k* ] == *i* . pattern_out *********** This input value of *pattern_out* does not matter. Upon return it is a dependency or sparsity pattern for the Jacobian of :math:`g(x)`, the function corresponding to :ref:`atomic_four_ctor@atomic_user@afun` . To be specific, there are non-negative indices *i* , *j* , *k* such that | |tab| *pattern_out* . ``row`` ()[ *k* ] == *i* | |tab| *pattern_out* . ``col`` ()[ *k* ] == *j* if and only if *select_x* [ *j* ] is true, *select_y* [ *j* ] is true, and :math:`g_i(x)` depends on the value of :math:`x_j` (and the partial of :math:`g_i(x)` with respect to :math:`x_j` is possibly non-zero). ok ** If this calculation succeeded, *ok* is true. Otherwise it is false. Example ******* The following is an example ``jac_sparsity`` definition taken from :ref:`atomic_four_norm_sq.cpp-name` : {xrst_literal example/atomic_four/norm_sq.cpp // BEGIN JAC_SPARSITY // END JAC_SPARSITY } {xrst_end atomic_four_jac_sparsity} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE // // BEGIN_PROTOTYPE template bool atomic_four::jac_sparsity( size_t call_id , bool dependency , const vector& ident_zero_x , const vector& select_x , const vector& select_y , sparse_rc< vector >& pattern_out ) // END_PROTOTYPE { return false; } // // deprecated version of jac_sparsity callback template bool atomic_four::jac_sparsity( size_t call_id , bool dependency , const vector& select_x , const vector& select_y , sparse_rc< vector >& pattern_out ) { return false; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/four/rev_depend.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_FOUR_REV_DEPEND_HPP # define CPPAD_CORE_ATOMIC_FOUR_REV_DEPEND_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_rev_depend} Atomic Function Reverse Dependency ################################## Syntax ****** You can define one or the other of the following callbacks, but you should not define both. Preferred ========= | *ok* = *afun* . ``rev_depend`` ( *call_id* , | |tab| *ident_zero_x* , *depend_x* , *depend_y* | ) Deprecated 2022-05-10 ===================== | *ok* = *afun* . ``rev_depend`` ( *call_id* , | |tab| *depend_x* , *depend_y* | ) Prototype ========= {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } Dependency Analysis ******************* This calculation is sometimes referred to as a reverse dependency analysis. Implementation ************** This function must be defined if :ref:`atomic_four_ctor@atomic_user@afun` is used to define an :ref:`ADFun-name` object *f* , and :ref:`f.optimize()` is used. Base **** See :ref:`atomic_four_call@Base` . vector ****** is the :ref:`CppAD_vector-name` template class. call_id ******* See :ref:`atomic_four_call@call_id` . ident_zero_x ************ This can sometimes be used to create more efficient dependency (fewer true values in *depend_x* ). If you do not see a way to do this, you can just ignore it. This argument has size equal to the number of arguments to this atomic function; i.e. the size of *ax* . If *ident_zero_x* [ *j* ] is true, the argument *ax* [ *j* ] is a constant parameter that is identically zero. An identically zero value times any other value can be treated as being identically zero. depend_x ******** This vector has size equal to the number of arguments for this atomic function; i.e. *n* = *ax* . ``size`` () (see :ref:`atomic_four_call@ax` ). The input values of the elements of *depend_x* are not specified (must not matter). Upon return, for :math:`j = 0 , \ldots , n-1`, *depend_x* [ *j* ] is true if the values of interest depend on the value of *ax* [ *j* ] in the corresponding atomic function call. Optimize ======== Parameters and variables, that the values of interest do not depend on, may get removed by :ref:`optimization` . The corresponding values in :ref:`atomic_four_forward@taylor_x` (after optimization has removed them) are currently zero, but perhaps these should be changed back to nan. depend_y ******** This vector has size equal to the number of results for this atomic function; i.e. *m* = *ay* . ``size`` () (see :ref:`atomic_four_call@ay` ). For :math:`i = 0 , \ldots , m-1`, *depend_y* [ *i* ] is true if the values of interest depend on the value of *ay* [ *i* ] in the corresponding atomic function call. ok ** If this calculation succeeded, *ok* is true. Otherwise, it is false. Example ******* The following is an example ``rev_depend`` definition taken from :ref:`atomic_four_norm_sq.cpp-name` : {xrst_literal example/atomic_four/norm_sq.cpp // BEGIN REV_DEPEND // END REV_DEPEND } {xrst_end atomic_four_rev_depend} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN_PROTOTYPE template bool atomic_four::rev_depend( size_t call_id , const vector& ident_zero_x , vector& depend_x , const vector& depend_y ) // END_PROTOTYPE { return false; } // deprecated version template bool atomic_four::rev_depend( size_t call_id , vector& depend_x , const vector& depend_y ) // end deprecated version { return false; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/four/reverse.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_FOUR_REVERSE_HPP # define CPPAD_CORE_ATOMIC_FOUR_REVERSE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_reverse} {xrst_spell apartial ataylor } Atomic Function Reverse Mode ############################ Syntax ****** Base ==== | *ok* = *afun* . ``reverse`` ( | |tab| *call_id* , *select_x* , | |tab| *order_up* , *taylor_x* , *taylor_y* , *partial_x* , *partial_y* | ) AD ======== | *ok* = *afun* . ``reverse`` ( | |tab| *call_id* , *select_x* , | |tab| *order_up* , *ataylor_x* , *ataylor_y* , *apartial_x* , *apartial_y* | ) Prototype ********* Base ==== {xrst_literal // BEGIN_PROTOTYPE_BASE // END_PROTOTYPE_BASE } AD ======== {xrst_literal // BEGIN_PROTOTYPE_AD_BASE // END_PROTOTYPE_AD_BASE } Base **** see :ref:`atomic_four_call@Base` . vector ****** is the :ref:`CppAD_vector-name` template class. Usage ***** Base ==== This syntax is used by *f* . ``Reverse`` where *f* has prototype ``ADFun`` < *Base* > *f* and atomic function *afun* is used in *f* ; see :ref:`atomic_four_call@Base` . AD ======== This syntax is used by *af* . ``Reverse`` where *af* has prototype ``ADFun< AD<`` *Base* > , *Base* > *af* and the atomic function *afun* is used in *af* ; see :ref:`base2ad-name` . Implementation ************** This function must be defined if :ref:`atomic_four_ctor@atomic_user@afun` is used during the recording of an :ref:`ADFun-name` object *f* , and reverse mode derivatives are computed for *f* . It can return *ok* == ``false`` (and not compute anything) for values of *order_up* that are greater than those used by your :ref:`Reverse-name` mode calculations. call_id ******* See :ref:`atomic_four_call@call_id` . select_x ******** This argument has size equal to the number of arguments to this atomic function; i.e. the size of :ref:`atomic_four_call@ax` . It specifies which components of *x* the corresponding partial derivatives *partial_x* must be computed. order_up ******** This argument is one greater than highest order Taylor coefficient that computing the derivative of. q * We use the notation *q* = *order_up* + 1 below. This is one less than the number of Taylor coefficients for each component of *x* and *y* . taylor_x ******** The size of *taylor_x* is *q* * *n* . For :math:`j = 0 , \ldots , n-1` and :math:`k = 0 , \ldots , q-1`, we use the Taylor coefficient notation .. math:: :nowrap: \begin{eqnarray} x_j^k & = & \R{taylor\_x} [ j * q + k ] \\ X_j (t) & = & x_j^0 + x_j^1 t^1 + \cdots + x_j^{q-1} t^{q-1} \end{eqnarray} Note that superscripts represent an index for :math:`x_j^k` and an exponent for :math:`t^k`. Also note that the Taylor coefficients for :math:`X(t)` correspond to the derivatives of :math:`X(t)` at :math:`t = 0` in the following way: .. math:: x_j^k = \frac{1}{ k ! } X_j^{(k)} (0) parameters ========== If the *j*-th component of *x* is a parameter, *type_x* [ *j* ] < ``CppAD::variable_enum`` In this case, for *k* > 0 , *taylor_x* [ *j* * *q* + *k* ] == 0 ataylor_x ********* The specifications for *ataylor_x* is the same as for *taylor_x* (only the type of *ataylor_x* is different). taylor_y ******** The size of *taylor_y* is *q* * *m* . For :math:`i = 0 , \ldots , m-1` and :math:`k = 0 , \ldots , q-1`, we use the Taylor coefficient notation .. math:: :nowrap: \begin{eqnarray} Y_i (t) & = & g_i [ X(t) ] \\ Y_i (t) & = & y_i^0 + y_i^1 t^1 + \cdots + y_i^{q-1} t^{q-1} + o ( t^{q-1} ) \\ y_i^k & = & \R{taylor\_y} [ i * q + k ] \end{eqnarray} where :math:`o( t^{q-1} ) / t^{q-1} \rightarrow 0` as :math:`t \rightarrow 0`. Note that superscripts represent an index for :math:`y_j^k` and an exponent for :math:`t^k`. Also note that the Taylor coefficients for :math:`Y(t)` correspond to the derivatives of :math:`Y(t)` at :math:`t = 0` in the following way: .. math:: y_j^k = \frac{1}{ k ! } Y_j^{(k)} (0) ataylor_y ********* The specifications for *ataylor_y* is the same as for *taylor_y* (only the type of *ataylor_y* is different). F * We use the notation :math:`\{ x_j^k \} \in \B{R}^{n \times q}` for .. math:: \{ x_j^k \W{:} j = 0 , \ldots , n-1, k = 0 , \ldots , q-1 \} We use the notation :math:`\{ y_i^k \} \in \B{R}^{m \times q}` for .. math:: \{ y_i^k \W{:} i = 0 , \ldots , m-1, k = 0 , \ldots , q-1 \} We use :math:`F : \B{R}^{n \times q} \rightarrow \B{R}^{m \times q}` by to denote the function corresponding to the forward mode calculations .. math:: y_i^k = F_i^k [ \{ x_j^k \} ] Note that .. math:: F_i^0 ( \{ x_j^k \} ) = g_i ( X(0) ) = g_i ( x^0 ) We also note that :math:`F_i^\ell ( \{ x_j^k \} )` is a function of :math:`x^0 , \ldots , x^\ell`; i.e., it is determined by the derivatives of :math:`g_i (x)` up to order :math:`\ell`. G, H **** We use :math:`G : \B{R}^{m \times q} \rightarrow \B{R}` to denote an arbitrary scalar valued function of :math:`\{ y_i^k \}`. We use :math:`H : \B{R}^{n \times q} \rightarrow \B{R}` defined by .. math:: H ( \{ x_j^k \} ) = G[ F( \{ x_j^k \} ) ] partial_y ********* The size of *partial_y* is *q* * *m* . For :math:`i = 0 , \ldots , m-1`, :math:`k = 0 , \ldots , q-1`, .. math:: \R{partial\_y} [ i * q + k ] = \partial G / \partial y_i^k apartial_y ********** The specifications for *apartial_y* is the same as for *partial_y* (only the type of *apartial_y* is different). partial_x ********* The size of *partial_x* is *q* * *n* . The input values of the elements of *partial_x* are not specified (must not matter). Upon return, for :math:`j = 0 , \ldots , n-1` and :math:`\ell = 0 , \ldots , q-1`, .. math:: :nowrap: \begin{eqnarray} \R{partial\_x} [ j * q + \ell ] & = & \partial H / \partial x_j^\ell \\ & = & ( \partial G / \partial \{ y_i^k \} ) \cdot ( \partial \{ y_i^k \} / \partial x_j^\ell ) \\ & = & \sum_{k=0}^{q-1} \sum_{i=0}^{m-1} ( \partial G / \partial y_i^k ) ( \partial y_i^k / \partial x_j^\ell ) \\ & = & \sum_{k=\ell}^{q-1} \sum_{i=0}^{m-1} \R{partial\_y}[ i * q + k ] ( \partial F_i^k / \partial x_j^\ell ) \end{eqnarray} Note that we have used the fact that for :math:`k < \ell`, :math:`\partial F_i^k / \partial x_j^\ell = 0`. azmul ===== An :ref:`optimized` function will use zero for values in *taylor_x* and *taylor_y* that are not necessary in the current context. If you divide by these values when computing :math:`( \partial F_i^k / \partial x_j^\ell )` you could get an nan if the corresponding value in *partial_y* is zero. To be careful, if you do divide by *taylor_x* or *taylor_y* , use :ref:`azmul-name` for to avoid zero over zero calculations. apartial_x ********** The specifications for *apartial_x* is the same as for *partial_x* (only the type of *apartial_x* is different). ok ** If this calculation succeeded, *ok* is true. Otherwise it is false. Example ******* The following is an example ``reverse`` definition taken from :ref:`atomic_four_norm_sq.cpp-name` : {xrst_literal example/atomic_four/norm_sq.cpp // BEGIN REVERSE // END REVERSE } Examples ******** The file :ref:`atomic_four_norm_sq.cpp-name` contains an example that defines this routine. {xrst_end atomic_four_reverse} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN_PROTOTYPE_BASE template bool atomic_four::reverse( size_t call_id , const vector& select_x , size_t order_up , const vector& taylor_x , const vector& taylor_y , vector& partial_x , const vector& partial_y ) // END_PROTOTYPE_BASE { return false; } // BEGIN_PROTOTYPE_AD_BASE template bool atomic_four::reverse( size_t call_id , const vector& select_x , size_t order_up , const vector< AD >& ataylor_x , const vector< AD >& ataylor_y , vector< AD >& apartial_x , const vector< AD >& apartial_y ) // END_PROTOTYPE_AD_BASE { return false; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/one/atomic.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_ONE_ATOMIC_HPP # define CPPAD_CORE_ATOMIC_ONE_ATOMIC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_one app} {xrst_spell px py tvector tx vx vy } Defining Atomic Functions: First Generation ########################################### Deprecated 2013-05-27 ********************* Using ``CPPAD_USER_ATOMIC`` has been deprecated. Use :ref:`atomic_three-name` instead. Syntax Function *************** | ``CPPAD_USER_ATOMIC`` ( *afun* , *Tvector* , *Base* , | |tab| *forward* , *reverse* , *for_jac_sparse* , *rev_jac_sparse* , *rev_hes_sparse* | ) Use Function ============ *afun* ( *id* , *ax* , *ay* ) Callback Routines ================= | *ok* = *forward* ( *id* , *k* , *n* , *m* , *vx* , *vy* , *tx* , *ty* ) | *ok* = *reverse* ( *id* , *k* , *n* , *m* , *tx* , *ty* , *px* , *py* ) | *ok* = *for_jac_sparse* ( *id* , *n* , *m* , *q* , *r* , *s* ) | *ok* = *rev_jac_sparse* ( *id* , *n* , *m* , *q* , *r* , *s* ) | *ok* = *rev_hes_sparse* ( *id* , *n* , *m* , *q* , *r* , *s* , *t* , *u* , *v* ) Free Static Memory ================== ``user_atomic`` < *Base* >:: ``clear`` () Purpose ******* In some cases, the user knows how to compute the derivative of a function .. math:: y = f(x) \; {\rm where} \; f : \B{R}^n \rightarrow \B{R}^m more efficiently than by coding it using ``AD`` < *Base* > :ref:`atomic_base` operations and letting CppAD do the rest. In this case, ``CPPAD_USER_ATOMIC`` can be used add the user code for :math:`f(x)`, and its derivatives, to the set of ``AD`` < *Base* > atomic operations. Another possible purpose is to reduce the size of the tape. Partial Implementation ********************** The routines :ref:`atomic_one@forward` , :ref:`atomic_one@reverse` , :ref:`atomic_one@for_jac_sparse` , :ref:`atomic_one@rev_jac_sparse` , and :ref:`atomic_one@rev_hes_sparse` , must be defined by the user. The *forward* the routine, for the case *k* = 0 , must be implemented. Functions with the correct prototype, that just return ``false`` , can be used for the other cases (unless they are required by your calculations). For example, you need not implement *forward* for the case *k* == 2 until you require forward mode calculation of second derivatives. CPPAD_USER_ATOMIC ***************** The macro | ``CPPAD_USER_ATOMIC`` ( *afun* , *Tvector* , *Base* , | |tab| *forward* , *reverse* , *for_jac_sparse* , *rev_jac_sparse* , *rev_hes_sparse* | ) defines the ``AD`` < *Base* > routine *afun* . This macro can be placed within a namespace (not the ``CppAD`` namespace) but must be outside of any routine. Tvector ======= The macro argument *Tvector* must be a :ref:`simple vector template class` . It determines the type of vectors used as arguments to the routine *afun* . Base ==== The macro argument *Base* specifies the :ref:`base type` corresponding to ``AD`` < *Base>* operation sequences. Calling the routine *afun* will add the operator defined by this macro to an ``AD`` < *Base>* operation sequence. ok ** For all routines documented below, the return value *ok* has prototype ``bool`` *ok* If it is ``true`` , the corresponding evaluation succeeded, otherwise it failed. id ** For all routines documented below, the argument *id* has prototype ``size_t`` *id* Its value in all other calls is the same as in the corresponding call to *afun* . It can be used to store and retrieve extra information about a specific call to *afun* . k * For all routines documented below, the argument *k* has prototype ``size_t`` *k* The value *k* is the order of the Taylor coefficient that we are evaluating (:ref:`atomic_one@forward` ) or taking the derivative of (:ref:`atomic_one@reverse` ). n * For all routines documented below, the argument *n* has prototype ``size_t`` *n* It is the size of the vector *ax* in the corresponding call to *afun* ( *id* , *ax* , *ay* ) ; i.e., the dimension of the domain space for :math:`y = f(x)`. m * For all routines documented below, the argument *m* has prototype ``size_t`` *m* It is the size of the vector *ay* in the corresponding call to *afun* ( *id* , *ax* , *ay* ) ; i.e., the dimension of the range space for :math:`y = f(x)`. tx ** For all routines documented below, the argument *tx* has prototype ``const CppAD::vector<`` *Base* >& *tx* and *tx* . ``size`` () >= ( *k* + 1) * *n* . For :math:`j = 0 , \ldots , n-1` and :math:`\ell = 0 , \ldots , k`, we use the Taylor coefficient notation .. math:: :nowrap: \begin{eqnarray} x_j^\ell & = & tx [ j * ( k + 1 ) + \ell ] \\ X_j (t) & = & x_j^0 + x_j^1 t^1 + \cdots + x_j^k t^k \end{eqnarray} If *tx* . ``size`` () > ( *k* + 1) * *n* , the other components of *tx* are not specified and should not be used. Note that superscripts represent an index for :math:`x_j^\ell` and an exponent for :math:`t^\ell`. Also note that the Taylor coefficients for :math:`X(t)` correspond to the derivatives of :math:`X(t)` at :math:`t = 0` in the following way: .. math:: x_j^\ell = \frac{1}{ \ell ! } X_j^{(\ell)} (0) ty ** In calls to :ref:`atomic_one@forward` , the argument *ty* has prototype ``CppAD::vector<`` *Base* >& *ty* while in calls to :ref:`atomic_one@reverse` it has prototype ``const CppAD::vector<`` *Base* >& *ty* For all calls, *tx* . ``size`` () >= ( *k* + 1) * *m* . For :math:`i = 0 , \ldots , m-1` and :math:`\ell = 0 , \ldots , k`, we use the Taylor coefficient notation .. math:: :nowrap: \begin{eqnarray} y_i^\ell & = & ty [ i * ( k + 1 ) + \ell ] \\ Y_i (t) & = & y_i^0 + y_i^1 t^1 + \cdots + y_i^k t^k + o ( t^k ) \end{eqnarray} where :math:`o( t^k ) / t^k \rightarrow 0` as :math:`t \rightarrow 0`. If *ty* . ``size`` () > ( *k* + 1) * *m* , the other components of *ty* are not specified and should not be used. Note that superscripts represent an index for :math:`y_j^\ell` and an exponent for :math:`t^\ell`. Also note that the Taylor coefficients for :math:`Y(t)` correspond to the derivatives of :math:`Y(t)` at :math:`t = 0` in the following way: .. math:: y_j^\ell = \frac{1}{ \ell ! } Y_j^{(\ell)} (0) forward ======= In the case of *forward* , for :math:`i = 0 , \ldots , m-1`, :math:`ty[ i *( k + 1) + k ]` is an output and all the other components of *ty* are inputs. reverse ======= In the case of *reverse* , all the components of *ty* are inputs. afun **** The macro argument *afun* , is the name of the AD function corresponding to this atomic operation (as it is used in the source code). CppAD uses the other functions, where the arguments are vectors with elements of type *Base* , to implement the function *afun* ( *id* , *ax* , *ay* ) where the argument are vectors with elements of type ``AD`` < *Base* > . ax == The *afun* argument *ax* has prototype ``const`` *Tvector* < ``AD`` < *Base* > >& *ax* It is the argument vector :math:`x \in \B{R}^n` at which the ``AD`` < *Base* > version of :math:`y = f(x)` is to be evaluated. The dimension of the domain space for :math:`y = f (x)` is specified by :ref:`atomic_one@n` = *ax* . ``size`` () , which must be greater than zero. ay == The *afun* result *ay* has prototype *Tvector* < ``AD`` < *Base* > >& *ay* The input values of its elements are not specified (must not matter). Upon return, it is the ``AD`` < *Base* > version of the result vector :math:`y = f(x)`. The dimension of the range space for :math:`y = f (x)` is specified by :ref:`atomic_one@m` = *ay* . ``size`` () , which must be greater than zero. Parallel Mode ============= The first call to *afun* ( *id* , *ax* , *ay* ) must not be in :ref:`parallel` mode. In addition, the :ref:`atomic_one clear` routine cannot be called while in parallel mode. forward ******* The macro argument *forward* is a user defined function *ok* = *forward* ( *id* , *k* , *n* , *m* , *vx* , *vy* , *tx* , *ty* ) that computes results during a :ref:`Forward-name` mode sweep. For this call, we are given the Taylor coefficients in *tx* form order zero through *k* , and the Taylor coefficients in *ty* with order less than *k* . The *forward* routine computes the *k* order Taylor coefficients for :math:`y` using the definition :math:`Y(t) = f[ X(t) ]`. For example, for :math:`i = 0 , \ldots , m-1`, .. math:: :nowrap: \begin{eqnarray} y_i^0 & = & Y(0) = f_i ( x^0 ) \\ y_i^1 & = & Y^{(1)} ( 0 ) = f_i^{(1)} ( x^0 ) X^{(1)} ( 0 ) = f_i^{(1)} ( x^0 ) x^1 \\ y_i^2 & = & \frac{1}{2 !} Y^{(2)} (0) \\ & = & \frac{1}{2} X^{(1)} (0)^\R{T} f_i^{(2)} ( x^0 ) X^{(1)} ( 0 ) + \frac{1}{2} f_i^{(1)} ( x^0 ) X^{(2)} ( 0 ) \\ & = & \frac{1}{2} (x^1)^\R{T} f_i^{(2)} ( x^0 ) x^1 + f_i^{(1)} ( x^0 ) x^2 \end{eqnarray} Then, for :math:`i = 0 , \ldots , m-1`, it sets .. math:: ty [ i * (k + 1) + k ] = y_i^k The other components of *ty* must be left unchanged. Usage ===== This routine is used, with *vx* . ``size`` () > 0 and *k* == 0 , by calls to *afun* . It is used, with *vx* . ``size`` () = 0 and *k* equal to the order of the derivative begin computed, by calls to :ref:`forward` . vx == The *forward* argument *vx* has prototype ``const CppAD::vector&`` *vx* The case *vx* . ``size`` () > 0 occurs once for each call to *afun* , during the call, and before any of the other callbacks corresponding to that call. Hence such a call can be used to cache information attached to the corresponding *id* (such as the elements of *vx* ). If *vx* . ``size`` () > 0 then *k* == 0 , *vx* . ``size`` () >= *n* , and for :math:`j = 0 , \ldots , n-1`, *vx* [ *j* ] is true if and only if *ax* [ *j* ] is a :ref:`glossary@Variable` . If *vx* . ``size`` () == 0 , then *vy* . ``size`` () == 0 and neither of these vectors should be used. vy == The *forward* argument *vy* has prototype ``CppAD::vector&`` *vy* If *vy* . ``size`` () == 0 , it should not be used. Otherwise, *k* == 0 and *vy* . ``size`` () >= *m* . The input values of the elements of *vy* are not specified (must not matter). Upon return, for :math:`j = 0 , \ldots , m-1`, *vy* [ *i* ] is true if and only if *ay* [ *j* ] is a variable. (CppAD uses *vy* to reduce the necessary computations.) reverse ******* The macro argument *reverse* is a user defined function *ok* = *reverse* ( *id* , *k* , *n* , *m* , *tx* , *ty* , *px* , *py* ) that computes results during a :ref:`Reverse-name` mode sweep. The input value of the vectors *tx* and *ty* contain Taylor coefficient, up to order *k* , for :math:`X(t)` and :math:`Y(t)` respectively. We use the :math:`\{ x_j^\ell \}` and :math:`\{ y_i^\ell \}` to denote these Taylor coefficients where the implicit range indices are :math:`i = 0 , \ldots , m-1`, :math:`j = 0 , \ldots , n-1`, :math:`\ell = 0 , \ldots , k`. Using the calculations done by :ref:`atomic_one@forward` , the Taylor coefficients :math:`\{ y_i^\ell \}` are a function of the Taylor coefficients for :math:`\{ x_j^\ell \}`; i.e., given :math:`y = f(x)` we define the function :math:`F : \B{R}^{n \times (k+1)} \rightarrow \B{R}^{m \times (k+1)}` by .. math:: y_i^\ell = F_i^\ell ( \{ x_j^\ell \} ) We use :math:`G : \B{R}^{m \times (k+1)} \rightarrow \B{R}` to denote an arbitrary scalar valued function of the Taylor coefficients for :math:`Y(t)` and write :math:`z = G( \{ y_i^\ell \} )`. The ``reverse`` routine is given the derivative of :math:`z` with respect to :math:`\{ y_i^\ell \}` and computes its derivative with respect to :math:`\{ x_j^\ell \}`. Usage ===== This routine is used, with *k* + 1 equal to the order of the derivative being calculated, by calls to :ref:`reverse` . py == The *reverse* argument *py* has prototype ``const CppAD::vector<`` *Base* >& *py* and *py* . ``size`` () >= ( *k* + 1) * *m* . For :math:`i = 0 , \ldots , m-1` and :math:`\ell = 0 , \ldots , k`, .. math:: py[ i * (k + 1 ) + \ell ] = \partial G / \partial y_i^\ell If *py* . ``size`` () > ( *k* + 1) * *m* , the other components of *py* are not specified and should not be used. px == We define the function .. math:: H ( \{ x_j^\ell \} ) = G[ F( \{ x_j^\ell \} ) ] The *reverse* argument *px* has prototype ``CppAD::vector<`` *Base* >& *px* and *px* . ``size`` () >= ( *k* + 1) * *n* . The input values of the elements of *px* are not specified (must not matter). Upon return, for :math:`j = 0 , \ldots , n-1` and :math:`p = 0 , \ldots , k`, .. math:: :nowrap: \begin{eqnarray} px [ j * (k + 1) + p ] & = & \partial H / \partial x_j^p \\ & = & ( \partial G / \partial \{ y_i^\ell \} ) ( \partial \{ y_i^\ell \} / \partial x_j^p ) \\ & = & \sum_{i=0}^{m-1} \sum_{\ell=0}^k ( \partial G / \partial y_i^\ell ) ( \partial y_i^\ell / \partial x_j^p ) \\ & = & \sum_{i=0}^{m-1} \sum_{\ell=p}^k py[ i * (k + 1 ) + \ell ] ( \partial F_i^\ell / \partial x_j^p ) \end{eqnarray} Note that we have used the fact that for :math:`\ell < p`, :math:`\partial F_i^\ell / \partial x_j^p = 0`. If *px* . ``size`` () > ( *k* + 1) * *n* , the other components of *px* are not specified and should not be used. for_jac_sparse ************** The macro argument *for_jac_sparse* is a user defined function *ok* = *for_jac_sparse* ( *id* , *n* , *m* , *q* , *r* , *s* ) that is used to compute results during a forward Jacobian sparsity sweep. For a fixed :math:`n \times q` matrix :math:`R`, the Jacobian of :math:`f( x + R * u)` with respect to :math:`u \in \B{R}^q` is .. math:: S(x) = f^{(1)} (x) * R Given a :ref:`glossary@Sparsity Pattern` for :math:`R`, *for_jac_sparse* computes a sparsity pattern for :math:`S(x)`. Usage ===== This routine is used by calls to :ref:`ForSparseJac-name` . q = The *for_jac_sparse* argument *q* has prototype ``size_t`` *q* It specifies the number of columns in :math:`R \in \B{R}^{n \times q}` and the Jacobian :math:`S(x) \in \B{R}^{m \times q}`. r = The *for_jac_sparse* argument *r* has prototype ``const CppAD::vector< std::set >&`` *r* and *r* . ``size`` () >= *n* . For :math:`j = 0 , \ldots , n-1`, all the elements of *r* [ *j* ] are between zero and *q* ``-1`` inclusive. This specifies a sparsity pattern for the matrix :math:`R`. s = The *for_jac_sparse* return value *s* has prototype ``CppAD::vector< std::set >&`` *s* and *s* . ``size`` () >= *m* . The input values of its sets are not specified (must not matter). Upon return for :math:`i = 0 , \ldots , m-1`, all the elements of *s* [ *i* ] are between zero and *q* ``-1`` inclusive. This represents a sparsity pattern for the matrix :math:`S(x)`. rev_jac_sparse ************** The macro argument *rev_jac_sparse* is a user defined function *ok* = *rev_jac_sparse* ( *id* , *n* , *m* , *q* , *r* , *s* ) that is used to compute results during a reverse Jacobian sparsity sweep. For a fixed :math:`q \times m` matrix :math:`S`, the Jacobian of :math:`S * f( x )` with respect to :math:`x \in \B{R}^n` is .. math:: R(x) = S * f^{(1)} (x) Given a :ref:`glossary@Sparsity Pattern` for :math:`S`, *rev_jac_sparse* computes a sparsity pattern for :math:`R(x)`. Usage ===== This routine is used by calls to :ref:`RevSparseJac-name` and to :ref:`optimize-name` . q = The *rev_jac_sparse* argument *q* has prototype ``size_t`` *q* It specifies the number of rows in :math:`S \in \B{R}^{q \times m}` and the Jacobian :math:`R(x) \in \B{R}^{q \times n}`. s = The *rev_jac_sparse* argument *s* has prototype ``const CppAD::vector< std::set >&`` *s* and *s* . ``size`` () >= *m* . For :math:`i = 0 , \ldots , m-1`, all the elements of *s* [ *i* ] are between zero and *q* ``-1`` inclusive. This specifies a sparsity pattern for the matrix :math:`S^\R{T}`. r = The *rev_jac_sparse* return value *r* has prototype ``CppAD::vector< std::set >&`` *r* and *r* . ``size`` () >= *n* . The input values of its sets are not specified (must not matter). Upon return for :math:`j = 0 , \ldots , n-1`, all the elements of *r* [ *j* ] are between zero and *q* ``-1`` inclusive. This represents a sparsity pattern for the matrix :math:`R(x)^\R{T}`. rev_hes_sparse ************** The macro argument *rev_hes_sparse* is a user defined function *ok* = *rev_hes_sparse* ( *id* , *n* , *m* , *q* , *r* , *s* , *t* , *u* , *v* ) There is an unspecified scalar valued function :math:`g : \B{R}^m \rightarrow \B{R}`. Given a sparsity pattern for :math:`R` and information about the function :math:`z = g(y)`, this routine computes the sparsity pattern for .. math:: V(x) = (g \circ f)^{(2)}( x ) R Usage ===== This routine is used by calls to :ref:`RevSparseHes-name` . q = The *rev_hes_sparse* argument *q* has prototype ``size_t`` *q* It specifies the number of columns in the sparsity patterns. r = The *rev_hes_sparse* argument *r* has prototype ``const CppAD::vector< std::set >&`` *r* and *r* . ``size`` () >= *n* . For :math:`j = 0 , \ldots , n-1`, all the elements of *r* [ *j* ] are between zero and *q* ``-1`` inclusive. This specifies a sparsity pattern for the matrix :math:`R \in \B{R}^{n \times q}`. s = The *rev_hes_sparse* argument *s* has prototype ``const CppAD::vector&`` *s* and *s* . ``size`` () >= *m* . This specifies a sparsity pattern for the matrix :math:`S(x) = g^{(1)} (y) \in \B{R}^{1 \times m}`. t = The *rev_hes_sparse* argument *t* has prototype ``CppAD::vector&`` *t* and *t* . ``size`` () >= *n* . The input values of its elements are not specified (must not matter). Upon return it represents a sparsity pattern for the matrix :math:`T(x) \in \B{R}^{1 \times n}` defined by .. math:: T(x) = (g \circ f)^{(1)} (x) = S(x) * f^{(1)} (x) u = The *rev_hes_sparse* argument *u* has prototype ``const CppAD::vector< std::set >&`` *u* and *u* . ``size`` () >= *m* . For :math:`i = 0 , \ldots , m-1`, all the elements of *u* [ *i* ] are between zero and *q* ``-1`` inclusive. This specifies a sparsity pattern for the matrix :math:`U(x) \in \B{R}^{m \times q}` defined by .. math:: :nowrap: \begin{eqnarray} U(x) & = & \partial_u \{ \partial_y g[ y + f^{(1)} (x) R u ] \}_{u=0} \\ & = & \partial_u \{ g^{(1)} [ y + f^{(1)} (x) R u ] \}_{u=0} \\ & = & g^{(2)} (y) f^{(1)} (x) R \end{eqnarray} v = The *rev_hes_sparse* argument *v* has prototype ``CppAD::vector< std::set >&`` *v* and *v* . ``size`` () >= *n* . The input values of its elements are not specified (must not matter). Upon return, for :math:`j = 0, \ldots , n-1`, all the elements of *v* [ *j* ] are between zero and *q* ``-1`` inclusive. This represents a sparsity pattern for the matrix :math:`V(x) \in \B{R}^{n \times q}` defined by .. math:: :nowrap: \begin{eqnarray} V(x) & = & \partial_u [ \partial_x (g \circ f) ( x + R u ) ]_{u=0} \\ & = & \partial_u [ (g \circ f)^{(1)}( x + R u ) ]_{u=0} \\ & = & (g \circ f)^{(2)}( x ) R \\ & = & f^{(1)} (x)^\R{T} g^{(2)} ( y ) f^{(1)} (x) R + \sum_{i=1}^m [ g^{(1)} (y) ]_i \; f_i^{(2)} (x) R \\ & = & f^{(1)} (x)^\R{T} U(x) + \sum_{i=1}^m S(x)_i \; f_i^{(2)} (x) R \end{eqnarray} clear ***** User atomic functions hold onto static work space in order to increase speed by avoiding system memory allocation calls. The function call ``user_atomic`` < *Base* >:: ``clear`` () makes to work space :ref:`available` to for other uses by the same thread. This should be called when you are done using the atomic functions for a specific value of *Base* . Restriction =========== The atomic function ``clear`` routine cannot be called while in :ref:`parallel` execution mode. {xrst_end atomic_one} ------------------------------------------------------------------------------ */ # include # include // needed before one can use CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic_one.hpp user defined atomic operations. */ /*! \def CPPAD_USER_ATOMIC(afun, Tvector, forward, reverse, for_jac_sparse, rev_jac_sparse, rev_hes_sparse ) Defines the function afun(id, ax, ay) where id is ax and ay are vectors with AD elements. \par Tvector the Simple Vector template class for this function. \par Base the base type for the atomic operation. \par afun name of the CppAD defined function that corresponding to this operation. Note that afun, preceded` by a pound sign, is a version of afun with quotes around it. \par forward name of the user defined function that computes corresponding results during forward mode. \par reverse name of the user defined function that computes corresponding results during reverse mode. \par for_jac_sparse name of the user defined routine that computes corresponding results during forward mode jacobian sparsity sweeps. \par rev_jac_sparse name of the user defined routine that computes corresponding results during reverse mode jacobian sparsity sweeps. \par rev_hes_sparse name of the user defined routine that computes corresponding results during reverse mode Hessian sparsity sweeps. \par memory allocation Note that atomic_one is used as a static object, so its objects do note get deallocated until the program terminates. */ # define CPPAD_USER_ATOMIC( \ afun , \ Tvector , \ Base , \ forward , \ reverse , \ for_jac_sparse , \ rev_jac_sparse , \ rev_hes_sparse \ ) \ inline void afun ( \ size_t id , \ const Tvector< CppAD::AD >& ax , \ Tvector< CppAD::AD >& ay \ ) \ { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; \ static CppAD::atomic_one fun( \ #afun , \ forward , \ reverse , \ for_jac_sparse , \ rev_jac_sparse , \ rev_hes_sparse \ ); \ fun(id, ax, ay); \ } /// link so that user_atomic::clear() still works template class user_atomic : public atomic_base { }; /*! Class that actually implements the afun(id, ax, ay) calls. A new atomic_one object is generated each time the user invokes the CPPAD_USER_ATOMIC macro; see static object in that macro. */ template class atomic_one : public atomic_base { public: /// disable atomic_one::clear(void) static void clear(void) { CPPAD_ASSERT_KNOWN( false, "Deprecated API uses user_atomic::clear()" ); } /// type for user routine that computes forward mode results typedef bool (*F) ( size_t id , size_t k , size_t n , size_t m , const vector& vx , vector& vy , const vector& tx , vector& ty ); /// type for user routine that computes reverse mode results typedef bool (*R) ( size_t id , size_t k , size_t n , size_t m , const vector& tx , const vector& ty , vector& px , const vector& py ); /// type for user routine that computes forward mode Jacobian sparsity typedef bool (*FJS) ( size_t id , size_t n , size_t m , size_t q , const vector< std::set >& r , vector< std::set >& s ); /// type for user routine that computes reverse mode Jacobian sparsity typedef bool (*RJS) ( size_t id , size_t n , size_t m , size_t q , vector< std::set >& r , const vector< std::set >& s ); /// type for user routine that computes reverse mode Hessian sparsity typedef bool (*RHS) ( size_t id , size_t n , size_t m , size_t q , const vector< std::set >& r , const vector& s , vector& t , const vector< std::set >& u , vector< std::set >& v ); private: /// id value corresponding to next virtual callback size_t id_; /// user's implementation of forward mode const F f_; /// user's implementation of reverse mode const R r_; /// user's implementation of forward jacobian sparsity calculations const FJS fjs_; /// user's implementation of reverse jacobian sparsity calculations const RJS rjs_; /// user's implementation of reverse Hessian sparsity calculations const RHS rhs_; public: /*! Constructor called for each invocation of CPPAD_USER_ATOMIC. Put this object in the list of all objects for this class and set the constant private data f_, r_, fjs_, rjs_, rhs_. \param afun is the user's name for the AD version of this atomic operation. \param f user routine that does forward mode calculations for this operation. \param r user routine that does reverse mode calculations for this operation. \param fjs user routine that does forward Jacobian sparsity calculations. \param rjs user routine that does reverse Jacobian sparsity calculations. \param rhs user routine that does reverse Hessian sparsity calculations. \par This constructor can not be used in parallel mode because atomic_base has this restriction. */ atomic_one(const char* afun, F f, R r, FJS fjs, RJS rjs, RHS rhs) : atomic_base(afun) // name = afun , f_(f) , r_(r) , fjs_(fjs) , rjs_(rjs) , rhs_(rhs) { this->option( atomic_base::set_sparsity_enum ); } /*! Implement the user call to afun(id, ax, ay). \tparam ADVector A simple vector class with elements of type AD. \param id extra information vector that is just passed through by CppAD, and possibly used by user's routines. \param ax is the argument vector for this call, ax.size() determines the number of arguments. \param ay is the result vector for this call, ay.size() determines the number of results. */ template void operator()(size_t id, const ADVector& ax, ADVector& ay) { // call atomic_base function object this->atomic_base::operator()(ax, ay, id); return; } /*! Store id for next virtual function callback \param id id value corresponding to next virtual callback */ virtual void set_old(size_t id) { id_ = id; } /*! Link from atomic_one to forward mode \copydetails atomic_base::forward */ virtual bool forward( size_t p , size_t q , const vector& vx , vector& vy , const vector& tx , vector& ty ) { CPPAD_ASSERT_UNKNOWN( tx.size() % (q+1) == 0 ); CPPAD_ASSERT_UNKNOWN( ty.size() % (q+1) == 0 ); size_t n = tx.size() / (q+1); size_t m = ty.size() / (q+1); size_t i, j, k, ell; vector x(n * (q+1)); vector y(m * (q+1)); vector empty; // atomic_one interface can only handle one order at a time // so must just through hoops to get multiple orders at one time. bool ok = true; for(k = p; k <= q; k++) { for(j = 0; j < n; j++) for(ell = 0; ell <= k; ell++) x[ j * (k+1) + ell ] = tx[ j * (q+1) + ell ]; for(i = 0; i < m; i++) for(ell = 0; ell < k; ell++) y[ i * (k+1) + ell ] = ty[ i * (q+1) + ell ]; if( k == 0 ) ok &= f_(id_, k, n, m, vx, vy, x, y); else ok &= f_(id_, k, n, m, empty, empty, x, y); for(i = 0; i < m; i++) ty[ i * (q+1) + k ] = y[ i * (k+1) + k]; } return ok; } /*! Link from atomic_one to reverse mode \copydetails atomic_base::reverse */ virtual bool reverse( size_t q , const vector& tx , const vector& ty , vector& px , const vector& py ) { CPPAD_ASSERT_UNKNOWN( tx.size() % (q+1) == 0 ); CPPAD_ASSERT_UNKNOWN( ty.size() % (q+1) == 0 ); size_t n = tx.size() / (q+1); size_t m = ty.size() / (q+1); bool ok = r_(id_, q, n, m, tx, ty, px, py); return ok; } /*! Link from forward Jacobian sparsity sweep to atomic_one \copydetails atomic_base::for_sparse_jac */ virtual bool for_sparse_jac( size_t q , const vector< std::set >& r , vector< std::set >& s , const vector& x ) { size_t n = r.size(); size_t m = s.size(); bool ok = fjs_(id_, n, m, q, r, s); return ok; } /*! Link from reverse Jacobian sparsity sweep to atomic_one. \copydetails atomic_base::rev_sparse_jac */ virtual bool rev_sparse_jac( size_t q , const vector< std::set >& rt , vector< std::set >& st , const vector& x ) { size_t n = st.size(); size_t m = rt.size(); bool ok = rjs_(id_, n, m, q, st, rt); return ok; } /*! Link from reverse Hessian sparsity sweep to atomic_one \copydetails atomic_base::rev_sparse_hes */ virtual bool rev_sparse_hes( const vector& vx, const vector& s , vector& t , size_t q , const vector< std::set >& r , const vector< std::set >& u , vector< std::set >& v , const vector& x ) { size_t m = u.size(); size_t n = v.size(); CPPAD_ASSERT_UNKNOWN( r.size() == n ); CPPAD_ASSERT_UNKNOWN( s.size() == m ); CPPAD_ASSERT_UNKNOWN( t.size() == n ); // // old interface used id instead of vx bool ok = rhs_(id_, n, m, q, r, s, t, u, v); return ok; } }; } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/three/afun.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_THREE_AFUN_HPP # define CPPAD_CORE_ATOMIC_THREE_AFUN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_afun} Using AD Version of an Atomic Function ###################################### Syntax ****** | *afun* ( *ax* , *ay* ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } Purpose ******* Given *ax* , this call computes the corresponding value of *ay* . If ``AD`` < *Base* > operations are being recorded, it enters the computation as an atomic operation in the recording; see :ref:`Independent@Start Recording` . Base **** This is the *Base* type of the elements of *ax* and *ay* in the call to the *afun* atomic operation. To be specific, the elements of *ax* and *ay* have type ``AD`` < ``Base`` > . ADVector ******** The type *ADVector* must be a :ref:`simple vector class` with elements of type ``AD`` < *Base* > . afun **** is a :ref:`atomic_three_ctor@atomic_user` object and this *afun* function call is implemented by the :ref:`atomic_three_ctor@atomic_three` class. ax ** This argument has prototype ``const`` *ADVector* & *ax* and size must be equal to *n* . It specifies vector :math:`x \in \B{R}^n` at which an ``AD`` < *Base* > version of :math:`y = g(x)` is to be evaluated; see :ref:`atomic_three_ctor@atomic_three@Base` . ay ** This argument has prototype *ADVector* & *ay* and size must be equal to *m* . The input values of its elements are not specified (must not matter). Upon return, it is an ``AD`` < *Base* > version of :math:`y = g(x)`. {xrst_end atomic_three_afun} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic/three_afun.hpp Implement user call to an atomic_three function. */ /*! Implement the user call to afun(ax, ay) \tparam ADVector A simple vector class with elements of type AD. \param ax is the argument vector for this call, ax.size() determines the number of arguments. \param ay is the result vector for this call, ay.size() determines the number of results. */ // BEGIN_PROTOTYPE template template void atomic_three::operator()( const ADVector& ax , ADVector& ay ) // END_PROTOTYPE { size_t n = ax.size(); size_t m = ay.size(); # ifndef NDEBUG bool ok = true; std::string msg = "atomic_three: " + atomic_name() + ".eval: "; if( (n == 0) || (m == 0) ) { msg += "ax.size() or ay.size() is zero"; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # endif size_t thread = thread_alloc::thread_num(); allocate_work(thread); vector& taylor_x = work_[thread]->taylor_x; vector& taylor_y = work_[thread]->taylor_y; vector& type_x = work_[thread]->type_x; vector& type_y = work_[thread]->type_y; // type_x.resize(n); taylor_x.resize(n); // type_y.resize(m); taylor_y.resize(m); // // Determine tape corresponding to variables in ax tape_id_t tape_id = 0; local::ADTape* tape = nullptr; for(size_t j = 0; j < n; j++) { taylor_x[j] = ax[j].value_; if( Constant( ax[j] ) ) type_x[j] = constant_enum; else { type_x[j] = ax[j].ad_type_; if( tape_id == 0 ) { tape = ax[j].tape_this(); tape_id = ax[j].tape_id_; CPPAD_ASSERT_UNKNOWN( tape != nullptr ); } # ifndef NDEBUG if( Dynamic( ax[j] ) ) { CPPAD_ASSERT_UNKNOWN( type_x[j] == dynamic_enum ); } else { CPPAD_ASSERT_UNKNOWN( Variable( ax[j] ) ); CPPAD_ASSERT_UNKNOWN( type_x[j] == variable_enum ); } if( tape_id != ax[j].tape_id_ ) { msg += atomic_name() + ": ax contains non-constant values from different threads."; CPPAD_ASSERT_KNOWN(false, msg.c_str()); } # endif } } // Use zero order forward mode to compute all the components of y size_t need_y = size_t(variable_enum) + 1; size_t order_low = 0; size_t order_up = 0; CPPAD_ASSERT_UNKNOWN( need_y > size_t(variable_enum) ); # ifdef NDEBUG forward(taylor_x, type_x, need_y, order_low, order_up, taylor_x, taylor_y); for(size_t j = 0; j < n; ++j) if( type_x[j] == variable_enum ) taylor_x[j] = CppAD::numeric_limits::quiet_NaN(); for_type(taylor_x, type_x, type_y); # else ok &= forward( taylor_x, type_x, need_y, order_low, order_up, taylor_x, taylor_y ); for(size_t j = 0; j < n; ++j) if( type_x[j] == variable_enum ) taylor_x[j] = CppAD::numeric_limits::quiet_NaN(); ok &= for_type(taylor_x, type_x, type_y); if( ! ok ) { msg += atomic_name() + ": ok is false for " "type or zero order forward mode calculation."; CPPAD_ASSERT_KNOWN(false, msg.c_str()); } # endif bool record_dynamic = false; bool record_variable = false; // // set ay to be vector of constant parameters with correct value for(size_t i = 0; i < m; i++) { // pass back values ay[i].value_ = taylor_y[i]; // initialize entire vector as constants ay[i].tape_id_ = 0; ay[i].taddr_ = 0; // we need to record this operation if // any of the elements of ay are dynamics or variables, record_dynamic |= type_y[i] == dynamic_enum; record_variable |= type_y[i] == variable_enum; } # ifndef NDEBUG if( (record_dynamic || record_variable) && tape == nullptr ) { msg += "all elements of x are constants but y contains a non-constant"; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # endif if( record_dynamic) { size_t call_id = 0; tape->Rec_.put_dyn_atomic( tape_id, index_, call_id, type_x, type_y, ax, ay ); } // case where result contains a variable if( record_variable ) { size_t call_id = 0; // atomic_three does not user call_id tape->Rec_.put_var_atomic( tape_id, index_, call_id, type_x, type_y, ax, ay); } # ifndef NDEBUG for(size_t i = 0; i < m; ++i) switch( type_y[i] ) { // case constant_enum: CPPAD_ASSERT_UNKNOWN( Constant( ay[i] ) ); break; // case dynamic_enum: CPPAD_ASSERT_UNKNOWN( Dynamic( ay[i] ) ); break; // case variable_enum: CPPAD_ASSERT_UNKNOWN( Variable( ay[i] ) ); break; // default: CPPAD_ASSERT_KNOWN( false, "atomic_three: for_type: type_y[i]: is not a valid type" ); break; } # endif return; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/three/atomic.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_THREE_ATOMIC_HPP # define CPPAD_CORE_ATOMIC_THREE_ATOMIC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_define} {xrst_spell ctor } Defining Atomic Functions: Third Generation ########################################### Syntax ****** Define Class ============ | ``class`` *atomic_user* : ``public CppAD::atomic_three<`` *Base* > { | |tab| ... | }; Construct Atomic Function ========================= *atomic_user* *afun* ( *ctor_arg_list* ) Use Atomic Function =================== *afun* ( *ax* , *ay* ) Class Member Callbacks ====================== | *ok* = *afun* . ``for_type`` ( | |tab| *parameter_x* , *type_x* , *type_y* | ) | *ok* = *afun* . ``forward`` ( | |tab| *parameter_x* , *type_x* , | |tab| *need_y* , *order_low* , *order_up* , *taylor_x* , *taylor_y* | ) | *ok* = *afun* . ``reverse`` ( | |tab| *parameter_x* , *type_x* , | |tab| *order_up* , *taylor_x* , *taylor_y* , *partial_x* , *partial_y* | ) | *ok* = *afun* . ``jac_sparsity`` ( | |tab| *parameter_x* , *type_x* , *dependency* , *select_x* *select_y* , *pattern_out* | ) | *ok* = *afun* . ``hes_sparsity`` ( | |tab| *parameter_x* , *type_x* , *select_x* *select_y* , *pattern_out* | ) | *ok* = *afun* . ``rev_depend`` ( | |tab| *parameter_x* , *type_x* , *depend_x* , *depend_y* | ) See Also ******** :ref:`chkpoint_two-name` , :ref:`atomic_two-name` Purpose ******* Speed ===== In some cases, it is possible to compute derivatives of a function .. math:: y = g(x) \; {\rm where} \; g : \B{R}^n \rightarrow \B{R}^m more efficiently than by coding it using ``AD`` < *Base* > :ref:`glossary@Operation@Atomic` operations and letting CppAD do the rest. The class ``atomic_three`` < ``Base`` > is used to create a new atomic operation corresponding to a function :math:`g(x)` where the user specifies how to compute the derivatives and sparsity patterns for :math:`g(x)`. Reduce Memory ============= If the function :math:`g(x)` is used many times during the recording of an :ref:`ADFun-name` object, using an atomic version of :math:`g(x)` removes the need for repeated copies of the corresponding ``AD`` < *Base* > operations and variables in the recording. ad_type ******* The type ``CppAD::ad_type_enum`` is used to specify if an AD object is a :ref:`constant parameter` :ref:`dynamic parameter` or :ref:`glossary@Variable` . It has the following possible values: .. csv-table:: :widths: auto *ad_type_enum*,Meaning ``constant_enum``,constant parameter ``dynamic_enum``,dynamic parameter ``variable_enum``,variable In addition, ``constant_enum < dynamic_enum < variable_enum`` . Virtual Functions ***************** The :ref:`callback functions` are implemented by defining the virtual functions in the *atomic_user* class. These functions compute derivatives, sparsity patterns, and dependency relations. Each virtual function has a default implementation that returns *ok* == ``false`` . The :ref:`for_type` and :ref:`forward` function (for the case *order_up* == 0 ) must be implemented. Otherwise, only those functions and orders required by the your calculations need to be implemented. For example, *forward* for the case *order_up* == 2 can just return *ok* == ``false`` unless you require forward mode calculation of second derivatives. Base **** This is the base type of the elements of :ref:`atomic_three_afun@ax` and :ref:`atomic_three_afun@ay` in the corresponding *afun* ( *ax* , *ay* ) call; i.e., the elements of *ax* and *ay* have type ``AD`` < *Base* > . parameter_x *********** All the virtual functions include this argument which has prototype ``const CppAD::vector<`` *Base* > *parameter_x* Its size is equal to *n* = *ax* . ``size`` () in corresponding *afun* ( *ax* , *ay* ) call. Constant ======== For *j* =0,..., *n* ``-1`` , if *ax* [ *j* ] is a :ref:`con_dyn_var@Constant` parameter, *parameter_x* [ *j* ] == *ax* [ *j* ] Dynamic ======= If *ax* [ *j* ] is a :ref:`con_dyn_var@Dynamic` parameter, *parameter_x* [ *j* ] value of *ax* [ *j* ] corresponding to the previous call to :ref:`new_dynamic-name` for the corresponding function object. Variable ======== If *ax* [ *j* ] is a variable, the value of *parameter_x* [ *j* ] is not specified. See the :ref:`atomic_three_mat_mul.hpp` for an example using *parameter_x* . type_x ****** All the virtual functions include this argument. Its size is equal to *n* = *ax* . ``size`` () in corresponding *afun* ( *ax* , *ay* ) call. For *j* =0,..., *n* ``-1`` , if *ax* [ *j* ] is a constant parameter, *type_x* [ *j* ] == ``CppAD::constant_enum`` if *ax* [ *j* ] is a dynamic parameter, *type_x* [ *j* ] == ``CppAD::dynamic_enum`` if *ax* [ *j* ] is a variable, *type_x* [ *j* ] == ``CppAD::variable_enum`` See the :ref:`atomic_three_mat_mul.hpp` for an example using *type_x* . Contents ******** {xrst_toc_table include/cppad/core/atomic/three/ctor.hpp include/cppad/core/atomic/three/afun.hpp include/cppad/core/atomic/three/for_type.hpp include/cppad/core/atomic/three/forward.hpp include/cppad/core/atomic/three/reverse.hpp include/cppad/core/atomic/three/jac_sparsity.hpp include/cppad/core/atomic/three/hes_sparsity.hpp include/cppad/core/atomic/three/rev_depend.hpp } {xrst_end atomic_three_define} ------------------------------------------------------------------------------- */ # include # include # include // needed before one can use in_parallel # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic_three.hpp Base class for atomic function operations. */ template class atomic_three { // =================================================================== private: // ------------------------------------------------------ // constants // /// index of this object in local::atomic_index /// (set by constructor and not changed; i.e., effectively const) size_t index_; // // ----------------------------------------------------- // /// temporary work space used by member functions, declared here to avoid // memory allocation/deallocation for each usage struct work_struct { vector type_x; vector type_y; // vector taylor_x; vector taylor_y; // vector< AD > ataylor_x; vector< AD > ataylor_y; // sparse_rc< vector > pattern; }; // Use pointers, to avoid false sharing between threads. // Not using: vector work_; // so that deprecated atomic examples do not result in a memory leak. work_struct* work_[CPPAD_MAX_NUM_THREADS]; // ----------------------------------------------------- public: // // atomic_index size_t atomic_index(void) const { return index_; } // ===================================================================== // In User API // ===================================================================== // // --------------------------------------------------------------------- // ctor: doxygen in atomic/three_ctor.hpp atomic_three(void); atomic_three(const std::string& name); // ------------------------------------------------------------------------ // operator(): see doxygen in atomic_three/afun.hpp template void operator()( const ADVector& ax , ADVector& ay ); // ------------------------------------------------------------------------ // type: doxygen in atomic/three_for_type.hpp virtual bool for_type( const vector& parameter_x , const vector& type_x , vector& type_y ); // ------------------------------------------------------------------------ // type: doxygen in atomic/three_rev_depend.hpp virtual bool rev_depend( const vector& parameter_x , const vector& type_x , vector& depend_x , const vector& depend_y ); // ------------------------------------------------------------------------ // forward: see docygen in atomic/three_forward.hpp virtual bool forward( const vector& parameter_x , const vector& type_x , size_t need_y , size_t order_low , size_t order_up , const vector& taylor_x , vector& taylor_y ); virtual bool forward( const vector< AD >& aparameter_x , const vector& type_x , size_t need_y , size_t order_low , size_t order_up , const vector< AD >& ataylor_x , vector< AD >& ataylor_y ); // ------------------------------------------------------------------------ // reverse: see docygen in atomic/three_reverse.hpp virtual bool reverse( const vector& parameter_x , const vector& type_x , size_t order_up , const vector& taylor_x , const vector& taylor_y , vector& partial_x , const vector& partial_y ); virtual bool reverse( const vector< AD >& aparameter_x , const vector& type_x , size_t order_up , const vector< AD >& ataylor_x , const vector< AD >& ataylor_y , vector< AD >& apartial_x , const vector< AD >& apartial_y ); // ------------------------------------------------------------ // jac_sparsity: see doxygen in atomic/three_jac_sparsity.hpp virtual bool jac_sparsity( const vector& parameter_x , const vector& type_x , bool dependency , const vector& select_x , const vector& select_y , sparse_rc< vector >& pattern_out ); template bool for_jac_sparsity( bool dependency , const vector& parameter_x , const vector& type_x , const vector& x_index , const vector& y_index , InternalSparsity& var_sparsity ); template bool rev_jac_sparsity( bool dependency , const vector& parameter_x , const vector& type_x , const vector& x_index , const vector& y_index , InternalSparsity& var_sparsity ); // ------------------------------------------------------------ // hes_sparsity: see doxygen in atomic/three_jac_sparsity.hpp virtual bool hes_sparsity( const vector& parameter_x , const vector& type_x , const vector& select_x , const vector& select_y , sparse_rc< vector >& pattern_out ); template bool for_hes_sparsity( const vector& parameter_x , const vector& type_x , const vector& x_index , const vector& y_index , size_t np1 , size_t numvar , const InternalSparsity& rev_jac_sparsity , InternalSparsity& for_sparsity ); template bool rev_hes_sparsity( const vector& parameter_x , const vector& type_x , const vector& x_index , const vector& y_index , const InternalSparsity& for_jac_pattern , bool* rev_jac_flag , InternalSparsity& hes_sparsity ); // ===================================================================== // Not in User API // ===================================================================== /// Name corresponding to a atomic_three object const std::string atomic_name(void) const { bool set_null = false; size_t type = 0; // set to avoid warning std::string name; void* v_ptr = nullptr; // set to avoid warning local::atomic_index(set_null, index_, type, &name, v_ptr); CPPAD_ASSERT_UNKNOWN( type == 3 ); return name; } /// destructor informs CppAD that this atomic function with this index /// has dropped out of scope by setting its pointer to null virtual ~atomic_three(void) { // change object pointer to null, but leave name for error reporting bool set_null = true; size_t type = 0; // set to avoid warning std::string* name = nullptr; void* v_ptr = nullptr; // set to avoid warning local::atomic_index(set_null, index_, type, name, v_ptr); CPPAD_ASSERT_UNKNOWN( type == 3 ); // // free temporary work memory for(size_t thread = 0; thread < CPPAD_MAX_NUM_THREADS; thread++) free_work(thread); } /// allocates work_ for a specified thread void allocate_work(size_t thread) { if( work_[thread] == nullptr ) { // allocate the raw memory size_t min_bytes = sizeof(work_struct); size_t num_bytes; void* v_ptr = thread_alloc::get_memory(min_bytes, num_bytes); // save in work_ work_[thread] = reinterpret_cast( v_ptr ); // call constructor new( work_[thread] ) work_struct; } return; } /// frees work_ for a specified thread void free_work(size_t thread) { if( work_[thread] != nullptr ) { // call destructor work_[thread]->~work_struct(); // return memory to available pool for this thread thread_alloc::return_memory( reinterpret_cast(work_[thread]) ); // mark this thread as not allocated work_[thread] = nullptr; } return; } /// atomic_three function object corresponding to a certain index static atomic_three* class_object(size_t index) { bool set_null = false; size_t type = 0; // set to avoid warning std::string* name = nullptr; void* v_ptr = nullptr; // set to avoid warning local::atomic_index(set_null, index, type, name, v_ptr); CPPAD_ASSERT_UNKNOWN( type == 3 ); return reinterpret_cast( v_ptr ); } /// atomic_three function name corresponding to a certain index static const std::string class_name(size_t index) { bool set_null = false; size_t type = 0; // set to avoid warning std::string name; void* v_ptr = nullptr; // set to avoid warning local::atomic_index(set_null, index, type, &name, v_ptr); CPPAD_ASSERT_UNKNOWN( type == 3 ); return name; } /*! Set value of id (used by deprecated atomic_one class) This function is called just before calling any of the virtual function and has the corresponding id of the corresponding virtual call. */ virtual void set_old(size_t id) { } // --------------------------------------------------------------------------- }; } // END_CPPAD_NAMESPACE // member functions # include # include # include # include # include # include # include # include # endif ================================================ FILE: include/cppad/core/atomic/three/atomic.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin atomic_three} Atomic AD Functions: Third Generation ##################################### Contents ******** {xrst_toc_table include/cppad/core/atomic/three/atomic.hpp example/atomic_three/atomic_three.xrst } {xrst_end atomic_three} ================================================ FILE: include/cppad/core/atomic/three/ctor.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_THREE_CTOR_HPP # define CPPAD_CORE_ATOMIC_THREE_CTOR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_ctor} Atomic Function Constructor ########################### Syntax ****** | ``class`` *atomic_user* : ``public CppAD::atomic_three<`` *Base* > { | ``public:`` | |tab| *atomic_user* ( *ctor_arg_list* ) : ``CppAD::atomic_three<`` *Base* >( *name* ) | |tab| ... | }; | *atomic_user afun* ( *ctor_arg_list* ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } atomic_user *********** ctor_arg_list ============= Is a list of arguments for the *atomic_user* constructor. afun ==== The object *afun* must stay in scope for as long as the corresponding atomic function is used. This includes use by any :ref:`ADFun\` that has this *atomic_user* operation in its :ref:`operation sequence` . Implementation ============== The user defined *atomic_user* class is a publicly derived class of ``atomic_three`` < *Base* > . It should be declared as follows: | |tab| ``class`` *atomic_user* : ``public CppAD::atomic_three<`` *Base* > { | |tab| ``public:`` | |tab| |tab| *atomic_user* ( *ctor_arg_list* ) : ``atomic_three`` < *Base* >( *name* ) | |tab| ... | |tab| }; where ... denotes the rest of the implementation of the derived class. This includes completing the constructor and all the virtual functions that have their ``atomic_three`` implementations replaced by *atomic_user* implementations. atomic_three ************ Restrictions ============ The ``atomic_three`` constructor and destructor cannot be called in :ref:`parallel` mode. Base ==== The template parameter determines the :ref:`atomic_three_afun@Base` type for this ``AD`` < *Base* > atomic operation. name ==== This ``atomic_three`` constructor argument has the following prototype ``const std::string&`` *name* It is the name for this atomic function and is used for error reporting. The suggested value for *name* is *afun* or *atomic_user* , i.e., the name of the corresponding atomic object or class. Example ******* Define Constructor ================== The following is an example of a atomic function constructor definition: :ref:`get_started.cpp` . Use Constructor =============== The following is an example using a atomic function constructor: :ref:`get_started.cpp` . {xrst_end atomic_three_ctor} ------------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic/three_ctor.hpp Constructors for atomic_three class. */ /*! Base class for atomic_atomic functions. \tparam Base This class is used for defining an AD atomic operation y = g(x). \par make sure user does not invoke the default constructor */ template atomic_three::atomic_three(void) { CPPAD_ASSERT_KNOWN(false, "Attempt to use the atomic_three default constructor" ); } /*! Constructor \param name name used for error reporting */ // BEGIN_PROTOTYPE template atomic_three::atomic_three(const std::string& name ) // END_PROTOTYPE { CPPAD_ASSERT_KNOWN( ! thread_alloc::in_parallel() , "atomic_three: constructor cannot be called in parallel mode." ); // // atomic_index bool set_null = false; size_t index = 0; size_t type = 3; std::string copy_name = name; void* copy_this = reinterpret_cast( this ); index_ = local::atomic_index( set_null, index, type, ©_name, copy_this ); // initialize work pointers as null; for(size_t thread = 0; thread < CPPAD_MAX_NUM_THREADS; thread++) work_[thread] = nullptr; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/three/for_type.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_THREE_FOR_TYPE_HPP # define CPPAD_CORE_ATOMIC_THREE_FOR_TYPE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_for_type} Atomic Function Forward Type Calculation ######################################## Syntax ****** | *ok* = *afun* . ``for_type`` ( *parameter_x* , *type_x* , *type_y* ) Prototype ========= {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } Dependency Analysis ******************* This calculation is sometimes referred to as a forward dependency analysis. Usage ***** This syntax and prototype are used by *afun* ( *ax* , *ay* ) where :ref:`atomic_three_ctor@atomic_user@afun` is a user defined atomic function. Implementation ************** This virtual function must be defined by the :ref:`atomic_three_ctor@atomic_user` class. Base **** See :ref:`atomic_three_define@Base` . parameter_x *********** See :ref:`atomic_three_define@parameter_x` . type_x ****** See :ref:`atomic_three_define@type_x` . type_y ****** This vector has size equal to the number of results for this atomic function; i.e. *m* = *ay* . ``size`` () . The input values of the elements of *type_y* are not specified (must not matter). Upon return, for :math:`i = 0 , \ldots , m-1`, *type_y* [ *i* ] is set to one of the following values: #. It is ``constant_enum`` if *ay* [ *i* ] only depends on the arguments that are constants. #. It is ``dynamic_enum`` if *ay* [ *i* ] depends on a dynamic parameter and does not depend on any variables. #. It is ``variable_enum`` if *ay* [ *i* ] depends on a variable. ok ** If this calculation succeeded, *ok* is true. Otherwise, it is false. Example ******* The following is an example of a atomic function ``for_type`` definition: :ref:`get_started.cpp` . {xrst_end atomic_three_for_type} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic/three_for_type.hpp Third generation atomic type computation. */ /*! Link from atomic_three to type calculation \param parameter_x [in] is the value of the parameters in the corresponding function call afun(ax, ay). \param type_x [in] specifies which components of x are constants, dynamics, and variables \param type_y [out] specifies which components of y are constants, dynamics, and variables */ // BEGIN_PROTOTYPE template bool atomic_three::for_type( const vector& parameter_x , const vector& type_x , vector& type_y ) // END_PROTOTYPE { return false; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/three/forward.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_THREE_FORWARD_HPP # define CPPAD_CORE_ATOMIC_THREE_FORWARD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_forward} {xrst_spell aparameter ataylor } Atomic Function Forward Mode ############################ Base **** This syntax and prototype are used by :ref:`afun(ax, ay)` ; see :ref:`atomic_three_afun@Base` . They are also used by *f* . ``Forward`` and *f* . ``new_dynamic`` where *f* has prototype ``ADFun`` < *Base* > *f* and *afun* is used during the recording of *f* . Syntax ====== | *ok* = *afun* . ``forward`` ( | |tab| *parameter_x* , *type_x* , | |tab| *need_y* , *order_low* , *order_up* , *type_x* , *taylor_x* , *taylor_y* | ) Prototype ========= {xrst_literal // BEGIN_PROTOTYPE_BASE // END_PROTOTYPE_BASE } AD ******** This syntax and prototype are used by *af* . ``Forward`` and *af* . ``new_dynamic`` where *af* has prototype ``ADFun< AD<`` *Base* > , *Base* > *af* and *afun* is used in *af* (see :ref:`base2ad-name` ). Syntax ====== | *ok* = *afun* . ``forward`` ( | |tab| *parameter_x* , *type_x* , | |tab| *need_y* , *order_low* , *order_up* , *type_x* , *ataylor_x* , *ataylor_y* | ) Prototype ========= {xrst_literal // BEGIN_PROTOTYPE_AD_BASE // END_PROTOTYPE_AD_BASE } Implementation ************** The *taylor_x* , *taylor_y* version of this function must be defined by the :ref:`atomic_three_ctor@atomic_user` class. It can just return *ok* == ``false`` (and not compute anything) for values of *order_up* that are greater than those used by your :ref:`Forward-name` mode calculations (order zero must be implemented). parameter_x *********** See :ref:`atomic_three_define@parameter_x` . aparameter_x ************ The specifications for *aparameter_x* is the same as for :ref:`atomic_three_define@parameter_x` (only the type of *ataylor_x* is different). type_x ****** See :ref:`atomic_three_define@type_x` . need_y ****** One can ignore this argument and compute all the *taylor_y* Taylor coefficient. Often, this is not necessary and *need_y* is used to specify this. The value :ref:`atomic_three_for_type@type_y` is used to determine which coefficients are necessary as follows: Constant Parameters =================== If *need_y* == ``size_t`` ( ``constant_enum`` ) , then only the taylor coefficients for :math:`Y_i (t)` where *type_y* [ *i* ] == ``constant_enum`` are necessary. This is the case during a :ref:`from_json-name` operation. Dynamic Parameters ================== If *need_y* == ``size_t`` ( ``dynamic_enum`` ) , then only the taylor coefficients for :math:`Y_i (t)` where *type_y* [ *i* ] == ``dynamic_enum`` are necessary. This is the case during an :ref:`new_dynamic-name` operation. Variables ========= If *need_y* == ``size_t`` ( ``variable_enum`` ) , If ``ad_type_enum`` ( *need_y* ) == *variable_enum* , then only the taylor coefficients for :math:`Y_i (t)` where *type_y* [ *i* ] == ``variable_enum`` are necessary. This is the case during a :ref:`f.Forward` operation. T All === If *need_y > size_t* ( *variable_enum* ) , then the taylor coefficients for all :math:`Y_i (t)` are necessary. This is the case during an *afun* ( *ax* , *ay* ) operation. order_low ********* This argument specifies the lowest order Taylor coefficient that we are computing. p = We sometimes use the notation *p* = *order_low* below. order_up ******** This argument specifies the highest order Taylor coefficient that we are computing ( *order_low* <= *order_up* ). q = We sometimes use the notation *q* = *order_up* below. taylor_x ******** The size of *taylor_x* is ( *q* +1)* *n* . For :math:`j = 0 , \ldots , n-1` and :math:`k = 0 , \ldots , q`, we use the Taylor coefficient notation .. math:: :nowrap: \begin{eqnarray} x_j^k & = & \R{taylor\_x} [ j * ( q + 1 ) + k ] \\ X_j (t) & = & x_j^0 + x_j^1 t^1 + \cdots + x_j^q t^q \end{eqnarray} Note that superscripts represent an index for :math:`x_j^k` and an exponent for :math:`t^k`. Also note that the Taylor coefficients for :math:`X(t)` correspond to the derivatives of :math:`X(t)` at :math:`t = 0` in the following way: .. math:: x_j^k = \frac{1}{ k ! } X_j^{(k)} (0) parameters ========== If the *j*-th component of *x* corresponds to a parameter, *type_x* [ *j* ] < ``CppAD::variable_enum`` In this case, the *j*-th component of *parameter_x* is equal to :math:`x_j^0`; i.e., *parameter_x* [ *j* ] == *taylor_x* [ *j* * ( *q* + 1 ) + 0 ] Furthermore, for *k* > 0 , *taylor_x* [ *j* * ( *q* + 1 ) + *k* ] == 0 ataylor_x ********* The specifications for *ataylor_x* is the same as for *taylor_x* (only the type of *ataylor_x* is different). taylor_y ******** The size of *taylor_y* is ( *q* +1)* *m* . Upon return, For :math:`i = 0 , \ldots , m-1` and :math:`k = 0 , \ldots , q`, .. math:: :nowrap: \begin{eqnarray} Y_i (t) & = & g_i [ X(t) ] \\ Y_i (t) & = & y_i^0 + y_i^1 t^1 + \cdots + y_i^q t^q + o ( t^q ) \\ \R{taylor\_y} [ i * ( q + 1 ) + k ] & = & y_i^k \end{eqnarray} where :math:`o( t^q ) / t^q \rightarrow 0` as :math:`t \rightarrow 0`. Note that superscripts represent an index for :math:`y_j^k` and an exponent for :math:`t^k`. Also note that the Taylor coefficients for :math:`Y(t)` correspond to the derivatives of :math:`Y(t)` at :math:`t = 0` in the following way: .. math:: y_j^k = \frac{1}{ k ! } Y_j^{(k)} (0) If :math:`p > 0`, for :math:`i = 0 , \ldots , m-1` and :math:`k = 0 , \ldots , p-1`, the input of *taylor_y* satisfies .. math:: \R{taylor\_y} [ i * ( q + 1 ) + k ] = y_i^k These values do not need to be recalculated and can be used during the computation of the higher order coefficients. ataylor_y ********* The specifications for *ataylor_y* is the same as for *taylor_y* (only the type of *ataylor_y* is different). ok ** If this calculation succeeded, *ok* is true. Otherwise, it is false. Discussion ********** For example, suppose that *order_up* == 2 , and you know how to compute the function :math:`g(x)`, its first derivative :math:`g^{(1)} (x)`, and it component wise Hessian :math:`g_i^{(2)} (x)`. Then you can compute *taylor_x* using the following formulas: .. math:: :nowrap: \begin{eqnarray} y_i^0 & = & Y(0) = g_i ( x^0 ) \\ y_i^1 & = & Y^{(1)} ( 0 ) = g_i^{(1)} ( x^0 ) X^{(1)} ( 0 ) = g_i^{(1)} ( x^0 ) x^1 \\ y_i^2 & = & \frac{1}{2 !} Y^{(2)} (0) \\ & = & \frac{1}{2} X^{(1)} (0)^\R{T} g_i^{(2)} ( x^0 ) X^{(1)} ( 0 ) + \frac{1}{2} g_i^{(1)} ( x^0 ) X^{(2)} ( 0 ) \\ & = & \frac{1}{2} (x^1)^\R{T} g_i^{(2)} ( x^0 ) x^1 + g_i^{(1)} ( x^0 ) x^2 \end{eqnarray} For :math:`i = 0 , \ldots , m-1`, and :math:`k = 0 , 1 , 2`, .. math:: \R{taylor\_y} [ i * (q + 1) + k ] = y_i^k {xrst_toc_hidden example/atomic_three/forward.cpp example/atomic_three/dynamic.cpp } Examples ******** The files :ref:`atomic_three_forward.cpp-name` and :ref:`atomic_three_dynamic.cpp-name` contain examples and tests that uses this routine. {xrst_end atomic_three_forward} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic/three_forward.hpp Third generation atomic forward mode. */ /*! Link from atomic_three to forward mode \param parameter_x [in] contains the values, in afun(ax, ay), for arguments that are parameters. \param type_x [in] what is the type, in afun(ax, ay), for each component of x. \param need_y [in] specifies which components of taylor_y are needed, \param order_low [in] lowerest order for this forward mode calculation. \param order_up [in] highest order for this forward mode calculation. \param taylor_x [in] Taylor coefficients corresponding to x for this calculation. \param taylor_y [out] Taylor coefficient corresponding to y for this calculation See the forward mode in user's documentation for atomic_three */ // BEGIN_PROTOTYPE_BASE template bool atomic_three::forward( const vector& parameter_x , const vector& type_x , size_t need_y , size_t order_low , size_t order_up , const vector& taylor_x , vector& taylor_y ) // END_PROTOTYPE_BASE { return false; } /*! Link from atomic_three to forward mode \param aparameter_x [in] contains the values, in afun(ax, ay), for arguments that are parameters. \param type_x [in] what is the type, in afun(ax, ay), for each component of x. \param need_y [in] specifies which components of taylor_y are needed, \param order_low [in] lowerest order for this forward mode calculation. \param order_up [in] highest order for this forward mode calculation. \param ataylor_x [in] Taylor coefficients corresponding to x for this calculation. \param ataylor_y [out] Taylor coefficient corresponding to y for this calculation See the forward mode in user's documentation for base_three */ // BEGIN_PROTOTYPE_AD_BASE template bool atomic_three::forward( const vector< AD >& aparameter_x , const vector& type_x , size_t need_y , size_t order_low , size_t order_up , const vector< AD >& ataylor_x , vector< AD >& ataylor_y ) // END_PROTOTYPE_AD_BASE { return false; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/three/hes_sparsity.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_THREE_HES_SPARSITY_HPP # define CPPAD_CORE_ATOMIC_THREE_HES_SPARSITY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_hes_sparsity} Atomic Function Hessian Sparsity Patterns ######################################### Syntax ****** | *ok* = *afun* . ``hes_sparsity`` ( | |tab| *parameter_x* , *type_x* , *select_x* , *select_y* , *pattern_out* | ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } Implementation ************** This function must be defined if :ref:`atomic_three_ctor@atomic_user@afun` is used to define an :ref:`ADFun-name` object *f* , and Hessian sparsity patterns are computed for *f* . Base **** See :ref:`atomic_three_afun@Base` . parameter_x *********** See :ref:`atomic_three_define@parameter_x` . type_x ****** See :ref:`atomic_three_define@type_x` . select_x ******** This argument has size equal to the number of arguments to this atomic function; i.e. the size of *ax* . It specifies which domain components are included in the calculation of *pattern_out* . If *select_x* [ *j* ] is false, then there will be no indices *k* such that either of the following hold: | |tab| *pattern_out* . ``row`` ()[ *k* ] == *j* | |tab| *pattern_out* . ``col`` ()[ *k* ] == *j* . select_y ******** This argument has size equal to the number of results to this atomic function; i.e. the size of *ay* . It specifies which range component functions :math:`g_i (x)` are included in of *pattern_out* . pattern_out *********** This input value of *pattern_out* does not matter. Upon return it is the union, with respect to *i* such that *select_y* [ *i* ] is true, of the sparsity pattern for Hessian of :math:`g_i (x)`. To be specific, there are non-negative indices *i* , *r* , *c* , and *k* such that | |tab| *pattern_out* . ``row`` ()[ *k* ] == *r* | |tab| *pattern_out* . ``col`` ()[ *k* ] == *c* if and only if *select_y* [ *i* ] is true, *select_x* [ *r* ] is true, *select_x* [ *c* ] is true, and .. math:: \partial_{x(r)} \partial_{x(c)} g_i(x) is possibly non-zero. Note that the sparsity pattern should be symmetric. ok ** If this calculation succeeded, *ok* is true. Otherwise it is false. {xrst_toc_hidden example/atomic_three/hes_sparsity.cpp } Examples ******** The file :ref:`atomic_three_hes_sparsity.cpp-name` contains an example and test that uses this routine. {xrst_end atomic_three_hes_sparsity} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic/three_hes_sparsity.hpp Third generation atomic Hessian dependency and sparsity patterns. */ /*! atomic_three to Hessian dependency and sparsity calculations. \param parameter_x [in] contains the values for arguments that are parameters. \param type_x [in] what is the type, in afun(ax, ay), for each component of x. \param select_x [in] which domain components to include in the dependency or sparsity pattern. \param select_y [in] which range components to include in the dependency or sparsity pattern. \param pattern_out [out] is the sparsity pattern for Hessian. */ // BEGIN_PROTOTYPE template bool atomic_three::hes_sparsity( const vector& parameter_x , const vector& type_x , const vector& select_x , const vector& select_y , sparse_rc< vector >& pattern_out ) // END_PROTOTYPE { return false; } /*! Link from forward Hessian sweep to atomic_three. 2DO: move this function outside this file so can change developer documentation to omhelp formatting. \tparam InternalSparsity Is the used internally for sparsity calculations; i.e., sparse_pack or sparse_list. \param parameter_x is parameter arguments to the function, other components are nan. \param type_x [in] what is the type, in afun(ax, ay), for each component of x. \param x_index is the variable index, on the tape, for the arguments to this function. This size of x_index is n, the number of arguments to this function. The index zero is used for parameters. \param y_index is the variable index, on the tape, for the results for this function. This size of y_index is m, the number of results for this function. The index zero is used for parameters. \param for_jac_sparsity On input, for j = 0, ... , n-1, the sparsity pattern with index x_index[j], is the forward Jacobian sparsity for the j-th argument to this atomic function. \param rev_jac_pattern On input, for i = 0, ... , m-1, the sparsity pattern with index y_index[i], is the reverse Jacobian sparsity for the i-th result to this atomic function. This shows which components of the result affect the function we are computing the Hessian of. \param hes_sparsity_for This is the sparsity pattern for the Hessian. On input, the non-linear terms in the atomic function have not been included. Upon return, they have been included. */ template template bool atomic_three::for_hes_sparsity( const vector& parameter_x , const vector& type_x , const vector& x_index , const vector& y_index , size_t np1 , size_t numvar , const InternalSparsity& rev_jac_pattern , InternalSparsity& for_sparsity ) { typedef typename InternalSparsity::const_iterator const_iterator; // CPPAD_ASSERT_UNKNOWN( rev_jac_pattern.end() == 1 ); CPPAD_ASSERT_UNKNOWN( for_sparsity.end() == np1 ); CPPAD_ASSERT_UNKNOWN( for_sparsity.n_set() == np1 + numvar ); size_t n = x_index.size(); size_t m = y_index.size(); // // select_x vector select_x(n); for(size_t j = 0; j < n; j++) { // check if should compute pattern w.r.t x[j] select_x[j] = for_sparsity.number_elements(np1 + x_index[j]) > 0; } // // bool select_y vector select_y(m); for(size_t i = 0; i < m; i++) { // check if we should include y[i] select_y[i] = rev_jac_pattern.number_elements(y_index[i]) > 0; } // ------------------------------------------------------------------------ // call user's version of atomic function for Jacobian sparse_rc< vector > pattern_out; bool dependency = false; bool ok = jac_sparsity( parameter_x, type_x, dependency, select_x, select_y, pattern_out ); if( ! ok ) return false; // // transfer sparsity patterns from pattern_out to var_sparsity size_t nnz = pattern_out.nnz(); const vector& row( pattern_out.row() ); const vector& col( pattern_out.col() ); for(size_t k = 0; k < nnz; ++k) { size_t i = row[k]; size_t j = col[k]; CPPAD_ASSERT_KNOWN( select_y[i] && select_x[j], "atomic: jac_sparsity: pattern_out not in " "select_x or select_y range" ); const_iterator itr(for_sparsity, np1 + x_index[j]); size_t ell = *itr; while( ell < np1 ) { for_sparsity.post_element(np1 + y_index[i], ell ); ell = *(++itr); } } for(size_t i = 0; i < m; ++i) for_sparsity.process_post( np1 + y_index[i] ); // ------------------------------------------------------------------------ // call user's version of atomic function for Hessian ok = hes_sparsity( parameter_x, type_x, select_x, select_y, pattern_out ); if( ! ok ) return ok; // // add new elements to Hessian sparisty in calling routine nnz = pattern_out.nnz(); for(size_t k = 0; k < nnz; ++k) { size_t r = row[k]; size_t c = col[k]; CPPAD_ASSERT_KNOWN( select_x[r] && select_x[c], "atomic: hes_sparsity: pattern_out not in select_x range" ); const_iterator itr_1(for_sparsity, np1 + x_index[r]); size_t v1 = *itr_1; while( v1 < np1 ) { for_sparsity.binary_union( v1, v1, np1 + x_index[c], for_sparsity ); v1 = *(++itr_1); } // no need to add same elements twice if( c != r ) { const_iterator itr_2(for_sparsity, np1 + x_index[c]); size_t v2 = *itr_2; while( v2 < np1 ) { for_sparsity.binary_union( v2, v2, np1 + x_index[r], for_sparsity ); v2 = *(++itr_2); } } } return ok; } /*! Link from for_reverse Hessian sweep to atomic_three. \tparam InternalSparsity Is the used internally for sparsity calculations; i.e., sparse_pack or sparse_list. \param parameter_x is parameter arguments to the function, other components are nan. \param type_x [in] what is the type, in afun(ax, ay), for each component of x. \param x_index is the variable index, on the tape, for the arguments to this function. This size of x_index is n, the number of arguments to this function. The index zero is used for parameters. \param y_index is the variable index, on the tape, for the results for this function. This size of y_index is m, the number of results for this function. The index zero is used for parameters. \param for_jac_pattern On input, for j = 0, ... , n-1, the sparsity pattern with index x_index[j], is the forward Jacobian pattern for the j-th argument to this atomic function. \param rev_jac_flag On input, for i = 0, ... , m-1, rev_jac_flag[ y_index[i] ] is true if the function we are computing the Hessian of has possibly non-zero Jacobian w.r.t variable y_index[i]. On output, for j = 0, ... , n, rev_jac_flag[ x_index[j] ] is set to true if the variable with index x_index[j] has possible non-zero Jacobian with respect to one of the true y_index[i] cases. Otherwise, rev_jac_flag [ x_inde[j] ] is not changed. \param hes_sparsity_rev Is the reverse mode sparsity pattern for the Hessian. On input, the non-linear terms in the atomic function have not been included. Upon return, they have been included. */ template template bool atomic_three::rev_hes_sparsity( const vector& parameter_x , const vector& type_x , const vector& x_index , const vector& y_index , const InternalSparsity& for_jac_pattern , bool* rev_jac_flag , InternalSparsity& hes_sparsity_rev ) { typedef typename InternalSparsity::const_iterator const_iterator; size_t n = x_index.size(); size_t m = y_index.size(); // // select_x vector select_x(n); for(size_t j = 0; j < n; j++) { // check if should compute pattern w.r.t x[j] const_iterator itr(for_jac_pattern, x_index[j]); size_t i = *itr; select_x[j] = i < for_jac_pattern.end(); CPPAD_ASSERT_UNKNOWN( x_index[j] > 0 || ! select_x[j] ); } // // bool select_y vector select_y(m); for(size_t i = 0; i < m; i++) { // check if we should include y[i] select_y[i] = rev_jac_flag[ y_index[i] ]; CPPAD_ASSERT_UNKNOWN( y_index[i] > 0 || ! select_y[i] ); } // // call atomic function for Jacobain sparsity bool dependency = false; sparse_rc< vector > pattern_jac; bool ok = jac_sparsity( parameter_x, type_x, dependency, select_x, select_y, pattern_jac ); const vector& row_jac( pattern_jac.row() ); const vector& col_jac( pattern_jac.col() ); size_t nnz_jac = pattern_jac.nnz(); if( ! ok ) return ok; // // call atomic function for Hessian sparsity sparse_rc< vector > pattern_hes; ok = hes_sparsity(parameter_x, type_x, select_x, select_y, pattern_hes); const vector& row_hes( pattern_hes.row() ); const vector& col_hes( pattern_hes.col() ); size_t nnz_hes = pattern_hes.nnz(); if( ! ok ) return ok; // // propagate Hessian sparsity through the Jacobian for(size_t k = 0; k < nnz_jac; ++k) { size_t i = row_jac[k]; size_t j = col_jac[k]; CPPAD_ASSERT_KNOWN( select_y[i] && select_x[j] , "atomic: jac_sparsity: pattern_out not in " "select_x or select_y range" ); // from y_index[i] to x_index[j] hes_sparsity_rev.binary_union( x_index[j], x_index[j], y_index[i], hes_sparsity_rev ); } // // propagate rev_jac_flag through the Jacobian // (seems OK to exclude variables with zero forward jacobian) for(size_t k = 0; k < nnz_jac; ++k) { size_t j = col_jac[k]; rev_jac_flag[ x_index[j] ] = true; } // // new hessian sparsity terms between y and x for(size_t k = 0; k < nnz_hes; ++k) { size_t r = row_hes[k]; size_t c = col_hes[k]; CPPAD_ASSERT_KNOWN( select_x[r] && select_x[c] , "atomic: hes_sparsity: pattern_out not in select_x range" ); hes_sparsity_rev.binary_union( x_index[r], x_index[r], x_index[c], for_jac_pattern ); hes_sparsity_rev.binary_union( x_index[c], x_index[c], x_index[r], for_jac_pattern ); } return ok; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/three/jac_sparsity.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_THREE_JAC_SPARSITY_HPP # define CPPAD_CORE_ATOMIC_THREE_JAC_SPARSITY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_jac_sparsity} Atomic Function Jacobian Sparsity Patterns ########################################## Syntax ****** | *ok* = *afun* . ``jac_sparsity`` ( | |tab| *parameter_x* , *type_x* , *dependency* , *select_x* , *select_y* , *pattern_out* | ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } Implementation ************** This function must be defined if :ref:`atomic_three_ctor@atomic_user@afun` is used to define an :ref:`ADFun-name` object *f* , and Jacobian sparsity patterns are computed for *f* . (Computing Hessian sparsity patterns and optimizing requires Jacobian sparsity patterns.) Base **** See :ref:`atomic_three_afun@Base` . parameter_x *********** See :ref:`atomic_three_define@parameter_x` . type_x ****** See :ref:`atomic_three_define@type_x` . dependency ********** If *dependency* is true, then *pattern_out* is a :ref:`dependency.cpp@Dependency Pattern` for this atomic function. Otherwise it is a :ref:`glossary@Sparsity Pattern` for the derivative of the atomic function. select_x ******** This argument has size equal to the number of arguments to this atomic function; i.e. the size of *ax* . It specifies which domain components are included in the calculation of *pattern_out* . If *select_x* [ *j* ] is false, then there will be no indices *k* such that *pattern_out* . ``col`` ()[ *k* ] == *j* . select_y ******** This argument has size equal to the number of results to this atomic function; i.e. the size of *ay* . It specifies which range components are included in the calculation of *pattern_out* . If *select_y* [ *i* ] is false, then there will be no indices *k* such that *pattern_out* . ``row`` ()[ *k* ] == *i* . pattern_out *********** This input value of *pattern_out* does not matter. Upon return it is a dependency or sparsity pattern for the Jacobian of :math:`g(x)`, the function corresponding to :ref:`atomic_three_ctor@atomic_user@afun` ; *dependency* above. To be specific, there are non-negative indices *i* , *j* , *k* such that | |tab| *pattern_out* . ``row`` ()[ *k* ] == *i* | |tab| *pattern_out* . ``col`` ()[ *k* ] == *j* if and only if *select_x* [ *j* ] is true, *select_y* [ *j* ] is true, and :math:`g_i(x)` depends on the value of :math:`x_j` (and the partial of :math:`g_i(x)` with respect to :math:`x_j` is possibly non-zero). ok ** If this calculation succeeded, *ok* is true. Otherwise it is false. {xrst_toc_hidden example/atomic_three/jac_sparsity.cpp } Examples ******** The file :ref:`atomic_three_jac_sparsity.cpp-name` contains an example and test that uses this routine. {xrst_end atomic_three_jac_sparsity} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic/three_jac_sparsity.hpp Third generation atomic Jacobian dependency and sparsity patterns. */ /*! atomic_three to Jacobian dependency and sparsity calculations. \param parameter_x [in] contains the values for arguments that are parameters. \param type_x [in] what is the type, in afun(ax, ay), for each component of x. \param dependency [in] if true, calculate dependency pattern, otherwise calculate sparsity pattern. \param select_x [in] which domain components to include in the dependency or sparsity pattern. The index zero is used for parameters. \param select_y [in] which range components to include in the dependency or sparsity pattern. The index zero is used for parameters. \param pattern_out [out] is the dependency or sparsity pattern. */ // BEGIN_PROTOTYPE template bool atomic_three::jac_sparsity( const vector& parameter_x , const vector& type_x , bool dependency , const vector& select_x , const vector& select_y , sparse_rc< vector >& pattern_out ) // END_PROTOTYPE { return false; } /*! Link from forward Jacobian sparsity calculations to atomic_three \tparam InternalSparsity Is the type used for internal sparsity calculations; i.e., sparse_pack or sparse_list. \param dependency if true, calculate dependency pattern, otherwise calculate sparsity pattern. \param parameter_x is parameter arguments to the function, other components are nan. \param type_x [in] what is the type, in afun(ax, ay), for each component of x. \param x_index is the variable index, on the tape, for the arguments to this atomic function. This size of x_index is n, the number of arguments to this atomic function. The index zero is used for parameters. \param y_index is the variable index, on the tape, for the results for this atomic function. This size of y_index is m, the number of results for this atomic function. The index zero is used for parameters. \param var_sparsity On input, for j = 0, ... , n-1, the sparsity pattern with index x_index[j], is the sparsity for the j-th argument to this atomic function. On output, for i = 0, ... , m-1, the sparsity pattern with index y_index[i], is the sparsity for the i-th result for this atomic function. \return is true if the computation succeeds. */ template template bool atomic_three::for_jac_sparsity( bool dependency , const vector& parameter_x , const vector& type_x , const vector& x_index , const vector& y_index , InternalSparsity& var_sparsity ) { typedef typename InternalSparsity::const_iterator iterator; // number of arguments and results for this atomic function size_t n = x_index.size(); size_t m = y_index.size(); // select_y vector select_y(m); for(size_t i = 0; i < m; ++i) select_y[i] = y_index[i] != 0; // determine select_x vector select_x(n); for(size_t j = 0; j < n; ++j) { // check if x_j depends on any previous variable iterator itr(var_sparsity, x_index[j]); size_t ell = *itr; select_x[j] = ell < var_sparsity.end(); CPPAD_ASSERT_UNKNOWN( x_index[j] > 0 || ! select_x[j] ); } sparse_rc< vector > pattern_out; bool ok = jac_sparsity( parameter_x, type_x, dependency, select_x, select_y, pattern_out ); if( ! ok ) return false; // // transfer sparsity patterns from pattern_out to var_sparsity size_t nnz = pattern_out.nnz(); const vector& row( pattern_out.row() ); const vector& col( pattern_out.col() ); for(size_t k = 0; k < nnz; ++k) { size_t i = row[k]; size_t j = col[k]; CPPAD_ASSERT_KNOWN( select_y[i] && select_x[j], "atomic: jac_sparsity: pattern_out not in " "select_x or select_y range" ); iterator itr(var_sparsity, x_index[j]); size_t ell = *itr; while( ell < var_sparsity.end() ) { var_sparsity.post_element( y_index[i], ell ); ell = *(++itr); } } for(size_t i = 0; i < m; ++i) var_sparsity.process_post( y_index[i] ); // return true; } /*! Link from reverse Jacobian sparsity calculations to atomic_three \tparam InternalSparsity Is the type used for internal sparsity calculations; i.e., sparse_pack or sparse_list. \param dependency if true, calculate dependency pattern, otherwise calculate sparsity pattern. \param parameter_x is parameter arguments to the function, other components are nan. \param type_x [in] what is the type, in afun(ax, ay), for each component of x. \param x_index is the variable index, on the tape, for the arguments to this atomic function. This size of x_index is n, the number of arguments to this atomic function. \param y_index is the variable index, on the tape, for the results for this atomic function. This size of y_index is m, the number of results for this atomic function. \param var_sparsity On input, for i = 0, ... , m-1, the sparsity pattern with index y_index[i], is the sparsity of the outer function with respect to the i-th result for this atomic function. On input, for j = 0, ... , n-1, the sparsity pattern with index x_index[j], is the sparsity for the outer function with respect to the j-th argument to this atomic function. On output, for j = 0, ... , n-1, the sparsity pattern with index x_index[j], is the sparsity for the outer function with respect to the j-th argument to this atomic function with the atomic function results removed as arguments to the outer function. \return is true if the computation succeeds. */ template template bool atomic_three::rev_jac_sparsity( bool dependency , const vector& parameter_x , const vector& type_x , const vector& x_index , const vector& y_index , InternalSparsity& var_sparsity ) { typedef typename InternalSparsity::const_iterator iterator; // number of arguments and results for this atomic function size_t n = x_index.size(); size_t m = y_index.size(); // selection vectors vector select_x(n), select_y(m); // 2DO: perhaps we could use for_type(type_x, type_y) // to reduce the true components in select_x for(size_t j = 0; j < n; ++j) select_x[j] = true; // determine select_y for(size_t i = 0; i < m; ++i) { // check if y_i has sparsity is non-empty iterator itr(var_sparsity, y_index[i]); size_t ell = *itr; select_y[i] = ell < var_sparsity.end(); } sparse_rc< vector > pattern_out; bool ok = jac_sparsity( parameter_x, type_x, dependency, select_x, select_y, pattern_out ); if( ! ok ) return false; // // transfer sparsity patterns from pattern_out to var_sparsity size_t nnz = pattern_out.nnz(); const vector& row( pattern_out.row() ); const vector& col( pattern_out.col() ); for(size_t k = 0; k < nnz; ++k) { size_t i = row[k]; size_t j = col[k]; CPPAD_ASSERT_KNOWN( select_y[i] && select_x[j], "atomic: jac_sparsity: pattern_out not in " "select_x or select_y range" ); iterator itr(var_sparsity, y_index[i]); size_t ell = *itr; while( ell < var_sparsity.end() ) { var_sparsity.post_element( x_index[j], ell ); ell = *(++itr); } } for(size_t j = 0; j < n; ++j) var_sparsity.process_post( x_index[j] ); // return true; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/three/rev_depend.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_THREE_REV_DEPEND_HPP # define CPPAD_CORE_ATOMIC_THREE_REV_DEPEND_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_rev_depend} Atomic Function Reverse Dependency Calculation ############################################## Syntax ****** | *ok* = *afun* . ``rev_depend`` ( | |tab| *parameter_x* , *type_x* , *depend_x* , *depend_y* | ) Prototype ========= {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } Dependency Analysis ******************* This calculation is sometimes referred to as a reverse dependency analysis. Implementation ************** This function must be defined if :ref:`atomic_three_ctor@atomic_user@afun` is used to define an :ref:`ADFun-name` object *f* , and :ref:`f.optimize()` is used. Base **** See :ref:`atomic_three_afun@Base` . parameter_x *********** See :ref:`atomic_three_define@parameter_x` . type_x ****** See :ref:`atomic_three_define@type_x` . depend_x ******** This vector has size equal to the number of arguments for this atomic function; i.e. *n* = *ax* . ``size`` () . The input values of the elements of *depend_x* are not specified (must not matter). Upon return, for :math:`j = 0 , \ldots , n-1`, *depend_x* [ *j* ] is true if the values of interest depend on the value of :ref:`ax[j]` in the corresponding *afun* ( *ax* , *ay* ) call. Optimize ======== Parameters and variables, that the values of interest do not depend on, may get removed by :ref:`optimization` . The corresponding values in :ref:`atomic_three_define@parameter_x` , and :ref:`atomic_three_forward@taylor_x` (after optimization has removed them) are not specified. depend_y ******** This vector has size equal to the number of results for this atomic function; i.e. *m* = *ay* . ``size`` () . For :math:`i = 0 , \ldots , m-1`, *depend_y* [ *i* ] is true if the values of interest depend on the value of :ref:`ay[i]` in the corresponding *afun* ( *ax* , *ay* ) call. ok ** If this calculation succeeded, *ok* is true. Otherwise, it is false. Contents ******** {xrst_toc_table example/atomic_three/rev_depend.cpp } Example ******* The following is an example of a atomic function ``rev_depend`` definition: :ref:`atomic_three_rev_depend.cpp-name` . {xrst_end atomic_three_rev_depend} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic/three_rev_depend.hpp Third generation atomic type computation. */ /*! Link from atomic_three to reverse dependency calculation \param parameter_x [in] is the value of the parameters in the corresponding function call afun(ax, ay). \param type_x [in] is the value for each of the components of x. \param depend_x [out] specifies which components of x affect values of interest. \param depend_y [in] specifies which components of y affect values of interest. */ // BEGIN_PROTOTYPE template bool atomic_three::rev_depend( const vector& parameter_x , const vector& type_x , vector& depend_x , const vector& depend_y ) // END_PROTOTYPE { return false; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/three/reverse.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_THREE_REVERSE_HPP # define CPPAD_CORE_ATOMIC_THREE_REVERSE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_reverse} {xrst_spell aparameter apartial ataylor } Atomic Function Reverse Mode ############################ Base **** This syntax is used by *f* . ``Reverse`` where *f* has prototype ``ADFun`` < *Base* > *f* and *afun* is used in *f* ; see :ref:`atomic_three_afun@Base` . Syntax ====== | *ok* = *afun* . ``reverse`` ( | |tab| *parameter_x* , *type_x* , | |tab| *order_up* , *taylor_x* , *taylor_y* , *partial_x* , *partial_y* | ) Prototype ========= {xrst_literal // BEGIN_PROTOTYPE_BASE // END_PROTOTYPE_BASE } AD ******** This syntax is used by *af* . ``Reverse`` where *af* has prototype ``ADFun< AD<`` *Base* > , *Base* > *af* and *afun* is used in *af* (see :ref:`base2ad-name` ). Syntax ====== | *ok* = *afun* . ``reverse`` ( | |tab| *aparameter_x* , *type_x* , | |tab| *order_up* , *ataylor_x* , *ataylor_y* , *apartial_x* , *apartial_y* | ) Prototype ========= {xrst_literal // BEGIN_PROTOTYPE_AD_BASE // END_PROTOTYPE_AD_BASE } Implementation ************** This function must be defined if :ref:`atomic_three_ctor@atomic_user@afun` is used to define an :ref:`ADFun-name` object *f* , and reverse mode derivatives are computed for *f* . It can return *ok* == ``false`` (and not compute anything) for values of *order_up* that are greater than those used by your :ref:`Reverse-name` mode calculations. parameter_x *********** See :ref:`atomic_three_define@parameter_x` . aparameter_x ************ The specifications for *aparameter_x* is the same as for :ref:`atomic_three_define@parameter_x` (only the type of *ataylor_x* is different). type_x ****** See :ref:`atomic_three_define@type_x` . order_up ******** This argument specifies the highest order Taylor coefficient that computing the derivative of. taylor_x ******** The size of *taylor_x* is ( *q* +1)* *n* . For :math:`j = 0 , \ldots , n-1` and :math:`k = 0 , \ldots , q`, we use the Taylor coefficient notation .. math:: :nowrap: \begin{eqnarray} x_j^k & = & \R{taylor\_x} [ j * ( q + 1 ) + k ] \\ X_j (t) & = & x_j^0 + x_j^1 t^1 + \cdots + x_j^q t^q \end{eqnarray} Note that superscripts represent an index for :math:`x_j^k` and an exponent for :math:`t^k`. Also note that the Taylor coefficients for :math:`X(t)` correspond to the derivatives of :math:`X(t)` at :math:`t = 0` in the following way: .. math:: x_j^k = \frac{1}{ k ! } X_j^{(k)} (0) parameters ========== If the *j*-th component of *x* corresponds to a parameter, *type_x* [ *j* ] < ``CppAD::variable_enum`` In this case, the *j*-th component of *parameter_x* is equal to :math:`x_j^0`; i.e., *parameter_x* [ *j* ] == *taylor_x* [ *j* * ( *q* + 1 ) + 0 ] Furthermore, for *k* > 0 , *taylor_x* [ *j* * ( *q* + 1 ) + *k* ] == 0 ataylor_x ********* The specifications for *ataylor_x* is the same as for *taylor_x* (only the type of *ataylor_x* is different). taylor_y ******** The size of *taylor_y* is ( *q* +1)* *m* . For :math:`i = 0 , \ldots , m-1` and :math:`k = 0 , \ldots , q`, we use the Taylor coefficient notation .. math:: :nowrap: \begin{eqnarray} Y_i (t) & = & g_i [ X(t) ] \\ Y_i (t) & = & y_i^0 + y_i^1 t^1 + \cdots + y_i^q t^q + o ( t^q ) \\ y_i^k & = & \R{taylor\_y} [ i * ( q + 1 ) + k ] \end{eqnarray} where :math:`o( t^q ) / t^q \rightarrow 0` as :math:`t \rightarrow 0`. Note that superscripts represent an index for :math:`y_j^k` and an exponent for :math:`t^k`. Also note that the Taylor coefficients for :math:`Y(t)` correspond to the derivatives of :math:`Y(t)` at :math:`t = 0` in the following way: .. math:: y_j^k = \frac{1}{ k ! } Y_j^{(k)} (0) ataylor_y ********* The specifications for *ataylor_y* is the same as for *taylor_y* (only the type of *ataylor_y* is different). F * We use the notation :math:`\{ x_j^k \} \in \B{R}^{n \times (q+1)}` for .. math:: \{ x_j^k \W{:} j = 0 , \ldots , n-1, k = 0 , \ldots , q \} We use the notation :math:`\{ y_i^k \} \in \B{R}^{m \times (q+1)}` for .. math:: \{ y_i^k \W{:} i = 0 , \ldots , m-1, k = 0 , \ldots , q \} We define the function :math:`F : \B{R}^{n \times (q+1)} \rightarrow \B{R}^{m \times (q+1)}` by .. math:: y_i^k = F_i^k [ \{ x_j^k \} ] Note that .. math:: F_i^0 ( \{ x_j^k \} ) = g_i ( X(0) ) = g_i ( x^0 ) We also note that :math:`F_i^\ell ( \{ x_j^k \} )` is a function of :math:`x^0 , \ldots , x^\ell` and is determined by the derivatives of :math:`g_i (x)` up to order :math:`\ell`. G, H **** We use :math:`G : \B{R}^{m \times (q+1)} \rightarrow \B{R}` to denote an arbitrary scalar valued function of :math:`\{ y_i^k \}`. We use :math:`H : \B{R}^{n \times (q+1)} \rightarrow \B{R}` defined by .. math:: H ( \{ x_j^k \} ) = G[ F( \{ x_j^k \} ) ] partial_y ********* The size of *partial_y* is ( *q* +1)* *m* . For :math:`i = 0 , \ldots , m-1`, :math:`k = 0 , \ldots , q`, .. math:: \R{partial\_y} [ i * (q + 1 ) + k ] = \partial G / \partial y_i^k apartial_y ********** The specifications for *apartial_y* is the same as for *partial_y* (only the type of *apartial_y* is different). partial_x ********* The size of *partial_x* is ( *q* +1)* *n* . The input values of the elements of *partial_x* are not specified (must not matter). Upon return, for :math:`j = 0 , \ldots , n-1` and :math:`\ell = 0 , \ldots , q`, .. math:: :nowrap: \begin{eqnarray} \R{partial\_x} [ j * (q + 1) + \ell ] & = & \partial H / \partial x_j^\ell \\ & = & ( \partial G / \partial \{ y_i^k \} ) \cdot ( \partial \{ y_i^k \} / \partial x_j^\ell ) \\ & = & \sum_{k=0}^q \sum_{i=0}^{m-1} ( \partial G / \partial y_i^k ) ( \partial y_i^k / \partial x_j^\ell ) \\ & = & \sum_{k=\ell}^q \sum_{i=0}^{m-1} \R{partial\_y}[ i * (q + 1 ) + k ] ( \partial F_i^k / \partial x_j^\ell ) \end{eqnarray} Note that we have used the fact that for :math:`k < \ell`, :math:`\partial F_i^k / \partial x_j^\ell = 0`. Short Circuit Operations ======================== For the :ref:`atomic_three_reverse@Base` prototype, if ``IdenticalZero`` ( *partial_y* [ *i* * ( *q* +1)+ *k* ]) is true, one does not need to compute :math:`( \partial F_i^k / \partial x_j^\ell )`; see :ref:`base_identical-name` . This can be used, in a similar way to :ref:`atomic_three_forward@need_y` , to avoid unnecessary operations. azmul ===== An :ref:`optimized` function will use zero for values in *taylor_x* and *taylor_y* that are not necessary in the current context. If you divide by these values when computing :math:`( \partial F_i^k / \partial x_j^\ell )` you could get an nan if the corresponding value in *partial_y* is zero. To be careful, if you do divide by *taylor_x* or *taylor_y* , use :ref:`azmul-name` for to avoid zero over zero calculations. apartial_x ********** The specifications for *apartial_x* is the same as for *partial_x* (only the type of *apartial_x* is different). ok ** If this calculation succeeded, *ok* is true. Otherwise it is false. {xrst_toc_hidden example/atomic_three/reverse.cpp } Examples ******** The file :ref:`atomic_three_reverse.cpp-name` contains an example and test that uses this routine. {xrst_end atomic_three_reverse} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic/three_reverse.hpp Third Generation Atomic reverse mode. */ /*! Link from reverse mode sweep to users routine. \param parameter_x [in] contains the values, in afun(ax, ay), for arguments that are parameters. \param type_x [in] what is the type, in afun(ax, ay), for each component of x. \param order_up [in] highest order for this reverse mode calculation. \param taylor_x [in] Taylor coefficients corresponding to x for this calculation. \param taylor_y [in] Taylor coefficient corresponding to y for this calculation \param partial_x [out] Partials w.r.t. the x Taylor coefficients. \param partial_y [in] Partials w.r.t. the y Taylor coefficients. See atomic_three_reverse mode use documentation */ // BEGIN_PROTOTYPE_BASE template bool atomic_three::reverse( const vector& parameter_x , const vector& type_x , size_t order_up , const vector& taylor_x , const vector& taylor_y , vector& partial_x , const vector& partial_y ) // END_PROTOTYPE_BASE { return false; } /*! Link from reverse mode sweep to users routine. \param aparameter_x [in] contains the values, in afun(ax, ay), for arguments that are parameters. \param type_x [in] what is the type, in afun(ax, ay), for each component of x. \param order_up [in] highest order for this reverse mode calculation. \param ataylor_x [in] Taylor coefficients corresponding to x for this calculation. \param ataylor_y [in] Taylor coefficient corresponding to y for this calculation \param apartial_x [out] Partials w.r.t. the x Taylor coefficients. \param apartial_y [in] Partials w.r.t. the y Taylor coefficients. See atomic_three_reverse mode use documentation */ // BEGIN_PROTOTYPE_AD_BASE template bool atomic_three::reverse( const vector< AD >& aparameter_x , const vector& type_x , size_t order_up , const vector< AD >& ataylor_x , const vector< AD >& ataylor_y , vector< AD >& apartial_x , const vector< AD >& apartial_y ) // END_PROTOTYPE_AD_BASE { return false; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/two/afun.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_TWO_AFUN_HPP # define CPPAD_CORE_ATOMIC_TWO_AFUN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_two_afun app} Using AD Version of Atomic Function ################################### Syntax ****** | *afun* ( *ax* , *ay* ) Purpose ******* Given *ax* , this call computes the corresponding value of *ay* . If ``AD`` < *Base* > operations are being recorded, it enters the computation as an atomic operation in the recording; see :ref:`Independent@Start Recording` . ADVector ******** The type *ADVector* must be a :ref:`simple vector class` with elements of type ``AD`` < *Base* > ; see :ref:`atomic_two_ctor@atomic_base@Base` . afun **** is a :ref:`atomic_two_ctor@atomic_user` object and this *afun* function call is implemented by the :ref:`atomic` class. ax ** This argument has prototype ``const`` *ADVector* & *ax* and size must be equal to *n* . It specifies vector :math:`x \in \B{R}^n` at which an ``AD`` < *Base* > version of :math:`y = f(x)` is to be evaluated; see :ref:`atomic_two_ctor@atomic_base@Base` . ay ** This argument has prototype *ADVector* & *ay* and size must be equal to *m* . The input values of its elements are not specified (must not matter). Upon return, it is an ``AD`` < *Base* > version of :math:`y = f(x)`. {xrst_end atomic_two_afun} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic/two_afun.hpp Implement user call to an atomic_two function. */ /*! Implement the user call to afun(ax, ay) and atomic_one call to afun(ax, ay, id). \tparam ADVector A simple vector class with elements of type AD. \param id optional extra information vector that is just passed through by CppAD, and used by atomic_one derived class (not other derived classes). This is an extra parameter to the virtual callbacks for atomic_one; see the set_old member function. \param ax is the argument vector for this call, ax.size() determines the number of arguments. \param ay is the result vector for this call, ay.size() determines the number of results. */ template template void atomic_base::operator()( const ADVector& ax , ADVector& ay , size_t id ) { size_t i, j; size_t n = ax.size(); size_t m = ay.size(); # ifndef NDEBUG bool ok; std::string msg = "atomic_base: " + atomic_name() + ".eval: "; if( (n == 0) || (m == 0) ) { msg += "ax.size() or ay.size() is zero"; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # endif size_t thread = thread_alloc::thread_num(); allocate_work(thread); vector & tx = work_[thread]->tx; vector & ty = work_[thread]->ty; vector & vx = work_[thread]->vx; vector & vy = work_[thread]->vy; // if( vx.size() != n ) { vx.resize(n); tx.resize(n); } if( vy.size() != m ) { vy.resize(m); ty.resize(m); } // // Determine tape corresponding to variables in ax tape_id_t tape_id = 0; local::ADTape* tape = nullptr; for(j = 0; j < n; j++) { tx[j] = ax[j].value_; vx[j] = ! Constant( ax[j] ); if( vx[j] ) { if( tape_id == 0 ) { tape = ax[j].tape_this(); tape_id = ax[j].tape_id_; CPPAD_ASSERT_UNKNOWN( tape != nullptr ); } # ifndef NDEBUG if( tape_id != ax[j].tape_id_ ) { msg += atomic_name() + ": ax contains variables from different threads."; CPPAD_ASSERT_KNOWN(false, msg.c_str()); } # endif } } // Use zero order forward mode to compute values size_t p = 0, q = 0; set_old(id); # ifdef NDEBUG forward(p, q, vx, vy, tx, ty); # else ok = forward(p, q, vx, vy, tx, ty); if( ! ok ) { msg += atomic_name() + ": ok is false for " "zero order forward mode calculation."; CPPAD_ASSERT_KNOWN(false, msg.c_str()); } # endif bool record_operation = false; for(i = 0; i < m; i++) { // pass back values ay[i].value_ = ty[i]; // initialize entire vector parameters (not in tape) ay[i].tape_id_ = 0; ay[i].taddr_ = 0; // we need to record this operation if // any of the elements of ay are variables, record_operation |= vy[i]; } # ifndef NDEBUG if( record_operation & (tape == nullptr) ) { msg += "all elements of vx are false but vy contains a true element"; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # endif // if tape is not null, ay is on the tape if( record_operation ) { // Operator that marks beginning of this atomic operation CPPAD_ASSERT_UNKNOWN( local::NumRes(local::AFunOp) == 0 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::AFunOp) == 4 ); CPPAD_ASSERT_KNOWN( size_t( std::numeric_limits::max() ) >= std::max( std::max( std::max(index_, id), n), m ), "atomic_base: cppad_tape_addr_type maximum not large enough" ); tape->Rec_.PutArg(addr_t(index_), addr_t(id), addr_t(n), addr_t(m)); tape->Rec_.PutOp(local::AFunOp); // Now put n operators, one for each element of argument vector CPPAD_ASSERT_UNKNOWN( local::NumRes(local::FunavOp) == 0 ); CPPAD_ASSERT_UNKNOWN( local::NumRes(local::FunapOp) == 0 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::FunavOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::FunapOp) == 1 ); for(j = 0; j < n; j++) { if( Variable(ax[j]) ) { // information for an argument that is a variable tape->Rec_.PutArg(ax[j].taddr_); tape->Rec_.PutOp(local::FunavOp); } else { // information for an argument that is parameter addr_t par = ax[j].taddr_; if( ! Dynamic( ax[j] ) ) par = tape->Rec_.put_con_par(ax[j].value_); tape->Rec_.PutArg(par); tape->Rec_.PutOp(local::FunapOp); } } // Now put m operators, one for each element of result vector CPPAD_ASSERT_UNKNOWN( local::NumArg(local::FunrpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumRes(local::FunrpOp) == 0 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::FunrvOp) == 0 ); CPPAD_ASSERT_UNKNOWN( local::NumRes(local::FunrvOp) == 1 ); for(i = 0; i < m; i++) { if( vy[i] ) { ay[i].taddr_ = tape->Rec_.PutOp(local::FunrvOp); ay[i].tape_id_ = tape_id; ay[i].ad_type_ = variable_enum; } else { CPPAD_ASSERT_UNKNOWN( ! Dynamic( ay[i] ) ); addr_t par = tape->Rec_.put_con_par(ay[i].value_); tape->Rec_.PutArg(par); tape->Rec_.PutOp(local::FunrpOp); } } // Put a duplicate AFunOp at end of AFunOp sequence CPPAD_ASSERT_KNOWN( size_t( std::numeric_limits::max() ) >= std::max( std::max( std::max(index_, id), n), m ), "atomic_base: cppad_tape_addr_type maximum not large enough" ); tape->Rec_.PutArg(addr_t(index_), addr_t(id), addr_t(n), addr_t(m)); tape->Rec_.PutOp(local::AFunOp); } return; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/two/atomic.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_TWO_ATOMIC_HPP # define CPPAD_CORE_ATOMIC_TWO_ATOMIC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_two app} {xrst_spell ctor px py tx vx vy } Defining Atomic Functions: Second Generation ############################################ Deprecated 2019-01-01 ********************* Using the ``atomic_base`` class has been deprecated. Use :ref:`atomic_three-name` instead. Syntax ****** | *atomic_user* *afun* ( *ctor_arg_list* ) | *afun* ( *ax* , *ay* ) | *ok* = *afun* . ``forward`` ( *p* , *q* , *vx* , *vy* , *tx* , *ty* ) | *ok* = *afun* . ``reverse`` ( *q* , *tx* , *ty* , *px* , *py* ) | *ok* = *afun* . ``for_sparse_jac`` ( *q* , *r* , *s* , *x* ) | *ok* = *afun* . ``rev_sparse_jac`` ( *q* , *r* , *s* , *x* ) | *ok* = *afun* . ``for_sparse_hes`` ( *vx* , *r* , *s* , *h* , *x* ) | *ok* = *afun* . ``rev_sparse_hes`` ( *vx* , *s* , *t* , *q* , *r* , *u* , *v* , *x* ) | *atomic_base* < ``Base`` >:: *clear* () See Also ******** :ref:`checkpoint` Purpose ******* Speed ===== In some cases, the user knows how to compute derivatives of a function .. math:: y = f(x) \; {\rm where} \; f : \B{R}^n \rightarrow \B{R}^m more efficiently than by coding it using ``AD`` < *Base* > :ref:`atomic_base` operations and letting CppAD do the rest. In this case ``atomic_base`` < ``Base`` > can use the user code for :math:`f(x)`, and its derivatives, as ``AD`` < *Base* > atomic operations. Reduce Memory ============= If the function :math:`f(x)` is used often, using an atomic version of :math:`f(x)` remove the need for repeated copies of the corresponding ``AD`` < *Base* > operations. Virtual Functions ***************** User defined derivatives are implemented by defining the following virtual functions in the *atomic_base* class: :ref:`forward` , :ref:`reverse` , :ref:`for_sparse_jac` , :ref:`rev_sparse_jac` , and :ref:`rev_sparse_hes` . These virtual functions have a default implementation that returns *ok* == ``false`` . The ``forward`` function, for the case *q* == 0 , must be implemented. Otherwise, only those functions required by the your calculations need to be implemented. For example, *forward* for the case *q* == 2 can just return *ok* == ``false`` unless you require forward mode calculation of second derivatives. Examples ******** See :ref:`atomic_two_example-name` . Contents ******** {xrst_toc_table include/cppad/core/atomic/two/ctor.hpp include/cppad/core/atomic/two/option.hpp include/cppad/core/atomic/two/afun.hpp include/cppad/core/atomic/two/forward.hpp include/cppad/core/atomic/two/reverse.hpp include/cppad/core/atomic/two/for_sparse_jac.hpp include/cppad/core/atomic/two/rev_sparse_jac.hpp include/cppad/core/atomic/two/for_sparse_hes.hpp include/cppad/core/atomic/two/rev_sparse_hes.hpp include/cppad/core/atomic/two/clear.hpp } {xrst_end atomic_two} ------------------------------------------------------------------------------- {xrst_begin atomic_two_example app} Example Defining Atomic Functions: Second Generation #################################################### Getting Started *************** that shows the minimal amount of information required to create a user defined atomic operation. Scalar Function *************** where the user provides the code for computing derivatives. This example is simple because the domain and range are scalars. Vector Range ************ where the user provides the code for computing derivatives. This example is more complex because the range has two components. Hessian Sparsity Patterns ************************* where the user provides the code for computing Hessian sparsity patterns. Contents ******** {xrst_toc_table example/atomic_two/eigen_mat_mul.cpp example/atomic_two/eigen_mat_inv.cpp example/atomic_two/eigen_cholesky.cpp } {xrst_end atomic_two_example} ------------------------------------------------------------------------------- */ # include # include # include # include # include // needed before one can use in_parallel # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic_two.hpp Base class for atomic function operations. */ template class atomic_base { // =================================================================== public: enum option_enum { pack_sparsity_enum , bool_sparsity_enum , set_sparsity_enum }; // // atomic_index size_t atomic_index(void) const { return index_; } private: // ------------------------------------------------------ // constants // /// index of this object in local::atomic_index /// (set by constructor and not changed; i.e., effectively const) size_t index_; // // ----------------------------------------------------- // variables // /// sparsity pattern this object is currently using /// (set by constructor and option member functions) option_enum sparsity_; // /// temporary work space used by member functions, declared here to avoid // memory allocation/deallocation for each usage struct work_struct { vector vx; vector vy; // vector tx; vector ty; // vector< AD > atx; vector< AD > aty; // vector bool_t; // vectorBool pack_h; vectorBool pack_r; vectorBool pack_s; vectorBool pack_u; // vector bool_h; vector bool_r; vector bool_s; vector bool_u; // vector< std::set > set_h; vector< std::set > set_r; vector< std::set > set_s; vector< std::set > set_u; }; // Use pointers, to avoid false sharing between threads. // Not using: vector work_; // so that deprecated atomic examples do not result in a memory leak. work_struct* work_[CPPAD_MAX_NUM_THREADS]; public: // ===================================================================== // In User API // ===================================================================== // // --------------------------------------------------------------------- // ctor: doxygen in atomic_base/ctor.hpp atomic_base(void); atomic_base( const std::string& name, option_enum sparsity = bool_sparsity_enum ); // option: see doxygen in atomic_base/option.hpp void option(enum option_enum option_value); // operator(): see doxygen in atomic_base/afun.hpp template void operator()( const ADVector& ax , ADVector& ay , size_t id = 0 ); // ------------------------------------------------------------------------ // base_two version of forward virtual bool forward( size_t p , size_t q , const vector& vx , vector& vy , const vector& tx , vector& ty ); virtual bool forward( size_t p , size_t q , const vector& vx , vector& vy , const vector< AD >& atx , vector< AD >& aty ); // base_three version of forward bool forward( size_t order_low , size_t order_up , const vector& type_x , vector& type_y , const vector& taylor_x , vector& taylor_y ); bool forward( size_t order_low , size_t order_up , const vector& type_x , vector& type_y , const vector< AD >& ataylor_x , vector< AD >& ataylor_y ); // ------------------------------------------------------------------------ // reverse: see doxygen in atomic_base/reverse.hpp virtual bool reverse( size_t q , const vector& tx , const vector& ty , vector& px , const vector& py ); virtual bool reverse( size_t q , const vector< AD >& atx , const vector< AD >& aty , vector< AD >& apx , const vector< AD >& apy ); // ------------------------------------------------------------ // for_sparse_jac: see doxygen in atomic_base/for_sparse_jac.hpp virtual bool for_sparse_jac( size_t q , const vector< std::set >& r , vector< std::set >& s , const vector& x ); virtual bool for_sparse_jac( size_t q , const vector& r , vector& s , const vector& x ); virtual bool for_sparse_jac( size_t q , const vectorBool& r , vectorBool& s , const vector& x ); template bool for_sparse_jac( const vector& x , const vector& x_index , const vector& y_index , InternalSparsity& var_sparsity ); // deprecated versions virtual bool for_sparse_jac( size_t q , const vector< std::set >& r , vector< std::set >& s ); virtual bool for_sparse_jac( size_t q , const vector& r , vector& s ); virtual bool for_sparse_jac( size_t q , const vectorBool& r , vectorBool& s ); // ------------------------------------------------------------ // rev_sparse_jac: see doxygen in atomic_base/rev_sparse_jac.hpp virtual bool rev_sparse_jac( size_t q , const vector< std::set >& rt , vector< std::set >& st , const vector& x ); virtual bool rev_sparse_jac( size_t q , const vector& rt , vector& st , const vector& x ); virtual bool rev_sparse_jac( size_t q , const vectorBool& rt , vectorBool& st , const vector& x ); template bool rev_sparse_jac( const vector& x , const vector& x_index , const vector& y_index , InternalSparsity& var_sparsity ); // deprecated versions virtual bool rev_sparse_jac( size_t q , const vector< std::set >& rt , vector< std::set >& st ); virtual bool rev_sparse_jac( size_t q , const vector& rt , vector& st ); virtual bool rev_sparse_jac( size_t q , const vectorBool& rt , vectorBool& st ); // ------------------------------------------------------------ // for_sparse_hes: see doxygen in atomic_base/for_sparse_hes.hpp virtual bool for_sparse_hes( const vector& vx , const vector& r , const vector& s , vector< std::set >& h , const vector& x ); virtual bool for_sparse_hes( const vector& vx , const vector& r , const vector& s , vector& h , const vector& x ); virtual bool for_sparse_hes( const vector& vx , const vector& r , const vector& s , vectorBool& h , const vector& x ); template bool for_sparse_hes( const vector& x , const vector& x_index , const vector& y_index , size_t np1 , size_t numvar , const InternalSparsity& rev_jac_sparsity , InternalSparsity& for_sparsity ); // deprecated versions virtual bool for_sparse_hes( const vector& vx , const vector& r , const vector& s , vector< std::set >& h ); virtual bool for_sparse_hes( const vector& vx , const vector& r , const vector& s , vector& h ); virtual bool for_sparse_hes( const vector& vx , const vector& r , const vector& s , vectorBool& h ); // ------------------------------------------------------------ // rev_sparse_hes: see doxygen in atomic_base/rev_sparse_hes.hpp virtual bool rev_sparse_hes( const vector& vx , const vector& s , vector& t , size_t q , const vector< std::set >& r , const vector< std::set >& u , vector< std::set >& v , const vector& x ); virtual bool rev_sparse_hes( const vector& vx , const vector& s , vector& t , size_t q , const vector& r , const vector& u , vector& v , const vector& x ); virtual bool rev_sparse_hes( const vector& vx , const vector& s , vector& t , size_t q , const vectorBool& r , const vectorBool& u , vectorBool& v , const vector& x ); template bool rev_sparse_hes( const vector& x , const vector& x_index , const vector& y_index , const InternalSparsity& for_jac_sparsity , bool* rev_jac_flag , InternalSparsity& rev_hes_sparsity ); // deprecated virtual bool rev_sparse_hes( const vector& vx , const vector& s , vector& t , size_t q , const vector< std::set >& r , const vector< std::set >& u , vector< std::set >& v ); virtual bool rev_sparse_hes( const vector& vx , const vector& s , vector& t , size_t q , const vector& r , const vector& u , vector& v ); virtual bool rev_sparse_hes( const vector& vx , const vector& s , vector& t , size_t q , const vectorBool& r , const vectorBool& u , vectorBool& v ); // ------------------------------------------------------------ // atomic_three like interface for reverse dependency analysis bool rev_depend( const vector& parameter_x , const vector& type_x , vector& depend_x , const vector& depend_y ); // ------------------------------------------------------------ // clear: see doxygen in atomic_base/clear.hpp static void clear(void); // ===================================================================== // Not in User API // ===================================================================== /// current sparsity setting option_enum sparsity(void) const { return sparsity_; } /// Name corresponding to a atomic_base object const std::string atomic_name(void) const { bool set_null = false; size_t type = 0; // set to avoid warning std::string name; void* v_ptr = nullptr; // set to avoid warning local::atomic_index(set_null, index_, type, &name, v_ptr); CPPAD_ASSERT_UNKNOWN( type == 2 ); return name; } /// destructor informs CppAD that this atomic function with this index /// has dropped out of scope by setting its pointer to null virtual ~atomic_base(void) { // change object pointer to null, but leave name for error reporting bool set_null = true; size_t type = 0; // set to avoid warning std::string* name = nullptr; void* v_ptr = nullptr; // set to avoid warning local::atomic_index(set_null, index_, type, name, v_ptr); CPPAD_ASSERT_UNKNOWN( type == 2 ); // // free temporary work memory for(size_t thread = 0; thread < CPPAD_MAX_NUM_THREADS; thread++) free_work(thread); } /// allocates work_ for a specified thread void allocate_work(size_t thread) { if( work_[thread] == nullptr ) { // allocate the raw memory size_t min_bytes = sizeof(work_struct); size_t num_bytes; void* v_ptr = thread_alloc::get_memory(min_bytes, num_bytes); // save in work_ work_[thread] = reinterpret_cast( v_ptr ); // call constructor new( work_[thread] ) work_struct; } return; } /// frees work_ for a specified thread void free_work(size_t thread) { if( work_[thread] != nullptr ) { // call destructor work_[thread]->~work_struct(); // return memory to available pool for this thread thread_alloc::return_memory( reinterpret_cast(work_[thread]) ); // mark this thread as not allocated work_[thread] = nullptr; } return; } /// atomic_base function object corresponding to a certain index static atomic_base* class_object(size_t index) { bool set_null = false; size_t type = 0; // set to avoid warning std::string* name = nullptr; void* v_ptr = nullptr; // set to avoid warning local::atomic_index(set_null, index, type, name, v_ptr); CPPAD_ASSERT_UNKNOWN( type == 2 ); return reinterpret_cast( v_ptr ); } /// atomic_base function name corresponding to a certain index static const std::string class_name(size_t index) { bool set_null = false; size_t type = 0; // set to avoid warning std::string name; void* v_ptr = nullptr; // set to avoid warning local::atomic_index(set_null, index, type, &name, v_ptr); CPPAD_ASSERT_UNKNOWN( type == 2 ); return name; } /*! Set value of id (used by deprecated atomic_one class) This function is called just before calling any of the virtual function and has the corresponding id of the corresponding virtual call. */ virtual void set_old(size_t id) { } // --------------------------------------------------------------------------- }; } // END_CPPAD_NAMESPACE // functions implemented in cppad/core/atomic_base files # include # include # include # include # include # include # include # include # include # include # include # endif ================================================ FILE: include/cppad/core/atomic/two/clear.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_TWO_CLEAR_HPP # define CPPAD_CORE_ATOMIC_TWO_CLEAR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_two_clear app} Free Static Variables ##################### Syntax ****** | ``atomic_base`` < *Base* >:: ``clear`` () Purpose ******* Each ``atomic_base`` objects holds onto work space in order to avoid repeated memory allocation calls and thereby increase speed (until it is deleted). If an the ``atomic_base`` object is global or static because, the it does not get deleted. This is a problem when using ``thread_alloc`` :ref:`free_all` to check that all allocated memory has been freed. Calling this ``clear`` function will free all the memory currently being held onto by the ``atomic_base`` < *Base* > class. Future Use ********** If there is future use of an ``atomic_base`` object, after a call to ``clear`` , the work space will be reallocated and held onto. Restriction *********** This routine cannot be called while in :ref:`parallel` execution mode. {xrst_end atomic_two_clear} ------------------------------------------------------------------------------ */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic/two_clear.hpp Free static variables in atomic_base class. */ /*! Free all thread_alloc static memory held by atomic_base (avoids reallocations). (This does not include class_object() which is an std::vector.) */ template void atomic_base::clear(void) { CPPAD_ASSERT_KNOWN( ! thread_alloc::in_parallel() , "cannot use atomic_base clear during parallel execution" ); bool set_null = true; size_t index = 0; size_t type = 0; // set to avoid warning std::string* name = nullptr; void* v_ptr = nullptr; // set to avoid warning size_t n_atomic = local::atomic_index( set_null, index, type, name, v_ptr ); // set_null = false; for(index = 1; index <= n_atomic; ++index) { local::atomic_index(set_null, index, type, name, v_ptr); if( type == 2 ) { atomic_base* op = reinterpret_cast(v_ptr); if( op != nullptr ) { for(size_t thread = 0; thread < CPPAD_MAX_NUM_THREADS; thread++) op->free_work(thread); } } } return; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/two/ctor.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_TWO_CTOR_HPP # define CPPAD_CORE_ATOMIC_TWO_CTOR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_two_ctor app} Atomic Function Constructor ########################### Syntax ****** | *atomic_user afun* ( *ctor_arg_list* ) | ``atomic_base`` < *Base* >( *name* , *sparsity* ) atomic_user *********** ctor_arg_list ============= Is a list of arguments for the *atomic_user* constructor. afun ==== The object *afun* must stay in scope for as long as the corresponding atomic function is used. This includes use by any :ref:`ADFun\` that has this *atomic_user* operation in its :ref:`operation sequence` . Implementation ============== The user defined *atomic_user* class is a publicly derived class of ``atomic_base`` < *Base* > . It should be declared as follows: | |tab| ``class`` *atomic_user* : ``public CppAD::atomic_base<`` *Base* > { | |tab| ``public:`` | |tab| |tab| *atomic_user* ( *ctor_arg_list* ) : ``atomic_base`` < *Base* >( *name* , *sparsity* ) | |tab| ... | |tab| }; where ... denotes the rest of the implementation of the derived class. This includes completing the constructor and all the virtual functions that have their ``atomic_base`` implementations replaced by *atomic_user* implementations. atomic_base *********** Restrictions ============ The ``atomic_base`` constructor and destructor cannot be called in :ref:`parallel` mode. Base ==== The template parameter determines the *Base* type for this ``AD`` < *Base* > atomic operation. name ==== This ``atomic_base`` constructor argument has the following prototype ``const std::string&`` *name* It is the name for this atomic function and is used for error reporting. The suggested value for *name* is *afun* or *atomic_user* , i.e., the name of the corresponding atomic object or class. sparsity ======== This ``atomic_base`` constructor argument has prototype ``atomic_base`` < *Base* >:: ``option_enum`` *sparsity* The current *sparsity* for an ``atomic_base`` object determines which type of sparsity patterns it uses and its value is one of the following: .. list-table:: :widths: auto * - *sparsity* - sparsity patterns * - ``atomic_base`` < *Base* >:: ``pack_sparsity_enum`` - :ref:`CppAD_vector@vectorBool` * - ``atomic_base`` < *Base* >:: ``bool_sparsity_enum`` - :ref:`vector` ```` * - ``atomic_base`` < *Base* >:: ``set_sparsity_enum`` - :ref:`vector` `` >`` There is a default value for *sparsity* if it is not included in the constructor (which may be either the bool or set option). {xrst_end atomic_two_ctor} ------------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic/two_ctor.hpp Constructors for atomic_base class. */ /*! Base class for atomic_atomic functions. \tparam Base This class is used for defining an AD atomic operation y = f(x). \par make sure user does not invoke the default constructor */ template atomic_base::atomic_base(void) { CPPAD_ASSERT_KNOWN(false, "Attempt to use the atomic_base default constructor" ); } /*! Constructor \param name name used for error reporting \param sparsity [in] what type of sparsity patterns are computed by this function, bool_sparsity_enum or set_sparsity_enum. Default value is bool sparsity patterns. */ template atomic_base::atomic_base( const std::string& name, option_enum sparsity ) : sparsity_( sparsity ) { CPPAD_ASSERT_KNOWN( ! thread_alloc::in_parallel() , "atomic_base: constructor cannot be called in parallel mode." ); CPPAD_ASSERT_UNKNOWN( constant_enum < dynamic_enum ); CPPAD_ASSERT_UNKNOWN( dynamic_enum < variable_enum ); // // atomic_index bool set_null = false; size_t index = 0; size_t type = 2; std::string copy_name = name; void* copy_this = reinterpret_cast( this ); index_ = local::atomic_index( set_null, index, type, ©_name, copy_this ); // initialize work pointers as null; for(size_t thread = 0; thread < CPPAD_MAX_NUM_THREADS; thread++) work_[thread] = nullptr; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/two/for_sparse_hes.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_TWO_FOR_SPARSE_HES_HPP # define CPPAD_CORE_ATOMIC_TWO_FOR_SPARSE_HES_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_two_for_sparse_hes app} {xrst_spell vx } Atomic Forward Hessian Sparsity Patterns ######################################## Syntax ****** | *ok* = *afun* . ``for_sparse_hes`` ( *vx* , *r* , *s* , *h* , *x* ) Deprecated 2016-06-27 ********************* *ok* = *afun* . ``for_sparse_hes`` ( *vx* , *r* , *s* , *h* ) Purpose ******* This function is used by :ref:`ForSparseHes-name` to compute Hessian sparsity patterns. If you are using :ref:`ForSparseHes-name` , one of the versions of this virtual function must be defined by the :ref:`atomic_two_ctor@atomic_user` class. Given a :ref:`glossary@Sparsity Pattern` for a diagonal matrix :math:`R \in \B{R}^{n \times n}`, and a row vector :math:`S \in \B{R}^{1 \times m}`, this routine computes the sparsity pattern for .. math:: H(x) = R^\R{T} \cdot (S \cdot f)^{(2)}( x ) \cdot R Implementation ************** If you are using and :ref:`ForSparseHes-name` , this virtual function must be defined by the :ref:`atomic_two_ctor@atomic_user` class. vx == The argument *vx* has prototype ``const CppAD:vector&`` *vx* *vx* . ``size`` () == *n* , and for :math:`j = 0 , \ldots , n-1`, *vx* [ *j* ] is true if and only if *ax* [ *j* ] is a :ref:`glossary@Variable` or :ref:`dynamic parameter` in the corresponding call to *afun* ( *ax* , *ay* ) r = This argument has prototype ``const CppAD:vector&`` *r* and is a :ref:`atomic_two_option@atomic_sparsity` pattern for the diagonal of :math:`R \in \B{R}^{n \times n}`. s = The argument *s* has prototype ``const CppAD:vector&`` *s* and its size is *m* . It is a sparsity pattern for :math:`S \in \B{R}^{1 \times m}`. h = This argument has prototype *atomic_sparsity* & *h* The input value of its elements are not specified (must not matter). Upon return, *h* is a :ref:`atomic_two_option@atomic_sparsity` pattern for :math:`H(x) \in \B{R}^{n \times n}` which is defined above. x = The argument has prototype ``const CppAD::vector<`` *Base* >& *x* and size is equal to the *n* . This is the :ref:`Value-name` corresponding to the parameters in the vector :ref:`atomic_two_afun@ax` (when the atomic function was called). To be specific, if | |tab| ``if`` ( ``Parameter`` ( *ax* [ *i* ]) == ``true`` ) | |tab| |tab| *x* [ *i* ] = ``Value`` ( *ax* [ *i* ] ); | |tab| ``else`` | |tab| |tab| *x* [ *i* ] = ``CppAD::numeric_limits<`` *Base* >:: ``quiet_NaN`` (); The version of this function with out the *x* argument is deprecated; i.e., you should include the argument even if you do not use it. {xrst_end atomic_two_for_sparse_hes} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic/two_for_sparse_hes.hpp Atomic forward mode Hessian sparsity patterns. */ /*! Link, after case split, from for_hes_sweep to atomic_base. \param vx [in] which components of x are variables. \param r [in] is the forward Jacobian sparsity pattern w.r.t the argument vector x. \param s [in] is the reverse Jacobian sparsity pattern w.r.t the result vector y. \param h [out] is the Hessian sparsity pattern w.r.t the argument vector x. \param x is the integer value of the x arguments that are parameters. */ template bool atomic_base::for_sparse_hes( const vector& vx , const vector& r , const vector& s , vector< std::set >& h , const vector& x ) { return false; } template bool atomic_base::for_sparse_hes( const vector& vx , const vector& r , const vector& s , vector& h , const vector& x ) { return false; } template bool atomic_base::for_sparse_hes( const vector& vx , const vector& r , const vector& s , vectorBool& h , const vector& x ) // deprecated versions { return false; } template bool atomic_base::for_sparse_hes( const vector& vx , const vector& r , const vector& s , vector< std::set >& h ) { return false; } template bool atomic_base::for_sparse_hes( const vector& vx , const vector& r , const vector& s , vector& h ) { return false; } template bool atomic_base::for_sparse_hes( const vector& vx , const vector& r , const vector& s , vectorBool& h ) { return false; } /*! Link, before case split, from for_hes_sweep to atomic_base. 2DO: move this function outside this file so can change developer documentation to omhelp formatting. \tparam InternalSparsity Is the used internally for sparsity calculations; i.e., sparse_pack or sparse_list. \param x is parameter arguments to the function, other components are nan. \param x_index is the variable index, on the tape, for the arguments to this function. This size of x_index is n, the number of arguments to this function. \param y_index is the variable index, on the tape, for the results for this function. This size of y_index is m, the number of results for this function. \param for_jac_sparsity On input, for j = 0, ... , n-1, the sparsity pattern with index x_index[j], is the forward Jacobian sparsity for the j-th argument to this atomic function. \param rev_jac_sparsity On input, for i = 0, ... , m-1, the sparsity pattern with index y_index[i], is the reverse Jacobian sparsity for the i-th result to this atomic function. This shows which components of the result affect the function we are computing the Hessian of. \param for_hes_sparsity This is the sparsity pattern for the Hessian. On input, the non-linear terms in the atomic function have not been included. Upon return, they have been included. */ template template bool atomic_base::for_sparse_hes( const vector& x , const vector& x_index , const vector& y_index , size_t np1 , size_t numvar , const InternalSparsity& rev_jac_sparsity , InternalSparsity& for_sparsity ) { typedef typename InternalSparsity::const_iterator const_iterator; CPPAD_ASSERT_UNKNOWN( rev_jac_sparsity.end() == 1 ); CPPAD_ASSERT_UNKNOWN( for_sparsity.end() == np1 ); CPPAD_ASSERT_UNKNOWN( for_sparsity.n_set() == np1 + numvar ); size_t n = x_index.size(); size_t m = y_index.size(); bool ok = false; size_t thread = thread_alloc::thread_num(); allocate_work(thread); // // vx vector vx(n); for(size_t j = 0; j < n; j++) vx[j] = x_index[j] != 0; // // bool_r vector& bool_r( work_[thread]->bool_r ); bool_r.resize(n); for(size_t j = 0; j < n; j++) { // check if we must compute row and column j of h const_iterator itr(for_sparsity, np1 + x_index[j]); size_t i = *itr; bool_r[j] = i < np1; } // // bool s vector& bool_s( work_[thread]->bool_s ); bool_s.resize(m); for(size_t i = 0; i < m; i++) { // check if row i of result is included in h bool_s[i] = rev_jac_sparsity.is_element(y_index[i], 0); } // // h vectorBool& pack_h( work_[thread]->pack_h ); vector& bool_h( work_[thread]->bool_h ); vector< std::set >& set_h( work_[thread]->set_h ); // // call user's version of atomic function std::string msg = ": atomic_base.for_sparse_hes: returned false"; if( sparsity_ == pack_sparsity_enum ) { pack_h.resize(n * n); ok = for_sparse_hes(vx, bool_r, bool_s, pack_h, x); if( ! ok ) ok = for_sparse_hes(vx, bool_r, bool_s, pack_h); if( ! ok ) { msg = atomic_name() + msg + " sparsity = pack_sparsity_enum"; CPPAD_ASSERT_KNOWN(false, msg.c_str()); } } else if( sparsity_ == bool_sparsity_enum ) { bool_h.resize(n * n); ok = for_sparse_hes(vx, bool_r, bool_s, bool_h, x); if( ! ok ) ok = for_sparse_hes(vx, bool_r, bool_s, bool_h); if( ! ok ) { msg = atomic_name() + msg + " sparsity = bool_sparsity_enum"; CPPAD_ASSERT_KNOWN(false, msg.c_str()); } } else { CPPAD_ASSERT_UNKNOWN( sparsity_ == set_sparsity_enum ) set_h.resize(n); ok = for_sparse_hes(vx, bool_r, bool_s, set_h, x); if( ! ok ) ok = for_sparse_hes(vx, bool_r, bool_s, set_h); if( ! ok ) { msg = atomic_name() + msg + " sparsity = set_sparsity_enum"; CPPAD_ASSERT_KNOWN(false, msg.c_str()); } } CPPAD_ASSERT_UNKNOWN( ok ); // // modify hessian in calling routine for(size_t i = 0; i < n; i++) { for(size_t j = 0; j < n; j++) { if( (x_index[i] > 0) && (x_index[j] > 0) ) { bool flag = false; switch( sparsity_ ) { case pack_sparsity_enum: flag = pack_h[i * n + j]; break; // case bool_sparsity_enum: flag = bool_h[i * n + j]; break; // case set_sparsity_enum: flag = set_h[i].find(j) != set_h[i].end(); break; } if( flag ) { const_iterator itr_i(for_sparsity, np1 + x_index[i]); size_t i_x = *itr_i; while( i_x < np1 ) { for_sparsity.binary_union( i_x, i_x, np1 + x_index[j], for_sparsity ); i_x = *(++itr_i); } const_iterator itr_j(for_sparsity, np1 + x_index[j]); size_t j_x = *itr_j; while( j_x < np1 ) { for_sparsity.binary_union( j_x, j_x, np1 + x_index[i], for_sparsity ); j_x = *(++itr_j); } } } } } return ok; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/two/for_sparse_jac.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_TWO_FOR_SPARSE_JAC_HPP # define CPPAD_CORE_ATOMIC_TWO_FOR_SPARSE_JAC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_two_for_sparse_jac app} Atomic Forward Jacobian Sparsity Patterns ######################################### Syntax ****** | *ok* = *afun* . ``for_sparse_jac`` ( *q* , *r* , *s* , *x* ) Deprecated 2016-06-27 ********************* *ok* = *afun* . ``for_sparse_jac`` ( *q* , *r* , *s* ) Purpose ******* This function is used by :ref:`ForSparseJac-name` to compute Jacobian sparsity patterns. For a fixed matrix :math:`R \in \B{R}^{n \times q}`, the Jacobian of :math:`f( x + R * u)` with respect to :math:`u \in \B{R}^q` is .. math:: S(x) = f^{(1)} (x) * R Given a :ref:`glossary@Sparsity Pattern` for :math:`R`, ``for_sparse_jac`` computes a sparsity pattern for :math:`S(x)`. Implementation ************** If you are using :ref:`ForSparseJac-name` , :ref:`ForSparseHes-name` , or :ref:`RevSparseHes-name` , one of the versions of this virtual function must be defined by the :ref:`atomic_two_ctor@atomic_user` class. q = The argument *q* has prototype ``size_t`` *q* It specifies the number of columns in :math:`R \in \B{R}^{n \times q}` and the Jacobian :math:`S(x) \in \B{R}^{m \times q}`. r = This argument has prototype ``const`` *atomic_sparsity* & *r* and is a :ref:`atomic_two_option@atomic_sparsity` pattern for :math:`R \in \B{R}^{n \times q}`. s = This argument has prototype *atomic_sparsity* & *s* The input values of its elements are not specified (must not matter). Upon return, *s* is a :ref:`atomic_two_option@atomic_sparsity` pattern for :math:`S(x) \in \B{R}^{m \times q}`. x = The argument has prototype ``const CppAD::vector<`` *Base* >& *x* and size is equal to the *n* . This is the :ref:`Value-name` corresponding to the parameters in the vector :ref:`atomic_two_afun@ax` (when the atomic function was called). To be specific, if | |tab| ``if`` ( ``Parameter`` ( *ax* [ *i* ]) == ``true`` ) | |tab| |tab| *x* [ *i* ] = ``Value`` ( *ax* [ *i* ] ); | |tab| ``else`` | |tab| |tab| *x* [ *i* ] = ``CppAD::numeric_limits<`` *Base* >:: ``quiet_NaN`` (); The version of this function with out the *x* argument is deprecated; i.e., you should include the argument even if you do not use it. ok ** The return value *ok* has prototype ``bool`` *ok* If it is ``true`` , the corresponding evaluation succeeded, otherwise it failed. {xrst_end atomic_two_for_sparse_jac} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic/two_for_sparse_jac.hpp Atomic forward Jacobian sparsity pattern. */ /*! Link, after case split, from for_jac_sweep to atomic_base. \param q is the column dimension for the Jacobian sparsity patterns. \param r is the Jacobian sparsity pattern for the argument vector x \param s is the Jacobian sparsity pattern for the result vector y \param x is the integer value for x arguments that are parameters. */ template bool atomic_base::for_sparse_jac( size_t q , const vector< std::set >& r , vector< std::set >& s , const vector& x ) { return false; } template bool atomic_base::for_sparse_jac( size_t q , const vector& r , vector& s , const vector& x ) { return false; } template bool atomic_base::for_sparse_jac( size_t q , const vectorBool& r , vectorBool& s , const vector& x ) { return false; } // deprecated versions template bool atomic_base::for_sparse_jac( size_t q , const vector< std::set >& r , vector< std::set >& s ) { return false; } template bool atomic_base::for_sparse_jac( size_t q , const vector& r , vector& s ) { return false; } template bool atomic_base::for_sparse_jac( size_t q , const vectorBool& r , vectorBool& s ) { return false; } /*! Link, before case split, from for_jac_sweep to atomic_base. \tparam InternalSparsity Is the type used for internal sparsity calculations; i.e., sparse_pack or sparse_list. \param x is parameter arguments to the function, other components are nan. \param x_index is the variable index, on the tape, for the arguments to this function. This size of x_index is n, the number of arguments to this function. \param y_index is the variable index, on the tape, for the results for this function. This size of y_index is m, the number of results for this function. \param var_sparsity On input, for j = 0, ... , n-1, the sparsity pattern with index x_index[j], is the sparsity for the j-th argument to this atomic function. On output, for i = 0, ... , m-1, the sparsity pattern with index y_index[i], is the sparsity for the i-th result for this atomic function. */ template template bool atomic_base::for_sparse_jac( const vector& x , const vector& x_index , const vector& y_index , InternalSparsity& var_sparsity ) { // // pod_x_index, pod_y_index local::pod_vector pod_x_index( x_index.size() ); local::pod_vector pod_y_index( y_index.size() ); for(size_t j = 0; j < x_index.size(); ++j) pod_x_index[j] = x_index[j]; for(size_t i = 0; i < y_index.size(); ++i) pod_y_index[i] = y_index[i]; // // initial results are empty during forward mode size_t q = var_sparsity.end(); bool input_empty = true; bool zero_empty = true; bool transpose = false; size_t m = pod_y_index.size(); bool ok = false; size_t thread = thread_alloc::thread_num(); allocate_work(thread); // std::string msg = ": atomic_base.for_sparse_jac: returned false"; if( sparsity_ == pack_sparsity_enum ) { vectorBool& pack_r ( work_[thread]->pack_r ); vectorBool& pack_s ( work_[thread]->pack_s ); local::sparse::get_internal_pattern( transpose, pod_x_index, var_sparsity, pack_r ); // pack_s.resize(m * q ); ok = for_sparse_jac(q, pack_r, pack_s, x); if( ! ok ) ok = for_sparse_jac(q, pack_r, pack_s); if( ! ok ) { msg = atomic_name() + msg + " sparsity = pack_sparsity_enum"; CPPAD_ASSERT_KNOWN(false, msg.c_str()); } local::sparse::set_internal_pattern(zero_empty, input_empty, transpose, pod_y_index, var_sparsity, pack_s ); } else if( sparsity_ == bool_sparsity_enum ) { vector& bool_r ( work_[thread]->bool_r ); vector& bool_s ( work_[thread]->bool_s ); local::sparse::get_internal_pattern( transpose, pod_x_index, var_sparsity, bool_r ); bool_s.resize(m * q ); ok = for_sparse_jac(q, bool_r, bool_s, x); if( ! ok ) ok = for_sparse_jac(q, bool_r, bool_s); if( ! ok ) { msg = atomic_name() + msg + " sparsity = bool_sparsity_enum"; CPPAD_ASSERT_KNOWN(false, msg.c_str()); } local::sparse::set_internal_pattern(zero_empty, input_empty, transpose, pod_y_index, var_sparsity, bool_s ); } else { CPPAD_ASSERT_UNKNOWN( sparsity_ == set_sparsity_enum ); vector< std::set >& set_r ( work_[thread]->set_r ); vector< std::set >& set_s ( work_[thread]->set_s ); local::sparse::get_internal_pattern( transpose, pod_x_index, var_sparsity, set_r ); // set_s.resize(m); ok = for_sparse_jac(q, set_r, set_s, x); if( ! ok ) ok = for_sparse_jac(q, set_r, set_s); if( ! ok ) { msg = atomic_name() + msg + " sparsity = set_sparsity_enum"; CPPAD_ASSERT_KNOWN(false, msg.c_str()); } local::sparse::set_internal_pattern(zero_empty, input_empty, transpose, pod_y_index, var_sparsity, set_s ); } return ok; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/two/forward.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_TWO_FORWARD_HPP # define CPPAD_CORE_ATOMIC_TWO_FORWARD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_two_forward app} {xrst_spell atx aty tx vx vy } Atomic Forward Mode ################### Syntax ****** Base ==== *ok* = *afun* . ``forward`` ( *p* , *q* , *vx* , *vy* , *tx* , *ty* ) This syntax is used by *f* . ``Forward`` where *f* has prototype ``ADFun`` < *Base* > *f* and *afun* is used in *f* . AD ======== *ok* = *afun* . ``forward`` ( *p* , *q* , *vx* , *vy* , *atx* , *aty* ) This syntax is used by *af* . ``Forward`` where *af* has prototype ``ADFun< AD<`` *Base* > , *Base* > *af* and *afun* is used in *af* (see :ref:`base2ad-name` ). Purpose ******* This virtual function is used by :ref:`atomic_two_afun-name` to evaluate function values. It is also used buy :ref:`f.Forward` (and *af* . ``Forward`` ) to compute function vales and derivatives. Implementation ************** This virtual function must be defined by the :ref:`atomic_two_ctor@atomic_user` class. It can just return *ok* == ``false`` (and not compute anything) for values of *q* > 0 that are greater than those used by your :ref:`Forward-name` mode calculations. p * The argument *p* has prototype ``size_t`` *p* It specifies the lowest order Taylor coefficient that we are evaluating. During calls to :ref:`atomic_two_afun-name` , *p* == 0 . q * The argument *q* has prototype ``size_t`` *q* It specifies the highest order Taylor coefficient that we are evaluating. During calls to :ref:`atomic_two_afun-name` , *q* == 0 . vx ** The ``forward`` argument *vx* has prototype ``const CppAD::vector&`` *vx* The case *vx* . ``size`` () > 0 only occurs while evaluating a call to :ref:`atomic_two_afun-name` . In this case, *p* == *q* == 0 , *vx* . ``size`` () == *n* , and for :math:`j = 0 , \ldots , n-1`, *vx* [ *j* ] is true if and only if *ax* [ *j* ] is a :ref:`glossary@Variable` or :ref:`dynamic parameter` in the corresponding call to *afun* ( *ax* , *ay* ) If *vx* . ``size`` () == 0 , then *vy* . ``size`` () == 0 and neither of these vectors should be used. vy ** The ``forward`` argument *vy* has prototype ``CppAD::vector&`` *vy* If *vy* . ``size`` () == 0 , it should not be used. Otherwise, *q* == 0 and *vy* . ``size`` () == *m* . The input values of the elements of *vy* are not specified (must not matter). Upon return, for :math:`j = 0 , \ldots , m-1`, *vy* [ *i* ] is true if and only if *ay* [ *i* ] is a variable or dynamic parameter (CppAD uses *vy* to reduce the necessary computations). tx ** The argument *tx* has prototype ``const CppAD::vector<`` *Base* >& *tx* and *tx* . ``size`` () == ( *q* +1)* *n* . It is used by *f* . ``Forward`` where *f* has type ``ADFun`` < *Base* > *f* and *afun* is used in *f* . For :math:`j = 0 , \ldots , n-1` and :math:`k = 0 , \ldots , q`, we use the Taylor coefficient notation .. math:: :nowrap: \begin{eqnarray} x_j^k & = & tx [ j * ( q + 1 ) + k ] \\ X_j (t) & = & x_j^0 + x_j^1 t^1 + \cdots + x_j^q t^q \end{eqnarray} Note that superscripts represent an index for :math:`x_j^k` and an exponent for :math:`t^k`. Also note that the Taylor coefficients for :math:`X(t)` correspond to the derivatives of :math:`X(t)` at :math:`t = 0` in the following way: .. math:: x_j^k = \frac{1}{ k ! } X_j^{(k)} (0) atx *** The argument *atx* has prototype ``const CppAD::vector< AD<`` *Base* > >& *atx* Otherwise, *atx* specifications are the same as for *tx* . ty ** The argument *ty* has prototype ``CppAD::vector<`` *Base* >& *ty* and *tx* . ``size`` () == ( *q* +1)* *m* . It is set by *f* . ``Forward`` where *f* has type ``ADFun`` < *Base* > *f* and *afun* is used in *f* . Upon return, For :math:`i = 0 , \ldots , m-1` and :math:`k = 0 , \ldots , q`, .. math:: :nowrap: \begin{eqnarray} Y_i (t) & = & f_i [ X(t) ] \\ Y_i (t) & = & y_i^0 + y_i^1 t^1 + \cdots + y_i^q t^q + o ( t^q ) \\ ty [ i * ( q + 1 ) + k ] & = & y_i^k \end{eqnarray} where :math:`o( t^q ) / t^q \rightarrow 0` as :math:`t \rightarrow 0`. Note that superscripts represent an index for :math:`y_j^k` and an exponent for :math:`t^k`. Also note that the Taylor coefficients for :math:`Y(t)` correspond to the derivatives of :math:`Y(t)` at :math:`t = 0` in the following way: .. math:: y_j^k = \frac{1}{ k ! } Y_j^{(k)} (0) If :math:`p > 0`, for :math:`i = 0 , \ldots , m-1` and :math:`k = 0 , \ldots , p-1`, the input of *ty* satisfies .. math:: ty [ i * ( q + 1 ) + k ] = y_i^k and hence the corresponding elements need not be recalculated. aty *** The argument *aty* has prototype ``const CppAD::vector< AD<`` *Base* > >& *aty* Otherwise, *aty* specifications are the same as for *ty* . ok ** If the required results are calculated, *ok* should be true. Otherwise, it should be false. Discussion ********** For example, suppose that *q* == 2 , and you know how to compute the function :math:`f(x)`, its first derivative :math:`f^{(1)} (x)`, and it component wise Hessian :math:`f_i^{(2)} (x)`. Then you can compute *ty* using the following formulas: .. math:: :nowrap: \begin{eqnarray} y_i^0 & = & Y(0) = f_i ( x^0 ) \\ y_i^1 & = & Y^{(1)} ( 0 ) = f_i^{(1)} ( x^0 ) X^{(1)} ( 0 ) = f_i^{(1)} ( x^0 ) x^1 \\ y_i^2 & = & \frac{1}{2 !} Y^{(2)} (0) \\ & = & \frac{1}{2} X^{(1)} (0)^\R{T} f_i^{(2)} ( x^0 ) X^{(1)} ( 0 ) + \frac{1}{2} f_i^{(1)} ( x^0 ) X^{(2)} ( 0 ) \\ & = & \frac{1}{2} (x^1)^\R{T} f_i^{(2)} ( x^0 ) x^1 + f_i^{(1)} ( x^0 ) x^2 \end{eqnarray} For :math:`i = 0 , \ldots , m-1`, and :math:`k = 0 , 1 , 2`, .. math:: ty [ i * (q + 1) + k ] = y_i^k {xrst_end atomic_two_forward} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic/two_forward.hpp Atomic forward mode */ /*! Link from atomic_base to forward mode (for replacement by derived class) \param p [in] lowerest order for this forward mode calculation. \param q [in] highest order for this forward mode calculation. \param vx [in] if size not zero, which components of x are variables \param vy [out] if size not zero, which components of y are variables \param tx [in] Taylor coefficients corresponding to x for this calculation. \param ty [out] Taylor coefficient corresponding to y for this calculation See the forward mode in user's documentation for atomic_two */ template bool atomic_base::forward( size_t p , size_t q , const vector& vx , vector& vy , const vector& tx , vector& ty ) { return false; } /*! Link from atomic_base to forward mode (for replacement by derived class) \param p [in] lowerest order for this forward mode calculation. \param q [in] highest order for this forward mode calculation. \param vx [in] if size not zero, which components of x are variables \param vy [out] if size not zero, which components of y are variables \param atx [in] Taylor coefficients corresponding to x for this calculation. \param aty [out] Taylor coefficient corresponding to y for this calculation See the forward mode in user's documentation for atomic_two */ template bool atomic_base::forward( size_t p , size_t q , const vector& vx , vector& vy , const vector< AD >& atx , vector< AD >& aty ) { return false; } /*! Convert atomic_three interface to atomic_two interface \param order_low [in] lowerest order for this forward mode calculation. \param order_up [in] highest order for this forward mode calculation. \param type_x [in] if size not zero, which components of x are variables \param type_y [out] if size not zero, which components of y are variables \param taylor_x [in] Taylor coefficients corresponding to x for this calculation. \param taylor_y [out] Taylor coefficient corresponding to y for this calculation See the forward mode in user's documentation for atomic_three */ # define CPPAD_ATOMIC_BASE_MUSTDO 0 template bool atomic_base::forward( size_t order_low , size_t order_up , const vector& type_x , vector& type_y , const vector& taylor_x , vector& taylor_y ) { // // atomic_base::afun(ax, ay) calls bool version directly CPPAD_ASSERT_UNKNOWN( type_x.size() == 0 ); CPPAD_ASSERT_UNKNOWN( type_y.size() == 0 ); // # if CPPAD_ATOMIC_BASE_MUSTDO size_t thread = thread_alloc::thread_num(); allocate_work(thread); vector & vx = work_[thread]->vx; vector & vy = work_[thread]->vy; vx.resize(type_x.size()); vy.resize(type_y.size()); # else vector vx, vy; # endif // bool ok = forward(order_low, order_up, vx, vy, taylor_x, taylor_y); // return ok; } # undef CPPAD_ATOMIC_BASE_MUSTDO /*! Convert atomic_three interface to atomic_two interface \param order_low [in] lowerest order for this forward mode calculation. \param order_up [in] highest order for this forward mode calculation. \param type_x [in] if size not zero, which components of x are variables \param type_y [out] if size not zero, which components of y are variables \param ataylor_x [in] Taylor coefficients corresponding to x for this calculation. \param ataylor_y [out] Taylor coefficient corresponding to y for this calculation See the forward mode in user's documentation for atomic_three */ template bool atomic_base::forward( size_t order_low , size_t order_up , const vector& type_x , vector& type_y , const vector< AD >& ataylor_x , vector< AD >& ataylor_y ) { // // atomic_base::afun(ax, ay) calls bool version directly CPPAD_ASSERT_UNKNOWN( type_x.size() == 0 ); CPPAD_ASSERT_UNKNOWN( type_y.size() == 0 ); // vector vx, vy; bool ok = forward(order_low, order_up, vx, vy, ataylor_x, ataylor_y); // return ok; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/two/option.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_TWO_OPTION_HPP # define CPPAD_CORE_ATOMIC_TWO_OPTION_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_two_option app} {xrst_spell typedef } Set Atomic Function Options ########################### Syntax ****** | *afun* . ``option`` ( *option_value* ) Scope ***** These settings do not apply to individual *afun* calls, but rather all subsequent uses of the corresponding atomic operation in an :ref:`ADFun-name` object. atomic_sparsity *************** Note that, if you use :ref:`optimize-name` , these sparsity patterns are used to determine the :ref:`dependency` relationship between argument and result variables. pack_sparsity_enum ================== If *option_value* is ``atomic_base`` < *Base* >:: ``pack_sparsity_enum`` , then the type used by *afun* for :ref:`sparsity patterns` , (after the option is set) will be ``typedef CppAD::vectorBool`` *atomic_sparsity* If *r* is a sparsity pattern for a matrix :math:`R \in \B{R}^{p \times q}`: *r* . ``size`` () == *p* * *q* . bool_sparsity_enum ================== If *option_value* is ``atomic_base`` < *Base* >:: ``bool_sparsity_enum`` , then the type used by *afun* for :ref:`sparsity patterns` , (after the option is set) will be ``typedef CppAD::vector`` *atomic_sparsity* If *r* is a sparsity pattern for a matrix :math:`R \in \B{R}^{p \times q}`: *r* . ``size`` () == *p* * *q* . set_sparsity_enum ================= If *option_value* is *atomic_base* < ``Base`` >:: *set_sparsity_enum* , then the type used by *afun* for :ref:`sparsity patterns` , (after the option is set) will be ``typedef CppAD::vector< std::set >`` *atomic_sparsity* If *r* is a sparsity pattern for a matrix :math:`R \in \B{R}^{p \times q}`: *r* . ``size`` () == *p* , and for :math:`i = 0 , \ldots , p-1`, the elements of *r* [ *i* ] are between zero and :math:`q-1` inclusive. {xrst_end atomic_two_option} ------------------------------------------------------------------------------ */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic/two_option.hpp Setting atomic_base options. */ /*! Setting atomic_base options. \param option_value new option value. */ template void atomic_base::option(enum option_enum option_value) { switch( option_value ) { case pack_sparsity_enum: case bool_sparsity_enum: case set_sparsity_enum: sparsity_ = option_value; break; default: CPPAD_ASSERT_KNOWN( false, "atoic_base::option: option_value is not valid" ); } return; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/two/rev_depend.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_TWO_REV_DEPEND_HPP # define CPPAD_CORE_ATOMIC_TWO_REV_DEPEND_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic/two_rev_depend.hpp Third generation atomic type computation. */ /*! Link from atomic_two to reverse dependency calculation \param parameter_x [in] is the value of the parameters in the corresponding function call afun(ax, ay). \param type_x [in] is the type for each component of ax in the corresponding function call afun(ax, ay). \param depend_x [out] specifies which components of x affect values of interest. \param depend_y [in] specifies which components of y affect values of interest. */ // BEGIN_PROTOTYPE template bool atomic_base::rev_depend( const vector& parameter_x , const vector& type_x , vector& depend_x , const vector& depend_y ) // END_PROTOTYPE { bool ok = true; CPPAD_ASSERT_UNKNOWN( depend_x.size() == parameter_x.size() ); size_t n = depend_x.size(); size_t m = depend_y.size(); // size_t thread = thread_alloc::thread_num(); allocate_work(thread); // if( sparsity_ == pack_sparsity_enum ) { vectorBool& rt ( work_[thread]->pack_r ); vectorBool& st ( work_[thread]->pack_s ); // st.resize(n * 1 ); rt.resize(m * 1 ); for(size_t i = 0; i < m; ++i) rt[i] = depend_y[i]; ok = rev_sparse_jac(1, rt, st, parameter_x); if( ! ok ) ok = rev_sparse_jac(1, rt, st); if( ! ok ) return false; for(size_t j = 0; j < n; ++j) depend_x[j] = st[j]; } else if( sparsity_ == bool_sparsity_enum ) { ok = rev_sparse_jac(1, depend_y, depend_x, parameter_x); if( ! ok ) ok = rev_sparse_jac(m, depend_y, depend_x); if( ! ok ) return false; } else { CPPAD_ASSERT_UNKNOWN( sparsity_ == set_sparsity_enum ); vector< std::set >& rt ( work_[thread]->set_r ); vector< std::set >& st ( work_[thread]->set_s ); rt.resize(m); st.resize(n); for(size_t i = 0; i < m; ++i) { if( depend_y[i] ) rt[i].insert(0); } ok = rev_sparse_jac(m, rt, st, parameter_x); if( ! ok ) ok = rev_sparse_jac(m, rt, st); if( ! ok ) return false; for(size_t j = 0; j < n; ++j) depend_x[j] = ! st[j].empty(); } return ok; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/two/rev_sparse_hes.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_TWO_REV_SPARSE_HES_HPP # define CPPAD_CORE_ATOMIC_TWO_REV_SPARSE_HES_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_two_rev_sparse_hes app} {xrst_spell vx } Atomic Reverse Hessian Sparsity Patterns ######################################## Syntax ****** | *ok* = *afun* . ``rev_sparse_hes`` ( *vx* , *s* , *t* , *q* , *r* , *u* , *v* , *x* ) Deprecated 2016-06-27 ********************* *ok* = *afun* . ``rev_sparse_hes`` ( *vx* , *s* , *t* , *q* , *r* , *u* , *v* ) Purpose ******* This function is used by :ref:`RevSparseHes-name` to compute Hessian sparsity patterns. If you are using :ref:`RevSparseHes-name` to compute one of the versions of this virtual function muse be defined by the :ref:`atomic_two_ctor@atomic_user` class. There is an unspecified scalar valued function :math:`g : \B{R}^m \rightarrow \B{R}`. Given a :ref:`glossary@Sparsity Pattern` for :math:`R \in \B{R}^{n \times q}`, and information about the function :math:`z = g(y)`, this routine computes the sparsity pattern for .. math:: V(x) = (g \circ f)^{(2)}( x ) R Implementation ************** If you are using and :ref:`RevSparseHes-name` , this virtual function must be defined by the :ref:`atomic_two_ctor@atomic_user` class. vx == The argument *vx* has prototype ``const CppAD:vector&`` *vx* *vx* . ``size`` () == *n* , and for :math:`j = 0 , \ldots , n-1`, *vx* [ *j* ] is true if and only if *ax* [ *j* ] is a :ref:`glossary@Variable` or :ref:`dynamic parameter` in the corresponding call to *afun* ( *ax* , *ay* ) s = The argument *s* has prototype ``const CppAD:vector&`` *s* and its size is *m* . It is a sparsity pattern for :math:`S(x) = g^{(1)} [ f(x) ] \in \B{R}^{1 \times m}`. t = This argument has prototype ``CppAD:vector&`` *t* and its size is *m* . The input values of its elements are not specified (must not matter). Upon return, *t* is a sparsity pattern for :math:`T(x) \in \B{R}^{1 \times n}` where .. math:: T(x) = (g \circ f)^{(1)} (x) = S(x) * f^{(1)} (x) q = The argument *q* has prototype ``size_t`` *q* It specifies the number of columns in :math:`R \in \B{R}^{n \times q}`, :math:`U(x) \in \B{R}^{m \times q}`, and :math:`V(x) \in \B{R}^{n \times q}`. r = This argument has prototype ``const`` *atomic_sparsity* & *r* and is a :ref:`atomic_two_option@atomic_sparsity` pattern for :math:`R \in \B{R}^{n \times q}`. u * This argument has prototype ``const`` *atomic_sparsity* & *u* and is a :ref:`atomic_two_option@atomic_sparsity` pattern for :math:`U(x) \in \B{R}^{m \times q}` which is defined by .. math:: :nowrap: \begin{eqnarray} U(x) & = & \{ \partial_u \{ \partial_y g[ y + f^{(1)} (x) R u ] \}_{y=f(x)} \}_{u=0} \\ & = & \partial_u \{ g^{(1)} [ f(x) + f^{(1)} (x) R u ] \}_{u=0} \\ & = & g^{(2)} [ f(x) ] f^{(1)} (x) R \end{eqnarray} v = This argument has prototype *atomic_sparsity* & *v* The input value of its elements are not specified (must not matter). Upon return, *v* is a :ref:`atomic_two_option@atomic_sparsity` pattern for :math:`V(x) \in \B{R}^{n \times q}` which is defined by .. math:: :nowrap: \begin{eqnarray} V(x) & = & \partial_u [ \partial_x (g \circ f) ( x + R u ) ]_{u=0} \\ & = & \partial_u [ (g \circ f)^{(1)}( x + R u ) ]_{u=0} \\ & = & (g \circ f)^{(2)}( x ) R \\ & = & f^{(1)} (x)^\R{T} g^{(2)} [ f(x) ] f^{(1)} (x) R + \sum_{i=1}^m g_i^{(1)} [ f(x) ] \; f_i^{(2)} (x) R \\ & = & f^{(1)} (x)^\R{T} U(x) + \sum_{i=1}^m S_i (x) \; f_i^{(2)} (x) R \end{eqnarray} x = The argument has prototype ``const CppAD::vector<`` *Base* >& *x* and size is equal to the *n* . This is the :ref:`Value-name` corresponding to the parameters in the vector :ref:`atomic_two_afun@ax` (when the atomic function was called). To be specific, if | |tab| ``if`` ( ``Parameter`` ( *ax* [ *i* ]) == ``true`` ) | |tab| |tab| *x* [ *i* ] = ``Value`` ( *ax* [ *i* ] ); | |tab| ``else`` | |tab| |tab| *x* [ *i* ] = ``CppAD::numeric_limits<`` *Base* >:: ``quiet_NaN`` (); The version of this function with out the *x* argument is deprecated; i.e., you should include the argument even if you do not use it. {xrst_end atomic_two_rev_sparse_hes} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic/two_rev_sparse_hes.hpp Atomic reverse mode Hessian sparsity patterns. */ /*! Link from reverse Hessian sparsity sweep to atomic_base \param vx [in] which components of x are variables. \param s [in] is the reverse Jacobian sparsity pattern w.r.t the result vector y. \param t [out] is the reverse Jacobian sparsity pattern w.r.t the argument vector x. \param q [in] is the column dimension for the sparsity patterns. \param r [in] is the forward Jacobian sparsity pattern w.r.t the argument vector x \param u [in] is the Hessian sparsity pattern w.r.t the result vector y. \param v [out] is the Hessian sparsity pattern w.r.t the argument vector x. \param x [in] is the integer value of the x arguments that are parameters. */ template bool atomic_base::rev_sparse_hes( const vector& vx , const vector& s , vector& t , size_t q , const vector< std::set >& r , const vector< std::set >& u , vector< std::set >& v , const vector& x ) { return false; } template bool atomic_base::rev_sparse_hes( const vector& vx , const vector& s , vector& t , size_t q , const vector& r , const vector& u , vector& v , const vector& x ) { return false; } template bool atomic_base::rev_sparse_hes( const vector& vx , const vector& s , vector& t , size_t q , const vectorBool& r , const vectorBool& u , vectorBool& v , const vector& x ) { return false; } // deprecated template bool atomic_base::rev_sparse_hes( const vector& vx , const vector& s , vector& t , size_t q , const vector< std::set >& r , const vector< std::set >& u , vector< std::set >& v ) { return false; } template bool atomic_base::rev_sparse_hes( const vector& vx , const vector& s , vector& t , size_t q , const vector& r , const vector& u , vector& v ) { return false; } template bool atomic_base::rev_sparse_hes( const vector& vx , const vector& s , vector& t , size_t q , const vectorBool& r , const vectorBool& u , vectorBool& v ) { return false; } /*! Link, before case split, from rev_hes_sweep to atomic_base. \tparam InternalSparsity Is the used internally for sparsity calculations; i.e., sparse_pack or sparse_list. \param x is parameter arguments to the function, other components are nan. \param x_index is the variable index, on the tape, for the arguments to this function. This size of x_index is n, the number of arguments to this function. \param y_index is the variable index, on the tape, for the results for this function. This size of y_index is m, the number of results for this function. \param for_jac_sparsity On input, for j = 0, ... , n-1, the sparsity pattern with index x_index[j], is the forward Jacobian sparsity for the j-th argument to this atomic function. \param rev_jac_flag This shows which variables affect the function we are computing the Hessian of. On input, for i = 0, ... , m-1, the rev_jac_flag[ y_index[i] ] is true if the Jacobian of function (we are computing sparsity for) is no-zero. Upon return, for j = 0, ... , n-1, rev_jac_flag [ x_index[j] ] as been adjusted to account removing this atomic function. \param rev_hes_sparsity This is the sparsity pattern for the Hessian. On input, for i = 0, ... , m-1, row y_index[i] is the reverse Hessian sparsity with one of the partials with respect to to y_index[i]. */ template template bool atomic_base::rev_sparse_hes( const vector& x , const vector& x_index , const vector& y_index , const InternalSparsity& for_jac_sparsity , bool* rev_jac_flag , InternalSparsity& rev_hes_sparsity ) { CPPAD_ASSERT_UNKNOWN( for_jac_sparsity.end() == rev_hes_sparsity.end() ); // // pod_x_index, pod_y_index local::pod_vector pod_x_index( x_index.size() ); local::pod_vector pod_y_index( y_index.size() ); for(size_t j = 0; j < x_index.size(); ++j) pod_x_index[j] = x_index[j]; for(size_t i = 0; i < y_index.size(); ++i) pod_y_index[i] = y_index[i]; // size_t q = rev_hes_sparsity.end(); size_t n = pod_x_index.size(); size_t m = pod_y_index.size(); bool ok = false; size_t thread = thread_alloc::thread_num(); allocate_work(thread); bool zero_empty = true; bool input_empty = false; bool transpose = false; // // vx vector vx(n); for(size_t j = 0; j < n; j++) vx[j] = pod_x_index[j] != 0; // // note that s and t are vectors so transpose does not matter for bool case vector bool_s( work_[thread]->bool_s ); vector bool_t( work_[thread]->bool_t ); // bool_s.resize(m); bool_t.resize(n); // for(size_t i = 0; i < m; i++) { if( pod_y_index[i] > 0 ) bool_s[i] = rev_jac_flag[ pod_y_index[i] ]; } // std::string msg = ": atomic_base.rev_sparse_hes: returned false"; if( sparsity_ == pack_sparsity_enum ) { vectorBool& pack_r( work_[thread]->pack_r ); vectorBool& pack_u( work_[thread]->pack_u ); vectorBool& pack_v( work_[thread]->pack_h ); // pack_v.resize(n * q); // local::sparse::get_internal_pattern( transpose, pod_x_index, for_jac_sparsity, pack_r ); local::sparse::get_internal_pattern( transpose, pod_y_index, rev_hes_sparsity, pack_u ); // ok = rev_sparse_hes(vx, bool_s, bool_t, q, pack_r, pack_u, pack_v, x); if( ! ok ) ok = rev_sparse_hes(vx, bool_s, bool_t, q, pack_r, pack_u, pack_v); if( ! ok ) { msg = atomic_name() + msg + " sparsity = pack_sparsity_enum"; CPPAD_ASSERT_KNOWN(false, msg.c_str()); } local::sparse::set_internal_pattern(zero_empty, input_empty, transpose, pod_x_index, rev_hes_sparsity, pack_v ); } else if( sparsity_ == bool_sparsity_enum ) { vector& bool_r( work_[thread]->bool_r ); vector& bool_u( work_[thread]->bool_u ); vector& bool_v( work_[thread]->bool_h ); // bool_v.resize(n * q); // local::sparse::get_internal_pattern( transpose, pod_x_index, for_jac_sparsity, bool_r ); local::sparse::get_internal_pattern( transpose, pod_y_index, rev_hes_sparsity, bool_u ); // ok = rev_sparse_hes(vx, bool_s, bool_t, q, bool_r, bool_u, bool_v, x); if( ! ok ) ok = rev_sparse_hes(vx, bool_s, bool_t, q, bool_r, bool_u, bool_v); if( ! ok ) { msg = atomic_name() + msg + " sparsity = bool_sparsity_enum"; CPPAD_ASSERT_KNOWN(false, msg.c_str()); } local::sparse::set_internal_pattern(zero_empty, input_empty, transpose, pod_x_index, rev_hes_sparsity, bool_v ); } else { CPPAD_ASSERT_UNKNOWN( sparsity_ == set_sparsity_enum ); vector< std::set >& set_r( work_[thread]->set_r ); vector< std::set >& set_u( work_[thread]->set_u ); vector< std::set >& set_v( work_[thread]->set_h ); // set_v.resize(n); // local::sparse::get_internal_pattern( transpose, pod_x_index, for_jac_sparsity, set_r ); local::sparse::get_internal_pattern( transpose, pod_y_index, rev_hes_sparsity, set_u ); // ok = rev_sparse_hes(vx, bool_s, bool_t, q, set_r, set_u, set_v, x); if( ! ok ) ok = rev_sparse_hes(vx, bool_s, bool_t, q, set_r, set_u, set_v); if( ! ok ) { msg = atomic_name() + msg + " sparsity = set_sparsity_enum"; CPPAD_ASSERT_KNOWN(false, msg.c_str()); } local::sparse::set_internal_pattern(zero_empty, input_empty, transpose, pod_x_index, rev_hes_sparsity, set_v ); } for(size_t j = 0; j < n; j++) { if( pod_x_index[j] > 0 ) rev_jac_flag[ pod_x_index[j] ] |= bool_t[j]; } return ok; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/two/rev_sparse_jac.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_TWO_REV_SPARSE_JAC_HPP # define CPPAD_CORE_ATOMIC_TWO_REV_SPARSE_JAC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_two_rev_sparse_jac app} {xrst_spell rt } Atomic Reverse Jacobian Sparsity Patterns ######################################### Syntax ****** | *ok* = *afun* . ``rev_sparse_jac`` ( *q* , *rt* , *st* , *x* ) Deprecated 2016-06-27 ********************* *ok* = *afun* . ``rev_sparse_jac`` ( *q* , *rt* , *st* ) Purpose ******* This function is used by :ref:`RevSparseJac-name` to compute Jacobian sparsity patterns. If you are using :ref:`RevSparseJac-name` , one of the versions of this virtual function must be defined by the :ref:`atomic_two_ctor@atomic_user` class. For a fixed matrix :math:`R \in \B{R}^{q \times m}`, the Jacobian of :math:`R * f( x )` with respect to :math:`x \in \B{R}^n` is .. math:: S(x) = R * f^{(1)} (x) Given a :ref:`glossary@Sparsity Pattern` for :math:`R`, ``rev_sparse_jac`` computes a sparsity pattern for :math:`S(x)`. Implementation ************** If you are using :ref:`RevSparseJac-name` or :ref:`ForSparseHes-name` , this virtual function must be defined by the :ref:`atomic_two_ctor@atomic_user` class. q = The argument *q* has prototype ``size_t`` *q* It specifies the number of rows in :math:`R \in \B{R}^{q \times m}` and the Jacobian :math:`S(x) \in \B{R}^{q \times n}`. rt == This argument has prototype ``const`` *atomic_sparsity* & *rt* and is a :ref:`atomic_two_option@atomic_sparsity` pattern for :math:`R^\R{T} \in \B{R}^{m \times q}`. st == This argument has prototype *atomic_sparsity* & *st* The input value of its elements are not specified (must not matter). Upon return, *s* is a :ref:`atomic_two_option@atomic_sparsity` pattern for :math:`S(x)^\R{T} \in \B{R}^{n \times q}`. x = The argument has prototype ``const CppAD::vector<`` *Base* >& *x* and size is equal to the *n* . This is the :ref:`Value-name` corresponding to the parameters in the vector :ref:`atomic_two_afun@ax` (when the atomic function was called). To be specific, if | |tab| ``if`` ( ``Parameter`` ( *ax* [ *i* ]) == ``true`` ) | |tab| |tab| *x* [ *i* ] = ``Value`` ( *ax* [ *i* ] ); | |tab| ``else`` | |tab| |tab| *x* [ *i* ] = ``CppAD::numeric_limits<`` *Base* >:: ``quiet_NaN`` (); The version of this function with out the *x* argument is deprecated; i.e., you should include the argument even if you do not use it. ok ** The return value *ok* has prototype ``bool`` *ok* If it is ``true`` , the corresponding evaluation succeeded, otherwise it failed. {xrst_end atomic_two_rev_sparse_jac} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic/two_rev_sparse_jac.hpp Atomic reverse mode Jacobian sparsity patterns. */ /*! Link, after case split, from rev_jac_sweep to atomic_base \param q [in] is the row dimension for the Jacobian sparsity patterns \param rt [out] is the tansposed Jacobian sparsity pattern w.r.t to range variables y \param st [in] is the tansposed Jacobian sparsity pattern for the argument variables x \param x is the integer value for x arguments that are parameters. */ template bool atomic_base::rev_sparse_jac( size_t q , const vector< std::set >& rt , vector< std::set >& st , const vector& x ) { return false; } template bool atomic_base::rev_sparse_jac( size_t q , const vector& rt , vector& st , const vector& x ) { return false; } template bool atomic_base::rev_sparse_jac( size_t q , const vectorBool& rt , vectorBool& st , const vector& x ) { return false; } // deprecated versions template bool atomic_base::rev_sparse_jac( size_t q , const vector< std::set >& rt , vector< std::set >& st ) { return false; } template bool atomic_base::rev_sparse_jac( size_t q , const vector& rt , vector& st ) { return false; } template bool atomic_base::rev_sparse_jac( size_t q , const vectorBool& rt , vectorBool& st ) { return false; } /*! Link, before case split, from rev_jac_sweep to atomic_base. \tparam InternalSparsity Is the used internally for sparsity calculations; i.e., sparse_pack or sparse_list. \param x is parameter arguments to the function, other components are nan. \param x_index is the variable index, on the tape, for the arguments to this function. This size of x_index is n, the number of arguments to this function. \param y_index is the variable index, on the tape, for the results for this function. This size of y_index is m, the number of results for this function. \param var_sparsity On input, for i = 0, ... , m-1, the sparsity pattern with index y_index[i], is the sparsity for the i-th argument to this atomic function. On output, for j = 0, ... , n-1, the sparsity pattern with index x_index[j], the sparsity has been updated to remove y as a function of x. */ template template bool atomic_base::rev_sparse_jac( const vector& x , const vector& x_index , const vector& y_index , InternalSparsity& var_sparsity ) { // // pod_x_index, pod_y_index local::pod_vector pod_x_index( x_index.size() ); local::pod_vector pod_y_index( y_index.size() ); for(size_t j = 0; j < x_index.size(); ++j) pod_x_index[j] = x_index[j]; for(size_t i = 0; i < y_index.size(); ++i) pod_y_index[i] = y_index[i]; // // initial results may be non-empty during reverse mode size_t q = var_sparsity.end(); bool input_empty = false; bool zero_empty = true; bool transpose = false; size_t n = pod_x_index.size(); bool ok = false; size_t thread = thread_alloc::thread_num(); allocate_work(thread); // std::string msg = ": atomic_base.rev_sparse_jac: returned false"; if( sparsity_ == pack_sparsity_enum ) { vectorBool& pack_rt ( work_[thread]->pack_r ); vectorBool& pack_st ( work_[thread]->pack_s ); local::sparse::get_internal_pattern( transpose, pod_y_index, var_sparsity, pack_rt ); // pack_st.resize(n * q ); ok = rev_sparse_jac(q, pack_rt, pack_st, x); if( ! ok ) ok = rev_sparse_jac(q, pack_rt, pack_st); if( ! ok ) { msg = atomic_name() + msg + " sparsity = pack_sparsity_enum"; CPPAD_ASSERT_KNOWN(false, msg.c_str()); } local::sparse::set_internal_pattern(zero_empty, input_empty, transpose, pod_x_index, var_sparsity, pack_st ); } else if( sparsity_ == bool_sparsity_enum ) { vector& bool_rt ( work_[thread]->bool_r ); vector& bool_st ( work_[thread]->bool_s ); local::sparse::get_internal_pattern( transpose, pod_y_index, var_sparsity, bool_rt ); bool_st.resize(n * q ); ok = rev_sparse_jac(q, bool_rt, bool_st, x); if( ! ok ) ok = rev_sparse_jac(q, bool_rt, bool_st); if( ! ok ) { msg = atomic_name() + msg + " sparsity = bool_sparsity_enum"; CPPAD_ASSERT_KNOWN(false, msg.c_str()); } local::sparse::set_internal_pattern(zero_empty, input_empty, transpose, pod_x_index, var_sparsity, bool_st ); } else { CPPAD_ASSERT_UNKNOWN( sparsity_ == set_sparsity_enum ); vector< std::set >& set_rt ( work_[thread]->set_r ); vector< std::set >& set_st ( work_[thread]->set_s ); local::sparse::get_internal_pattern( transpose, pod_y_index, var_sparsity, set_rt ); set_st.resize(n); ok = rev_sparse_jac(q, set_rt, set_st, x); if( ! ok ) ok = rev_sparse_jac(q, set_rt, set_st); if( ! ok ) { msg = atomic_name() + msg + " sparsity = set_sparsity_enum"; CPPAD_ASSERT_KNOWN(false, msg.c_str()); } local::sparse::set_internal_pattern(zero_empty, input_empty, transpose, pod_x_index, var_sparsity, set_st ); } return ok; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/atomic/two/reverse.hpp ================================================ # ifndef CPPAD_CORE_ATOMIC_TWO_REVERSE_HPP # define CPPAD_CORE_ATOMIC_TWO_REVERSE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_two_reverse app} {xrst_spell apx apy atx aty px py tx } Atomic Reverse Mode ################### Syntax ****** Base ==== *ok* = *afun* . ``reverse`` ( *q* , *tx* , *ty* , *px* , *py* ) This syntax is used by *f* . ``Forward`` where *f* has prototype ``ADFun`` < *Base* > *f* and *afun* is used in *f* . AD ======== *ok* = *afun* . ``reverse`` ( *q* , *atx* , *aty* , *apx* , *apy* ) This syntax is used by *af* . ``Forward`` where *af* has prototype ``ADFun< AD<`` *Base* > , *Base* > *af* and *afun* is used in *af* (see :ref:`base2ad-name` ). Purpose ******* This function is used by :ref:`Reverse-name` to compute derivatives. Implementation ************** If you are using :ref:`Reverse-name` mode, this virtual function must be defined by the :ref:`atomic_two_ctor@atomic_user` class. It can just return *ok* == ``false`` (and not compute anything) for values of *q* that are greater than those used by your :ref:`Reverse-name` mode calculations. q * The argument *q* has prototype ``size_t`` *q* It specifies the highest order Taylor coefficient that computing the derivative of. tx ** The argument *tx* has prototype ``const CppAD::vector<`` *Base* >& *tx* and *tx* . ``size`` () == ( *q* +1)* *n* . For :math:`j = 0 , \ldots , n-1` and :math:`k = 0 , \ldots , q`, we use the Taylor coefficient notation .. math:: :nowrap: \begin{eqnarray} x_j^k & = & tx [ j * ( q + 1 ) + k ] \\ X_j (t) & = & x_j^0 + x_j^1 t^1 + \cdots + x_j^q t^q \end{eqnarray} Note that superscripts represent an index for :math:`x_j^k` and an exponent for :math:`t^k`. Also note that the Taylor coefficients for :math:`X(t)` correspond to the derivatives of :math:`X(t)` at :math:`t = 0` in the following way: .. math:: x_j^k = \frac{1}{ k ! } X_j^{(k)} (0) atx *** The argument *atx* has prototype ``const CppAD::vector< AD<`` *Base* > >& *atx* Otherwise, *atx* specifications are the same as for *tx* . ty ** The argument *ty* has prototype ``const CppAD::vector<`` *Base* >& *ty* and *tx* . ``size`` () == ( *q* +1)* *m* . For :math:`i = 0 , \ldots , m-1` and :math:`k = 0 , \ldots , q`, we use the Taylor coefficient notation .. math:: :nowrap: \begin{eqnarray} Y_i (t) & = & f_i [ X(t) ] \\ Y_i (t) & = & y_i^0 + y_i^1 t^1 + \cdots + y_i^q t^q + o ( t^q ) \\ y_i^k & = & ty [ i * ( q + 1 ) + k ] \end{eqnarray} where :math:`o( t^q ) / t^q \rightarrow 0` as :math:`t \rightarrow 0`. Note that superscripts represent an index for :math:`y_j^k` and an exponent for :math:`t^k`. Also note that the Taylor coefficients for :math:`Y(t)` correspond to the derivatives of :math:`Y(t)` at :math:`t = 0` in the following way: .. math:: y_j^k = \frac{1}{ k ! } Y_j^{(k)} (0) aty *** The argument *aty* has prototype ``const CppAD::vector< AD<`` *Base* > >& *aty* Otherwise, *aty* specifications are the same as for *ty* . F * We use the notation :math:`\{ x_j^k \} \in \B{R}^{n \times (q+1)}` for .. math:: \{ x_j^k \W{:} j = 0 , \ldots , n-1, k = 0 , \ldots , q \} We use the notation :math:`\{ y_i^k \} \in \B{R}^{m \times (q+1)}` for .. math:: \{ y_i^k \W{:} i = 0 , \ldots , m-1, k = 0 , \ldots , q \} We define the function :math:`F : \B{R}^{n \times (q+1)} \rightarrow \B{R}^{m \times (q+1)}` by .. math:: y_i^k = F_i^k [ \{ x_j^k \} ] Note that .. math:: F_i^0 ( \{ x_j^k \} ) = f_i ( X(0) ) = f_i ( x^0 ) We also note that :math:`F_i^\ell ( \{ x_j^k \} )` is a function of :math:`x^0 , \ldots , x^\ell` and is determined by the derivatives of :math:`f_i (x)` up to order :math:`\ell`. G, H **** We use :math:`G : \B{R}^{m \times (q+1)} \rightarrow \B{R}` to denote an arbitrary scalar valued function of :math:`\{ y_i^k \}`. We use :math:`H : \B{R}^{n \times (q+1)} \rightarrow \B{R}` defined by .. math:: H ( \{ x_j^k \} ) = G[ F( \{ x_j^k \} ) ] py ** The argument *py* has prototype ``const CppAD::vector<`` *Base* >& *py* and *py* . ``size`` () == ``m`` * ( *q* +1) . For :math:`i = 0 , \ldots , m-1`, :math:`k = 0 , \ldots , q`, .. math:: py[ i * (q + 1 ) + k ] = \partial G / \partial y_i^k apy *** The argument *apy* has prototype ``const CppAD::vector< AD<`` *Base* > >& *apy* Otherwise, *apy* specifications are the same as for *py* . px == The *px* has prototype ``CppAD::vector<`` *Base* >& *px* and *px* . ``size`` () == ``n`` * ( *q* +1) . The input values of the elements of *px* are not specified (must not matter). Upon return, for :math:`j = 0 , \ldots , n-1` and :math:`\ell = 0 , \ldots , q`, .. math:: :nowrap: \begin{eqnarray} px [ j * (q + 1) + \ell ] & = & \partial H / \partial x_j^\ell \\ & = & ( \partial G / \partial \{ y_i^k \} ) \cdot ( \partial \{ y_i^k \} / \partial x_j^\ell ) \\ & = & \sum_{k=0}^q \sum_{i=0}^{m-1} ( \partial G / \partial y_i^k ) ( \partial y_i^k / \partial x_j^\ell ) \\ & = & \sum_{k=\ell}^q \sum_{i=0}^{m-1} py[ i * (q + 1 ) + k ] ( \partial F_i^k / \partial x_j^\ell ) \end{eqnarray} Note that we have used the fact that for :math:`k < \ell`, :math:`\partial F_i^k / \partial x_j^\ell = 0`. apx *** The argument *apx* has prototype ``CppAD::vector< AD<`` *Base* > >& *apx* Otherwise, *apx* specifications are the same as for *px* . ok ** The return value *ok* has prototype ``bool`` *ok* If it is ``true`` , the corresponding evaluation succeeded, otherwise it failed. {xrst_end atomic_two_reverse} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file atomic/two_reverse.hpp Atomic reverse mode. */ /*! Link from reverse mode sweep to users routine. \param q [in] highest order for this reverse mode calculation. \param tx [in] Taylor coefficients corresponding to x for this calculation. \param ty [in] Taylor coefficient corresponding to y for this calculation \param px [out] Partials w.r.t. the x Taylor coefficients. \param py [in] Partials w.r.t. the y Taylor coefficients. See atomic_reverse mode use documentation */ template bool atomic_base::reverse( size_t q , const vector& tx , const vector& ty , vector& px , const vector& py ) { return false; } template bool atomic_base::reverse( size_t q , const vector< AD >& atx , const vector< AD >& aty , vector< AD >& apx , const vector< AD >& apy ) { return false; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/azmul.hpp ================================================ # ifndef CPPAD_CORE_AZMUL_HPP # define CPPAD_CORE_AZMUL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin azmul} {xrst_spell ieee } Absolute Zero Multiplication ############################ Syntax ****** | *z* = ``azmul`` ( *x* , *y* ) Purpose ******* Evaluates multiplication with an absolute zero for any of the possible types listed below. The result is given by .. math:: z = \left\{ \begin{array}{ll} 0 & {\rm if} \; x = 0 \\ x \cdot y & {\rm otherwise} \end{array} \right. Note if *x* is zero and *y* is infinity, ieee multiplication would result in not a number whereas *z* would be zero. Base **** If *Base* satisfies the :ref:`base type requirements` and arguments *x* , *y* have prototypes | |tab| ``const`` *Base* & *x* | |tab| ``const`` *Base* & *y* then the result *z* has prototype *Base* *z* AD ******** If the arguments *x* , *y* have prototype | |tab| ``const AD`` < *Base* >& *x* | |tab| ``const AD`` < *Base* >& *y* then the result *z* has prototype ``AD`` < *Base* > *z* VecAD *********** If the arguments *x* , *y* have prototype | |tab| ``const VecAD`` < *Base* >:: ``reference&`` *x* | |tab| ``const VecAD`` < *Base* >:: ``reference&`` *y* then the result *z* has prototype ``AD`` < *Base* > *z* Example ******* {xrst_toc_hidden example/general/azmul.cpp } The file :ref:`azmul.cpp-name` is an examples and tests of this function. {xrst_end azmul} */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE // ========================================================================== // case where x and y are AD ------------------------------------------- template AD azmul(const AD& x, const AD& y) { // compute the Base part AD result; result.value_ = azmul(x.value_, y.value_); // check if there is a recording in progress local::ADTape* tape = AD::tape_ptr(); if( tape == nullptr ) return result; tape_id_t tape_id = tape->id_; // tape_id cannot match the default value for tape_id_; i.e., 0 CPPAD_ASSERT_UNKNOWN( tape_id > 0 ); // check if x and y tapes match bool match_x = x.tape_id_ == tape_id; bool match_y = y.tape_id_ == tape_id; // check if x and y are dynamic parameters bool dyn_x = match_x & (x.ad_type_ == dynamic_enum); bool dyn_y = match_y & (y.ad_type_ == dynamic_enum); // check if x and y are variables bool var_x = match_x & (x.ad_type_ != dynamic_enum); bool var_y = match_y & (y.ad_type_ != dynamic_enum); CPPAD_ASSERT_KNOWN( x.tape_id_ == y.tape_id_ || ! match_x || ! match_y , "azmul: AD variables or dynamic parameters on different threads." ); if( var_x ) { if( var_y ) { // result = azmul(variable, variable) CPPAD_ASSERT_UNKNOWN( local::NumRes(local::ZmulvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::ZmulvvOp) == 2 ); // put operand addresses in tape tape->Rec_.PutArg(x.taddr_, y.taddr_); // put operator in the tape result.taddr_ = tape->Rec_.PutOp(local::ZmulvvOp); // make result a variable result.tape_id_ = tape_id; result.ad_type_ = variable_enum; } else if( ( ! dyn_y ) && IdenticalZero( y.value_ ) ) { // result = variable * 0 } else if( ( ! dyn_y ) && IdenticalOne( y.value_ ) ) { // result = variable * 1 result.make_variable(x.tape_id_, x.taddr_); } else { // result = zmul(variable, parameter) CPPAD_ASSERT_UNKNOWN( local::NumRes(local::ZmulvpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::ZmulvpOp) == 2 ); // put operand addresses in tape addr_t p = y.taddr_; if( ! dyn_y ) p = tape->Rec_.put_con_par(y.value_); tape->Rec_.PutArg(x.taddr_, p); // put operator in the tape result.taddr_ = tape->Rec_.PutOp(local::ZmulvpOp); // make result a variable result.tape_id_ = tape_id; result.ad_type_ = variable_enum; } } else if( var_y ) { if( ( ! dyn_x ) && IdenticalZero(x.value_) ) { // result = 0 * variable } else if( ( ! dyn_x ) && IdenticalOne( x.value_ ) ) { // result = 1 * variable result.make_variable(y.tape_id_, y.taddr_); } else { // result = zmul(parameter, variable) CPPAD_ASSERT_UNKNOWN( local::NumRes(local::ZmulpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::ZmulpvOp) == 2 ); // put operand addresses in tape addr_t p = x.taddr_; if( ! dyn_x ) p = tape->Rec_.put_con_par(x.value_); tape->Rec_.PutArg(p, y.taddr_); // put operator in the tape result.taddr_ = tape->Rec_.PutOp(local::ZmulpvOp); // make result a variable result.tape_id_ = tape_id; result.ad_type_ = variable_enum; } } else if( dyn_x | dyn_y ) { if( (! dyn_x) && IdenticalZero(x.value_) ) { // result = 0 * dynamic } else if ( ( ! dyn_y ) && IdenticalZero(y.value_) ) { // result = dynamic * 0 } else if( ( ! dyn_x ) && IdenticalOne(x.value_) ) { // result = 1 * dynamic result.make_dynamic(y.tape_id_, y.taddr_); } else if( ( ! dyn_y ) && IdenticalOne(y.value_) ) { // result = dynamic * 1 result.make_dynamic(x.tape_id_, x.taddr_ ); } else { addr_t arg0 = x.taddr_; addr_t arg1 = y.taddr_; if( ! dyn_x ) arg0 = tape->Rec_.put_con_par(x.value_); if( ! dyn_y ) arg1 = tape->Rec_.put_con_par(y.value_); // // parameters with a dynamic parameter result result.taddr_ = tape->Rec_.put_dyn_par( result.value_, local::zmul_dyn, arg0, arg1 ); result.tape_id_ = tape_id; result.ad_type_ = dynamic_enum; } } return result; } // ========================================================================= // Fold operations into case above // ------------------------------------------------------------------------- // Operations with VecAD_reference and AD only template AD azmul(const AD& x, const VecAD_reference& y) { return azmul(x, y.ADBase()); } template AD azmul(const VecAD_reference& x, const VecAD_reference& y) { return azmul(x.ADBase(), y.ADBase()); } template AD azmul(const VecAD_reference& x, const AD& y) { return azmul(x.ADBase(), y); } // ------------------------------------------------------------------------- // Operations with Base template AD azmul(const Base& x, const AD& y) { return azmul(AD(x), y); } template AD azmul(const Base& x, const VecAD_reference& y) { return azmul(AD(x), y.ADBase()); } template AD azmul(const AD& x, const Base& y) { return azmul(x, AD(y)); } template AD azmul(const VecAD_reference& x, const Base& y) { return azmul(x.ADBase(), AD(y)); } // ========================================================================== } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/base2ad.hpp ================================================ # ifndef CPPAD_CORE_BASE2AD_HPP # define CPPAD_CORE_BASE2AD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin base2ad} Create an AD Function From a Base Function ################################################ Syntax ****** | *af* = *f* . ``base2ad`` () See Also ******** :ref:`mul_level-name` Base **** This is the base type used to recorded the operation sequence in *f* and *af* ; i.e., the type ``AD`` < *Base* > was used to record the operation sequence. f * This object has prototype ``ADFun`` < *Base* > *f* It does it's derivative calculations using the type *Base* . af ** This object has prototype ``ADFun< AD<`` *Base* > , *Base* > *af* It has the same operation sequence as *f* , but it does it's derivative calculations using the type ``AD`` < *Base>* . This enables one to record new functions that are defined using derivatives of the function *f* . Initially, there are no Taylor coefficients stored in *af* and :ref:`af.size_order()` is zero. {xrst_toc_hidden example/general/base2ad.cpp example/general/base2vec_ad.cpp } Example ******* The file :ref:`base2ad.cpp-name` contains an example and test of this operation. VecAD ***** Forward mode on a ``base2ad`` function does not preserve :ref:`VecAD-name` operations (which might be expected); see the :ref:`base2vec_ad.cpp-name` example. {xrst_end base2ad} ---------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file base2ad.hpp */ /// Create an ADFun< AD, Base > from this ADFun template ADFun< AD, RecBase > ADFun::base2ad(void) const { ADFun< AD, RecBase > fun; // // bool values fun.has_been_optimized_ = has_been_optimized_; fun.check_for_nan_ = check_for_nan_; // // size_t values fun.compare_change_count_ = compare_change_count_; fun.compare_change_number_ = compare_change_number_; fun.compare_change_op_index_ = compare_change_op_index_; CPPAD_ASSERT_UNKNOWN( fun.num_order_taylor_ == 0 ) ; CPPAD_ASSERT_UNKNOWN( fun.cap_order_taylor_ == 0 ); CPPAD_ASSERT_UNKNOWN( fun.num_direction_taylor_ == 0 ); fun.num_var_tape_ = num_var_tape_; // // pod_vector objects fun.ind_taddr_ = ind_taddr_; fun.dep_taddr_ = dep_taddr_; fun.dep_parameter_ = dep_parameter_; fun.cskip_op_ = cskip_op_; fun.load_op2var_ = load_op2var_; // // pod_maybe_vector< AD > = pod_maybe_vector CPPAD_ASSERT_UNKNOWN( fun.taylor_.size() == 0 ); // // player // (uses move semantics) fun.play_ = play_.base2ad(); // // subgraph fun.subgraph_info_ = subgraph_info_; // // sparse_pack fun.for_jac_sparse_pack_ = for_jac_sparse_pack_; // // sparse_list fun.for_jac_sparse_set_ = for_jac_sparse_set_; // return fun; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/base_complex.hpp ================================================ # ifndef CPPAD_CORE_BASE_COMPLEX_HPP # define CPPAD_CORE_BASE_COMPLEX_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include /* {xrst_begin base_complex.hpp} {xrst_spell azmul isnan } Enable use of AD where Base is std::complex ######################################################### {xrst_toc_hidden example/general/complex_poly.cpp } Example ******* The file :ref:`complex_poly.cpp-name` contains an example use of ``std::complex`` type for a CppAD *Base* type. Include Order ************* This file is included before ```` so it is necessary to define the error handler in addition to including :ref:`base_require.hpp` {xrst_spell_off} {xrst_code cpp} */ # include # include # include # include /* {xrst_code} {xrst_spell_on} CondExpOp ********* The type ``std::complex`` does not support the ``<`` , ``<=`` , ``>=`` , and ``>`` operators; see :ref:`base_cond_exp@CondExpTemplate@Not Ordered` . Hence these operators and ``CondExpOp`` function are defined by {xrst_spell_off} {xrst_code cpp} */ # define CPPAD_TEMP(op) \ inline bool operator op( \ const std::complex& left , \ const std::complex& right ) \ { CppAD::ErrorHandler::Call( \ true , __LINE__ , __FILE__ , \ "std::complex " #op " std::complex" , \ "Error: std::complex is not an ordered type" \ ); \ return false; \ } namespace CppAD { CPPAD_TEMP(<) CPPAD_TEMP(<=) CPPAD_TEMP(>=) CPPAD_TEMP(>) inline std::complex CondExpOp( enum CppAD::CompareOp cop , const std::complex &left , const std::complex &right , const std::complex &trueCase , const std::complex &falseCase ) { CppAD::ErrorHandler::Call( true , __LINE__ , __FILE__ , "std::complex CondExpOp(...)", "Error: cannot use CondExp with a complex type" ); return std::complex(0); } } # undef CPPAD_TEMP /* {xrst_code} {xrst_spell_on} CondExpRel ********** The :ref:`CPPAD_COND_EXP_REL` macro invocation {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { CPPAD_COND_EXP_REL( std::complex ) } /* {xrst_code} {xrst_spell_on} used ``CondExpOp`` above to define ``CondExp`` *Rel* for ``std::complex`` arguments and *Rel* equal to ``Lt`` , ``Le`` , ``Eq`` , ``Ge`` , and ``Gt`` . EqualOpSeq ********** Complex numbers do not carry operation sequence information. Thus they are equal in this sense if and only if there values are equal. {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline bool EqualOpSeq( const std::complex &x , const std::complex &y ) { return x == y; } } /* {xrst_code} {xrst_spell_on} Identical ********* Complex numbers do not carry operation sequence information. Thus they are all parameters so the identical functions just check values. {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline bool IdenticalCon(const std::complex &x) { return true; } inline bool IdenticalZero(const std::complex &x) { return (x == std::complex(0., 0.) ); } inline bool IdenticalOne(const std::complex &x) { return (x == std::complex(1., 0.) ); } inline bool IdenticalEqualCon( const std::complex &x, const std::complex &y) { return (x == y); } } /* {xrst_code} {xrst_spell_on} Ordered ******* Complex types do not support comparison operators, {xrst_spell_off} {xrst_code cpp} */ # undef CPPAD_USER_MACRO # define CPPAD_USER_MACRO(Fun) \ inline bool Fun(const std::complex& x) \ { CppAD::ErrorHandler::Call( \ true , __LINE__ , __FILE__ , \ #Fun"(x)", \ "Error: cannot use " #Fun " with x complex " \ ); \ return false; \ } namespace CppAD { CPPAD_USER_MACRO(LessThanZero) CPPAD_USER_MACRO(LessThanOrZero) CPPAD_USER_MACRO(GreaterThanOrZero) CPPAD_USER_MACRO(GreaterThanZero) inline bool abs_geq( const std::complex& x , const std::complex& y ) { return std::abs(x) >= std::abs(y); } } /* {xrst_code} {xrst_spell_on} Integer ******* The implementation of this function must agree with the CppAD user specifications for complex arguments to the :ref:`Integer` function: {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline int Integer(const std::complex &x) { return static_cast( x.real() ); } } /* {xrst_code} {xrst_spell_on} azmul ***** {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { CPPAD_AZMUL( std::complex ) } /* {xrst_code} {xrst_spell_on} isnan ***** The gcc 4.1.1 compiler defines the function ``int std::complex::isnan`` ( ``std::complex`` *z* ) (which is not specified in the C++ 1998 standard ISO/IEC 14882). This causes an ambiguity between the function above and the CppAD :ref:`isnan` template function. We avoid this ambiguity by defining a non-template version of this function in the CppAD namespace. {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline bool isnan(const std::complex& z) { return (z != z); } } /* {xrst_code} {xrst_spell_on} Valid Unary Math **************** The following macro invocations define the standard unary math functions that are valid with complex arguments and are required to use ``AD< std::complex >`` . {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { CPPAD_STANDARD_MATH_UNARY(std::complex, cos) CPPAD_STANDARD_MATH_UNARY(std::complex, cosh) CPPAD_STANDARD_MATH_UNARY(std::complex, exp) CPPAD_STANDARD_MATH_UNARY(std::complex, log) CPPAD_STANDARD_MATH_UNARY(std::complex, sin) CPPAD_STANDARD_MATH_UNARY(std::complex, sinh) CPPAD_STANDARD_MATH_UNARY(std::complex, sqrt) } /* {xrst_code} {xrst_spell_on} Invalid Unary Math ****************** The following macro definition and invocations define the standard unary math functions that are invalid with complex arguments and are required to use ``AD< std::complex >`` . {xrst_spell_off} {xrst_code cpp} */ # undef CPPAD_USER_MACRO # define CPPAD_USER_MACRO(Fun) \ inline std::complex Fun(const std::complex& x) \ { CppAD::ErrorHandler::Call( \ true , __LINE__ , __FILE__ , \ #Fun"(x)", \ "Error: cannot use " #Fun " with x complex " \ ); \ return std::complex(0); \ } namespace CppAD { CPPAD_USER_MACRO(abs) CPPAD_USER_MACRO(fabs) CPPAD_USER_MACRO(acos) CPPAD_USER_MACRO(asin) CPPAD_USER_MACRO(atan) CPPAD_USER_MACRO(sign) CPPAD_USER_MACRO(asinh) CPPAD_USER_MACRO(acosh) CPPAD_USER_MACRO(atanh) CPPAD_USER_MACRO(erf) CPPAD_USER_MACRO(erfc) CPPAD_USER_MACRO(expm1) CPPAD_USER_MACRO(log1p) } /* {xrst_code} {xrst_spell_on} pow *** The following defines a ``CppAD::pow`` function that is required to use ``AD< std::complex >`` : {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline std::complex pow( const std::complex &x , const std::complex &y ) { return std::pow(x, y); } } /* {xrst_code} {xrst_spell_on} numeric_limits ************** The following defines the CppAD :ref:`numeric_limits-name` for the type ``std::complex`` : {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { CPPAD_NUMERIC_LIMITS(double, std::complex) } /* {xrst_code} {xrst_spell_on} to_string ********* The following defines the function CppAD :ref:`to_string-name` for the type ``std::complex`` : {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { CPPAD_TO_STRING(std::complex) } /* {xrst_code} {xrst_spell_on} {xrst_end base_complex.hpp} */ # undef CPPAD_USER_MACRO_ONE # define CPPAD_USER_MACRO_ONE(Fun) \ inline bool Fun(const std::complex& x) \ { CppAD::ErrorHandler::Call( \ true , __LINE__ , __FILE__ , \ #Fun"(x)", \ "Error: cannot use " #Fun " with x complex " \ ); \ return false; \ } # undef CPPAD_USER_MACRO_TWO # define CPPAD_USER_MACRO_TWO(Fun) \ inline std::complex Fun(const std::complex& x) \ { CppAD::ErrorHandler::Call( \ true , __LINE__ , __FILE__ , \ #Fun"(x)", \ "Error: cannot use " #Fun " with x complex " \ ); \ return std::complex(0); \ } namespace CppAD { // CondExpOp ------------------------------------------------------ inline std::complex CondExpOp( enum CppAD::CompareOp cop , const std::complex &left , const std::complex &right , const std::complex &trueCase , const std::complex &falseCase ) { CppAD::ErrorHandler::Call( true , __LINE__ , __FILE__ , "std::complex CondExpOp(...)", "Error: cannot use CondExp with a complex type" ); return std::complex(0); } // CondExpRel -------------------------------------------------------- CPPAD_COND_EXP_REL( std::complex ) // EqualOpSeq ----------------------------------------------------- inline bool EqualOpSeq( const std::complex &x , const std::complex &y ) { return x == y; } // Identical ------------------------------------------------------ inline bool IdenticalCon(const std::complex &x) { return true; } inline bool IdenticalZero(const std::complex &x) { return (x == std::complex(0., 0.) ); } inline bool IdenticalOne(const std::complex &x) { return (x == std::complex(1., 0.) ); } inline bool IdenticalEqualCon( const std::complex &x, const std::complex &y) { return (x == y); } // Ordered -------------------------------------------------------- CPPAD_USER_MACRO_ONE(LessThanZero) CPPAD_USER_MACRO_ONE(LessThanOrZero) CPPAD_USER_MACRO_ONE(GreaterThanOrZero) CPPAD_USER_MACRO_ONE(GreaterThanZero) inline bool abs_geq( const std::complex& x , const std::complex& y ) { return std::abs(x) >= std::abs(y); } // Integer ------------------------------------------------------ inline int Integer(const std::complex &x) { return static_cast( x.real() ); } // isnan ------------------------------------------------------------- inline bool isnan(const std::complex& z) { return (z != z); } // Valid standard math functions -------------------------------- CPPAD_STANDARD_MATH_UNARY(std::complex, cos) CPPAD_STANDARD_MATH_UNARY(std::complex, cosh) CPPAD_STANDARD_MATH_UNARY(std::complex, exp) CPPAD_STANDARD_MATH_UNARY(std::complex, log) CPPAD_STANDARD_MATH_UNARY(std::complex, sin) CPPAD_STANDARD_MATH_UNARY(std::complex, sinh) CPPAD_STANDARD_MATH_UNARY(std::complex, sqrt) // Invalid standard math functions ------------------------------- CPPAD_USER_MACRO_TWO(abs) CPPAD_USER_MACRO_TWO(acos) CPPAD_USER_MACRO_TWO(asin) CPPAD_USER_MACRO_TWO(atan) CPPAD_USER_MACRO_TWO(sign) // The pow function inline std::complex pow( const std::complex &x , const std::complex &y ) { return std::pow(x, y); } // numeric_limits ------------------------------------------------- CPPAD_NUMERIC_LIMITS(float, std::complex) // to_string ------------------------------------------------- CPPAD_TO_STRING(std::complex) } // undefine macros only used by this file # undef CPPAD_USER_MACRO # undef CPPAD_USER_MACRO_ONE # undef CPPAD_USER_MACRO_TWO # endif ================================================ FILE: include/cppad/core/base_cond_exp.hpp ================================================ # ifndef CPPAD_CORE_BASE_COND_EXP_HPP # define CPPAD_CORE_BASE_COND_EXP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin base_cond_exp} {xrst_spell adolc } Base Type Requirements for Conditional Expressions ################################################## Purpose ******* These definitions are required by the user's code to support the ``AD`` < *Base* > type for :ref:`CondExp-name` operations: CompareOp ********* The following ``enum`` type is used in the specifications below: :: namespace CppAD { // The conditional expression operator enum type enum CompareOp { CompareLt, // less than CompareLe, // less than or equal CompareEq, // equal CompareGe, // greater than or equal CompareGt, // greater than CompareNe // not equal }; } CondExpTemplate *************** The type *Base* must support the syntax | |tab| *result* = ``CppAD::CondExpOp`` ( | |tab| |tab| *cop* , *left* , *right* , *exp_if_true* , *exp_if_false* | |tab| ) which computes implements the corresponding :ref:`CondExp-name` function when the result has prototype *Base* *result* The argument *cop* has prototype ``enum CppAD::CompareOp`` *cop* The other arguments have the prototype | |tab| ``const`` *Base* & *left* | |tab| ``const`` *Base* & *right* | |tab| ``const`` *Base* & *exp_if_true* | |tab| ``const`` *Base* & *exp_if_false* Ordered Type ============ If *Base* is a relatively simple type that supports ``<`` , ``<=`` , ``==`` , ``>=`` , and ``>`` operators its ``CondExpOp`` function can be defined by | ``namespace CppAD`` { | |tab| ``inline`` *Base* ``CondExpOp`` ( | |tab| ``enum CppAD::CompareOp`` *cop* , | |tab| ``const`` *Base* & *left* , | |tab| ``const`` *Base* & *right* , | |tab| ``const`` *Base* & *exp_if_true* , | |tab| ``const`` *Base* & *exp_if_false* ) | |tab| { ``return CondExpTemplate`` ( | |tab| |tab| |tab| ``cop`` , ``left`` , ``right`` , ``trueCase`` , ``falseCase`` ); | |tab| } | } For example, see :ref:`double CondExpOp` . For an example of and implementation of ``CondExpOp`` with a more involved *Base* type see :ref:`adolc CondExpOp` . Not Ordered =========== If the type *Base* does not support ordering; i.e., does not support the ``<`` , ``<=`` , ``>=`` , or ``>`` operator they should be defined as resulting in error when used; e.g. | ``namespace CppAD`` { | |tab| ``inline bool operator<`` ( | |tab| ``const`` *Base* & *left* , | |tab| ``const`` *Base* & *right* ) | |tab| { // ``attempt to use < operator with`` *Base* ``arguments`` | |tab| |tab| ``assert`` (0); | |tab| |tab| ``return`` false; | |tab| } | } Using the ``CondExpOp`` function does should also be defined a an error; e.g., | ``namespace CppAD`` { | |tab| ``inline`` *Base* ``CondExpOp`` ( | |tab| ``enum CompareOp`` *cop* , | |tab| ``const`` *Base* & *left* , | |tab| ``const`` *Base* & *right* , | |tab| ``const`` *Base* & *exp_if_true* , | |tab| ``const`` *Base* & *exp_if_false* ) | |tab| { // ``attempt to use CondExp with a`` *Base* ``argument`` | |tab| |tab| ``assert`` (0); | |tab| |tab| ``return`` *Base* (0); | |tab| } | } For example, see :ref:`complex CondExpOp` . CondExpRel ********** The macro invocation ``CPPAD_COND_EXP_REL`` ( *Base* ) uses ``CondExpOp`` above to define the following functions | |tab| ``CondExpLt`` ( *left* , *right* , *exp_if_true* , *exp_if_false* ) | |tab| ``CondExpLe`` ( *left* , *right* , *exp_if_true* , *exp_if_false* ) | |tab| ``CondExpEq`` ( *left* , *right* , *exp_if_true* , *exp_if_false* ) | |tab| ``CondExpGe`` ( *left* , *right* , *exp_if_true* , *exp_if_false* ) | |tab| ``CondExpGt`` ( *left* , *right* , *exp_if_true* , *exp_if_false* ) where the arguments have type *Base* . This should be done inside of the CppAD namespace. For example, see :ref:`base_alloc` . {xrst_end base_cond_exp} */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file base_cond_exp.hpp CondExp operations that aid in meeting Base type requirements. */ /*! \def CPPAD_COND_EXP_BASE_REL(Type, Rel, Op) This macro defines the operation \verbatim CondExpRel(left, right, exp_if_true, exp_if_false) \endverbatim The argument Type is the Base type for this base require operation. The argument Rel is one of Lt, Le, Eq, Ge, Gt. The argument Op is the corresponding CompareOp value. */ # define CPPAD_COND_EXP_BASE_REL(Type, Rel, Op) \ inline Type CondExp##Rel( \ const Type& left , \ const Type& right , \ const Type& exp_if_true , \ const Type& exp_if_false ) \ { return CondExpOp(Op, left, right, exp_if_true, exp_if_false); \ } /*! \def CPPAD_COND_EXP_REL(Type) The macro defines the operations \verbatim CondExpLt(left, right, exp_if_true, exp_if_false) CondExpLe(left, right, exp_if_true, exp_if_false) CondExpEq(left, right, exp_if_true, exp_if_false) CondExpGe(left, right, exp_if_true, exp_if_false) CondExpGt(left, right, exp_if_true, exp_if_false) \endverbatim The argument Type is the Base type for this base require operation. */ # define CPPAD_COND_EXP_REL(Type) \ CPPAD_COND_EXP_BASE_REL(Type, Lt, CompareLt) \ CPPAD_COND_EXP_BASE_REL(Type, Le, CompareLe) \ CPPAD_COND_EXP_BASE_REL(Type, Eq, CompareEq) \ CPPAD_COND_EXP_BASE_REL(Type, Ge, CompareGe) \ CPPAD_COND_EXP_BASE_REL(Type, Gt, CompareGt) /*! Template function to implement Conditional Expressions for simple types that have comparison operators. \tparam CompareType is the type of the left and right operands to the comparison operator. \tparam ResultType is the type of the result, which is the same as CompareType except during forward and reverse mode sparese calculations. \param cop spedifies which comparison to use; i.e., $code <$$, $code <=$$, $code ==$$, $code >=$$, $code >$$, or $code !=$$. \param left is the left operand to the comparison operator. \param right is the right operand to the comparison operator. \param exp_if_true is the return value is the comparison results in true. \param exp_if_false is the return value is the comparison results in false. \return see exp_if_true and exp_if_false above. */ template ResultType CondExpTemplate( enum CompareOp cop , const CompareType& left , const CompareType& right , const ResultType& exp_if_true , const ResultType& exp_if_false ) { ResultType returnValue; switch( cop ) { case CompareLt: if( left < right ) returnValue = exp_if_true; else returnValue = exp_if_false; break; case CompareLe: if( left <= right ) returnValue = exp_if_true; else returnValue = exp_if_false; break; case CompareEq: if( left == right ) returnValue = exp_if_true; else returnValue = exp_if_false; break; case CompareGe: if( left >= right ) returnValue = exp_if_true; else returnValue = exp_if_false; break; case CompareGt: if( left > right ) returnValue = exp_if_true; else returnValue = exp_if_false; break; default: CPPAD_ASSERT_UNKNOWN(0); returnValue = exp_if_true; } return returnValue; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/base_double.hpp ================================================ # ifndef CPPAD_CORE_BASE_DOUBLE_HPP # define CPPAD_CORE_BASE_DOUBLE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include /* {xrst_begin base_double.hpp} {xrst_spell azmul namespaces } Enable use of AD where Base is double ########################################### CondExpOp ********* The type ``double`` is a relatively simple type that supports ``<`` , ``<=`` , ``==`` , ``>=`` , and ``>`` operators; see :ref:`base_cond_exp@CondExpTemplate@Ordered Type` . Hence its ``CondExpOp`` function is defined by {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline double CondExpOp( enum CompareOp cop , const double& left , const double& right , const double& exp_if_true , const double& exp_if_false ) { return CondExpTemplate(cop, left, right, exp_if_true, exp_if_false); } } /* {xrst_code} {xrst_spell_on} CondExpRel ********** The :ref:`CPPAD_COND_EXP_REL` macro invocation {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { CPPAD_COND_EXP_REL(double) } /* {xrst_code} {xrst_spell_on} uses ``CondExpOp`` above to define ``CondExp`` *Rel* for ``double`` arguments and *Rel* equal to ``Lt`` , ``Le`` , ``Eq`` , ``Ge`` , and ``Gt`` . EqualOpSeq ********** The type ``double`` is simple (in this respect) and so we define {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline bool EqualOpSeq(const double& x, const double& y) { return x == y; } } /* {xrst_code} {xrst_spell_on} Identical ********* The type ``double`` is simple (in this respect) and so we define {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline bool IdenticalCon(const double& x) { return true; } inline bool IdenticalZero(const double& x) { return (x == 0.); } inline bool IdenticalOne(const double& x) { return (x == 1.); } inline bool IdenticalEqualCon(const double& x, const double& y) { return (x == y); } } /* {xrst_code} {xrst_spell_on} Integer ******* {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline int Integer(const double& x) { return static_cast(x); } } /* {xrst_code} {xrst_spell_on} azmul ***** {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { CPPAD_AZMUL( double ) } /* {xrst_code} {xrst_spell_on} Ordered ******* The ``double`` type supports ordered comparisons {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline bool GreaterThanZero(const double& x) { return x > 0.; } inline bool GreaterThanOrZero(const double& x) { return x >= 0.; } inline bool LessThanZero(const double& x) { return x < 0.; } inline bool LessThanOrZero(const double& x) { return x <= 0.; } inline bool abs_geq(const double& x, const double& y) { return std::fabs(x) >= std::fabs(y); } } /* {xrst_code} {xrst_spell_on} Unary Standard Math ******************* The following macro invocations import the ``double`` versions of the unary standard math functions into the ``CppAD`` namespace. Importing avoids ambiguity errors when using both the ``CppAD`` and ``std`` namespaces. Note this also defines the :ref:`float` versions of these functions. {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { using std::acos; using std::asin; using std::atan; using std::cos; using std::cosh; using std::exp; using std::fabs; using std::log; using std::log10; using std::sin; using std::sinh; using std::sqrt; using std::tan; using std::tanh; using std::asinh; using std::acosh; using std::atanh; using std::erf; using std::erfc; using std::expm1; using std::log1p; } /* {xrst_code} {xrst_spell_on} The absolute value function is special because its ``std`` name is ``fabs`` {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline double abs(const double& x) { return std::fabs(x); } } /* {xrst_code} {xrst_spell_on} sign **** The following defines the ``CppAD::sign`` function that is required to use ``AD`` : {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline double sign(const double& x) { if( x > 0. ) return 1.; if( x == 0. ) return 0.; return -1.; } } /* {xrst_code} {xrst_spell_on} pow *** The following defines a ``CppAD::pow`` function that is required to use ``AD`` . As with the unary standard math functions, this has the exact same signature as ``std::pow`` , so use it instead of defining another function. {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { using std::pow; } /* {xrst_code} {xrst_spell_on} numeric_limits ************** The following defines the CppAD :ref:`numeric_limits-name` for the type ``double`` : {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { CPPAD_NUMERIC_LIMITS(double, double) } /* {xrst_code} {xrst_spell_on} to_string ********* There is no need to define ``to_string`` for ``double`` because it is defined by including ``cppad/utility/to_string.hpp`` ; see :ref:`to_string-name` . See :ref:`base_complex.hpp` for an example where it is necessary to define ``to_string`` for a *Base* type. {xrst_end base_double.hpp} */ # endif ================================================ FILE: include/cppad/core/base_float.hpp ================================================ # ifndef CPPAD_CORE_BASE_FLOAT_HPP # define CPPAD_CORE_BASE_FLOAT_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include /* {xrst_begin base_float.hpp} {xrst_spell azmul namespaces } Enable use of AD where Base is float ########################################## CondExpOp ********* The type ``float`` is a relatively simple type that supports ``<`` , ``<=`` , ``==`` , ``>=`` , and ``>`` operators; see :ref:`base_cond_exp@CondExpTemplate@Ordered Type` . Hence its ``CondExpOp`` function is defined by {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline float CondExpOp( enum CompareOp cop , const float& left , const float& right , const float& exp_if_true , const float& exp_if_false ) { return CondExpTemplate(cop, left, right, exp_if_true, exp_if_false); } } /* {xrst_code} {xrst_spell_on} CondExpRel ********** The :ref:`CPPAD_COND_EXP_REL` macro invocation {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { CPPAD_COND_EXP_REL(float) } /* {xrst_code} {xrst_spell_on} uses ``CondExpOp`` above to define ``CondExp`` *Rel* for ``float`` arguments and *Rel* equal to ``Lt`` , ``Le`` , ``Eq`` , ``Ge`` , and ``Gt`` . EqualOpSeq ********** The type ``float`` is simple (in this respect) and so we define {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline bool EqualOpSeq(const float& x, const float& y) { return x == y; } } /* {xrst_code} {xrst_spell_on} Identical ********* The type ``float`` is simple (in this respect) and so we define {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline bool IdenticalCon(const float& x) { return true; } inline bool IdenticalZero(const float& x) { return (x == 0.f); } inline bool IdenticalOne(const float& x) { return (x == 1.f); } inline bool IdenticalEqualCon(const float& x, const float& y) { return (x == y); } } /* {xrst_code} {xrst_spell_on} Integer ******* {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline int Integer(const float& x) { return static_cast(x); } } /* {xrst_code} {xrst_spell_on} azmul ***** {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { CPPAD_AZMUL( float ) } /* {xrst_code} {xrst_spell_on} Ordered ******* The ``float`` type supports ordered comparisons {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline bool GreaterThanZero(const float& x) { return x > 0.f; } inline bool GreaterThanOrZero(const float& x) { return x >= 0.f; } inline bool LessThanZero(const float& x) { return x < 0.f; } inline bool LessThanOrZero(const float& x) { return x <= 0.f; } inline bool abs_geq(const float& x, const float& y) { return std::fabs(x) >= std::fabs(y); } } /* {xrst_code} {xrst_spell_on} Unary Standard Math ******************* The following macro invocations import the ``float`` versions of the unary standard math functions into the ``CppAD`` namespace. Importing avoids ambiguity errors when using both the ``CppAD`` and ``std`` namespaces. Note this also defines the :ref:`double` versions of these functions. {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { using std::acos; using std::asin; using std::atan; using std::cos; using std::cosh; using std::exp; using std::fabs; using std::log; using std::log10; using std::sin; using std::sinh; using std::sqrt; using std::tan; using std::tanh; using std::asinh; using std::acosh; using std::atanh; using std::erf; using std::erfc; using std::expm1; using std::log1p; } /* {xrst_code} {xrst_spell_on} The absolute value function is special because its ``std`` name is ``fabs`` {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline float abs(const float& x) { return std::fabs(x); } } /* {xrst_code} {xrst_spell_on} sign **** The following defines the ``CppAD::sign`` function that is required to use ``AD`` : {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline float sign(const float& x) { if( x > 0.f ) return 1.f; if( x == 0.f ) return 0.f; return -1.f; } } /* {xrst_code} {xrst_spell_on} pow *** The following defines a ``CppAD::pow`` function that is required to use ``AD`` . As with the unary standard math functions, this has the exact same signature as ``std::pow`` , so use it instead of defining another function. {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { using std::pow; } /* {xrst_code} {xrst_spell_on} numeric_limits ************** The following defines the CppAD :ref:`numeric_limits-name` for the type ``float`` : {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { CPPAD_NUMERIC_LIMITS(float, float) } /* {xrst_code} {xrst_spell_on} to_string ********* There is no need to define ``to_string`` for ``float`` because it is defined by including ``cppad/utility/to_string.hpp`` ; see :ref:`to_string-name` . See :ref:`base_complex.hpp` for an example where it is necessary to define ``to_string`` for a *Base* type. {xrst_end base_float.hpp} */ # endif ================================================ FILE: include/cppad/core/base_hash.hpp ================================================ # ifndef CPPAD_CORE_BASE_HASH_HPP # define CPPAD_CORE_BASE_HASH_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin base_hash} {xrst_spell adouble valgrind } Base Type Requirements for Hash Coding Values ############################################# Syntax ****** | *code* = ``hash_code`` ( *x* ) Purpose ******* CppAD uses a table of *Base* type values when recording ``AD`` < *Base* > operations. A hashing function is used to reduce number of values stored in this table; for example, it is not necessary to store the value 3.0 every time it is used as a :ref:`con_dyn_var@Parameter` . Default ******* The default hashing function works with the set of bits that correspond to a *Base* value. In most cases this works well, but in some cases it does not. For example, in the :ref:`base_adolc.hpp-name` case, an ``adouble`` value can have fields that are not initialized and ``valgrind`` reported an error when these are used to form the hash code. x * This argument has prototype *const* ``Base`` & ``x`` It is the value we are forming a hash code for. code **** The return value *code* has prototype ``unsigned short`` *code* It is the hash code corresponding to *x* . This intention is the commonly used values will have different hash codes. The hash code must satisfy *code* < ``CPPAD_HASH_TABLE_SIZE`` so that it is a valid index into the hash code table. inline ****** If you define this function, it should declare it to be ``inline`` , so that you do not get multiple definitions from different compilation units. Example ******* See the ``base_alloc`` :ref:`base_alloc.hpp@hash_code` and the ``adouble`` :ref:`base_adolc.hpp@hash_code` . {xrst_end base_hash} */ /*! \def CPPAD_HASH_TABLE_SIZE the codes returned by hash_code are between zero and CPPAD_HASH_TABLE_SIZE minus one. */ # define CPPAD_HASH_TABLE_SIZE 10000 # endif ================================================ FILE: include/cppad/core/base_limits.hpp ================================================ # ifndef CPPAD_CORE_BASE_LIMITS_HPP # define CPPAD_CORE_BASE_LIMITS_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin base_limits} Base Type Requirements for Numeric Limits ######################################### numeric_limits ************** A specialization for :ref:`CppAD::numeric_limits` must be defined in order to use the type ``AD`` < *Base* > . CppAD does not use a specialization of ``std::numeric_limits<`` *Base* > . Since C++11, using a specialization of ``std::numeric_limits<`` *Base* > would require that *Base* be a literal type. CPPAD_NUMERIC_LIMITS ******************** In most cases, this macro can be used to define the specialization where the numeric limits for the type *Base* are the same as the standard numeric limits for the type *Other* . For most *Base* types, there is a choice of *Other* , for which the following preprocessor macro invocation suffices: | |tab| ``namespace CppAD`` { | |tab| |tab| ``CPPAD_NUMERIC_LIMITS`` ( *Other* , *Base* ) | |tab| } e.g., see :ref:`base_double.hpp@numeric_limits` for the type ``double`` . The macro ``CPPAD_NUMERIC_LIMITS`` is defined by {xrst_spell_off} {xrst_code cpp} */ # define CPPAD_NUMERIC_LIMITS(Other, Base) \ template <> class numeric_limits\ {\ public:\ static Base min(void) \ { return static_cast( std::numeric_limits::min() ); }\ static Base max(void) \ { return static_cast( std::numeric_limits::max() ); }\ static Base epsilon(void) \ { return static_cast( std::numeric_limits::epsilon() ); }\ static Base quiet_NaN(void) \ { return static_cast( std::numeric_limits::quiet_NaN() ); }\ static Base infinity(void) \ { return static_cast( std::numeric_limits::infinity() ); }\ static const int digits10 = std::numeric_limits::digits10;\ static const int max_digits10 = std::numeric_limits::max_digits10;\ }; /* {xrst_code} {xrst_spell_on} {xrst_end base_limits} */ # endif ================================================ FILE: include/cppad/core/base_std_math.hpp ================================================ # ifndef CPPAD_CORE_BASE_STD_MATH_HPP # define CPPAD_CORE_BASE_STD_MATH_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin base_std_math} {xrst_spell acosh asinh erfc expm isnan } Base Type Requirements for Standard Math Functions ################################################## Purpose ******* These definitions are required for the user's code to use the type ``AD`` < *Base* > : Unary Standard Math ******************* The type *Base* must support the following functions unary standard math functions (in the CppAD namespace): .. csv-table:: :widths: auto **Syntax**,**Result** *y* = ``abs`` ( *x* ),absolute value *y* = ``acos`` ( *x* ),inverse cosine *y* = ``acosh`` ( *x* ),inverse hyperbolic cosine *y* = ``asin`` ( *x* ),inverse sine *y* = ``asinh`` ( *x* ),inverse hyperbolic sin *y* = ``atan`` ( *x* ),inverse tangent *y* = ``atanh`` ( *x* ),inverse hyperbolic tangent *y* = ``cos`` ( *x* ),cosine *y* = ``cosh`` ( *x* ),hyperbolic cosine *y* = ``erf`` ( *x* ),error function *y* = ``erfc`` ( *x* ),complementary error function *y* = ``exp`` ( *x* ),exponential *y* = ``expm1`` ( *x* ),exponential of x minus one *y* = ``fabs`` ( *x* ),absolute value *y* = ``log`` ( *x* ),natural logarithm *y* = ``log1p`` ( *x* ),logarithm of one plus x *y* = ``sin`` ( *x* ),sine *y* = ``sinh`` ( *x* ),hyperbolic sine *y* = ``sqrt`` ( *x* ),square root *y* = ``tan`` ( *x* ),tangent where the arguments and return value have the prototypes | |tab| ``const`` *Base* & *x* | |tab| *Base* *y* For example, :ref:`base_alloc` , CPPAD_STANDARD_MATH_UNARY ************************* The macro invocation, within the CppAD namespace, ``CPPAD_STANDARD_MATH_UNARY`` ( *Base* , *Fun* ) defines the syntax *y* = ``CppAD::`` *Fun* ( *x* ) This macro uses the functions ``std::`` *Fun* which must be defined and have the same prototype as ``CppAD::`` *Fun* . For example, :ref:`float` . sign **** The type *Base* must support the syntax *y* = ``CppAD::sign`` ( *x* ) which computes .. math:: y = \left\{ \begin{array}{ll} +1 & {\rm if} \; x > 0 \\ 0 & {\rm if} \; x = 0 \\ -1 & {\rm if} \; x < 0 \end{array} \right. where *x* and *y* have the same prototype as above. For example, see :ref:`base_alloc` . Note that, if ordered comparisons are not defined for the type *Base* , the ``code sign`` function should generate an assert if it is used; see :ref:`complex invalid unary math` . pow *** The type *Base* must support the syntax *z* = ``CppAD::pow`` ( *x* , *y* ) which computes :math:`z = x^y`. The arguments *x* and *y* have prototypes | |tab| ``const`` *Base* & *x* | |tab| ``const`` *Base* & *y* and the return value *z* has prototype *Base* *z* For example, see :ref:`base_alloc` . isnan ***** If *Base* defines the ``isnan`` function, you may also have to provide a definition in the CppAD namespace (to avoid a function ambiguity). For example, see :ref:`base_complex` . {xrst_end base_std_math} ------------------------------------------------------------------------------- */ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file base_std_math.hpp Definitions that aid meeting Base type requirements for standard math functions. */ /*! \def CPPAD_STANDARD_MATH_UNARY(Type, Fun) This macro defines the function \verbatim y = CppAD:Fun(x) \endverbatim where the argument x and return value y have type Type using the corresponding function std::Fun. */ # define CPPAD_STANDARD_MATH_UNARY(Type, Fun) \ inline Type Fun(const Type& x) \ { return std::Fun(x); } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/base_to_string.hpp ================================================ # ifndef CPPAD_CORE_BASE_TO_STRING_HPP # define CPPAD_CORE_BASE_TO_STRING_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin base_to_string} {xrst_spell struct } Extending to_string To Another Floating Point Type ################################################## Base Requirement **************** If the function :ref:`to_string-name` is used by an :ref:`glossary@AD Type Above Base` , A specialization for the template structure ``CppAD::to_string_struct`` must be defined. CPPAD_TO_STRING *************** For most *Base* types, the following can be used to define the specialization: | |tab| ``namespace CppAD`` { | |tab| |tab| ``CPPAD_TO_STRING`` ( *Base* ) | |tab| } Note that the ``CPPAD_TO_STRING`` macro assumes that the :ref:`base_limits-name` and :ref:`base_std_math-name` have already been defined for this type. This macro is defined as follows: {xrst_spell_off} {xrst_code cpp} */ # define CPPAD_TO_STRING(Base) \ template <> struct to_string_struct\ { std::string operator()(const Base& value) \ { std::stringstream os;\ int n_digits = 1 + CppAD::numeric_limits::digits10; \ os << std::setprecision(n_digits);\ os << value;\ return os.str();\ }\ }; /* {xrst_code} {xrst_spell_on} {xrst_end base_to_string} ------------------------------------------------------------------------------ */ // make sure to_string has been included # include # endif ================================================ FILE: include/cppad/core/bender_quad.hpp ================================================ # ifndef CPPAD_CORE_BENDER_QUAD_HPP # define CPPAD_CORE_BENDER_QUAD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin BenderQuad app} {xrst_spell avector gx gxx } Computing Jacobian and Hessian of Bender's Reduced Objective ############################################################ Syntax ****** | # ``include `` | *BenderQuad* ( ``x`` , ``y`` , ``fun`` , ``g`` , ``gx`` , ``gxx`` ) See Also ******** :ref:`opt_val_hes-name` Problem ******* The type :ref:`BenderQuad@ADvector` cannot be determined form the arguments above (currently the type *ADvector* must be ``CPPAD_TESTVECTOR`` ( *Base* ) .) This will be corrected in the future by requiring *Fun* to define *Fun* :: ``vector_type`` which will specify the type *ADvector* . Purpose ******* We are given the optimization problem .. math:: :nowrap: \begin{eqnarray} {\rm minimize} & F(x, y) & {\rm w.r.t.} \; (x, y) \in \B{R}^n \times \B{R}^m \end{eqnarray} that is convex with respect to :math:`y`. In addition, we are given a set of equations :math:`H(x, y)` such that .. math:: H[ x , Y(x) ] = 0 \;\; \Rightarrow \;\; F_y [ x , Y(x) ] = 0 (In fact, it is often the case that :math:`H(x, y) = F_y (x, y)`.) Furthermore, it is easy to calculate a Newton step for these equations; i.e., .. math:: dy = - [ \partial_y H(x, y)]^{-1} H(x, y) The purpose of this routine is to compute the value, Jacobian, and Hessian of the reduced objective function .. math:: G(x) = F[ x , Y(x) ] Note that if only the value and Jacobian are needed, they can be computed more quickly using the relations .. math:: G^{(1)} (x) = \partial_x F [x, Y(x) ] x * The ``BenderQuad`` argument *x* has prototype ``const`` *BAvector* & *x* (see :ref:`BenderQuad@BAvector` below) and its size must be equal to *n* . It specifies the point at which we evaluating the reduced objective function and its derivatives. y * The ``BenderQuad`` argument *y* has prototype ``const`` *BAvector* & *y* and its size must be equal to *m* . It must be equal to :math:`Y(x)`; i.e., it must solve the problem in :math:`y` for this given value of :math:`x` .. math:: :nowrap: \begin{eqnarray} {\rm minimize} & F(x, y) & {\rm w.r.t.} \; y \in \B{R}^m \end{eqnarray} fun *** The ``BenderQuad`` object *fun* must support the member functions listed below. The ``AD`` < *Base* > arguments will be variables for a tape created by a call to :ref:`Independent-name` from ``BenderQuad`` (hence they can not be combined with variables corresponding to a different tape). fun.f ===== The ``BenderQuad`` argument *fun* supports the syntax *f* = *fun* . ``f`` ( *x* , *y* ) The *fun* . ``f`` argument *x* has prototype ``const`` *ADvector* & *x* (see :ref:`BenderQuad@ADvector` below) and its size must be equal to *n* . The *fun* . ``f`` argument *y* has prototype ``const`` *ADvector* & *y* and its size must be equal to *m* . The *fun* . ``f`` result *f* has prototype *ADvector* *f* and its size must be equal to one. The value of *f* is .. math:: f = F(x, y) fun.h ===== The ``BenderQuad`` argument *fun* supports the syntax *h* = *fun* . ``h`` ( *x* , *y* ) The *fun* . ``h`` argument *x* has prototype ``const`` *ADvector* & *x* and its size must be equal to *n* . The *fun* . ``h`` argument *y* has prototype ``const`` *BAvector* & *y* and its size must be equal to *m* . The *fun* . ``h`` result *h* has prototype *ADvector* *h* and its size must be equal to *m* . The value of *h* is .. math:: h = H(x, y) fun.dy ====== The ``BenderQuad`` argument *fun* supports the syntax | |tab| *dy* = *fun* . ``dy`` ( *x* , *y* , *h* ) | | *x* The *fun* . ``dy`` argument *x* has prototype ``const`` *BAvector* & *x* and its size must be equal to *n* . Its value will be exactly equal to the ``BenderQuad`` argument *x* and values depending on it can be stored as private objects in *f* and need not be recalculated. *y* The *fun* . ``dy`` argument *y* has prototype ``const`` *BAvector* & *y* and its size must be equal to *m* . Its value will be exactly equal to the ``BenderQuad`` argument *y* and values depending on it can be stored as private objects in *f* and need not be recalculated. *h* The *fun* . ``dy`` argument *h* has prototype ``const`` *ADvector* & *h* and its size must be equal to *m* . *dy* The *fun* . ``dy`` result *dy* has prototype *ADvector* *dy* and its size must be equal to *m* . The return value *dy* is given by .. math:: dy = - [ \partial_y H (x , y) ]^{-1} h Note that if *h* is equal to :math:`H(x, y)`, :math:`dy` is the Newton step for finding a zero of :math:`H(x, y)` with respect to :math:`y`; i.e., :math:`y + dy` is an approximate solution for the equation :math:`H (x, y + dy) = 0`. g * The argument *g* has prototype *BAvector* & *g* and has size one. The input value of its element does not matter. On output, it contains the value of :math:`G (x)`; i.e., .. math:: g[0] = G (x) gx ** The argument *gx* has prototype *BAvector* & *gx* and has size :math:`n`. The input values of its elements do not matter. On output, it contains the Jacobian of :math:`G (x)`; i.e., for :math:`j = 0 , \ldots , n-1`, .. math:: gx[ j ] = G^{(1)} (x)_j gxx *** The argument *gx* has prototype *BAvector* & *gxx* and has size :math:`n \times n`. The input values of its elements do not matter. On output, it contains the Hessian of :math:`G (x)`; i.e., for :math:`i = 0 , \ldots , n-1`, and :math:`j = 0 , \ldots , n-1`, .. math:: gxx[ i * n + j ] = G^{(2)} (x)_{i,j} BAvector ******** The type *BAvector* must be a :ref:`SimpleVector-name` class. We use *Base* to refer to the type of the elements of *BAvector* ; i.e., *BAvector* :: ``value_type`` ADvector ******** The type *ADvector* must be a :ref:`SimpleVector-name` class with elements of type ``AD`` < *Base* > ; i.e., *ADvector* :: ``value_type`` must be the same type as ``AD`` < *BAvector* :: ``value_type >`` . Example ******* {xrst_toc_hidden example/general/bender_quad.cpp } The file :ref:`bender_quad.cpp-name` contains an example and test of this operation. {xrst_end BenderQuad} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN CppAD namespace template void BenderQuad( const BAvector &x , const BAvector &y , Fun fun , BAvector &g , BAvector &gx , BAvector &gxx ) { // determine the base type typedef typename BAvector::value_type Base; // check that BAvector is a SimpleVector class CheckSimpleVector(); // declare the ADvector type typedef CPPAD_TESTVECTOR(AD) ADvector; // size of the x and y spaces size_t n = size_t(x.size()); size_t m = size_t(y.size()); // check the size of gx and gxx CPPAD_ASSERT_KNOWN( g.size() == 1, "BenderQuad: size of the vector g is not equal to 1" ); CPPAD_ASSERT_KNOWN( size_t(gx.size()) == n, "BenderQuad: size of the vector gx is not equal to n" ); CPPAD_ASSERT_KNOWN( size_t(gxx.size()) == n * n, "BenderQuad: size of the vector gxx is not equal to n * n" ); // some temporary indices size_t i, j; // variable versions x ADvector vx(n); for(j = 0; j < n; j++) vx[j] = x[j]; // declare the independent variables Independent(vx); // evaluate h = H(x, y) ADvector h(m); h = fun.h(vx, y); // evaluate dy (x) = Newton step as a function of x through h only ADvector dy(m); dy = fun.dy(x, y, h); // variable version of y ADvector vy(m); for(j = 0; j < m; j++) vy[j] = y[j] + dy[j]; // evaluate G~ (x) = F [ x , y + dy(x) ] ADvector gtilde(1); gtilde = fun.f(vx, vy); // AD function object that corresponds to G~ (x) // We will make heavy use of this tape, so optimize it ADFun Gtilde; Gtilde.Dependent(vx, gtilde); Gtilde.optimize(); // value of G(x) g = Gtilde.Forward(0, x); // initial forward direction vector as zero BAvector dx(n); for(j = 0; j < n; j++) dx[j] = Base(0.0); // weight, first and second order derivative values BAvector dg(1), w(1), ddw(2 * n); w[0] = 1.; // Jacobian and Hessian of G(x) is equal Jacobian and Hessian of Gtilde for(j = 0; j < n; j++) { // compute partials in x[j] direction dx[j] = Base(1.0); dg = Gtilde.Forward(1, dx); gx[j] = dg[0]; // restore the dx vector to zero dx[j] = Base(0.0); // compute second partials w.r.t x[j] and x[l] for l = 1, n ddw = Gtilde.Reverse(2, w); for(i = 0; i < n; i++) gxx[ i * n + j ] = ddw[ i * 2 + 1 ]; } return; } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/bool_fun.hpp ================================================ # ifndef CPPAD_CORE_BOOL_FUN_HPP # define CPPAD_CORE_BOOL_FUN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin bool_fun} AD Boolean Functions #################### Syntax ****** | ``CPPAD_BOOL_UNARY`` ( *Base* , *unary_name* ) | *b* = *unary_name* ( *u* ) | *b* = *unary_name* ( *x* ) | ``CPPAD_BOOL_BINARY`` ( *Base* , *binary_name* ) | *b* = *binary_name* ( *u* , *v* ) | *b* = *binary_name* ( *x* , *y* ) Purpose ******* Create a ``bool`` valued function that has ``AD`` < *Base* > arguments. unary_name ********** This is the name of the ``bool`` valued function with one argument (as it is used in the source code). The user must provide a version of *unary_name* where the argument has type *Base* . CppAD uses this to create a version of *unary_name* where the argument has type ``AD`` < *Base* > . u * The argument *u* has prototype ``const`` *Base* & *u* It is the value at which the user provided version of *unary_name* is to be evaluated. It is also used for the first argument to the user provided version of *binary_name* . x * The argument *x* has prototype ``const AD`` < *Base* > & *x* It is the value at which the CppAD provided version of *unary_name* is to be evaluated. It is also used for the first argument to the CppAD provided version of *binary_name* . b * The result *b* has prototype ``bool`` *b* Create Unary ************ The preprocessor macro invocation ``CPPAD_BOOL_UNARY`` ( *Base* , *unary_name* ) defines the version of *unary_name* with a ``AD`` < *Base* > argument. This can with in a namespace (not the ``CppAD`` namespace) but must be outside of any routine. binary_name *********** This is the name of the ``bool`` valued function with two arguments (as it is used in the source code). The user must provide a version of *binary_name* where the arguments have type *Base* . CppAD uses this to create a version of *binary_name* where the arguments have type ``AD`` < *Base* > . v * The argument *v* has prototype ``const`` *Base* & *v* It is the second argument to the user provided version of *binary_name* . y * The argument *x* has prototype ``const AD`` < *Base* > & *y* It is the second argument to the CppAD provided version of *binary_name* . Create Binary ************* The preprocessor macro invocation ``CPPAD_BOOL_BINARY`` ( *Base* , *binary_name* ) defines the version of *binary_name* with ``AD`` < *Base* > arguments. This can with in a namespace (not the ``CppAD`` namespace) but must be outside of any routine. Operation Sequence ****************** The result of this operation is not an :ref:`glossary@AD of Base` object. Thus it will not be recorded as part of an AD of *Base* :ref:`operation sequence` . Example ******* {xrst_toc_hidden example/general/bool_fun.cpp } The file :ref:`bool_fun.cpp-name` contains an example and test of these operations. Deprecated 2007-07-31 ********************* The preprocessor symbols ``CppADCreateUnaryBool`` and ``CppADCreateBinaryBool`` are defined to be the same as ``CPPAD_BOOL_UNARY`` and ``CPPAD_BOOL_BINARY`` respectively (but their use is deprecated). {xrst_end bool_fun} */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file bool_fun.hpp Routines and macros that implement functions from AD to bool. */ /*! Macro that defines a unary function bool F(AD x) using bool F(Base x). \param Base base for the AD type of arguments to this unary bool valued function. \param unary_name name of this unary function; i.e., F. */ # define CPPAD_BOOL_UNARY(Base, unary_name) \ inline bool unary_name (const CppAD::AD &x) \ { \ return CppAD::AD::UnaryBool(unary_name, x); \ } /*! Deprecated name for CPPAD_BOOL_UNARY */ # define CppADCreateUnaryBool CPPAD_BOOL_UNARY /*! Link a function name, and AD value pair to function call with base argument and bool return value. \param FunName is the name of the function that we are linking. \param x is the argument where we are evaluating the function. */ template bool AD::UnaryBool( bool FunName(const Base &x), const AD &x ) { return FunName(x.value_); } /*! Macro that defines a binary function bool F(AD x, AD y) using bool F(Base x, Base y). \param Base base for the AD type of arguments to this binary bool valued function. \param binary_name name of this binary function; i.e., F. */ # define CPPAD_BOOL_BINARY(Base, binary_name) \ inline bool binary_name ( \ const CppAD::AD &x, const CppAD::AD &y) \ { \ return CppAD::AD::BinaryBool(binary_name, x, y); \ } /*! Deprecated name for CPPAD_BOOL_BINARY */ # define CppADCreateBinaryBool CPPAD_BOOL_BINARY /*! Link a function name, and two AD values to function call with base arguments and bool return value. \param FunName is the name of the function that we are linking. \param x is the first argument where we are evaluating the function at. \param y is the second argument where we are evaluating the function at. */ template bool AD::BinaryBool( bool FunName(const Base &x, const Base &y), const AD &x, const AD &y ) { return FunName(x.value_, y.value_); } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/bool_valued.hpp ================================================ # ifndef CPPAD_CORE_BOOL_VALUED_HPP # define CPPAD_CORE_BOOL_VALUED_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin bool_valued} {xrst_spell dyn ext } Bool Valued Operations and Functions with AD Arguments ###################################################### {xrst_toc_hidden include/cppad/core/compare.hpp include/cppad/core/near_equal_ext.hpp include/cppad/core/bool_fun.hpp include/cppad/core/con_dyn_var.hpp include/cppad/core/equal_op_seq.hpp } .. csv-table:: :widths: auto Compare,:ref:`Compare-title` near_equal_ext,:ref:`near_equal_ext-title` bool_fun,:ref:`bool_fun-title` con_dyn_var,:ref:`con_dyn_var-title` EqualOpSeq,:ref:`EqualOpSeq-title` {xrst_end bool_valued} */ # include # include # include # include # include # endif ================================================ FILE: include/cppad/core/capacity_order.hpp ================================================ # ifndef CPPAD_CORE_CAPACITY_ORDER_HPP # define CPPAD_CORE_CAPACITY_ORDER_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin capacity_order} {xrst_spell pre xq yq } Controlling Taylor Coefficients Memory Allocation ################################################# Syntax ****** | *f* . ``capacity_order`` ( *c* ) See Also ======== :ref:`fun_property-name` Purpose ******* The Taylor coefficients calculated by :ref:`Forward-name` mode calculations are retained in an :ref:`ADFun-name` object for subsequent use during :ref:`Reverse-name` mode and higher order Forward mode calculations. For example, a call to :ref:`Forward` with the syntax *yq* = *f* . ``Forward`` ( *q* , *xq* ) where *q* > 0 and *xq* . ``size`` () == *f* . ``Domain`` () , uses the lower order Taylor coefficients and computes the *q*-th order Taylor coefficients for all the variables in the operation sequence corresponding to *f* . The ``capacity_order`` operation allows you to control that amount of memory that is retained by an AD function object (to hold ``Forward`` results for subsequent calculations). f * The object *f* has prototype ``ADFun`` < *Base* > *f* c * The argument *c* has prototype ``size_t`` *c* It specifies the number of Taylor coefficient orders that are allocated in the AD operation sequence corresponding to *f* . Pre-Allocating Memory ===================== If you plan to make calls to ``Forward`` with the maximum value of *q* equal to *Q* , it should be faster to pre-allocate memory for these calls using *f* . ``capacity_order`` ( *c* ) with *c* equal to :math:`Q + 1`. If you do no do this, ``Forward`` will automatically allocate memory and will copy the results to a larger buffer, when necessary. Note that each call to :ref:`Dependent-name` frees the old memory connected to the function object and sets the corresponding taylor capacity to zero. Freeing Memory ============== If you no longer need the Taylor coefficients of order *q* and higher (that are stored in *f* ), you can reduce the memory allocated to *f* using *f* . ``capacity_order`` ( *c* ) with *c* equal to *q* . Note that, if :ref:`ta_hold_memory-name` is true, this memory is not actually returned to the system, but rather held for future use by the same thread. Original State ************** If *f* is :ref:`constructed` with the syntax ``ADFun`` < *Base* > *f* ( *x* , *y* ) , there is an implicit call to :ref:`forward_zero-name` with *xq* equal to the value of the :ref:`independent variables` when the AD operation sequence was recorded. This corresponds to *c* == 1 . {xrst_toc_hidden example/general/capacity_order.cpp } Example ******* The file :ref:`capacity_order.cpp-name` contains an example and test of these operations. {xrst_end capacity_order} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file capacity_order.hpp Control of number of orders allocated. \} */ /*! Control of number of orders and directions allocated. \tparam Base The type used during the forward mode computations; i.e., the corresponding recording of operations used the type AD. \param c is the number of orders to allocate memory for. If c == 0 then r must also be zero. In this case num_order_taylor_, cap_order_taylor_, and num_direction_taylor_ are all set to zero. In addition, taylor_.clear() is called. \param r is the number of directions to allocate memory for. If c == 1 then r must also be one. In all cases, it must hold that r == num_direction_taylor_ || num_order_taylor <= 1 Upon return, num_direction_taylor_ is equal to r. \par num_order_taylor_ The output value of num_order_taylor_ is the mininumum of its input value and c. This minimum is the number of orders that are copied to the new taylor coefficient buffer. \par num_direction_taylor_ The output value of num_direction_taylor_ is equal to r. */ template void ADFun::capacity_order(size_t c, size_t r) { // temporary indices size_t i, k, ell; if( (c == cap_order_taylor_) && (r == num_direction_taylor_) ) return; if( c == 0 ) { CPPAD_ASSERT_UNKNOWN( r == 0 ); taylor_.clear(); num_order_taylor_ = 0; cap_order_taylor_ = 0; num_direction_taylor_ = r; return; } CPPAD_ASSERT_UNKNOWN(r==num_direction_taylor_ || num_order_taylor_<=1); // Allocate new taylor with requested number of orders and directions size_t new_len = ( (c-1)*r + 1 ) * num_var_tape_; local::pod_vector_maybe new_taylor(new_len); // number of orders to copy size_t p = std::min(num_order_taylor_, c); if( p > 0 ) { // old order capacity size_t C = cap_order_taylor_; // old number of directions size_t R = num_direction_taylor_; // copy the old data into the new matrix CPPAD_ASSERT_UNKNOWN( p == 1 || r == R ); for(i = 0; i < num_var_tape_; i++) { // copy zero order size_t old_index = ((C-1) * R + 1) * i + 0; size_t new_index = ((c-1) * r + 1) * i + 0; new_taylor[ new_index ] = taylor_[ old_index ]; // copy higher orders for(k = 1; k < p; k++) { for(ell = 0; ell < R; ell++) { old_index = ((C-1) * R + 1) * i + (k-1) * R + ell + 1; new_index = ((c-1) * r + 1) * i + (k-1) * r + ell + 1; new_taylor[ new_index ] = taylor_[ old_index ]; } } } } // replace taylor_ by new_taylor taylor_.swap(new_taylor); cap_order_taylor_ = c; num_order_taylor_ = p; num_direction_taylor_ = r; // note that the destructor for new_taylor will free the old taylor memory return; } /*! User API control of number of orders allocated. \tparam Base The type used during the forward mode computations; i.e., the corresponding recording of operations used the type AD. \param c is the number of orders to allocate memory for. If c == 0, num_order_taylor_, cap_order_taylor_, and num_direction_taylor_ are all set to zero. In addition, taylor_.clear() is called. \par num_order_taylor_ The output value of num_order_taylor_ is the mininumum of its input value and c. This minimum is the number of orders that are copied to the new taylor coefficient buffer. \par num_direction_taylor_ If is zero (one), num_direction_taylor_ is set to zero (one). Otherwise, if num_direction_taylor_ is zero, it is set to one. Otherwise, num_direction_taylor_ is not modified. */ template void ADFun::capacity_order(size_t c) { size_t r; if( (c == 0) || (c == 1) ) { r = c; capacity_order(c, r); return; } r = num_direction_taylor_; if( r == 0 ) r = 1; capacity_order(c, r); return; } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/check_for_nan.hpp ================================================ # ifndef CPPAD_CORE_CHECK_FOR_NAN_HPP # define CPPAD_CORE_CHECK_FOR_NAN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin check_for_nan} {xrst_spell newline } Check an ADFun Object For Nan Results ##################################### Syntax ****** | *f* . ``check_for_nan`` ( *b* ) | *b* = *f* . ``check_for_nan`` () | ``get_check_for_nan`` ( *vec* , *file* ) Debugging ********* If ``NDEBUG`` is not defined, and the result of a :ref:`forward` or :ref:`reverse` calculation contains a :ref:`nan-name` , CppAD can halt with an error message. f * For the syntax where *b* is an argument, *f* has prototype ``ADFun`` < *Base* > *f* (see ``ADFun`` < *Base* > :ref:`constructor` ). For the syntax where *b* is the result, *f* has prototype ``const ADFun`` < *Base* > *f* b * This argument or result has prototype ``bool`` *b* If *b* is true (false), future calls to *f* . ``Forward`` will (will not) check for ``nan`` . Default ******* The value for this setting after construction of *f* is true. The value of this setting is not affected by calling :ref:`Dependent-name` for this function object. Error Message ************* If this error is detected during zero order forward mode, the values of the independent variables that resulted in the ``nan`` are written to a temporary binary file. This is so that you can run the original source code with those values to see what is causing the ``nan`` . vector_size =========== The error message with contain the text ``vector_size`` = *vector_size* followed the newline character ``'\n'`` . The value of *vector_size* is the number of elements in the independent vector. file_name ========= The error message with contain the text ``file_name`` = *file_name* followed the newline character ``'\n'`` . The value of *file_name* is the name of the temporary file that contains the dependent variable values. index ===== The error message will contain the text ``index`` = *index* followed by the newline character ``'\n'`` . The value of *index* is the lowest dependent variable index that has the value ``nan`` . get_check_for_nan ***************** This routine can be used to get the independent variable values that result in a ``nan`` . vec === This argument has prototype ``CppAD::vector<`` *Base* >& *vec* It size must be equal to the corresponding value of :ref:`check_for_nan@Error Message@vector_size` in the corresponding error message. The input value of its elements does not matter. Upon return, it will contain the values for the independent variables, in the corresponding call to :ref:`Independent-name` , that resulted in the ``nan`` . (Note that the call to ``Independent`` uses an vector with elements of type ``AD`` < *Base* > and *vec* has elements of type *Base* .) file ==== This argument has prototype ``const std::string&`` *file* It must be the value of :ref:`check_for_nan@Error Message@file_name` in the corresponding error message. Example ******* {xrst_toc_hidden example/general/check_for_nan.cpp } The file :ref:`check_for_nan.cpp-name` contains an example and test of these operations. {xrst_end check_for_nan} */ # include # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! Set check_for_nan \param value new value for this flag. */ template void ADFun::check_for_nan(bool value) { check_for_nan_ = value; } /*! Get check_for_nan \return current value of check_for_nan_. */ template bool ADFun::check_for_nan(void) const { return check_for_nan_; } /*! Stores a vector in a file when nans occur. \param vec [in] is the vector that is stored. \param [out] file_name is the file where the vector is stored */ template void put_check_for_nan(const CppAD::vector& vec, std::string& file_name) { // char_size std::streamsize char_size = std::streamsize( sizeof(Base) * vec.size() ); // // char_ptr const char* char_ptr = reinterpret_cast( vec.data() ); // // file_name file_name = local::temp_file(); // // write data to file_name std::fstream file_out(file_name.c_str(), std::ios::out|std::ios::binary ); file_out.write(char_ptr, char_size); file_out.close(); // return; } /*! Gets a vector that was stored by put_check_for_nan. \param vec [out] is the vector that is stored. \param file_name [in] is the file where the vector is stored */ template void get_check_for_nan(CppAD::vector& vec, const std::string& file_name) { // std::streamsize char_size = std::streamsize( sizeof(Base) * vec.size() ); char* char_ptr = reinterpret_cast( vec.data() ); // std::fstream file_in(file_name.c_str(), std::ios::in|std::ios::binary ); file_in.read(char_ptr, char_size); // return; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/chkpoint_one/chkpoint_one.hpp ================================================ # ifndef CPPAD_CORE_CHKPOINT_ONE_CHKPOINT_ONE_HPP # define CPPAD_CORE_CHKPOINT_ONE_CHKPOINT_ONE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file chkpoint_one.hpp First generation checkpoint functions. */ /* {xrst_begin chkpoint_one app} {xrst_spell algo sv } Checkpoint Functions: First Generation ###################################### Deprecated 2019-01-14 ********************* Using the ``checkpoint`` class has been deprecated. Use :ref:`chkpoint_two-name` instead. Syntax ****** | ``checkpoint`` < *Base* > *atom_fun* ( | |tab| *name* , *algo* , *ax* , *ay* , *sparsity* , *optimize* | ) | *sv* = *atom_fun* . ``size_var`` () | *atom_fun* . ``option`` ( *option_value* ) | *algo* ( *ax* , *ay* ) | *atom_fun* ( *ax* , *ay* ) | *checkpoint* < ``Base`` >:: *clear* () See Also ******** :ref:`atomic_two-name` , :ref:`rev_checkpoint.cpp-name` Purpose ******* Reduce Memory ============= You can reduce the size of the tape and memory required for AD by checkpointing functions of the form :math:`y = f(x)` where :math:`f : \B{R}^n \rightarrow \B{R}^m`. Faster Recording ================ It may also reduce the time to make a recording the same function for different values of the independent variable. Note that the operation sequence for a recording that uses :math:`f(x)` may depend on its independent variables. Repeating Forward ================= Normally, CppAD store :ref:`forward-name` mode results until they freed using :ref:`capacity_order-name` or the corresponding :ref:`ADFun-name` object is deleted. This is not true for checkpoint functions because a checkpoint function may be used repeatedly with different arguments in the same tape. Thus, forward mode results are recomputed each time a checkpoint function is used during a forward or reverse mode sweep. Restriction =========== The :ref:`operation sequence` representing :math:`f(x)` cannot depend on the value of :math:`x`. The approach in the :ref:`rev_checkpoint.cpp-name` example case be applied when the operation sequence depends on :math:`x`. Multiple Level AD ================= If *Base* is an AD type, it is possible to record *Base* operations. Note that *atom_fun* will treat *algo* as an atomic operation while recording ``AD`` < ``Base`` > operations, but not while recording *Base* operations. See the ``chkpoint_one_mul_level.cpp`` example. Method ****** The ``checkpoint`` class is derived from ``atomic_base`` and makes this easy. It implements all the ``atomic_base`` :ref:`atomic_two@Virtual Functions` and hence its source code ``cppad/core/chkpoint_one/chkpoint_one.hpp`` provides an example implementation of :ref:`atomic_two-name` . The difference is that ``chkpoint_one.hpp`` uses AD instead of user provided derivatives. constructor *********** The syntax for the checkpoint constructor is ``checkpoint`` < *Base* > *atom_fun* ( *name* , *algo* , *ax* , *ay* ) #. This constructor cannot be called in :ref:`parallel` mode. #. You cannot currently be recording ``AD`` < *Base* > operations when the constructor is called. #. This object *atom_fun* must not be destructed for as long as any ``ADFun`` < *Base* > object uses its atomic operation. #. This class is implemented as a derived class of :ref:`atomic` and hence some of its error message will refer to ``atomic_base`` . Base **** The type *Base* specifies the base type for AD operations. ADVector ******** The type *ADVector* must be a :ref:`simple vector class` with elements of type ``AD`` < *Base* > . name **** This *checkpoint* constructor argument has prototype ``const char`` * *name* It is the name used for error reporting. The suggested value for *name* is *atom_fun* ; i.e., the same name as used for the object being constructed. ax ** This argument has prototype ``const`` *ADVector* & *ax* and size must be equal to *n* . It specifies vector :math:`x \in \B{R}^n` at which an ``AD`` < *Base* > version of :math:`y = f(x)` is to be evaluated. ay ** This argument has prototype *ADVector* & *ay* Its input size must be equal to *m* and does not change. The input values of its elements do not matter. Upon return, it is an ``AD`` < *Base* > version of :math:`y = f(x)`. sparsity ******** This argument has prototype ``atomic_base`` < *Base* >:: ``option_enum`` *sparsity* It specifies :ref:`atomic_two_ctor@atomic_base@sparsity` in the ``atomic_base`` constructor and must be either ``atomic_base`` < *Base* >:: ``pack_sparsity_enum`` , ``atomic_base`` < *Base* >:: ``bool_sparsity_enum`` , or ``atomic_base`` < *Base* >:: ``set_sparsity_enum`` . This argument is optional and its default value is unspecified. optimize ******** This argument has prototype ``bool`` *optimize* It specifies if the recording corresponding to the atomic function should be :ref:`optimized` . One expects to use a checkpoint function many times, so it should be worth the time to optimize its operation sequence. For debugging purposes, it may be useful to use the original operation sequence (before optimization) because it corresponds more closely to *algo* . This argument is optional and its default value is true. size_var ******** This ``size_var`` member function return value has prototype ``size_t`` *sv* It is the :ref:`fun_property@size_var` for the ``ADFun`` < *Base* > object is used to store the operation sequence corresponding to *algo* . option ****** The ``option`` syntax can be used to set the type of sparsity pattern used by *atom_fun* . This is an ``atomic_base`` < *Base* > function and its documentation can be found at :ref:`atomic_two_option-name` . algo **** The type of *algo* is arbitrary, except for the fact that the syntax *algo* ( *ax* , *ay* ) must evaluate the function :math:`y = f(x)` using ``AD`` < *Base* > operations. In addition, we assume that the :ref:`operation sequence` does not depend on the value of *ax* . atom_fun ******** Given *ax* it computes the corresponding value of *ay* using the operation sequence corresponding to *algo* . If ``AD`` < *Base* > operations are being recorded, it enters the computation as single operation in the recording see :ref:`Independent@Start Recording` . (Currently each use of *atom_fun* actually corresponds to *m* + *n* +2 operations and creates *m* new variables, but this is not part of the CppAD specifications and my change.) Memory ****** Restriction =========== The ``clear`` routine cannot be called while in :ref:`parallel` execution mode. Parallel Mode ************* The CppAD checkpoint function delays the caching of certain calculations until they are needed. In :ref:`parallel model` , this may result in :ref:`thread_alloc::inuse(thread)` being non-zero even though the specified thread is no longer active. This memory will be freed when the checkpoint object is deleted. clear ===== The ``atomic_base`` class holds onto static work space that is not connected to a particular object (in order to increase speed by avoiding system memory allocation calls). This call makes to work space :ref:`available` to for other uses by the same thread. This should be called when you are done using the atomic functions for a specific value of *Base* . {xrst_end chkpoint_one} */ template class checkpoint : public atomic_base { // --------------------------------------------------------------------------- private: /// same as option_enum in base class typedef typename atomic_base::option_enum option_enum; // // ------------------------------------------------------------------------ // member_ // ------------------------------------------------------------------------ // same checkpoint object can be used by multiple threads struct member_struct { // /// AD function corresponding to this checkpoint object ADFun f_; ADFun< AD, Base > af_; // /// sparsity for entire Jacobian f(x)^{(1)} /// does not change so can cache it local::sparse::list_setvec jac_sparse_set_; vectorBool jac_sparse_bool_; // /// sparsity for sum_i f_i(x)^{(2)} does not change so can cache it local::sparse::list_setvec hes_sparse_set_; vectorBool hes_sparse_bool_; }; /// This version of work is const except during constructor member_struct const_member_; /// use pointers and allocate memory to avoid false sharing member_struct* member_[CPPAD_MAX_NUM_THREADS]; // /// allocate member_ for this thread void allocate_member(size_t thread) { if( member_[thread] == nullptr ) { member_[thread] = new member_struct; // The function is recorded in sequential mode and placed in // const_member_.f_, other threads have copy. member_[thread]->f_ = const_member_.f_; } return; } // /// free member_ for this thread void free_member(size_t thread) { if( member_[thread] != nullptr ) { delete member_[thread]; member_[thread] = nullptr; } return; } // ------------------------------------------------------------------------ option_enum sparsity(void) { return static_cast< atomic_base* >(this)->sparsity(); } // ------------------------------------------------------------------------ /// set jac_sparse_set_ void set_jac_sparse_set(void); /// set jac_sparse_bool_ void set_jac_sparse_bool(void); // ------------------------------------------------------------------------ /// set hes_sparse_set_ void set_hes_sparse_set(void); /// set hes_sparse_bool_ void set_hes_sparse_bool(void); // ------------------------------------------------------------------------ /*! Link from user_atomic to forward sparse Jacobian pack and bool \copydetails atomic_base::for_sparse_jac */ template bool for_sparse_jac_sparsity_type( size_t q , const sparsity_type& r , sparsity_type& s , const vector& x ); // ------------------------------------------------------------------------ /*! Link from user_atomic to reverse sparse Jacobian pack and bool \copydetails atomic_base::rev_sparse_jac */ template bool rev_sparse_jac_sparsity_type( size_t q , const sparsity_type& rt , sparsity_type& st , const vector& x ); /*! Link from user_atomic to reverse sparse Hessian bools \copydetails atomic_base::rev_sparse_hes */ template bool rev_sparse_hes_sparsity_type( const vector& vx , const vector& s , vector& t , size_t q , const sparsity_type& r , const sparsity_type& u , sparsity_type& v , const vector& x ); public: // ------------------------------------------------------------------------ /*! Constructor of a checkpoint object \param name [in] is the user's name for the AD version of this atomic operation. \param algo [in/out] user routine that compute AD function values (not const because state may change during evaluation). \param ax [in] argument value where algo operation sequence is taped. \param ay [out] function value at specified argument value. \param sparsity [in] what type of sparsity patterns are computed by this function, pack_sparsity_enum bool_sparsity_enum, or set_sparsity_enum. The default value is unspecified. \param optimize [in] should the operation sequence corresponding to the algo be optimized. The default value is true, but it is sometimes useful to use false for debugging purposes. */ template checkpoint( const char* name , Algo& algo , const ADVector& ax , ADVector& ay , option_enum sparsity = atomic_base::pack_sparsity_enum , bool optimize = true ); /// destructor ~checkpoint(void) { # ifndef NDEBUG if( thread_alloc::in_parallel() ) { std::string msg = atomic_base::atomic_name(); msg += ": checkpoint destructor called in parallel mode."; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # endif for(size_t thread = 0; thread < CPPAD_MAX_NUM_THREADS; ++thread) free_member(thread); } // ------------------------------------------------------------------------ /*! Implement the user call to atom_fun.size_var(). */ size_t size_var(void) { // make sure member_ is allocated for this thread size_t thread = thread_alloc::thread_num(); allocate_member(thread); // return member_[thread]->f_.size_var(); } // ------------------------------------------------------------------------ /*! Implement the user call to atom_fun(ax, ay). \tparam ADVector A simple vector class with elements of type AD. \param id optional parameter which must be zero if present. \param ax is the argument vector for this call, ax.size() determines the number of arguments. \param ay is the result vector for this call, ay.size() determines the number of results. */ template void operator()(const ADVector& ax, ADVector& ay, size_t id = 0) { CPPAD_ASSERT_KNOWN( id == 0, "checkpoint: id is non-zero in atom_fun(ax, ay, id)" ); this->atomic_base::operator()(ax, ay, id); } // ------------------------------------------------------------------------ /*! Link from user_atomic to forward mode \copydetails atomic_base::forward */ virtual bool forward( size_t p , size_t q , const vector& vx , vector& vy , const vector& tx , vector& ty ); virtual bool forward( size_t p , size_t q , const vector& vx , vector& vy , const vector< AD >& atx , vector< AD >& aty ); // ------------------------------------------------------------------------ /*! Link from user_atomic to reverse mode \copydetails atomic_base::reverse */ virtual bool reverse( size_t q , const vector& tx , const vector& ty , vector& px , const vector& py ); virtual bool reverse( size_t q , const vector< AD >& atx , const vector< AD >& aty , vector< AD >& apx , const vector< AD >& apy ); // ------------------------------------------------------------------------ /*! Link from user_atomic to forward sparse Jacobian pack \copydetails atomic_base::for_sparse_jac */ virtual bool for_sparse_jac( size_t q , const vectorBool& r , vectorBool& s , const vector& x ); /*! Link from user_atomic to forward sparse Jacobian bool \copydetails atomic_base::for_sparse_jac */ virtual bool for_sparse_jac( size_t q , const vector& r , vector& s , const vector& x ); /*! Link from user_atomic to forward sparse Jacobian sets \copydetails atomic_base::for_sparse_jac */ virtual bool for_sparse_jac( size_t q , const vector< std::set >& r , vector< std::set >& s , const vector& x ); // ------------------------------------------------------------------------ /*! Link from user_atomic to reverse sparse Jacobian pack \copydetails atomic_base::rev_sparse_jac */ virtual bool rev_sparse_jac( size_t q , const vectorBool& rt , vectorBool& st , const vector& x ); /*! Link from user_atomic to reverse sparse Jacobian bool \copydetails atomic_base::rev_sparse_jac */ virtual bool rev_sparse_jac( size_t q , const vector& rt , vector& st , const vector& x ); /*! Link from user_atomic to reverse Jacobian sets \copydetails atomic_base::rev_sparse_jac */ virtual bool rev_sparse_jac( size_t q , const vector< std::set >& rt , vector< std::set >& st , const vector& x ); // ------------------------------------------------------------------------ /*! Link from user_atomic to reverse sparse Hessian pack \copydetails atomic_base::rev_sparse_hes */ virtual bool rev_sparse_hes( const vector& vx , const vector& s , vector& t , size_t q , const vectorBool& r , const vectorBool& u , vectorBool& v , const vector& x ); /*! Link from user_atomic to reverse sparse Hessian bool \copydetails atomic_base::rev_sparse_hes */ virtual bool rev_sparse_hes( const vector& vx , const vector& s , vector& t , size_t q , const vector& r , const vector& u , vector& v , const vector& x ); /*! Link from user_atomic to reverse sparse Hessian sets \copydetails atomic_base::rev_sparse_hes */ virtual bool rev_sparse_hes( const vector& vx , const vector& s , vector& t , size_t q , const vector< std::set >& r , const vector< std::set >& u , vector< std::set >& v , const vector& x ); }; } // END_CPPAD_NAMESPACE // functions implemented in cppad/core/checkpoint files # include # include # include # include # include # include # include # include # include # include # endif ================================================ FILE: include/cppad/core/chkpoint_one/ctor.hpp ================================================ # ifndef CPPAD_CORE_CHKPOINT_ONE_CTOR_HPP # define CPPAD_CORE_CHKPOINT_ONE_CTOR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { // BEGIN_CPPAD_NAMESPACE template template checkpoint::checkpoint( const char* name , Algo& algo , const ADVector& ax , ADVector& ay , option_enum sparsity , bool optimize ) : atomic_base(name, sparsity) { # ifndef NDEBUG if( thread_alloc::in_parallel() ) { std::string msg = name; msg += ": checkpoint constructor called in parallel mode."; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # endif for(size_t thread = 0; thread < CPPAD_MAX_NUM_THREADS; ++thread) member_[thread] = nullptr; // CheckSimpleVector< CppAD::AD , ADVector>(); // // make a copy of ax because Independent modifies AD information ADVector x_tmp(ax); // declare x_tmp as the independent variables Independent(x_tmp); // record mapping from x_tmp to ay algo(x_tmp, ay); // create function f_ : x -> y const_member_.f_.Dependent(ay); if( optimize ) { // suppress checking for nan in f_ results // (see optimize documentation for atomic functions) const_member_.f_.check_for_nan(false); // // now optimize const_member_.f_.optimize(); } // now disable checking of comparison operations // 2DO: add a debugging mode that checks for changes and aborts const_member_.f_.compare_change_count(0); } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/chkpoint_one/for_sparse_jac.hpp ================================================ # ifndef CPPAD_CORE_CHKPOINT_ONE_FOR_SPARSE_JAC_HPP # define CPPAD_CORE_CHKPOINT_ONE_FOR_SPARSE_JAC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { // BEGIN_CPPAD_NAMESPACE template template bool checkpoint::for_sparse_jac_sparsity_type( size_t q , const sparsity_type& r , sparsity_type& s , const vector& x ) { // make sure member_ is allocated for this thread size_t thread = thread_alloc::thread_num(); allocate_member(thread); // // during user sparsity calculations size_t m = member_[thread]->f_.Range(); size_t n = member_[thread]->f_.Domain(); if( member_[thread]->jac_sparse_bool_.size() == 0 ) set_jac_sparse_bool(); if( member_[thread]->jac_sparse_set_.n_set() != 0 ) member_[thread]->jac_sparse_set_.resize(0, 0); CPPAD_ASSERT_UNKNOWN( member_[thread]->jac_sparse_bool_.size() == m * n ); CPPAD_ASSERT_UNKNOWN( member_[thread]->jac_sparse_set_.n_set() == 0 ); CPPAD_ASSERT_UNKNOWN( r.size() == n * q ); CPPAD_ASSERT_UNKNOWN( s.size() == m * q ); // bool ok = true; for(size_t i = 0; i < m; i++) { for(size_t k = 0; k < q; k++) s[i * q + k] = false; } // sparsity for s = jac_sparse_bool_ * r for(size_t i = 0; i < m; i++) { for(size_t k = 0; k < q; k++) { // initialize sparsity for S(i,k) bool s_ik = false; // S(i,k) = sum_j J(i,j) * R(j,k) for(size_t j = 0; j < n; j++) { bool J_ij = member_[thread]->jac_sparse_bool_[ i * n + j]; bool R_jk = r[j * q + k ]; s_ik |= ( J_ij & R_jk ); } s[i * q + k] = s_ik; } } return ok; } template bool checkpoint::for_sparse_jac( size_t q , const vectorBool& r , vectorBool& s , const vector& x ) { // make sure member_ is allocated for this thread size_t thread = thread_alloc::thread_num(); allocate_member(thread); // return for_sparse_jac_sparsity_type< vectorBool >(q, r, s, x); } template bool checkpoint::for_sparse_jac( size_t q , const vector& r , vector& s , const vector& x ) { // make sure member_ is allocated for this thread size_t thread = thread_alloc::thread_num(); allocate_member(thread); // return for_sparse_jac_sparsity_type< vector >(q, r, s, x); } template bool checkpoint::for_sparse_jac( size_t q , const vector< std::set >& r , vector< std::set >& s , const vector& x ) { // make sure member_ is allocated for this thread size_t thread = thread_alloc::thread_num(); allocate_member(thread); // // during user sparsity calculations size_t m = member_[thread]->f_.Range(); size_t n = member_[thread]->f_.Domain(); if( member_[thread]->jac_sparse_bool_.size() != 0 ) member_[thread]->jac_sparse_bool_.clear(); if( member_[thread]->jac_sparse_set_.n_set() == 0 ) set_jac_sparse_set(); CPPAD_ASSERT_UNKNOWN( member_[thread]->jac_sparse_bool_.size() == 0 ); CPPAD_ASSERT_UNKNOWN( member_[thread]->jac_sparse_set_.n_set() == m ); CPPAD_ASSERT_UNKNOWN( member_[thread]->jac_sparse_set_.end() == n ); CPPAD_ASSERT_UNKNOWN( r.size() == n ); CPPAD_ASSERT_UNKNOWN( s.size() == m ); bool ok = true; for(size_t i = 0; i < m; i++) s[i].clear(); // sparsity for s = jac_sparse_set_ * r for(size_t i = 0; i < m; i++) { // compute row i of the return pattern local::sparse::list_setvec::const_iterator set_itr( member_[thread]->jac_sparse_set_, i ); size_t j = *set_itr; while(j < n ) { std::set::const_iterator itr_j; const std::set& r_j( r[j] ); for(itr_j = r_j.begin(); itr_j != r_j.end(); itr_j++) { size_t k = *itr_j; CPPAD_ASSERT_UNKNOWN( k < q ); s[i].insert(k); } j = *(++set_itr); } } return ok; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/chkpoint_one/forward.hpp ================================================ # ifndef CPPAD_CORE_CHKPOINT_ONE_FORWARD_HPP # define CPPAD_CORE_CHKPOINT_ONE_FORWARD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { // BEGIN_CPPAD_NAMESPACE // --------------------------------------------------------------------------- template bool checkpoint::forward( size_t p , size_t q , const vector& vx , vector& vy , const vector& tx , vector& ty ) { // make sure member_ is allocated for this thread size_t thread = thread_alloc::thread_num(); allocate_member(thread); // size_t n = member_[thread]->f_.Domain(); size_t m = member_[thread]->f_.Range(); // CPPAD_ASSERT_UNKNOWN( member_[thread]->f_.size_var() > 0 ); CPPAD_ASSERT_UNKNOWN( tx.size() % (q+1) == 0 ); CPPAD_ASSERT_UNKNOWN( ty.size() % (q+1) == 0 ); CPPAD_ASSERT_UNKNOWN( n == tx.size() / (q+1) ); CPPAD_ASSERT_UNKNOWN( m == ty.size() / (q+1) ); bool ok = true; // if( vx.size() == 0 ) { // during user forward mode if( member_[thread]->jac_sparse_set_.n_set() != 0 ) member_[thread]->jac_sparse_set_.resize(0,0); if( member_[thread]->jac_sparse_bool_.size() != 0 ) member_[thread]->jac_sparse_bool_.clear(); // if( member_[thread]->hes_sparse_set_.n_set() != 0 ) member_[thread]->hes_sparse_set_.resize(0,0); if( member_[thread]->hes_sparse_bool_.size() != 0 ) member_[thread]->hes_sparse_bool_.clear(); } if( vx.size() > 0 ) { // need Jacobian sparsity pattern to determine variable relation // during user recording using checkpoint functions if( sparsity() == atomic_base::set_sparsity_enum ) { if( member_[thread]->jac_sparse_set_.n_set() == 0 ) set_jac_sparse_set(); CPPAD_ASSERT_UNKNOWN( member_[thread]->jac_sparse_set_.n_set() == m ); CPPAD_ASSERT_UNKNOWN( member_[thread]->jac_sparse_set_.end() == n ); // for(size_t i = 0; i < m; i++) { vy[i] = false; local::sparse::list_setvec::const_iterator set_itr( member_[thread]->jac_sparse_set_, i ); size_t j = *set_itr; while(j < n ) { // y[i] depends on the value of x[j] // cast avoid Microsoft warning (should not be needed) vy[i] |= static_cast( vx[j] ); j = *(++set_itr); } } } else { if( member_[thread]->jac_sparse_set_.n_set() != 0 ) member_[thread]->jac_sparse_set_.resize(0, 0); if( member_[thread]->jac_sparse_bool_.size() == 0 ) set_jac_sparse_bool(); CPPAD_ASSERT_UNKNOWN( member_[thread]->jac_sparse_set_.n_set() == 0 ); CPPAD_ASSERT_UNKNOWN( member_[thread]->jac_sparse_bool_.size() == m * n ); // for(size_t i = 0; i < m; i++) { vy[i] = false; for(size_t j = 0; j < n; j++) { if( member_[thread]->jac_sparse_bool_[ i * n + j ] ) { // y[i] depends on the value of x[j] // cast avoid Microsoft warning vy[i] |= static_cast( vx[j] ); } } } } } // compute forward results for orders zero through q ty = member_[thread]->f_.Forward(q, tx); // no longer need the Taylor coefficients in f_ // (have to reconstruct them every time) // Hold onto sparsity pattern because it is always good. size_t c = 0; size_t r = 0; member_[thread]->f_.capacity_order(c, r); return ok; } // --------------------------------------------------------------------------- template bool checkpoint::forward( size_t p , size_t q , const vector& vx , vector& vy , const vector< AD >& atx , vector< AD >& aty ) { // make sure member_ is allocated for this thread size_t thread = thread_alloc::thread_num(); allocate_member(thread); // // make sure af_ is defined if( member_[thread]->af_.size_var() == 0 ) member_[thread]->af_ = member_[thread]->f_.base2ad(); // # ifndef NDEBUG size_t n = member_[thread]->f_.Domain(); size_t m = member_[thread]->f_.Range(); # endif // CPPAD_ASSERT_UNKNOWN( member_[thread]->f_.size_var() > 0 ); CPPAD_ASSERT_UNKNOWN( atx.size() % (q+1) == 0 ); CPPAD_ASSERT_UNKNOWN( aty.size() % (q+1) == 0 ); CPPAD_ASSERT_UNKNOWN( n == atx.size() / (q+1) ); CPPAD_ASSERT_UNKNOWN( m == aty.size() / (q+1) ); CPPAD_ASSERT_UNKNOWN( vx.size() == 0 ) bool ok = true; // // during user forward mode if( member_[thread]->jac_sparse_set_.n_set() != 0 ) member_[thread]->jac_sparse_set_.resize(0,0); if( member_[thread]->jac_sparse_bool_.size() != 0 ) member_[thread]->jac_sparse_bool_.clear(); // if( member_[thread]->hes_sparse_set_.n_set() != 0 ) member_[thread]->hes_sparse_set_.resize(0,0); if( member_[thread]->hes_sparse_bool_.size() != 0 ) member_[thread]->hes_sparse_bool_.clear(); // // compute forward results for orders zero through q aty = member_[thread]->af_.Forward(q, atx); // no longer need the Taylor coefficients in af_ // (have to reconstruct them every time) // Hold onto sparsity pattern because it is always good. size_t c = 0; size_t r = 0; member_[thread]->af_.capacity_order(c, r); // return ok; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/chkpoint_one/rev_sparse_hes.hpp ================================================ # ifndef CPPAD_CORE_CHKPOINT_ONE_REV_SPARSE_HES_HPP # define CPPAD_CORE_CHKPOINT_ONE_REV_SPARSE_HES_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { // BEGIN_CPPAD_NAMESPACE template template bool checkpoint::rev_sparse_hes_sparsity_type( const vector& vx , const vector& s , vector& t , size_t q , const sparsity_type& r , const sparsity_type& u , sparsity_type& v , const vector& x ) { // make sure member_ is allocated for this thread size_t thread = thread_alloc::thread_num(); allocate_member(thread); // size_t n = member_[thread]->f_.Domain(); # ifndef NDEBUG size_t m = member_[thread]->f_.Range(); # endif CPPAD_ASSERT_UNKNOWN( vx.size() == n ); CPPAD_ASSERT_UNKNOWN( s.size() == m ); CPPAD_ASSERT_UNKNOWN( t.size() == n ); CPPAD_ASSERT_UNKNOWN( r.size() == n * q ); CPPAD_ASSERT_UNKNOWN( u.size() == m * q ); CPPAD_ASSERT_UNKNOWN( v.size() == n * q ); // bool ok = true; // make sure hes_sparse_bool_ has been set if( member_[thread]->hes_sparse_bool_.size() == 0 ) set_hes_sparse_bool(); if( member_[thread]->hes_sparse_set_.n_set() != 0 ) member_[thread]->hes_sparse_set_.resize(0, 0); CPPAD_ASSERT_UNKNOWN( member_[thread]->hes_sparse_bool_.size() == n * n ); CPPAD_ASSERT_UNKNOWN( member_[thread]->hes_sparse_set_.n_set() == 0 ); // compute sparsity pattern for T(x) = S(x) * f'(x) t = member_[thread]->f_.RevSparseJac(1, s); # ifndef NDEBUG for(size_t j = 0; j < n; j++) CPPAD_ASSERT_UNKNOWN( vx[j] || ! t[j] ) # endif // V(x) = f'(x)^T * g''(y) * f'(x) * R + g'(y) * f''(x) * R // U(x) = g''(y) * f'(x) * R // S(x) = g'(y) // compute sparsity pattern for A(x) = f'(x)^T * U(x) bool transpose = true; sparsity_type a(n * q); a = member_[thread]->f_.RevSparseJac(q, u, transpose); // Need sparsity pattern for H(x) = (S(x) * f(x))''(x) * R, // but use less efficient sparsity for f(x)''(x) * R so that // hes_sparse_set_ can be used every time this is needed. for(size_t i = 0; i < n; i++) { for(size_t k = 0; k < q; k++) { // initialize sparsity pattern for H(i,k) bool h_ik = false; // H(i,k) = sum_j f''(i,j) * R(j,k) for(size_t j = 0; j < n; j++) { bool f_ij = member_[thread]->hes_sparse_bool_[i * n + j]; bool r_jk = r[j * q + k]; h_ik |= ( f_ij & r_jk ); } // sparsity for H(i,k) v[i * q + k] = h_ik; } } // compute sparsity pattern for V(x) = A(x) + H(x) for(size_t i = 0; i < n; i++) { for(size_t k = 0; k < q; k++) // v[ i * q + k ] |= a[ i * q + k]; v[ i * q + k ] = bool(v[ i * q + k]) || bool(a[ i * q + k]); } return ok; } template bool checkpoint::rev_sparse_hes( const vector& vx , const vector& s , vector& t , size_t q , const vectorBool& r , const vectorBool& u , vectorBool& v , const vector& x ) { // make sure member_ is allocated for this thread size_t thread = thread_alloc::thread_num(); allocate_member(thread); // return rev_sparse_hes_sparsity_type< vectorBool >(vx, s, t, q, r, u, v, x); } template bool checkpoint::rev_sparse_hes( const vector& vx , const vector& s , vector& t , size_t q , const vector& r , const vector& u , vector& v , const vector& x ) { // make sure member_ is allocated for this thread size_t thread = thread_alloc::thread_num(); allocate_member(thread); // return rev_sparse_hes_sparsity_type< vector >(vx, s, t, q, r, u, v, x); } template bool checkpoint::rev_sparse_hes( const vector& vx , const vector& s , vector& t , size_t q , const vector< std::set >& r , const vector< std::set >& u , vector< std::set >& v , const vector& x ) { // make sure member_ is allocated for this thread size_t thread = thread_alloc::thread_num(); allocate_member(thread); // size_t n = member_[thread]->f_.Domain(); # ifndef NDEBUG size_t m = member_[thread]->f_.Range(); # endif CPPAD_ASSERT_UNKNOWN( vx.size() == n ); CPPAD_ASSERT_UNKNOWN( s.size() == m ); CPPAD_ASSERT_UNKNOWN( t.size() == n ); CPPAD_ASSERT_UNKNOWN( r.size() == n ); CPPAD_ASSERT_UNKNOWN( u.size() == m ); CPPAD_ASSERT_UNKNOWN( v.size() == n ); // bool ok = true; // make sure hes_sparse_set_ has been set if( member_[thread]->hes_sparse_bool_.size() != 0 ) member_[thread]->hes_sparse_bool_.clear(); if( member_[thread]->hes_sparse_set_.n_set() == 0 ) set_hes_sparse_set(); CPPAD_ASSERT_UNKNOWN( member_[thread]->hes_sparse_bool_.size() == 0 ); CPPAD_ASSERT_UNKNOWN( member_[thread]->hes_sparse_set_.n_set() == n ); CPPAD_ASSERT_UNKNOWN( member_[thread]->hes_sparse_set_.end() == n ); // compute sparsity pattern for T(x) = S(x) * f'(x) t = member_[thread]->f_.RevSparseJac(1, s); # ifndef NDEBUG for(size_t j = 0; j < n; j++) CPPAD_ASSERT_UNKNOWN( vx[j] || ! t[j] ) # endif // V(x) = f'(x)^T * g''(y) * f'(x) * R + g'(y) * f''(x) * R // U(x) = g''(y) * f'(x) * R // S(x) = g'(y) // compute sparsity pattern for A(x) = f'(x)^T * U(x) // 2DO: change a to use INTERNAL_SPARSE_SET bool transpose = true; vector< std::set > a(n); a = member_[thread]->f_.RevSparseJac(q, u, transpose); // Need sparsity pattern for H(x) = (S(x) * f(x))''(x) * R, // but use less efficient sparsity for f(x)''(x) * R so that // hes_sparse_set_ can be used every time this is needed. for(size_t i = 0; i < n; i++) { v[i].clear(); local::sparse::list_setvec::const_iterator set_itr( member_[thread]->hes_sparse_set_, i ); size_t j = *set_itr; while( j < n ) { std::set::const_iterator itr_j; const std::set& r_j( r[j] ); for(itr_j = r_j.begin(); itr_j != r_j.end(); itr_j++) { size_t k = *itr_j; v[i].insert(k); } j = *(++set_itr); } } // compute sparsity pattern for V(x) = A(x) + H(x) std::set::const_iterator itr; for(size_t i = 0; i < n; i++) { for(itr = a[i].begin(); itr != a[i].end(); itr++) { size_t j = *itr; CPPAD_ASSERT_UNKNOWN( j < q ); v[i].insert(j); } } return ok; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/chkpoint_one/rev_sparse_jac.hpp ================================================ # ifndef CPPAD_CORE_CHKPOINT_ONE_REV_SPARSE_JAC_HPP # define CPPAD_CORE_CHKPOINT_ONE_REV_SPARSE_JAC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { // BEGIN_CPPAD_NAMESPACE template template bool checkpoint::rev_sparse_jac_sparsity_type( size_t q , const sparsity_type& rt , sparsity_type& st , const vector& x ) { // make sure member_ is allocated for this thread size_t thread = thread_alloc::thread_num(); allocate_member(thread); // // during user sparsity calculations size_t m = member_[thread]->f_.Range(); size_t n = member_[thread]->f_.Domain(); if( member_[thread]->jac_sparse_bool_.size() == 0 ) set_jac_sparse_bool(); if( member_[thread]->jac_sparse_set_.n_set() != 0 ) member_[thread]->jac_sparse_set_.resize(0, 0); CPPAD_ASSERT_UNKNOWN( member_[thread]->jac_sparse_bool_.size() == m * n ); CPPAD_ASSERT_UNKNOWN( member_[thread]->jac_sparse_set_.n_set() == 0 ); CPPAD_ASSERT_UNKNOWN( rt.size() == m * q ); CPPAD_ASSERT_UNKNOWN( st.size() == n * q ); bool ok = true; // // S = R * J where J is jacobian for(size_t i = 0; i < q; i++) { for(size_t j = 0; j < n; j++) { // initialize sparsity for S(i,j) bool s_ij = false; // S(i,j) = sum_k R(i,k) * J(k,j) for(size_t k = 0; k < m; k++) { // sparsity for R(i, k) bool R_ik = rt[ k * q + i ]; bool J_kj = member_[thread]->jac_sparse_bool_[ k * n + j ]; s_ij |= (R_ik & J_kj); } // set sparsity for S^T st[ j * q + i ] = s_ij; } } return ok; } template bool checkpoint::rev_sparse_jac( size_t q , const vectorBool& rt , vectorBool& st , const vector& x ) { // make sure member_ is allocated for this thread size_t thread = thread_alloc::thread_num(); allocate_member(thread); // return rev_sparse_jac_sparsity_type< vectorBool >(q, rt, st, x); } template bool checkpoint::rev_sparse_jac( size_t q , const vector& rt , vector& st , const vector& x ) { // make sure member_ is allocated for this thread size_t thread = thread_alloc::thread_num(); allocate_member(thread); // return rev_sparse_jac_sparsity_type< vector >(q, rt, st, x); } template bool checkpoint::rev_sparse_jac( size_t q , const vector< std::set >& rt , vector< std::set >& st , const vector& x ) { // make sure member_ is allocated for this thread size_t thread = thread_alloc::thread_num(); allocate_member(thread); // // during user sparsity calculations size_t m = member_[thread]->f_.Range(); size_t n = member_[thread]->f_.Domain(); if( member_[thread]->jac_sparse_bool_.size() != 0 ) member_[thread]->jac_sparse_bool_.clear(); if( member_[thread]->jac_sparse_set_.n_set() == 0 ) set_jac_sparse_set(); CPPAD_ASSERT_UNKNOWN( member_[thread]->jac_sparse_bool_.size() == 0 ); CPPAD_ASSERT_UNKNOWN( member_[thread]->jac_sparse_set_.n_set() == m ); CPPAD_ASSERT_UNKNOWN( member_[thread]->jac_sparse_set_.end() == n ); CPPAD_ASSERT_UNKNOWN( rt.size() == m ); CPPAD_ASSERT_UNKNOWN( st.size() == n ); // bool ok = true; // for(size_t j = 0; j < n; j++) st[j].clear(); // // sparsity for s = r * jac_sparse_set_ // s^T = jac_sparse_set_^T * r^T for(size_t i = 0; i < m; i++) { // i is the row index in r^T std::set::const_iterator itr_i; const std::set& r_i( rt[i] ); for(itr_i = r_i.begin(); itr_i != r_i.end(); itr_i++) { // k is the column index in r^T size_t k = *itr_i; CPPAD_ASSERT_UNKNOWN( k < q ); // // i is column index in jac_sparse_set^T local::sparse::list_setvec::const_iterator set_itr( member_[thread]->jac_sparse_set_, i ); size_t j = *set_itr; while( j < n ) { // j is row index in jac_sparse_set^T st[j].insert(k); j = *(++set_itr); } } } return ok; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/chkpoint_one/reverse.hpp ================================================ # ifndef CPPAD_CORE_CHKPOINT_ONE_REVERSE_HPP # define CPPAD_CORE_CHKPOINT_ONE_REVERSE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { // BEGIN_CPPAD_NAMESPACE // --------------------------------------------------------------------------- template bool checkpoint::reverse( size_t q , const vector& tx , const vector& ty , vector& px , const vector& py ) { // make sure member_ is allocated for this thread size_t thread = thread_alloc::thread_num(); allocate_member(thread); // # ifndef NDEBUG size_t n = member_[thread]->f_.Domain(); size_t m = member_[thread]->f_.Range(); # endif CPPAD_ASSERT_UNKNOWN( member_[thread]->f_.size_var() > 0 ); CPPAD_ASSERT_UNKNOWN( n == tx.size() / (q+1) ); CPPAD_ASSERT_UNKNOWN( m == ty.size() / (q+1) ); CPPAD_ASSERT_UNKNOWN( tx.size() % (q+1) == 0 ); CPPAD_ASSERT_UNKNOWN( ty.size() % (q+1) == 0 ); CPPAD_ASSERT_UNKNOWN( px.size() == n * (q+1) ); CPPAD_ASSERT_UNKNOWN( py.size() == m * (q+1) ); bool ok = true; // put proper forward mode coefficients in f_ # ifdef NDEBUG // compute forward results for orders zero through q member_[thread]->f_.Forward(q, tx); # else size_t i, j, k; // // compute forward results for orders zero through q vector check_ty = member_[thread]->f_.Forward(q, tx); for(i = 0; i < m; i++) { for(k = 0; k <= q; k++) { j = i * (q+1) + k; CPPAD_ASSERT_UNKNOWN( check_ty[j] == ty[j] ); } } # endif // now can run reverse mode px = member_[thread]->f_.Reverse(q+1, py); // no longer need the Taylor coefficients in f_ // (have to reconstruct them every time) size_t c = 0; size_t r = 0; member_[thread]->f_.capacity_order(c, r); return ok; } // --------------------------------------------------------------------------- template bool checkpoint::reverse( size_t q , const vector< AD >& atx , const vector< AD >& aty , vector< AD >& apx , const vector< AD >& apy ) { // make sure member_ is allocated for this thread size_t thread = thread_alloc::thread_num(); allocate_member(thread); // // make sure af_ is defined if( member_[thread]->af_.size_var() == 0 ) member_[thread]->af_ = member_[thread]->f_.base2ad(); # ifndef NDEBUG size_t n = member_[thread]->f_.Domain(); size_t m = member_[thread]->f_.Range(); # endif CPPAD_ASSERT_UNKNOWN( member_[thread]->f_.size_var() > 0 ); CPPAD_ASSERT_UNKNOWN( n == atx.size() / (q+1) ); CPPAD_ASSERT_UNKNOWN( m == aty.size() / (q+1) ); CPPAD_ASSERT_UNKNOWN( atx.size() % (q+1) == 0 ); CPPAD_ASSERT_UNKNOWN( aty.size() % (q+1) == 0 ); CPPAD_ASSERT_UNKNOWN( apx.size() == n * (q+1) ); CPPAD_ASSERT_UNKNOWN( apy.size() == m * (q+1) ); bool ok = true; // put proper forward mode coefficients in f_ # ifdef NDEBUG // compute forward results for orders zero through q member_[thread]->af_.Forward(q, atx); # else size_t i, j, k; // // compute forward results for orders zero through q vector< AD > check_aty = member_[thread]->af_.Forward(q, atx); for(i = 0; i < m; i++) { for(k = 0; k <= q; k++) { j = i * (q+1) + k; CPPAD_ASSERT_UNKNOWN( check_aty[j] == aty[j] ); } } # endif // now can run reverse mode apx = member_[thread]->af_.Reverse(q+1, apy); // no longer need the Taylor coefficients in f_ // (have to reconstruct them every time) size_t c = 0; size_t r = 0; member_[thread]->af_.capacity_order(c, r); return ok; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/chkpoint_one/set_hes_sparse_bool.hpp ================================================ # ifndef CPPAD_CORE_CHKPOINT_ONE_SET_HES_SPARSE_BOOL_HPP # define CPPAD_CORE_CHKPOINT_ONE_SET_HES_SPARSE_BOOL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { // BEGIN_CPPAD_NAMESPACE template void checkpoint::set_hes_sparse_bool(void) { // make sure member_ is allocated for this thread size_t thread = thread_alloc::thread_num(); allocate_member(thread); // CPPAD_ASSERT_UNKNOWN( member_[thread]->hes_sparse_bool_.size() == 0 ); size_t n = member_[thread]->f_.Domain(); size_t m = member_[thread]->f_.Range(); // // set version of sparsity for vector of all ones vectorBool all_one(m); for(size_t i = 0; i < m; i++) all_one[i] = true; // set version of sparsity for n by n identity matrix vectorBool identity(n * n); for(size_t j = 0; j < n; j++) { for(size_t i = 0; i < n; i++) identity[ i * n + j ] = (i == j); } // compute sparsity pattern for H(x) = sum_i f_i(x)^{(2)} bool transpose = false; bool dependency = false; member_[thread]->f_.ForSparseJac(n, identity, transpose, dependency); member_[thread]->hes_sparse_bool_ = member_[thread]->f_.RevSparseHes(n, all_one, transpose); CPPAD_ASSERT_UNKNOWN( member_[thread]->hes_sparse_bool_.size() == n * n ); // // drop the forward sparsity results from f_ member_[thread]->f_.size_forward_bool(0); CPPAD_ASSERT_UNKNOWN( member_[thread]->f_.size_forward_bool() == 0 ); CPPAD_ASSERT_UNKNOWN( member_[thread]->f_.size_forward_set() == 0 ); } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/chkpoint_one/set_hes_sparse_set.hpp ================================================ # ifndef CPPAD_CORE_CHKPOINT_ONE_SET_HES_SPARSE_SET_HPP # define CPPAD_CORE_CHKPOINT_ONE_SET_HES_SPARSE_SET_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { // BEGIN_CPPAD_NAMESPACE template void checkpoint::set_hes_sparse_set(void) { // make sure member_ is allocated for this thread size_t thread = thread_alloc::thread_num(); allocate_member(thread); // CPPAD_ASSERT_UNKNOWN( member_[thread]->hes_sparse_set_.n_set() == 0 ); size_t n = member_[thread]->f_.Domain(); size_t m = member_[thread]->f_.Range(); // // set version of sparsity for vector of all ones vector all_one(m); for(size_t i = 0; i < m; i++) all_one[i] = true; // set version of sparsity for n by n identity matrix local::sparse::list_setvec identity; identity.resize(n, n); for(size_t j = 0; j < n; j++) { // Not using post_element because only adding one element per set identity.add_element(j, j); } // compute sparsity pattern for H(x) = sum_i f_i(x)^{(2)} bool transpose = false; bool dependency = false; member_[thread]->f_.ForSparseJacCheckpoint( n, identity, transpose, dependency, member_[thread]->jac_sparse_set_ ); member_[thread]->f_.RevSparseHesCheckpoint( n, all_one, transpose, member_[thread]->hes_sparse_set_ ); CPPAD_ASSERT_UNKNOWN( member_[thread]->hes_sparse_set_.n_set() == n ); CPPAD_ASSERT_UNKNOWN( member_[thread]->hes_sparse_set_.end() == n ); // // drop the forward sparsity results from f_ member_[thread]->f_.size_forward_set(0); } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/chkpoint_one/set_jac_sparse_bool.hpp ================================================ # ifndef CPPAD_CORE_CHKPOINT_ONE_SET_JAC_SPARSE_BOOL_HPP # define CPPAD_CORE_CHKPOINT_ONE_SET_JAC_SPARSE_BOOL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { // BEGIN_CPPAD_NAMESPACE template void checkpoint::set_jac_sparse_bool(void) { // make sure member_ is allocated for this thread size_t thread = thread_alloc::thread_num(); allocate_member(thread); // CPPAD_ASSERT_UNKNOWN( member_[thread]->jac_sparse_bool_.size() == 0 ); bool transpose = false; bool dependency = true; size_t n = member_[thread]->f_.Domain(); size_t m = member_[thread]->f_.Range(); // Use the choice for forward / reverse that results in smaller // size for the sparsity pattern of all variables in the tape. if( n <= m ) { vectorBool identity(n * n); for(size_t j = 0; j < n; j++) { for(size_t i = 0; i < n; i++) identity[ i * n + j ] = (i == j); } member_[thread]->jac_sparse_bool_ = member_[thread]->f_.ForSparseJac( n, identity, transpose, dependency ); member_[thread]->f_.size_forward_bool(0); } else { vectorBool identity(m * m); for(size_t j = 0; j < m; j++) { for(size_t i = 0; i < m; i++) identity[ i * m + j ] = (i == j); } member_[thread]->jac_sparse_bool_ = member_[thread]->f_.RevSparseJac( m, identity, transpose, dependency ); } CPPAD_ASSERT_UNKNOWN( member_[thread]->f_.size_forward_bool() == 0 ); CPPAD_ASSERT_UNKNOWN( member_[thread]->f_.size_forward_set() == 0 ); } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/chkpoint_one/set_jac_sparse_set.hpp ================================================ # ifndef CPPAD_CORE_CHKPOINT_ONE_SET_JAC_SPARSE_SET_HPP # define CPPAD_CORE_CHKPOINT_ONE_SET_JAC_SPARSE_SET_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { // BEGIN_CPPAD_NAMESPACE template void checkpoint::set_jac_sparse_set(void) { // make sure member_ is allocated for this thread size_t thread = thread_alloc::thread_num(); allocate_member(thread); // CPPAD_ASSERT_UNKNOWN( member_[thread]->jac_sparse_set_.n_set() == 0 ); bool transpose = false; bool dependency = true; size_t n = member_[thread]->f_.Domain(); size_t m = member_[thread]->f_.Range(); // Use the choice for forward / reverse that results in smaller // size for the sparsity pattern of all variables in the tape. if( n <= m ) { local::sparse::list_setvec identity; identity.resize(n, n); for(size_t j = 0; j < n; j++) { // Not using post_element because only adding one element per set identity.add_element(j, j); } member_[thread]->f_.ForSparseJacCheckpoint( n, identity, transpose, dependency, member_[thread]->jac_sparse_set_ ); member_[thread]->f_.size_forward_set(0); } else { local::sparse::list_setvec identity; identity.resize(m, m); for(size_t i = 0; i < m; i++) { // Not using post_element because only adding one element per set identity.add_element(i, i); } member_[thread]->f_.RevSparseJacCheckpoint( m, identity, transpose, dependency, member_[thread]->jac_sparse_set_ ); } CPPAD_ASSERT_UNKNOWN( member_[thread]->f_.size_forward_set() == 0 ); CPPAD_ASSERT_UNKNOWN( member_[thread]->f_.size_forward_bool() == 0 ); } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/chkpoint_two/chk_fun.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin chkpoint_two_chk_fun} Using Checkpoint Functions ########################## Syntax ****** | *chk_fun* ( *ax* , *ay* ) Purpose ******* Given *ax* , this call computes the corresponding value of *ay* . If ``AD`` < *Base* > operations are being recorded, it enters the computation as an :ref:`atomic_three-name` operation in the recording; see :ref:`Independent@Start Recording` . chk_fun ******* This object must have been created using the :ref:`chkpoint_two` constructor. ADVector ******** The type *ADVector* must be a :ref:`simple vector class` with elements of type ``AD`` < *Base* > . ax ** This argument has prototype ``const`` *ADVector* & ``ax`` and its size equal to *n* = *fun* . ``Domain`` () where :ref:`chkpoint_two_ctor@fun` is the ``ADFun`` < *Base* > function in used the constructor for *chk_fun* . It specifies vector :math:`x \in \B{R}^n` at which we are computing an ``AD`` < *Base* > version of :math:`y = g(x)`. ay ** This argument has prototype *ADVector* & ``ay`` and its size must be equal to *m* = *fun* . ``Range`` () . The input values of its elements do not matter. Upon return, it is an ``AD`` < *Base* > version of :math:`y = g(x)`. {xrst_end chkpoint_two_chk_fun} ================================================ FILE: include/cppad/core/chkpoint_two/chkpoint_two.hpp ================================================ # ifndef CPPAD_CORE_CHKPOINT_TWO_CHKPOINT_TWO_HPP # define CPPAD_CORE_CHKPOINT_TWO_CHKPOINT_TWO_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file chkpoint_two.hpp Second generation checkpoint functions. */ /* {xrst_begin chkpoint_two} {xrst_spell chk } Checkpoint Functions: Second Generation ####################################### Syntax ****** Constructor =========== | ``chkpoint_two`` < *Base* > *chk_fun* ( *fun* , *name* , | |tab| *internal_bool* , *use_hes_sparsity* , *use_base2ad* , *use_in_parallel* | ) Use Checkpoint Function ======================= *chk_fun* ( *ax* , *ay* ) new_dynamic =========== *chk_fun* . ``new_dynamic`` ( *dynamic* ) Reduce Memory ************* You can reduce the size of the tape and memory required for AD using a checkpoint representation of a function :math:`g : \B{R}^n \rightarrow \B{R}^m`. Faster Recording **************** It may also reduce the time to make a recording if the same :math:`g(x)` is used many times (with different values) during the recording of an ``ADFun`` < *Base* > object. Repeating Forward ***************** Normally, CppAD stores :ref:`forward-name` mode results, until they freed using :ref:`capacity_order-name` , or the corresponding :ref:`ADFun-name` object is deleted. This is not true for ``chkpoint_two`` functions because the same checkpoint function may be used repeatedly with different arguments during a single forward mode operation. Thus, forward mode results are computed for each use of *chk_fun* in a forward mode sweep. Operation Sequence ****************** The :ref:`operation sequence` representing :math:`g(x)` is fixed; i.e., it cannot depend on the value of :math:`x`. atomic_three ************ The ``chkpoint_two`` class is derived from ``atomic_three`` , hence some of its error message will refer to atomic operations. The ``chkpoint_two`` class implements all the :ref:`atomic_three_define@Virtual Functions` and hence its source code, ``include/cppad/core/chkpoint_two/chkpoint_two.hpp`` provides an example for :ref:`atomic_three-name` operations. The difference is that ``chkpoint_two.hpp`` uses AD instead of user provided derivatives. Base **** The type *Base* specifies the base type for AD operations; i.e., *chk_fun* can be used during the recording of ``AD`` < *Base* > operations. Contents ******** {xrst_toc_table include/cppad/core/chkpoint_two/ctor.hpp include/cppad/core/chkpoint_two/chk_fun.xrst include/cppad/core/chkpoint_two/dynamic.hpp example/chkpoint_two/get_started.cpp example/chkpoint_two/compare.cpp example/chkpoint_two/base2ad.cpp example/chkpoint_two/dynamic.cpp example/chkpoint_two/ode.cpp } {xrst_end chkpoint_two} */ template class chkpoint_two : public atomic_three { // --------------------------------------------------------------------------- private: /// are sparsity calculations using bools or sets of integers const bool internal_bool_; // /// can this checkpoint function calculate Hessian sparsity patterns const bool use_hes_sparsity_; // /// can this checkpoint function be used in base2ad recordings const bool use_base2ad_; // /// can this checkpoint function be used in parallel mode const bool use_in_parallel_; // /// Jacobian sparsity for g(x) with dependency true. /// This is set by the constructor and constant after that. sparse_rc< vector > jac_sparsity_; // /// Hessian sparsity for g(x). If use_hes_sparsity_ is true, /// This is set by the constructor and constant after that. sparse_rc< vector > hes_sparsity_; // /// Function corresponding to this checkpoint object. /// If use_in_parallel_, this is constant after the constructor. ADFun g_; // /// AD version of function corresponding to this checkpoint object /// If use_in_parallel_, this is constant after the constructor. ADFun< AD, Base> ag_; // ------------------------------------------------------------------------ // member_ // ------------------------------------------------------------------------ /// If use_in_parallel_ is true, must have a separate copy member data /// that is not constant. struct member_struct { // /// function corresponding to this checkpoint object ADFun g_; // /// AD version of this function object ADFun< AD, Base > ag_; // }; /// use pointers and allocate memory to avoid false sharing /// (initialized to null by constructor) member_struct* member_[CPPAD_MAX_NUM_THREADS]; // // ------------------------------------------------------------------------ /// allocate member_ for this thread void allocate_member(size_t thread) { CPPAD_ASSERT_UNKNOWN( use_in_parallel_ ); if( member_[thread] == nullptr ) { // allocaate raw memory size_t min_bytes = sizeof(member_struct); size_t num_bytes; void* v_ptr = thread_alloc::get_memory(min_bytes, num_bytes); // convert to member_struct* member_[thread] = reinterpret_cast(v_ptr); // call member_struct constructor new( member_[thread] ) member_struct; // // The thread has a copy of corresponding information. member_[thread]->g_ = g_; member_[thread]->ag_ = ag_; } return; } // // ------------------------------------------------------------------------ /// free member_ for this thread void free_member(size_t thread) { if( member_[thread] != nullptr ) { // call destructor member_[thread]->~member_struct(); // return raw m,emory to available pool for this thread void* v_ptr = reinterpret_cast(member_[thread]); thread_alloc::return_memory(v_ptr); // mark member for this thread as not allocated member_[thread] = nullptr; } return; } // ----------------------------------------------------------------------- // atomic_three virtual functions // ------------------------------------------------------------------------ // type virtual bool for_type( const vector& parameter_x , const vector& type_x , vector& type_y ); // forward virtual bool forward( const vector& parameter_x , const vector& type_x , size_t need_y , size_t order_low , size_t order_up , const vector& taylor_x , vector& taylor_y ); // AD forward virtual bool forward( const vector< AD >& aparameter_x , const vector& type_x , size_t need_y , size_t order_low , size_t order_up , const vector< AD >& ataylor_x , vector< AD >& ataylor_y ); // reverse virtual bool reverse( const vector& parameter_x , const vector& type_x , size_t order_up , const vector& taylor_x , const vector& taylor_y , vector& partial_x , const vector& partial_y ); // AD reverse virtual bool reverse( const vector< AD >& aparameter_x , const vector& type_x , size_t order_up , const vector< AD >& ataylor_x , const vector< AD >& ataylor_y , vector< AD >& apartial_x , const vector< AD >& apartial_y ); // jac_sparsity virtual bool jac_sparsity( const vector& parameter_x , const vector& type_x , bool dependency , const vector& select_x , const vector& select_y , sparse_rc< vector >& pattern_out ); // hes_sparsity virtual bool hes_sparsity( const vector& parameter_x , const vector& type_x , const vector& select_x , const vector& select_y , sparse_rc< vector >& pattern_out ); // rev_depend virtual bool rev_depend( const vector& parameter_x , const vector& type_x , vector& depend_x , const vector& depend_y ); public: // ctor chkpoint_two( const ADFun& fun , const std::string& name , bool internal_bool , bool use_hes_sparsity , bool use_base2ad , bool use_in_parallel ); // // destructor ~chkpoint_two(void); // // assignment operator void operator=(const chkpoint_two& other) { CPPAD_ASSERT_KNOWN(false, "cannot use chkpoint_two assignment operator" ); } // copy constructor chkpoint_two(const chkpoint_two& other) : internal_bool_ ( other.internal_bool_ ) , use_hes_sparsity_ ( other.use_hes_sparsity_ ) , use_base2ad_ ( other.use_base2ad_ ) , use_in_parallel_ ( other.use_in_parallel_ ) , jac_sparsity_ ( other.jac_sparsity_ ) , hes_sparsity_ ( other.hes_sparsity_ ) { g_ = other.g_; ag_ = other.ag_; } // // new_dynamic template void new_dynamic(const BaseVector& dynamic); }; } // END_CPPAD_NAMESPACE # include # include # include # include # include # include # include # include # endif ================================================ FILE: include/cppad/core/chkpoint_two/ctor.hpp ================================================ # ifndef CPPAD_CORE_CHKPOINT_TWO_CTOR_HPP # define CPPAD_CORE_CHKPOINT_TWO_CTOR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin chkpoint_two_ctor} {xrst_spell chk } Checkpoint Function Constructor ############################### Syntax ****** | ``chkpoint_two`` < *Base* > *chk_fun* ( *fun* , *name* , | |tab| *internal_bool* , *use_hes_sparsity* , *use_base2ad* , *use_in_parallel* | ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } Parallel ******** This constructor, and its corresponding destructor, must not be called in :ref:`parallel` mode. The object *chk_fun* should not be destructed for as long as there is an ``ADFun`` < *Base* > object the has *chk_fun* in its recording. Base **** The type *Base* specifies the base type for AD operations. fun *** This specifies the function :math:`g(x)`. Note that *fun* may or may not have been :ref:`optimized` before calling the constructor. This will determine if the internal representation for *g* ( *x* ) is optimized. name **** is the name used for reporting errors using this checkpoint function. internal_bool ************* If true, sparsity calculations are done with sets represented by vectors of boolean values. Otherwise, vectors of sets are used for sparsity patterns. use_hes_sparsity **************** If true, Hessian sparsity patterns can be calculated for ``ADFun`` < *Base* > objects that have uses of *chk_fun* in their recording. This requires some extra memory and extra computation during the constructor. use_base2ad *********** If this is true, *chk_fun* can be used during the recording of ``ADFun`` < *Base* > objects that get converted to ``ADFun< AD<`` *Base* > > objects using :ref:`base2ad-name` . This requires some extra memory and extra computation during the constructor. use_in_parallel *************** If this is true, *chk_fun* can be used :ref:`ta_parallel_setup@in_parallel` . This requires some extra memory for a constant copy of the *fun* information and a separate copy (that changes) for each thread. chk_fun ******* This is a checkpoint function representation of :math:`g(x)` that can be used during the recording of ``AD`` < *Base* > operations. {xrst_end chkpoint_two_ctor} */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file chkpoint_two/ctor.hpp Constructor for chkpoint_two class. */ /*! Constructor \tparam Base base class for recording AD operations using this checkpoint object. \param fun is the function g(x) corresponding to this checkpoint object. \param name is the name used for error reporting. \param internal_bool should sparisity calculations be done using bools (or sets). \param use_hes_sparsity will this checkpoint function be used with Hessian sparsity calculations. \param use_base2ad will this checkpoint function be used with base2ad. \param use_in_parallel will this checkpoint function be used in parallel mode. */ // BEGIN_PROTOTYPE template chkpoint_two::chkpoint_two( const ADFun& fun , const std::string& name , bool internal_bool , bool use_hes_sparsity , bool use_base2ad , bool use_in_parallel ) // END_PROTOTYPE : atomic_three(name) , internal_bool_( internal_bool ) , use_hes_sparsity_( use_hes_sparsity ) , use_base2ad_ ( use_base2ad ) , use_in_parallel_ ( use_in_parallel ) { CPPAD_ASSERT_KNOWN( ! thread_alloc::in_parallel() , "chkpoint_two: constructor cannot be called in parallel mode." ); // initialize member pointers as null; for(size_t thread = 0; thread < CPPAD_MAX_NUM_THREADS; thread++) member_[thread] = nullptr; // // g_ g_ = fun; // // suppress check for nan because chkpoint_two object can be used in a // function that gets optimized and some checkpoint results may not matter. g_.check_for_nan(false); // // ag_ if( use_base2ad ) ag_ = g_.base2ad(); // // jac_sparsity__ size_t n = g_.Domain(); size_t m = g_.Range(); sparse_rc< vector > pattern_in; bool transpose = false; bool dependency = true; if( n <= m || use_hes_sparsity ) { // use forward mode pattern_in.resize(n, n, n); for(size_t k = 0; k < n; ++k) pattern_in.set(k, k, k); g_.for_jac_sparsity( pattern_in, transpose, dependency, internal_bool, jac_sparsity_ ); } else { // use reverse mode pattern_in.resize(m, m, m); for(size_t k = 0; k < m; ++k) pattern_in.set(k, k, k); g_.rev_jac_sparsity( pattern_in, transpose, dependency, internal_bool, jac_sparsity_ ); } // // hes_sparsity_ if( use_hes_sparsity ) { vector select_y(m), select_x(n); for(size_t i = 0; i < m; ++i) select_y[i] = true; if( n <= m ) { for(size_t j = 0; j < n; ++j) select_x[j] = true; g_.for_hes_sparsity( select_x, select_y, internal_bool, hes_sparsity_ ); } else { // forward jacobian sparsity is stored in g_ g_.rev_hes_sparsity( select_y, transpose, internal_bool, hes_sparsity_ ); } } // free memory holding forward Jacobian sparsity if( internal_bool ) g_.size_forward_bool(0); else g_.size_forward_set(0); } /// destructor template chkpoint_two::~chkpoint_two(void) { # ifndef NDEBUG if( thread_alloc::in_parallel() ) { std::string msg = atomic_three::atomic_name(); msg += ": chkpoint_two destructor called in parallel mode."; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # endif for(size_t thread = 0; thread < CPPAD_MAX_NUM_THREADS; ++thread) free_member(thread); } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/chkpoint_two/dynamic.hpp ================================================ # ifndef CPPAD_CORE_CHKPOINT_TWO_DYNAMIC_HPP # define CPPAD_CORE_CHKPOINT_TWO_DYNAMIC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin chkpoint_two_dynamic} {xrst_spell chk dyn } Dynamic Parameters in Checkpoint Functions ########################################## Syntax ****** | *chk_fun* . ``new_dynamic`` ( *dynamic* ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } chk_fun ******* This object must have been created using the :ref:`chkpoint_two` constructor. Base ==== This is the :ref:`chkpoint_two_ctor@Base` type in the *chk_fun* constructor. fun === This is the function :ref:`chkpoint_two_ctor@fun` in the *chk_fun* constructor. BaseVector ********** This must be a :ref:`SimpleVector-name` with elements of type *Base* . dynamic ******* This is a vector with new values for the dynamic parameters in the function *fun* . Is size must be equal to :ref:`fun.size_dyn_ind()` . This only affects the copy of *fun* used by *chk_fun* . Multi-Threading *************** If one is using :ref:`in_parallel` , there is a separate copy of *fun* for each thread. In this case, only the dynamic parameters in the copy for the current :ref:`thread number` are changed. {xrst_end chkpoint_two_dynamic} */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file chkpoint_two/dynamic.hpp Change the dynnamic parameter in a checkpoint function. */ /*! Constructor \tparam Base base class for recording AD operations using this checkpoint object. \param dynamic is the new values for the dynamic parameters in the function defining this checkpoint object. */ // BEGIN_PROTOTYPE template template void chkpoint_two::new_dynamic(const BaseVector& dynamic) // END_PROTOTYPE { ADFun* g_ptr = &g_; if( use_in_parallel_ ) { size_t thread = thread_alloc::thread_num(); allocate_member(thread); g_ptr = &(member_[thread]->g_); } # ifndef NDEBUG else if( thread_alloc::in_parallel() ) { std::string msg = atomic_three::atomic_name(); msg += ": use_in_parallel is false and in_parallel() is true"; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # endif g_ptr->new_dynamic(dynamic); } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/chkpoint_two/for_type.hpp ================================================ # ifndef CPPAD_CORE_CHKPOINT_TWO_FOR_TYPE_HPP # define CPPAD_CORE_CHKPOINT_TWO_FOR_TYPE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file chkpoint_two/for_type.hpp Second generation checkpoint type computation. */ /*! Link from atomic_three to type calculation \param parameter_x [in] is the value of the parameters in the corresponding function call afun(ax, ay). \param type_x [in] specifies which components of x are constants, dynamics, and variables \param type_y [out] specifies which components of y are constants, dynamics, and variables */ template bool chkpoint_two::for_type( const vector& parameter_x , const vector& type_x , vector& type_y ) { size_t nr = jac_sparsity_.nr(); size_t nnz = jac_sparsity_.nnz(); const vector& row( jac_sparsity_.row() ); const vector& col( jac_sparsity_.col() ); // CPPAD_ASSERT_UNKNOWN( jac_sparsity_.nr() == type_y.size() ); CPPAD_ASSERT_UNKNOWN( jac_sparsity_.nc() == type_x.size() ); // // initialize type_y as constant_enum for(size_t i = 0; i < nr; ++i) type_y[i] = constant_enum; // // loop over entries in Dependency pattern for(size_t k = 0; k < nnz; ++k) { size_t i = row[k]; size_t j = col[k]; type_y[i] = std::max(type_y[i], type_x[j]); } return true; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/chkpoint_two/forward.hpp ================================================ # ifndef CPPAD_CORE_CHKPOINT_TWO_FORWARD_HPP # define CPPAD_CORE_CHKPOINT_TWO_FORWARD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file chkpoint_two/forward.hpp Second generation checkpoint forward mode. */ /*! Link from chkpoint_two to forward mode \param parameter_x [in] contains the values, in afun(ax, ay), for arguments that are parameters. \param type_x [in] what is the type, in afun(ax, ay), for each component of x. \param need_y [in] specifies which components of taylor_y are needed, \param order_low [in] lowerest order for this forward mode calculation. \param order_up [in] highest order for this forward mode calculation. \param taylor_x [in] Taylor coefficients corresponding to x for this calculation. \param taylor_y [out] Taylor coefficient corresponding to y for this calculation See the forward mode in user's documentation for atomic_three */ template bool chkpoint_two::forward( const vector& parameter_x , const vector& type_x , size_t need_y , size_t order_low , size_t order_up , const vector& taylor_x , vector& taylor_y ) { ADFun* g_ptr = &g_; if( use_in_parallel_ ) { size_t thread = thread_alloc::thread_num(); allocate_member(thread); g_ptr = &(member_[thread]->g_); } # ifndef NDEBUG else if( thread_alloc::in_parallel() ) { std::string msg = atomic_three::atomic_name(); msg += ": use_in_parallel is false and in_parallel() is true"; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # endif // compute forward mode results for all values and orders taylor_y = g_ptr->Forward(order_up, taylor_x); // return true; } /*! Link from chkpoint_two to AD forward mode \param aparameter_x [in] contains the values, in afun(ax, ay), for arguments that are parameters. \param type_x [in] what is the type, in afun(ax, ay), for each component of x. \param need_y [in] specifies which components of taylor_y are needed, \param order_low [in] lowerest order for this forward mode calculation. \param order_up [in] highest order for this forward mode calculation. \param ataylor_x [in] Taylor coefficients corresponding to x for this calculation. \param ataylor_y [out] Taylor coefficient corresponding to y for this calculation See the forward mode in user's documentation for atomic_three */ template bool chkpoint_two::forward( const vector< AD >& aparameter_x , const vector& type_x , size_t need_y , size_t order_low , size_t order_up , const vector< AD >& ataylor_x , vector< AD >& ataylor_y ) { if( ! use_base2ad_ ) return false; // ADFun< AD, Base >* ag_ptr = &ag_; if( use_in_parallel_ ) { size_t thread = thread_alloc::thread_num(); allocate_member(thread); ag_ptr = &(member_[thread]->ag_); } # ifndef NDEBUG else if( thread_alloc::in_parallel() ) { std::string msg = atomic_three::atomic_name(); msg += ": use_in_parallel is false and in_parallel() is true"; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # endif // compute forward mode results for all values and orders ataylor_y = ag_ptr->Forward(order_up, ataylor_x); // return true; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/chkpoint_two/hes_sparsity.hpp ================================================ # ifndef CPPAD_CORE_CHKPOINT_TWO_HES_SPARSITY_HPP # define CPPAD_CORE_CHKPOINT_TWO_HES_SPARSITY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file chkpoint_two/hes_sparsity.hpp Second generation checkpoint Jacobian sparsity patterns. */ /*! chkpoint_two to Hessian sparsity calculations. \param parameter_x [in] contains the values for arguments that are parameters. \param type_x [in] what is the type, in afun(ax, ay), for each component of x. \param select_x [in] which domain components to include in the dependency or sparsity pattern. The index zero is used for parameters. \param select_y [in] which range components to include in the dependency or sparsity pattern. The index zero is used for parameters. This argument is ignored because the sparsity pattern corresponding to all components true is computed during the construction and used for all cases. This errors on the side of caution for the sake of speed. \param pattern_out [out] is the dependency or sparsity pattern. */ // BEGIN_PROTOTYPE template bool chkpoint_two::hes_sparsity( const vector& parameter_x , const vector& type_x , const vector& select_x , const vector& select_y , sparse_rc< vector >& pattern_out ) // END_PROTOTYPE { CPPAD_ASSERT_UNKNOWN( hes_sparsity_.nr() == select_x.size() ); CPPAD_ASSERT_UNKNOWN( hes_sparsity_.nc() == select_x.size() ); if( ! use_hes_sparsity_ ) return false; // count number of non-zeros size_t nnz = hes_sparsity_.nnz(); size_t nr = hes_sparsity_.nr(); size_t nc = hes_sparsity_.nc(); const vector& row = hes_sparsity_.row(); const vector& col = hes_sparsity_.col(); size_t nnz_out = 0; for(size_t k = 0; k < nnz; ++k) { size_t i = row[k]; size_t j = col[k]; if( select_x[j] && select_x[i] ) ++nnz_out; } // set the output sparsity pattern pattern_out.resize(nr, nc, nnz_out); size_t ell = 0; for(size_t k = 0; k < nnz; ++k) { size_t i = row[k]; size_t j = col[k]; if( select_x[j] && select_x[i] ) pattern_out.set(ell++, i, j); } CPPAD_ASSERT_UNKNOWN( ell == nnz_out ); // return true; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/chkpoint_two/jac_sparsity.hpp ================================================ # ifndef CPPAD_CORE_CHKPOINT_TWO_JAC_SPARSITY_HPP # define CPPAD_CORE_CHKPOINT_TWO_JAC_SPARSITY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file chkpoint_two/jac_sparsity.hpp Second generation checkpoint Jacobian sparsity patterns. */ /*! chkpoint_two to Jacobian sparsity calculations. \param dependency [in] This argument is ignored. The return pattern is always a dependency pattern which is a correct, but possibly not efficient, sparsity pattern. \param parameter_x [in] contains the values for arguments that are parameters. \param type_x [in] what is the type, in afun(ax, ay), for each component of x. \param select_x [in] which domain components to include in the dependency or sparsity pattern. The index zero is used for parameters. \param select_y [in] which range components to include in the dependency or sparsity pattern. The index zero is used for parameters. \param pattern_out [out] is the dependency or sparsity pattern. */ // BEGIN_PROTOTYPE template bool chkpoint_two::jac_sparsity( const vector& parameter_x , const vector& type_x , bool dependency , const vector& select_x , const vector& select_y , sparse_rc< vector >& pattern_out ) // END_PROTOTYPE { CPPAD_ASSERT_UNKNOWN( jac_sparsity_.nr() == select_y.size() ); CPPAD_ASSERT_UNKNOWN( jac_sparsity_.nc() == select_x.size() ); // count number of non-zeros size_t nnz = jac_sparsity_.nnz(); size_t nr = jac_sparsity_.nr(); size_t nc = jac_sparsity_.nc(); const vector& row = jac_sparsity_.row(); const vector& col = jac_sparsity_.col(); size_t nnz_out = 0; for(size_t k = 0; k < nnz; ++k) { size_t i = row[k]; size_t j = col[k]; if( select_x[j] && select_y[i] ) ++nnz_out; } // set the output sparsity pattern pattern_out.resize(nr, nc, nnz_out); size_t ell = 0; for(size_t k = 0; k < nnz; ++k) { size_t i = row[k]; size_t j = col[k]; if( select_x[j] && select_y[i] ) pattern_out.set(ell++, i, j); } CPPAD_ASSERT_UNKNOWN( ell == nnz_out ); // return true; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/chkpoint_two/rev_depend.hpp ================================================ # ifndef CPPAD_CORE_CHKPOINT_TWO_REV_DEPEND_HPP # define CPPAD_CORE_CHKPOINT_TWO_REV_DEPEND_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file chkpoint_two/rev_depend.hpp Second generation checkpoint type computation. */ /*! Link from atomic_three to dependency calculation \param parameter_x [in] is the value of the parameters in the corresponding function call afun(ax, ay). \param type_x [in] is the AD type for ax in the corresponding afun(ax, ay) call. \param depend_x [out] specifies which components of x affect the values of interest \param depend_y [in] specifies which components of y affect the values of interest */ template bool chkpoint_two::rev_depend( const vector& parameter_x , const vector& type_x , vector& depend_x , const vector& depend_y ) { size_t nc = jac_sparsity_.nc(); size_t nnz = jac_sparsity_.nnz(); const vector& row( jac_sparsity_.row() ); const vector& col( jac_sparsity_.col() ); // CPPAD_ASSERT_UNKNOWN( jac_sparsity_.nr() == depend_y.size() ); CPPAD_ASSERT_UNKNOWN( jac_sparsity_.nc() == depend_x.size() ); // // initialize depend_x as false for(size_t j = 0; j < nc; ++j) depend_x[j] = false; // // loop over entries in Dependency pattern for(size_t k = 0; k < nnz; ++k) { size_t i = row[k]; size_t j = col[k]; if( depend_y[i] ) depend_x[j] = true; } return true; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/chkpoint_two/reverse.hpp ================================================ # ifndef CPPAD_CORE_CHKPOINT_TWO_REVERSE_HPP # define CPPAD_CORE_CHKPOINT_TWO_REVERSE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file chkpoint_two/reverse.hpp Second generation checkpoint reverse mode. */ /*! Link from chkpoint_two to reverse mode \param parameter_x [in] contains the values, in afun(ax, ay), for arguments that are parameters. \param type_x [in] what is the type, in afun(ax, ay), for each component of x. \param order_up [in] highest order Taylor coefficient aht we are computing derivative of \param taylor_x [in] Taylor coefficients corresponding to x for this calculation. \param taylor_y [in] Taylor coefficient corresponding to y for this calculation \param partial_x [out] Partials w.r.t. the x Taylor coefficients. \param partial_y [in] Partials w.r.t. the y Taylor coefficients. See the reverse mode in user's documentation for atomic_three */ template bool chkpoint_two::reverse( const vector& parameter_x , const vector& type_x , size_t order_up , const vector& taylor_x , const vector& taylor_y , vector& partial_x , const vector& partial_y ) { ADFun* g_ptr = &g_; if( use_in_parallel_ ) { size_t thread = thread_alloc::thread_num(); allocate_member(thread); g_ptr = &(member_[thread]->g_); } # ifndef NDEBUG else if( thread_alloc::in_parallel() ) { std::string msg = atomic_three::atomic_name(); msg += ": use_in_parallel is false and in_parallel() is true"; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # endif // compute forward mode Taylor coefficient orders 0 through order_up # ifdef NDEBUG g_ptr->Forward(order_up, taylor_x); # else vector check = g_ptr->Forward(order_up, taylor_x); CPPAD_ASSERT_UNKNOWN( taylor_y.size() == check.size() ) for(size_t i = 0; i < taylor_y.size(); ++i) CPPAD_ASSERT_UNKNOWN( taylor_y[i] == check[i] ); # endif // now can run reverse mode partial_x = g_ptr->Reverse(order_up+1, partial_y); // return true; } /*! Link from chkpoint_two to AD reverse mode \param aparameter_x [in] contains the values, in afun(ax, ay), for arguments that are parameters. \param type_x [in] what is the type, in afun(ax, ay), for each component of x. \param order_up [in] highest order Taylor coefficient aht we are computing derivative of \param ataylor_x [in] Taylor coefficients corresponding to x for this calculation. \param ataylor_y [in] Taylor coefficient corresponding to y for this calculation \param apartial_x [out] Partials w.r.t. the x Taylor coefficients. \param apartial_y [in] Partials w.r.t. the y Taylor coefficients. See the reverse mode in user's documentation for atomic_three */ template bool chkpoint_two::reverse( const vector< AD >& aparameter_x , const vector& type_x , size_t order_up , const vector< AD >& ataylor_x , const vector< AD >& ataylor_y , vector< AD >& apartial_x , const vector< AD >& apartial_y ) { ADFun< AD, Base >* ag_ptr = &ag_; if( use_in_parallel_ ) { size_t thread = thread_alloc::thread_num(); allocate_member(thread); ag_ptr = &(member_[thread]->ag_); } // compute forward mode Taylor coefficient orders 0 through order_up # ifdef NDEBUG ag_ptr->Forward(order_up, ataylor_x); # else vector< AD > acheck = ag_ptr->Forward(order_up, ataylor_x); CPPAD_ASSERT_UNKNOWN( ataylor_y.size() == acheck.size() ) for(size_t i = 0; i < ataylor_y.size(); ++i) CPPAD_ASSERT_UNKNOWN( ataylor_y[i] == acheck[i] ); # endif // now can run reverse mode apartial_x = ag_ptr->Reverse(order_up+1, apartial_y); // return true; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/compare.hpp ================================================ # ifndef CPPAD_CORE_COMPARE_HPP # define CPPAD_CORE_COMPARE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* ------------------------------------------------------------------------------- {xrst_begin Compare} AD Binary Comparison Operators ############################## Syntax ****** | *b* = *x* *Op* *y* Purpose ******* Compares two operands where one of the operands is an ``AD`` < *Base* > object. The comparison has the same interpretation as for the *Base* type. Op ** The operator *Op* is one of the following: .. csv-table:: :widths: auto **Op**,**Meaning** ``<``,is *x* less than *y* ``<=``,is *x* less than or equal *y* ``>``,is *x* greater than *y* ``>=``,is *x* greater than or equal *y* ``==``,is *x* equal to *y* ``!=``,is *x* not equal to *y* x * The operand *x* has prototype ``const`` *Type* & *x* where *Type* is ``AD`` < *Base* > , *Base* , or ``int`` . y * The operand *y* has prototype ``const`` *Type* & *y* where *Type* is ``AD`` < *Base* > , *Base* , or ``int`` . b * The result *b* has type ``bool`` *b* Operation Sequence ****************** The result of this operation is a ``bool`` value (not an :ref:`glossary@AD of Base` object). Thus it will not be recorded as part of an AD of *Base* :ref:`operation sequence` . For example, suppose *x* and *y* are ``AD`` < *Base* > objects, the tape corresponding to ``AD`` < *Base* > is recording, *b* is true, and the subsequent code is | |tab| ``if`` ( *b* ) | |tab| |tab| *y* = ``cos`` ( *x* ); | |tab| ``else`` | |tab| |tab| *y* = ``sin`` ( *x* ); only the assignment *y* = ``cos`` ( *x* ) is recorded on the tape (if *x* is a :ref:`glossary@Parameter` , nothing is recorded). The :ref:`CompareChange-name` function can yield some information about changes in comparison operation results. You can use :ref:`CondExp-name` to obtain comparison operations that depends on the :ref:`glossary@Tape@Independent Variable` values with out re-taping the AD sequence of operations. Assumptions *********** If one of the *Op* operators listed above is used with an ``AD`` < *Base* > object, it is assumed that the same operator is supported by the base type *Base* . Example ******* {xrst_toc_hidden example/general/compare.cpp } The file :ref:`compare.cpp-name` contains an example and test of these operations. {xrst_end Compare} ------------------------------------------------------------------------------- */ // BEGIN CppAD namespace namespace CppAD { // -------------------------------- < -------------------------- template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION bool operator < (const AD &left , const AD &right) { bool result = (left.value_ < right.value_); // // check if we are recording compare operators local::ADTape *tape = AD::tape_ptr(); if( tape == nullptr ) return result; if( ! tape->Rec_.get_record_compare() ) return result; tape_id_t tape_id = tape->id_; // tape_id cannot match the default value for tape_id_; i.e., 0 CPPAD_ASSERT_UNKNOWN( tape_id > 0 ); // check if left and right tapes match bool match_left = left.tape_id_ == tape_id; bool match_right = right.tape_id_ == tape_id; // check if left and right are dynamic parameters bool dyn_left = match_left & (left.ad_type_ == dynamic_enum); bool dyn_right = match_right & (right.ad_type_ == dynamic_enum); // check if left and right are variables bool var_left = match_left & (left.ad_type_ != dynamic_enum); bool var_right = match_right & (right.ad_type_ != dynamic_enum); CPPAD_ASSERT_KNOWN( left.tape_id_ == right.tape_id_ || ! match_left || ! match_right , "< : AD variables or dynamic parameters on different threads." ); if( var_left ) { if( var_right ) { // variable < variable if( result ) { tape->Rec_.PutOp(local::LtvvOp); tape->Rec_.PutArg(left.taddr_, right.taddr_); } else { tape->Rec_.PutOp(local::LevvOp); tape->Rec_.PutArg(right.taddr_, left.taddr_); } } else { // variable < parameter addr_t p = right.taddr_; if( ! dyn_right ) p = tape->Rec_.put_con_par(right.value_); if( result ) { tape->Rec_.PutOp(local::LtvpOp); tape->Rec_.PutArg(left.taddr_, p); } else { tape->Rec_.PutOp(local::LepvOp); tape->Rec_.PutArg(p, left.taddr_); } } } else if ( var_right ) { // parameter < variable addr_t p = left.taddr_; if( ! dyn_left ) p = tape->Rec_.put_con_par(left.value_); if( result ) { tape->Rec_.PutOp(local::LtpvOp); tape->Rec_.PutArg(p, right.taddr_); } else { tape->Rec_.PutOp(local::LevpOp); tape->Rec_.PutArg(right.taddr_, p); } } else if( dyn_left | dyn_right ) { // parameter < parameter addr_t arg0 = left.taddr_; addr_t arg1 = right.taddr_; if( ! dyn_left ) arg0 = tape->Rec_.put_con_par(left.value_); if( ! dyn_right ) arg1 = tape->Rec_.put_con_par(right.value_); // if( result ) { tape->Rec_.PutOp(local::LtppOp); tape->Rec_.PutArg(arg0, arg1); } else { tape->Rec_.PutOp(local::LeppOp); tape->Rec_.PutArg(arg1, arg0); } } return result; } // convert other cases into the case above CPPAD_FOLD_BOOL_VALUED_BINARY_OPERATOR(<) // -------------------------------- <= -------------------------- template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION bool operator <= (const AD &left , const AD &right) { bool result = (left.value_ <= right.value_); // // check if we are recording compare operators local::ADTape *tape = AD::tape_ptr(); if( tape == nullptr ) return result; if( ! tape->Rec_.get_record_compare() ) return result; tape_id_t tape_id = tape->id_; // tape_id cannot match the default value for tape_id_; i.e., 0 CPPAD_ASSERT_UNKNOWN( tape_id > 0 ); // check if left and right tapes match bool match_left = left.tape_id_ == tape_id; bool match_right = right.tape_id_ == tape_id; // check if left and right are dynamic parameters bool dyn_left = match_left & (left.ad_type_ == dynamic_enum); bool dyn_right = match_right & (right.ad_type_ == dynamic_enum); // check if left and right are variables bool var_left = match_left & (left.ad_type_ != dynamic_enum); bool var_right = match_right & (right.ad_type_ != dynamic_enum); CPPAD_ASSERT_KNOWN( left.tape_id_ == right.tape_id_ || ! match_left || ! match_right , "<= : AD variables or dynamic parameters on different threads." ); if( var_left ) { if( var_right ) { // variable <= variable if( result ) { tape->Rec_.PutOp(local::LevvOp); tape->Rec_.PutArg(left.taddr_, right.taddr_); } else { tape->Rec_.PutOp(local::LtvvOp); tape->Rec_.PutArg(right.taddr_, left.taddr_); } } else { // variable <= parameter addr_t p = right.taddr_; if( ! dyn_right ) p = tape->Rec_.put_con_par(right.value_); if( result ) { tape->Rec_.PutOp(local::LevpOp); tape->Rec_.PutArg(left.taddr_, p); } else { tape->Rec_.PutOp(local::LtpvOp); tape->Rec_.PutArg(p, left.taddr_); } } } else if ( var_right ) { // parameter <= variable addr_t p = left.taddr_; if( ! dyn_left ) p = tape->Rec_.put_con_par(left.value_); if( result ) { tape->Rec_.PutOp(local::LepvOp); tape->Rec_.PutArg(p, right.taddr_); } else { tape->Rec_.PutOp(local::LtvpOp); tape->Rec_.PutArg(right.taddr_, p); } } else if( dyn_left | dyn_right ) { // parameter <= parameter addr_t arg0 = left.taddr_; addr_t arg1 = right.taddr_; if( ! dyn_left ) arg0 = tape->Rec_.put_con_par(left.value_); if( ! dyn_right ) arg1 = tape->Rec_.put_con_par(right.value_); // if( result ) { tape->Rec_.PutOp(local::LeppOp); tape->Rec_.PutArg(arg0, arg1); } else { tape->Rec_.PutOp(local::LtppOp); tape->Rec_.PutArg(arg1, arg0); } } return result; } // convert other cases into the case above CPPAD_FOLD_BOOL_VALUED_BINARY_OPERATOR(<=) // -------------------------------- > -------------------------- template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION bool operator > (const AD &left , const AD &right) { bool result = (left.value_ > right.value_); // // check if we are recording compare operators local::ADTape *tape = AD::tape_ptr(); if( tape == nullptr ) return result; if( ! tape->Rec_.get_record_compare() ) return result; tape_id_t tape_id = tape->id_; // tape_id cannot match the default value for tape_id_; i.e., 0 CPPAD_ASSERT_UNKNOWN( tape_id > 0 ); // check if left and right tapes match bool match_left = left.tape_id_ == tape_id; bool match_right = right.tape_id_ == tape_id; // check if left and right are dynamic parameters bool dyn_left = match_left & (left.ad_type_ == dynamic_enum); bool dyn_right = match_right & (right.ad_type_ == dynamic_enum); // check if left and right are variables bool var_left = match_left & (left.ad_type_ != dynamic_enum); bool var_right = match_right & (right.ad_type_ != dynamic_enum); CPPAD_ASSERT_KNOWN( left.tape_id_ == right.tape_id_ || ! match_left || ! match_right , "> : AD variables or dynamic parameters on different threads." ); if( var_left ) { if( var_right ) { // variable > variable if( result ) { tape->Rec_.PutOp(local::LtvvOp); tape->Rec_.PutArg(right.taddr_, left.taddr_); } else { tape->Rec_.PutOp(local::LevvOp); tape->Rec_.PutArg(left.taddr_, right.taddr_); } } else { // variable > parameter addr_t p = right.taddr_; if( ! dyn_right ) p = tape->Rec_.put_con_par(right.value_); if( result ) { tape->Rec_.PutOp(local::LtpvOp); tape->Rec_.PutArg(p, left.taddr_); } else { tape->Rec_.PutOp(local::LevpOp); tape->Rec_.PutArg(left.taddr_, p); } } } else if ( var_right ) { // parameter > variable addr_t p = left.taddr_; if( ! dyn_left ) p = tape->Rec_.put_con_par(left.value_); if( result ) { tape->Rec_.PutOp(local::LtvpOp); tape->Rec_.PutArg(right.taddr_, p); } else { tape->Rec_.PutOp(local::LepvOp); tape->Rec_.PutArg(p, right.taddr_); } } else if( dyn_left | dyn_right ) { // parameter > parameter addr_t arg0 = left.taddr_; addr_t arg1 = right.taddr_; if( ! dyn_left ) arg0 = tape->Rec_.put_con_par(left.value_); if( ! dyn_right ) arg1 = tape->Rec_.put_con_par(right.value_); // if( result ) { tape->Rec_.PutOp(local::LtppOp); tape->Rec_.PutArg(arg1, arg0); } else { tape->Rec_.PutOp(local::LeppOp); tape->Rec_.PutArg(arg0, arg1); } } return result; } // convert other cases into the case above CPPAD_FOLD_BOOL_VALUED_BINARY_OPERATOR(>) // -------------------------------- >= -------------------------- template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION bool operator >= (const AD &left , const AD &right) { bool result = (left.value_ >= right.value_); // // check if we are recording compare operators local::ADTape *tape = AD::tape_ptr(); if( tape == nullptr ) return result; if( ! tape->Rec_.get_record_compare() ) return result; tape_id_t tape_id = tape->id_; // tape_id cannot match the default value for tape_id_; i.e., 0 CPPAD_ASSERT_UNKNOWN( tape_id > 0 ); // check if left and right tapes match bool match_left = left.tape_id_ == tape_id; bool match_right = right.tape_id_ == tape_id; // check if left and right are dynamic parameters bool dyn_left = match_left & (left.ad_type_ == dynamic_enum); bool dyn_right = match_right & (right.ad_type_ == dynamic_enum); // check if left and right are variables bool var_left = match_left & (left.ad_type_ != dynamic_enum); bool var_right = match_right & (right.ad_type_ != dynamic_enum); CPPAD_ASSERT_KNOWN( left.tape_id_ == right.tape_id_ || ! match_left || ! match_right , ">= : AD variables or dynamic parameters on different threads." ); if( var_left ) { if( var_right ) { // variable >= variable if( result ) { tape->Rec_.PutOp(local::LevvOp); tape->Rec_.PutArg(right.taddr_, left.taddr_); } else { tape->Rec_.PutOp(local::LtvvOp); tape->Rec_.PutArg(left.taddr_, right.taddr_); } } else { // variable >= parameter addr_t p = right.taddr_; if( ! dyn_right ) p = tape->Rec_.put_con_par(right.value_); if( result ) { tape->Rec_.PutOp(local::LepvOp); tape->Rec_.PutArg(p, left.taddr_); } else { tape->Rec_.PutOp(local::LtvpOp); tape->Rec_.PutArg(left.taddr_, p); } } } else if ( var_right ) { // parameter >= variable addr_t p = left.taddr_; if( ! dyn_left ) p = tape->Rec_.put_con_par(left.value_); if( result ) { tape->Rec_.PutOp(local::LevpOp); tape->Rec_.PutArg(right.taddr_, p); } else { tape->Rec_.PutOp(local::LtpvOp); tape->Rec_.PutArg(p, right.taddr_); } } else if( dyn_left | dyn_right ) { // parameter >= parameter addr_t arg0 = left.taddr_; addr_t arg1 = right.taddr_; if( ! dyn_left ) arg0 = tape->Rec_.put_con_par(left.value_); if( ! dyn_right ) arg1 = tape->Rec_.put_con_par(right.value_); // if( result ) { tape->Rec_.PutOp(local::LeppOp); tape->Rec_.PutArg(arg1, arg0); } else { tape->Rec_.PutOp(local::LtppOp); tape->Rec_.PutArg(arg0, arg1); } } return result; } // convert other cases into the case above CPPAD_FOLD_BOOL_VALUED_BINARY_OPERATOR(>=) // -------------------------------- == ------------------------- template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION bool operator == (const AD &left , const AD &right) { bool result = (left.value_ == right.value_); // // check if we are recording compare operators local::ADTape *tape = AD::tape_ptr(); if( tape == nullptr ) return result; if( ! tape->Rec_.get_record_compare() ) return result; tape_id_t tape_id = tape->id_; // tape_id cannot match the default value for tape_id_; i.e., 0 CPPAD_ASSERT_UNKNOWN( tape_id > 0 ); // check if left and right tapes match bool match_left = left.tape_id_ == tape_id; bool match_right = right.tape_id_ == tape_id; // check if left and right are dynamic parameters bool dyn_left = match_left & (left.ad_type_ == dynamic_enum); bool dyn_right = match_right & (right.ad_type_ == dynamic_enum); // check if left and right are variables bool var_left = match_left & (left.ad_type_ != dynamic_enum); bool var_right = match_right & (right.ad_type_ != dynamic_enum); CPPAD_ASSERT_KNOWN( left.tape_id_ == right.tape_id_ || ! match_left || ! match_right , "==: AD variables or dynamic parameters on different threads." ); // tape->Rec_.comp_eq( var_left, var_right, dyn_left, dyn_right, left, right, result ); // return result; } // convert other cases into the case above CPPAD_FOLD_BOOL_VALUED_BINARY_OPERATOR(==) // -------------------------------- != ------------------------- template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION bool operator != (const AD &left , const AD &right) { bool result = (left.value_ != right.value_); // // check if we are recording compare operators local::ADTape *tape = AD::tape_ptr(); if( tape == nullptr ) return result; if( ! tape->Rec_.get_record_compare() ) return result; tape_id_t tape_id = tape->id_; // tape_id cannot match the default value for tape_id_; i.e., 0 CPPAD_ASSERT_UNKNOWN( tape_id > 0 ); // check if left and right tapes match bool match_left = left.tape_id_ == tape_id; bool match_right = right.tape_id_ == tape_id; // check if left and right are dynamic parameters bool dyn_left = match_left & (left.ad_type_ == dynamic_enum); bool dyn_right = match_right & (right.ad_type_ == dynamic_enum); // check if left and right are variables bool var_left = match_left & (left.ad_type_ != dynamic_enum); bool var_right = match_right & (right.ad_type_ != dynamic_enum); CPPAD_ASSERT_KNOWN( left.tape_id_ == right.tape_id_ || ! match_left || ! match_right , "!=: AD variables or dynamic parameters on different threads." ); if( var_left ) { if( var_right ) { // variable == variable tape->Rec_.PutArg(left.taddr_, right.taddr_); if( result ) tape->Rec_.PutOp(local::NevvOp); else tape->Rec_.PutOp(local::EqvvOp); } else { // variable == parameter addr_t p = right.taddr_; if( ! dyn_right ) p = tape->Rec_.put_con_par(right.value_); tape->Rec_.PutArg(p, left.taddr_); if( result ) tape->Rec_.PutOp(local::NepvOp); else tape->Rec_.PutOp(local::EqpvOp); } } else if ( var_right ) { // parameter == variable addr_t p = left.taddr_; if( ! dyn_left ) p = tape->Rec_.put_con_par(left.value_); tape->Rec_.PutArg(p, right.taddr_); if( result ) tape->Rec_.PutOp(local::NepvOp); else tape->Rec_.PutOp(local::EqpvOp); } else if( dyn_left | dyn_right ) { // parameter == parameter addr_t arg0 = left.taddr_; addr_t arg1 = right.taddr_; if( ! dyn_left ) arg0 = tape->Rec_.put_con_par(left.value_); if( ! dyn_right ) arg1 = tape->Rec_.put_con_par(right.value_); // tape->Rec_.PutArg(arg0, arg1); if( result ) tape->Rec_.PutOp(local::NeppOp); else tape->Rec_.PutOp(local::EqppOp); } return result; } // convert other cases into the case above CPPAD_FOLD_BOOL_VALUED_BINARY_OPERATOR(!=) } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/compound_assign.hpp ================================================ # ifndef CPPAD_CORE_COMPOUND_ASSIGN_HPP # define CPPAD_CORE_COMPOUND_ASSIGN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* ------------------------------------------------------------------------------- {xrst_begin compound_assign} {xrst_spell div } AD Compound Assignment Operators ################################ Syntax ****** | *x* *Op* *y* Purpose ******* Performs compound assignment operations where either *x* has type ``AD`` < *Base* > . Op ** The operator *Op* is one of the following .. csv-table:: :widths: auto **Op**,**Meaning** ``+=``,*x* is assigned *x* plus *y* ``-=``,*x* is assigned *x* minus *y* ``*=``,*x* is assigned *x* times *y* ``/=``,*x* is assigned *x* divided by *y* Base **** The type *Base* is determined by the operand *x* . x * The operand *x* has the following prototype ``AD`` < *Base* > & *x* y * The operand *y* has the following prototype ``const`` *Type* & *y* where *Type* is ``VecAD`` < *Base* >:: ``reference`` , ``AD`` < *Base* > , *Base* , or ``double`` . Result ****** The result of this assignment can be used as a reference to *x* . For example, if *z* has the following type ``AD`` < *Base* > *z* then the syntax *z* = *x* += *y* will compute *x* plus *y* and then assign this value to both *x* and *z* . Operation Sequence ****************** This is an :ref:`atomic_base` :ref:`glossary@AD of Base` operation and hence it is part of the current AD of *Base* :ref:`operation sequence` . {xrst_toc_hidden example/general/add_eq.cpp example/general/sub_eq.cpp example/general/mul_eq.cpp example/general/div_eq.cpp } Example ******* The following files contain examples and tests of these functions. Each test returns true if it succeeds and false otherwise. .. csv-table:: :widths: auto add_eq.cpp,:ref:`add_eq.cpp-title` sub_eq.cpp,:ref:`sub_eq.cpp-title` mul_eq.cpp,:ref:`mul_eq.cpp-title` div_eq.cpp,:ref:`div_eq.cpp-title` Derivative ********** If :math:`f` and :math:`g` are :ref:`Base functions` Addition ======== .. math:: \D{[ f(x) + g(x) ]}{x} = \D{f(x)}{x} + \D{g(x)}{x} Subtraction =========== .. math:: \D{[ f(x) - g(x) ]}{x} = \D{f(x)}{x} - \D{g(x)}{x} Multiplication ============== .. math:: \D{[ f(x) * g(x) ]}{x} = g(x) * \D{f(x)}{x} + f(x) * \D{g(x)}{x} Division ======== .. math:: \D{[ f(x) / g(x) ]}{x} = [1/g(x)] * \D{f(x)}{x} - [f(x)/g(x)^2] * \D{g(x)}{x} {xrst_end compound_assign} ----------------------------------------------------------------------------- */ # include # include # include # include # endif ================================================ FILE: include/cppad/core/con_dyn_var.hpp ================================================ # ifndef CPPAD_CORE_CON_DYN_VAR_HPP # define CPPAD_CORE_CON_DYN_VAR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* --------------------------------------------------------------------------- {xrst_begin con_dyn_var} Constant, Dynamic, Parameter, and Variable ########################################## Syntax ****** | *b* = ``Constant`` ( *x* ) | *b* = ``Dynamic`` ( *x* ) | *b* = ``Parameter`` ( *x* ) | *b* = ``Variable`` ( *x* ) x * The argument *x* has prototype | |tab| ``const AD`` < *Base* > & *x* | |tab| ``const VecAD`` < *Base* > & *x* b * The return value *b* has prototype ``bool`` *b* Constant ******** The return value for ``Constant`` is true is true if and only if *x* is a :ref:`glossary@Parameter@Constant` parameter. A :ref:`VecAD\` object is a constant parameter if no element of the vector depends on the independent variables. Dynamic ******* The return value for ``Dynamic`` is true is true if and only if *x* is a :ref:`glossary@Parameter@Dynamic` parameter. No element of a :ref:`VecAD\` object can depend on the dynamic parameters and this function returns false for these objects. Parameter ********* The return value for ``Parameter`` is true is true if and only if *x* is a :ref:`glossary@Parameter` . A :ref:`VecAD\` object is a parameter if no element of the vector depends on the independent variables. Variable ******** The return value for ``Variable`` is true is true if and only if *x* is a :ref:`glossary@Variable` . A :ref:`VecAD\` object is a variable if any element of the vector depends on the independent variables. Operation Sequence ****************** The result of this operation is not an :ref:`glossary@AD of Base` object. Thus it will not be recorded as part of an AD of *Base* :ref:`operation sequence` . Example ******* {xrst_toc_hidden example/general/con_dyn_var.cpp } The file :ref:`con_dyn_var.cpp-name` contains an example and test of these functions. {xrst_end con_dyn_var} ----------------------------------------------------------------------------- */ namespace CppAD { // ----------------------------------------------------------------------- // Constant template bool Constant(const AD &x) { CPPAD_ASSERT_AD_TYPE( x ); if( x.tape_id_ == 0 ) return true; // size_t thread = size_t(x.tape_id_ % CPPAD_MAX_NUM_THREADS); return x.tape_id_ != *AD::tape_id_ptr(thread); } // template bool Constant(const VecAD &x) { CPPAD_ASSERT_AD_TYPE( x ); if( x.tape_id_ == 0 ) return true; // size_t thread = size_t(x.tape_id_ % CPPAD_MAX_NUM_THREADS); return x.tape_id_ != *AD::tape_id_ptr(thread); } // ----------------------------------------------------------------------- // Dynamic template bool Dynamic(const AD &x) { CPPAD_ASSERT_AD_TYPE( x ); if( (x.tape_id_ == 0) || (x.ad_type_ != dynamic_enum) ) return false; // size_t thread = size_t(x.tape_id_ % CPPAD_MAX_NUM_THREADS); return x.tape_id_ == *AD::tape_id_ptr(thread); } // template bool Dynamic(const VecAD &x) { CPPAD_ASSERT_AD_TYPE( x ); if( (x.tape_id_ == 0) || (x.ad_type_ != dynamic_enum) ) return false; // size_t thread = size_t(x.tape_id_ % CPPAD_MAX_NUM_THREADS); return x.tape_id_ == *AD::tape_id_ptr(thread); } // ----------------------------------------------------------------------- // Parameter template bool Parameter(const AD &x) { CPPAD_ASSERT_AD_TYPE( x ); if( (x.tape_id_ == 0) || (x.ad_type_ == dynamic_enum) ) return true; // size_t thread = size_t(x.tape_id_ % CPPAD_MAX_NUM_THREADS); return x.tape_id_ != *AD::tape_id_ptr(thread); } // template bool Parameter(const VecAD &x) { CPPAD_ASSERT_AD_TYPE( x ); if( (x.tape_id_ == 0) || (x.ad_type_ == dynamic_enum) ) return true; // size_t thread = size_t(x.tape_id_ % CPPAD_MAX_NUM_THREADS); return x.tape_id_ != *AD::tape_id_ptr(thread); } // ----------------------------------------------------------------------- // Variable template bool Variable(const AD &x) { CPPAD_ASSERT_AD_TYPE( x ); if( (x.tape_id_ == 0) || (x.ad_type_ != variable_enum) ) return false; // size_t thread = size_t(x.tape_id_ % CPPAD_MAX_NUM_THREADS); return x.tape_id_ == *AD::tape_id_ptr(thread); } // template bool Variable(const VecAD &x) { CPPAD_ASSERT_AD_TYPE( x ); if( (x.tape_id_ == 0) || (x.ad_type_ != variable_enum) ) return false; // size_t thread = size_t(x.tape_id_ % CPPAD_MAX_NUM_THREADS); return x.tape_id_ == *AD::tape_id_ptr(thread); } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/cond_exp.hpp ================================================ # ifndef CPPAD_CORE_COND_EXP_HPP # define CPPAD_CORE_COND_EXP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* ------------------------------------------------------------------------------- {xrst_begin CondExp} AD Conditional Expressions ########################## Syntax ****** | *result* = ``CondExp`` *Rel* ( *left* , *right* , *if_true* , *if_false* ) Purpose ******* Record, as part of an AD of *Base* :ref:`operation sequence` , the conditional result | |tab| ``if`` ( *left* *Cop* *right* ) | |tab| |tab| *result* = *if_true* | |tab| ``else`` | |tab| |tab| *result* = *if_false* The relational *Rel* and comparison operator *Cop* above have the following correspondence: .. csv-table:: *Rel* , ``Lt`` , ``Le`` , ``Eq`` , ``Ge`` , ``Gt`` *Cop* , < , <= , == , >= , > If *f* is the :ref:`ADFun-name` object corresponding to the AD operation sequence, the assignment choice for *result* in an AD conditional expression is made each time :ref:`f.Forward` is used to evaluate the zero order Taylor coefficients with new values for the :ref:`independent variables` . This is in contrast to the :ref:`AD comparison operators` which are boolean valued and not included in the AD operation sequence. Rel *** In the syntax above, the relation *Rel* represents one of the following two characters: ``Lt`` , ``Le`` , ``Eq`` , ``Ge`` , ``Gt`` . As in the table above, *Rel* determines which comparison operator *Cop* is used when comparing *left* and *right* . Type **** These functions are defined in the CppAD namespace for arguments of *Type* is *Base* or ``AD`` < *Base* > . Note that all four arguments, *left* , *right*, *if_true* , *if_false* , must have the same type. left **** The argument *left* has prototype ``const`` *Type* & *left* It specifies the value for the left side of the comparison operator. right ***** The argument *right* has prototype ``const`` *Type* & *right* It specifies the value for the right side of the comparison operator. if_true ******* The argument *if_true* has prototype ``const`` *Type* & *if_true* It specifies the return value if the result of the comparison is true. if_false ******** The argument *if_false* has prototype ``const`` *Type* & *if_false* It specifies the return value if the result of the comparison is false. result ****** The *result* has prototype *Type* & *if_false* Optimize ******** The :ref:`optimize-name` method will optimize conditional expressions in the following way: During :ref:`zero order forward mode` , once the value of the *left* and *right* have been determined, it is known if the true or false case is required. From this point on, values corresponding to the case that is not required are not computed. This optimization is done for the rest of zero order forward mode as well as forward and reverse derivatives calculations. Deprecate 2005-08-07 ******************** Previous versions of CppAD used ``CondExp`` ( *flag* , *if_true* , *if_false* ) for the same meaning as ``CondExpGt`` ( *flag* , *Type* (0), *if_true* , *if_false* ) Use of ``CondExp`` is deprecated, but continues to be supported. Operation Sequence ****************** This is an AD of *Base* :ref:`atomic operation` and hence is part of the current AD of *Base* :ref:`operation sequence` . Example ******* Test **** {xrst_toc_hidden example/general/cond_exp.cpp } The file :ref:`cond_exp.cpp-name` contains an example and test of this function. Atan2 ***** The following implementation of the AD :ref:`atan2-name` function is a more complex example of using conditional expressions: {xrst_literal include/cppad/core/atan2.hpp BEGIN CondExp // END CondExp } {xrst_end CondExp} ------------------------------------------------------------------------------- */ // BEGIN CppAD namespace namespace CppAD { template AD CondExpOp( enum CompareOp cop , const AD &left , const AD &right , const AD &if_true , const AD &if_false ) { AD result; CPPAD_ASSERT_UNKNOWN( Parameter(result) ); // check first case where do not need to tape if( IdenticalCon(left) && IdenticalCon(right) ) { result = CondExpOp( cop, left.value_, right.value_, if_true.value_, if_false.value_ ); return result; } // must use CondExp in case Base is an AD type and recording result.value_ = CondExpOp(cop, left.value_, right.value_, if_true.value_, if_false.value_); local::ADTape *tape = AD::tape_ptr(); // add this operation to the tape if( tape != nullptr ) tape->Rec_.cond_exp( tape->id_, cop, result, left, right, if_true, if_false ); return result; } // ------------ CondExpOp(left, right, if_true, if_false) ---------------- # define CPPAD_COND_EXP(Name) \ template \ CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION \ AD CondExp##Name( \ const AD &left , \ const AD &right , \ const AD &if_true , \ const AD &if_false ) \ { \ return CondExpOp(Compare##Name, \ left, right, if_true, if_false); \ } // AD CPPAD_COND_EXP(Lt) CPPAD_COND_EXP(Le) CPPAD_COND_EXP(Eq) CPPAD_COND_EXP(Ge) CPPAD_COND_EXP(Gt) template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION AD CondExp( const AD &flag , const AD &if_true , const AD &if_false ) { return CondExpOp(CompareGt, flag, AD(0), if_true, if_false); } # undef CPPAD_COND_EXP } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/convert.hpp ================================================ # ifndef CPPAD_CORE_CONVERT_HPP # define CPPAD_CORE_CONVERT_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin Convert} Conversion and I/O of AD Objects ################################ {xrst_toc_hidden include/cppad/core/value.hpp include/cppad/core/integer.hpp include/cppad/core/ad_to_string.hpp include/cppad/core/ad_io.hpp include/cppad/core/print_for.hpp include/cppad/core/var2par.hpp } .. csv-table:: :widths: auto Value,:ref:`Value-title` Integer,:ref:`Integer-title` ad_output,:ref:`ad_output-title` PrintFor,:ref:`PrintFor-title` Var2Par,:ref:`Var2Par-title` {xrst_end Convert} */ # include # include # include # include # include # include # endif ================================================ FILE: include/cppad/core/cppad_assert.hpp ================================================ # ifndef CPPAD_CORE_CPPAD_ASSERT_HPP # define CPPAD_CORE_CPPAD_ASSERT_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /*! \file cppad_assert.hpp Define the CppAD error checking macros (all of which begin with CPPAD_ASSERT_) */ /* ------------------------------------------------------------------------------- {xrst_begin cppad_assert} {xrst_spell msg } CppAD Assertions During Execution ################################# Syntax ****** | ``CPPAD_ASSERT_KNOWN`` ( *exp* , *msg* ) | ``CPPAD_ASSERT_UNKNOWN`` ( *exp* ) Purpose ******* These CppAD macros are used to detect and report errors. They are documented here because they correspond to the C++ source code that the error is reported at. NDEBUG ****** If the preprocessor symbol :ref:`Faq@Speed@NDEBUG` is defined, these macros do nothing; i.e., they are optimized out. Restriction *********** The CppAD user should not uses these macros. You can however write your own macros that do not begin with ``CPPAD`` and that call the :ref:`CppAD error handler` . Known ***** The ``CPPAD_ASSERT_KNOWN`` macro is used to check for an error with a known cause. For example, many CppAD routines uses these macros to make sure their arguments conform to their specifications. Unknown ******* The ``CPPAD_ASSERT_UNKNOWN`` macro is used to check that the CppAD internal data structures conform as expected. If this is not the case, CppAD does not know why the error has occurred; for example, the user may have written past the end of an allocated array. Exp *** The argument *exp* is a C++ source code expression that results in a ``bool`` value that should be true. If it is false, an error has occurred. This expression may be execute any number of times (including zero times) so it must have not side effects. Msg *** The argument *msg* has prototype ``const char`` * *msg* and contains a ``'\0'`` terminated character string. This string is a description of the error corresponding to *exp* being false. Error Handler ************* These macros use the :ref:`CppAD error handler` to report errors. This error handler can be replaced by the user. {xrst_end cppad_assert} ------------------------------------------------------------------------------ */ # include # include # include /*! \def CPPAD_ASSERT_KNOWN(exp, msg) Check that exp is true, if not print msg and terminate execution. The C++ expression exp is expected to be true. If it is false, the CppAD use has made an error that is described by msg. If the preprocessor symbol NDEBUG is not defined, and exp is false, this macro will report the source code line number at which this expected result occurred. In addition, it will print the specified error message msg. */ # ifdef NDEBUG # define CPPAD_ASSERT_KNOWN(exp, msg) // do nothing # else # define CPPAD_ASSERT_KNOWN(exp, msg) \ { if( ! ( exp ) ) \ CppAD::ErrorHandler::Call( \ true , \ __LINE__ , \ __FILE__ , \ #exp , \ msg ); \ } # endif /*! \def CPPAD_ASSERT_UNKNOWN(exp) Check that exp is true, if not terminate execution. The C++ expression exp is expected to be true. If it is false, CppAD has detected an error but does not know the cause of the error. If the preprocessor symbol NDEBUG is not defined, and exp is false, this macro will report the source code line number at which this expected result occurred. */ # ifdef NDEBUG # define CPPAD_ASSERT_UNKNOWN(exp) // do nothing # else # define CPPAD_ASSERT_UNKNOWN(exp) \ { if( ! ( exp ) ) \ CppAD::ErrorHandler::Call( \ false , \ __LINE__ , \ __FILE__ , \ #exp , \ "" ); \ } # endif /*! \def CPPAD_ASSERT_NARG_NRES(op, n_arg, n_res) Check that operator op has the specified number of of arguments and results. If NDEBUG is not defined and either the number of arguments or the number of results are not as expected, execution is terminated and the source code line number is reported. */ # define CPPAD_ASSERT_NARG_NRES(op, n_arg, n_res) \ CPPAD_ASSERT_UNKNOWN( NumArg(op) == n_arg ) \ CPPAD_ASSERT_UNKNOWN( NumRes(op) == n_res ) /*! \def CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL Check that the first call to a routine is not during parallel execution mode. If NDEBUG is defined, this macro has no effect (not even the definition of (assert_first_call). Otherwise, the variable \code static bool assert_first_call \endcode is defined and if the first call is executed in parallel mode, execution is terminated and the source code line number is reported. */ # ifdef NDEBUG # define CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL # else # define CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL \ static bool assert_first_call = true; \ if( assert_first_call ) \ { CPPAD_ASSERT_KNOWN( \ ! (CppAD::thread_alloc::in_parallel() ), \ "In parallel mode and parallel_setup has not been called." \ ); \ assert_first_call = false; \ } # endif # endif ================================================ FILE: include/cppad/core/dependent.hpp ================================================ # ifndef CPPAD_CORE_DEPENDENT_HPP # define CPPAD_CORE_DEPENDENT_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin Dependent} Stop Recording and Store Operation Sequence ########################################### Syntax ****** | *f* . ``Dependent`` ( *x* , *y* ) Stop Recording ************** The call stops the recording and the AD of *Base* :ref:`operation sequence` that started with the call ``Independent`` ( *x* ) Store Operation Sequence ************************ This call also stores the operation sequence in *f* . The operation sequence defines an :ref:`glossary@AD Function` .. math:: F : \B{R}^n \rightarrow \B{R}^m where :math:`B` is the space corresponding to objects of type *Base* . The value :math:`n` is the dimension of the :ref:`fun_property@Domain` space for the operation sequence. The value :math:`m` is the dimension of the :ref:`fun_property@Range` space for the operation sequence (which is determined by the size of *y* ). f * The object *f* has prototype ``ADFun`` < *Base* > *f* The AD of *Base* operation sequence is stored in *f* ; i.e., it becomes the operation sequence corresponding to *f* . If a previous operation sequence was stored in *f* , it is deleted. x * The argument *x* must be the vector argument in a previous call to :ref:`Independent-name` . Neither its size, or any of its values, are allowed to change between calling ``Independent`` ( *x* ) and *f* . ``Dependent`` ( *x* , *y* ) . y * The vector *y* has prototype ``const`` *ADvector* & *y* (see :ref:`ADvector` below). The length of *y* must be greater than zero and is the dimension of the range space for *f* . ADvector ******** The type *ADvector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` ``AD`` < *Base* > . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Taping ****** The tape, that was created when ``Independent`` ( *x* ) was called, will stop recording. The AD operation sequence will be transferred from the tape to the object *f* and the tape will then be deleted. Forward ******* No :ref:`Forward-name` calculation is preformed during this operation. Thus, directly after this operation, *f* . ``size_order`` () is zero (see :ref:`size_order-name` ). Parallel Mode ************* The call to ``Independent`` , and the corresponding call to ``ADFun`` < *Base* > *f* ( *x* , *y* ) or *f* . ``Dependent`` ( *x* , *y* ) or :ref:`abort_recording-name` , must be preformed by the same thread; i.e., :ref:`thread_alloc::thread_num` must be the same. Example ******* The file :ref:`fun_check.cpp-name` contains an example and test of this operation. {xrst_end Dependent} ---------------------------------------------------------------------------- */ // BEGIN CppAD namespace namespace CppAD { /*! \file dependent.hpp Different versions of Dependent function. */ /*! Determine the tape corresponding to this execution thread and then use Dependent(tape, y) to store this tapes recording in a function. \param y [in] The dependent variable vector for the corresponding function. */ template template void ADFun::Dependent(const ADvector &y) { local::ADTape* tape = AD::tape_ptr(); CPPAD_ASSERT_KNOWN( tape != nullptr, "Can't store current operation sequence in this ADFun object" "\nbecause there is no active tape (for this thread)." ); // code above just determines the tape and checks for errors Dependent(tape, y); } /*! Determine the tape corresponding to this execution thread and then use Dependent(tape, y) to store this tapes recording in a function. \param x [in] The independent variable vector for this tape. This information is also stored in the tape so a check is done to make sure it is correct (if NDEBUG is not defined). \param y [in] The dependent variable vector for the corresponding function. */ template template void ADFun::Dependent(const ADvector &x, const ADvector &y) { CPPAD_ASSERT_KNOWN( x.size() > 0, "Dependent: independent variable vector has size zero." ); CPPAD_ASSERT_KNOWN( Variable(x[0]), "Dependent: independent variable vector has been changed." ); local::ADTape *tape = AD::tape_ptr(x[0].tape_id_); CPPAD_ASSERT_KNOWN( tape->size_independent_ == size_t( x.size() ), "Dependent: independent variable vector has been changed." ); # ifndef NDEBUG size_t i, j; for(j = 0; j < size_t(x.size()); j++) { CPPAD_ASSERT_KNOWN( size_t(x[j].taddr_) == (j+1), "ADFun: independent variable vector has been changed." ); CPPAD_ASSERT_KNOWN( x[j].tape_id_ == x[0].tape_id_, "ADFun: independent variable vector has been changed." ); } for(i = 0; i < size_t(y.size()); i++) { CPPAD_ASSERT_KNOWN( CppAD::Parameter( y[i] ) || (y[i].tape_id_ == x[0].tape_id_) , "ADFun: dependent vector contains a variable for" "\na different tape (thread) than the independent variables." ); } # endif // code above just determines the tape and checks for errors Dependent(tape, y); } /*! Replace the floationg point operations sequence for this function object. \param tape is a tape that contains the new floating point operation sequence for this function. After this operation, all memory allocated for this tape is deleted. \param y The dependent variable vector for the function being stored in this object. \par All of the private member data in ad_fun.hpp is set to correspond to the new tape except for check_for_nan_. */ template template void ADFun::Dependent(local::ADTape *tape, const ADvector &y) { size_t m = y.size(); size_t n = tape->size_independent_; // check ADvector is Simple Vector class with AD elements CheckSimpleVector< AD, ADvector>(); CPPAD_ASSERT_KNOWN( y.size() > 0, "ADFun operation sequence dependent variable size is zero size" ); // --------------------------------------------------------------------- // Begin setting ad_fun.hpp private member data // --------------------------------------------------------------------- // dep_parameter_, dep_taddr_ CPPAD_ASSERT_UNKNOWN( local::NumRes(local::ParOp) == 1 ); dep_parameter_.resize(m); dep_taddr_.resize(m); for(size_t i = 0; i < m; i++) { dep_parameter_[i] = CppAD::Parameter(y[i]); addr_t y_taddr; if( dep_parameter_[i] ) { // make a tape copy of dependent variables that are parameters, y_taddr = tape->RecordParOp( y[i] ); } else y_taddr = y[i].taddr_; CPPAD_ASSERT_UNKNOWN( y_taddr > 0 ); dep_taddr_[i] = size_t( y_taddr ); } // put an EndOp at the end of the tape tape->Rec_.PutOp(local::EndOp); // bool values in this object except check_for_nan_ has_been_optimized_ = false; // // size_t values in this object compare_change_count_ = 1; compare_change_number_ = 0; compare_change_op_index_ = 0; num_order_taylor_ = 0; cap_order_taylor_ = 0; num_direction_taylor_ = 0; num_var_tape_ = tape->Rec_.num_var(); // taylor_ taylor_.resize(0); // cskip_op_ cskip_op_.resize( tape->Rec_.num_var_op() ); // load_op2var_ load_op2var_.resize( tape->Rec_.num_var_load() ); // play_ // Now that each dependent variable has a place in the tape, // and there is a EndOp at the end of the tape, we can transfer the // recording to the player and and erase the recording; i.e. ERASE Rec_. play_.get_recording(tape->Rec_, n); // ind_taddr_ // Note that play_ has been set, we can use it to check operators ind_taddr_.resize(n); CPPAD_ASSERT_UNKNOWN( n < num_var_tape_); for(size_t j = 0; j < n; j++) { CPPAD_ASSERT_UNKNOWN( play_.GetOp(j+1) == local::InvOp ); ind_taddr_[j] = j+1; } // for_jac_sparse_pack_, for_jac_sparse_set_ for_jac_sparse_pack_.resize(0, 0); for_jac_sparse_set_.resize(0,0); // resize subgraph_info_ subgraph_info_.resize( ind_taddr_.size(), // n_dep dep_taddr_.size(), // n_ind play_.num_var_op(), // n_op play_.num_var() // n_var ); // --------------------------------------------------------------------- // End set ad_fun.hpp private member data // --------------------------------------------------------------------- // now we can delete the tape AD::tape_manage(delete_tape_manage); // total number of variables in this recording CPPAD_ASSERT_UNKNOWN( num_var_tape_ == play_.num_var() ); // used to determine if there is an operation sequence in *this CPPAD_ASSERT_UNKNOWN( num_var_tape_ > 0 ); } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/discrete/devel.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin devel_discrete dev} Developer Documentation for Discrete Function ############################################# Contents ******** {xrst_toc_table include/cppad/core/discrete/discrete.hpp } {xrst_end devel_discrete} ================================================ FILE: include/cppad/core/discrete/discrete.hpp ================================================ # ifndef CPPAD_CORE_DISCRETE_DISCRETE_HPP # define CPPAD_CORE_DISCRETE_DISCRETE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include // needed before one can use CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /* ------------------------------------------------------------------------------ {xrst_begin discrete_create dev} Create a Discrete AD Function ############################# Syntax ****** | ``CPPAD_DISCRETE_FUNCTION`` ( *Base* , *name* ) | ``name`` ( *ax* , *ay* ) Base **** is the base type for the discrete function. name **** is the name of the user defined function that corresponding to this operation. ax ** Is a ``AD`` < *Base* > corresponding to the argument for the function. ay ** Is a ``AD`` < *Base* > corresponding to the result for the function. fun *** The local object ``fun`` is a member of the ``discrete`` class. Source Code *********** {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_DISCRETE_FUNCTION(Base, name) \ inline CppAD::AD name (const CppAD::AD& ax) \ { static CppAD::discrete fun(#name, name); \ return fun.ad(ax); \ } \ CppAD::AD this_variable_name_used_to_initialize_discrete_ ##name = \ name( CppAD::AD(0.0) ); // # define CppADCreateDiscrete CPPAD_DISCRETE_FUNCTION /* {xrst_code} {xrst_spell_on} {xrst_end discrete_create} ----------------------------------------------------------------------------- {xrst_begin discrete_class dev} Declare discrete Class and Member Data ###################################### parallel_ad *********** is a friend of this class so it can call List to initialize its static data. Fun *** is the type for the user routine that computes *Base* function values. name\_ ****** name of this user defined discrete function. f\_ *** user routine that computes *Base* function values. index\_ ******* index of this object in :ref:`discrete_list-name` for this *Base* . Source Code *********** {xrst_spell_off} {xrst_code hpp} */ template class discrete { private: template friend void parallel_ad(void); typedef Base (*Fun) (const Base& x); const std::string name_; const Fun f_; const size_t index_; /* {xrst_code} {xrst_spell_on} {xrst_end discrete_class} ------------------------------------------------------------------------------ {xrst_begin discrete_list dev} List of all objects in the discrete class ######################################### Syntax ****** *list* = ``discrete`` < *Base* >:: ``List`` () Base **** Is the :ref:`discrete_create@Base` type for this list of discrete functions. list **** is a reference to the list of all the ``discrete`` object currently defined. std::vector =========== We use ``std::vector`` instead of ``CppAD::vector`` so it does not appear that there is a :ref:`memory_leak-name` this list is not destroyed before :ref:`thread_alloc::free_all` is called by testing routines. Source Code *********** {xrst_spell_off} {xrst_code hpp} */ private: static std::vector& List(void) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; static std::vector list; return list; } /* {xrst_code} {xrst_spell_on} {xrst_end discrete_list} ------------------------------------------------------------------------------ {xrst_begin discrete_list_size dev} Size of the Discrete Function List ################################## Syntax ****** *size* = ``discrete`` < *Base* >:: ``list_size`` () Base **** Is the :ref:`discrete_create@Base` type for this list of discrete functions. size **** is the number of discrete functions for this *Base* type. Source Code *********** {xrst_spell_off} {xrst_code hpp} */ public: static size_t list_size(void) { return List().size(); } /* {xrst_code} {xrst_spell_on} {xrst_end discrete_list_size} ------------------------------------------------------------------------------ {xrst_begin discrete_ctor dev} Constructor Called by each Use of CPPAD_DISCRETE_FUNCTION ######################################################### Syntax ****** ``discrete`` < *Base* > *fun* ( *name* , *f* ) name **** is the name of this function. f * user routine that implements this function for Base class. fun *** is the ``discrete`` object created by this call to the constructor. name\_ ====== is set equal to *name* . f\_ === is set equal to *f* . index\_ ======= This object is put at the end of :ref:`discrete_list-name` and ``index_`` is set to the index of this object in the discrete list. Parallel ******** This constructor cannot be used in parallel mode because it changes the static object returned by :ref:`discrete_list-name` . {xrst_end discrete_ctor} */ public: discrete(const std::string& name, Fun f) : name_(name), f_(f) , index_( List().size() ) { std::string msg = "discrete: first call to the discrete function "; msg += name; msg += " is in parallel mode."; CPPAD_ASSERT_KNOWN( ! thread_alloc::in_parallel() , msg.c_str() ); List().push_back(this); } /* ------------------------------------------------------------------------------ {xrst_begin discrete_ad dev} Implement AD Version of a Discrete Function ########################################### Syntax ****** *ay* = *fun* . ``ad`` ( *ax* ) ax ** is the argument for the AD version of this function. ay ** is the return value for the AD version of this function. Prototype ********* {xrst_spell_off} {xrst_code hpp} */ AD ad(const AD &ax) const /* {xrst_code} {xrst_spell_on} {xrst_end discrete_ad} */ { CPPAD_ASSERT_KNOWN( size_t( std::numeric_limits::max() ) >= index_, "discrete: cppad_tape_addr_type maximum not large enough" ); // AD ay; ay.value_ = f_(ax.value_); // // check if there is a recording in progress local::ADTape* tape = AD::tape_ptr(); if( tape == nullptr ) return ay; // // check if argument is a constant parameter if( ax.tape_id_ != tape->id_ ) return ay; // if( ax.ad_type_ == dynamic_enum ) { // tape dynamic parameter operation ay.taddr_ = tape->Rec_.put_dyn_par( ay.value_, local::dis_dyn, addr_t(index_), ax.taddr_ ); ay.tape_id_ = ax.tape_id_; ay.ad_type_ = dynamic_enum; // make result a dynamic parameter ay.tape_id_ = tape->id_; ay.ad_type_ = dynamic_enum; CPPAD_ASSERT_UNKNOWN( Dynamic(ay) ); } else if( ax.ad_type_ == variable_enum ) { CPPAD_ASSERT_UNKNOWN( local::NumRes(local::DisOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::DisOp) == 2 ); // put operand addresses in the tape CPPAD_ASSERT_KNOWN( size_t( std::numeric_limits::max() ) >= index_, "discrete: cppad_tape_addr_type maximum not large enough" ); tape->Rec_.PutArg(addr_t(index_), ax.taddr_); // put operator in the tape ay.taddr_ = tape->Rec_.PutOp(local::DisOp); // make result a variable ay.tape_id_ = tape->id_; ay.ad_type_ = variable_enum; CPPAD_ASSERT_UNKNOWN( Variable(ay) ); } else { // other types not yet being used and should have this tape id CPPAD_ASSERT_UNKNOWN(false); } return ay; } /* ------------------------------------------------------------------------------ {xrst_begin discrete_index dev} {xrst_spell msg str } Index That Identifies a Discrete Function ######################################### {xrst_code hpp} */ static size_t index(const std::string& name) { size_t i = List().size(); while(i--) { if( List()[i]->name_ == name ) return i; } std::string msg = "There is no discrete function named " + name; CPPAD_ASSERT_KNOWN(false, msg.c_str()); return List().size(); } /* {xrst_code} {xrst_end discrete_index} */ /* ------------------------------------------------------------------------------ {xrst_begin discrete_name dev} Name Corresponding to a discrete Function ######################################### Syntax ****** ``discrete`` < *Base* >:: ``name`` ( *index* ) Base **** Is the :ref:`discrete_create@Base` type for this list of discrete functions. index ***** Is the index, in the list, for this discrete function. Source Code *********** {xrst_spell_off} {xrst_code hpp} */ static const std::string& name(size_t index) { return List()[index]->name_; } /* {xrst_code} {xrst_spell_on} {xrst_end discrete_name} ------------------------------------------------------------------------------ {xrst_begin discrete_eval dev} Link From Forward Mode Sweep to Users Routine ############################################# Syntax ****** *y* = ``discrete`` < *Base* >:: ``eval`` ( *index* , *x* ) Base **** Is the :ref:`discrete_create@Base` type for this list of discrete functions. index ***** index for this function in :ref:`discrete_list-name` . x * argument at which to evaluate *Base* version of this function. y * result for the *Base* version of this function. Source Code *********** {xrst_spell_off} {xrst_code hpp} */ static Base eval(size_t index, const Base& x) { CPPAD_ASSERT_UNKNOWN(index < List().size() ); return List()[index]->f_(x); } /* {xrst_code} {xrst_spell_on} {xrst_end discrete_eval} ------------------------------------------------------------------------------ {xrst_begin discrete_ad_eval dev} Link From Forward Mode Sweep to AD Version of Discrete Function ############################################################### Syntax ****** *ay* = ``discrete`` < *Base* >:: ``eval`` ( *index* , *ax* ) Base **** Is the :ref:`discrete_create@Base` type for this list of discrete functions. index ***** index for this function in :ref:`discrete_list-name` . ax ** argument at which to evaluate ``AD`` < *Base* > version of this function. ay ** result for the ``AD`` < *Base* > version of this function. Source Code *********** {xrst_spell_off} {xrst_code hpp} */ static AD ad_eval(size_t index, const AD& ax) { CPPAD_ASSERT_UNKNOWN(index < List().size() ); return List()[index]->ad(ax); } /* {xrst_code} {xrst_spell_on} {xrst_end discrete_ad_eval} */ }; } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/discrete/user.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- /* {xrst_begin Discrete} {xrst_spell retaping } Discrete AD Functions ##################### Syntax ****** | ``CPPAD_DISCRETE_FUNCTION`` ( *Base* , *name* ) | *y* = *name* ( *x* ) | *ay* = *name* ( *ax* ) Purpose ******* Record the evaluation of a discrete function as part of an ``AD`` < *Base* > :ref:`operation sequence` . The value of a discrete function can depend on the :ref:`independent variables` , but its derivative is identically zero. For example, suppose that the integer part of a :ref:`glossary@Variable` *x* is the index into an array of values. Base **** This is the :ref:`base type` corresponding to the operations sequence; i.e., use of the *name* with arguments of type ``AD`` < *Base* > can be recorded in an operation sequence. name **** This is the name of the function (as it is used in the source code). The user must provide a version of *name* where the argument has type *Base* . CppAD uses this to create a version of *name* where the argument has type ``AD`` < *Base* > . x * The argument *x* has prototype ``const`` *Base* & *x* It is the value at which the user provided version of *name* is to be evaluated. y * The result *y* has prototype *Base* *y* It is the return value for the user provided version of *name* . ax ** The argument *ax* has prototype ``const AD`` < *Base* >& *ax* It is the value at which the CppAD provided version of *name* is to be evaluated. ay ** The result *ay* has prototype ``AD`` < *Base* > *ay* It is the return value for the CppAD provided version of *name* . Create AD Version ***************** The preprocessor macro invocation ``CPPAD_DISCRETE_FUNCTION`` ( *Base* , *name* ) defines the ``AD`` < *Base* > version of *name* . This can be with in a namespace (not the ``CppAD`` namespace) but must be outside of any routine. Operation Sequence ****************** This is an AD of *Base* :ref:`atomic operation` and hence is part of the current AD of *Base* :ref:`operation sequence` . Derivatives *********** During a zero order :ref:`Forward-name` operation, an :ref:`ADFun-name` object will compute the value of *name* using the user provided *Base* version of this routine. All the derivatives of *name* will be evaluated as zero. Parallel Mode ************* The first call to *name* ( *ax* ) must not be in :ref:`parallel` execution mode. Example ******* {xrst_toc_hidden example/general/tape_index.cpp example/general/interp_onetape.cpp example/general/interp_retape.cpp } The file :ref:`tape_index.cpp-name` contains an example and test that uses a discrete function to vary an array index during :ref:`Forward-name` mode calculations. The file :ref:`interp_onetape.cpp-name` contains an example and test that uses discrete functions to avoid retaping a calculation that requires interpolation. (The file :ref:`interp_retape.cpp-name` shows how interpolation can be done with retaping.) CppADCreateDiscrete Deprecated 2007-07-28 ***************************************** The preprocessor symbol ``CppADCreateDiscrete`` is defined to be the same as ``CPPAD_DISCRETE_FUNCTION`` but its use is deprecated. {xrst_end Discrete} ================================================ FILE: include/cppad/core/div.hpp ================================================ # ifndef CPPAD_CORE_DIV_HPP # define CPPAD_CORE_DIV_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN CppAD namespace namespace CppAD { template AD operator / (const AD &left , const AD &right) { // compute the Base part AD result; result.value_ = left.value_ / right.value_; CPPAD_ASSERT_UNKNOWN( Parameter(result) ); // check if there is a recording in progress local::ADTape* tape = AD::tape_ptr(); if( tape == nullptr ) return result; tape_id_t tape_id = tape->id_; // tape_id cannot match the default value for tape_id_; i.e., 0 CPPAD_ASSERT_UNKNOWN( tape_id > 0 ); // check if left and right tapes match bool match_left = left.tape_id_ == tape_id; bool match_right = right.tape_id_ == tape_id; // check if left and right are dynamic parameters bool dyn_left = match_left & (left.ad_type_ == dynamic_enum); bool dyn_right = match_right & (right.ad_type_ == dynamic_enum); // check if left and right are variables bool var_left = match_left & (left.ad_type_ != dynamic_enum); bool var_right = match_right & (right.ad_type_ != dynamic_enum); CPPAD_ASSERT_KNOWN( left.tape_id_ == right.tape_id_ || ! match_left || ! match_right , "Divide: AD variables or dynamic parameters on different threads." ); if( var_left ) { if( var_right ) { // result = variable / variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::DivvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::DivvvOp) == 2 ); // put operand addresses in tape tape->Rec_.PutArg(left.taddr_, right.taddr_); // put operator in the tape result.taddr_ = tape->Rec_.PutOp(local::DivvvOp); // make result a variable result.tape_id_ = tape_id; result.ad_type_ = variable_enum; } else if( (! dyn_right) && IdenticalOne(right.value_) ) { // result = variable / 1 result.make_variable(left.tape_id_, left.taddr_); } else { // result = variable / parameter CPPAD_ASSERT_UNKNOWN( local::NumRes(local::DivvpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::DivvpOp) == 2 ); // put operand addresses in tape addr_t p = right.taddr_; if( ! dyn_right ) p = tape->Rec_.put_con_par(right.value_); tape->Rec_.PutArg(left.taddr_, p); // put operator in the tape result.taddr_ = tape->Rec_.PutOp(local::DivvpOp); // make result a variable result.tape_id_ = tape_id; result.ad_type_ = variable_enum; } } else if( var_right ) { if( (! dyn_left) && IdenticalZero(left.value_) ) { // result = 0 / variable result.value_ = Base(0.0); // incase right.value_ is zero or nan } else { // result = parameter / variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::DivpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::DivpvOp) == 2 ); // put operand addresses in tape addr_t p = left.taddr_; if( ! dyn_left ) p = tape->Rec_.put_con_par(left.value_); tape->Rec_.PutArg(p, right.taddr_); // put operator in the tape result.taddr_ = tape->Rec_.PutOp(local::DivpvOp); // make result a variable result.tape_id_ = tape_id; result.ad_type_ = variable_enum; } } else if( dyn_left | dyn_right ) { if ( (!dyn_left) && IdenticalZero(left.value_)) { // 0 / dynamic result.value_ = Base(0.0); } else if( (!dyn_right) && IdenticalOne(right.value_)) { // dynamic / 1 result.make_dynamic(left.tape_id_, left.taddr_); } else { addr_t arg0 = left.taddr_; addr_t arg1 = right.taddr_; if( ! dyn_left ) arg0 = tape->Rec_.put_con_par(left.value_); if( ! dyn_right ) arg1 = tape->Rec_.put_con_par(right.value_); // // parameters with a dynamic parameter result result.taddr_ = tape->Rec_.put_dyn_par( result.value_, local::div_dyn, arg0, arg1 ); result.tape_id_ = tape_id; result.ad_type_ = dynamic_enum; } } return result; } // convert other cases into the case above CPPAD_FOLD_AD_VALUED_BINARY_OPERATOR(/) } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/div_eq.hpp ================================================ # ifndef CPPAD_CORE_DIV_EQ_HPP # define CPPAD_CORE_DIV_EQ_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN CppAD namespace namespace CppAD { template AD& AD::operator /= (const AD &right) { // compute the Base part Base left; left = value_; value_ /= right.value_; // check if there is a recording in progress local::ADTape* tape = AD::tape_ptr(); if( tape == nullptr ) return *this; tape_id_t tape_id = tape->id_; // tape_id cannot match the default value for tape_id_; i.e., 0 CPPAD_ASSERT_UNKNOWN( tape_id > 0 ); // check if left and right tapes match bool match_left = tape_id_ == tape_id; bool match_right = right.tape_id_ == tape_id; // check if left and right are dynamic parameters bool dyn_left = match_left & (ad_type_ == dynamic_enum); bool dyn_right = match_right & (right.ad_type_ == dynamic_enum); // check if left and right are variables bool var_left = match_left & (ad_type_ != dynamic_enum); bool var_right = match_right & (right.ad_type_ != dynamic_enum); CPPAD_ASSERT_KNOWN( tape_id_ == right.tape_id_ || ! match_left || ! match_right , "/= : AD variables or dynamic parameters on different threads." ); if( var_left ) { if( var_right ) { // this = variable / variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::DivvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::DivvvOp) == 2 ); // put operand addresses in tape tape->Rec_.PutArg(taddr_, right.taddr_); // put operator in the tape taddr_ = tape->Rec_.PutOp(local::DivvvOp); // check that this is a variable CPPAD_ASSERT_UNKNOWN( tape_id_ == tape_id ); CPPAD_ASSERT_UNKNOWN( ad_type_ == variable_enum); } else if( (! dyn_right) && IdenticalOne(right.value_) ) { // this = variable * 1 } else { // this = variable / parameter CPPAD_ASSERT_UNKNOWN( local::NumRes(local::DivvpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::DivvpOp) == 2 ); // put operand addresses in tape addr_t p = right.taddr_; if( ! dyn_right ) p = tape->Rec_.put_con_par(right.value_); tape->Rec_.PutArg(taddr_, p); // put operator in the tape taddr_ = tape->Rec_.PutOp(local::DivvpOp); // check that this is a variable CPPAD_ASSERT_UNKNOWN( tape_id_ == tape_id ); CPPAD_ASSERT_UNKNOWN( ad_type_ == variable_enum); } } else if( var_right ) { if( (! dyn_left) && IdenticalZero(left) ) { // this = 0 / variable } else { // this = parameter / variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::DivpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::DivpvOp) == 2 ); // put operand addresses in tape addr_t p = taddr_; if( ! dyn_left ) p = tape->Rec_.put_con_par(left); tape->Rec_.PutArg(p, right.taddr_); // put operator in the tape taddr_ = tape->Rec_.PutOp(local::DivpvOp); // make this a variable tape_id_ = tape_id; ad_type_ = variable_enum; } } else if( dyn_left | dyn_right ) { if( (!dyn_right) && IdenticalOne(right.value_)) { // dynamic /= 1, so do nothing } else if( (!dyn_left) && IdenticalZero(left)) { // 0 /= dynamic, so do nothing } else { addr_t arg0 = taddr_; addr_t arg1 = right.taddr_; if( ! dyn_left ) arg0 = tape->Rec_.put_con_par(left); if( ! dyn_right ) arg1 = tape->Rec_.put_con_par(right.value_); // // parameters with a dynamic parameter results taddr_ = tape->Rec_.put_dyn_par( value_, local::div_dyn, arg0, arg1 ); tape_id_ = tape_id; ad_type_ = dynamic_enum; } } return *this; } CPPAD_FOLD_ASSIGNMENT_OPERATOR(/=) } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/drivers.hpp ================================================ # ifndef CPPAD_CORE_DRIVERS_HPP # define CPPAD_CORE_DRIVERS_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include # include # endif ================================================ FILE: include/cppad/core/epsilon.hpp ================================================ # ifndef CPPAD_CORE_EPSILON_HPP # define CPPAD_CORE_EPSILON_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* ------------------------------------------------------------------------------ {xrst_begin epsilon app} {xrst_spell eps } Machine Epsilon For AD Types ############################ Deprecated 2012-06-17 ********************* This routine has been deprecated. You should use the :ref:`numeric_limits-name` ``epsilon`` instead. Syntax ****** | *eps* = ``epsilon`` < *Float* >() Purpose ******* Obtain the value of machine epsilon corresponding to the type *Float* . Float ***** this type can either be ``AD`` < *Base* > , or it can be *Base* for any ``AD`` < *Base* > type. eps *** The result *eps* has prototype *Float* ``eps`` {xrst_end epsilon} ------------------------------------------------------------------------------ */ namespace CppAD { template inline Type epsilon(void) { return Type ( numeric_limits::epsilon() ); } } # endif ================================================ FILE: include/cppad/core/equal_op_seq.hpp ================================================ # ifndef CPPAD_CORE_EQUAL_OP_SEQ_HPP # define CPPAD_CORE_EQUAL_OP_SEQ_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* ------------------------------------------------------------------------------ {xrst_begin EqualOpSeq} Check if Two Value are Identically Equal ######################################## Syntax ****** | *b* = ``EqualOpSeq`` ( *x* , *y* ) Purpose ******* Determine if two *x* and *y* are identically equal; i.e., not only is *x* == *y* true, but if they are :ref:`variables` , they correspond have the same :ref:`operation sequence` . Motivation ********** Sometimes it is useful to cache information and only recalculate when a function's arguments change. In the case of AD variables, it may be important not only when the argument values are equal, but when they are related to the :ref:`independent variables` by the same operation sequence. After the assignment *y* = *x* these two AD objects would not only have equal values, but would also correspond to the same operation sequence. x * The argument *x* has prototype ``const AD`` < *Base* > & *x* y * The argument *y* has prototype ``const AD`` < *Base* > & *y* b * The result *b* has prototype ``bool`` *b* The result is true if and only if one of the following cases holds: #. Both *x* and *y* are variables and correspond to the same operation sequence. #. Both *x* and *y* are parameters, *Base* is an AD type, and ``EqualOpSeq`` ( ``Value`` ( *x* ) , ``Value`` ( *y* ) ) is true. #. Both *x* and *y* are parameters, *Base* is not an AD type, and *x* == *y* is true. Example ******* {xrst_toc_hidden example/general/equal_op_seq.cpp } The file :ref:`equal_op_seq.cpp-name` contains an example and test of ``EqualOpSeq`` . {xrst_end EqualOpSeq} ------------------------------------------------------------------------------ */ namespace CppAD { template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION bool EqualOpSeq(const AD &x, const AD &y) { if( Parameter(x) ) { if( Parameter(y) ) return EqualOpSeq(x.value_, y.value_); else return false; } else if( Parameter(y) ) return false; return (x.taddr_ == y.taddr_); } } # endif ================================================ FILE: include/cppad/core/for_hes_sparsity.hpp ================================================ # ifndef CPPAD_CORE_FOR_HES_SPARSITY_HPP # define CPPAD_CORE_FOR_HES_SPARSITY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin for_hes_sparsity} {xrst_spell rc walther } Forward Mode Hessian Sparsity Patterns ###################################### Syntax ****** | *f* . ``for_hes_sparsity`` ( | |tab| *select_domain* , *select_range* , *internal_bool* , *pattern_out* | ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to the operation sequence stored in *f* . Fix a diagonal matrix :math:`D \in \B{R}^{n \times n}`, a vector :math:`s \in \B{R}^m` and define the function .. math:: H(x) = D ( s^\R{T} F )^{(2)} ( x ) D Given the sparsity for :math:`D` and :math:`s`, ``for_hes_sparsity`` computes a sparsity pattern for :math:`H(x)`. x * Note that the sparsity pattern :math:`H(x)` corresponds to the operation sequence stored in *f* and does not depend on the argument *x* . BoolVector ********** The type *BoolVector* is a :ref:`SimpleVector-name` class with :ref:`elements of type` ``bool`` . SizeVector ********** The type *SizeVector* is a :ref:`SimpleVector-name` class with :ref:`elements of type` ``size_t`` . f * The object *f* has prototype ``ADFun`` < *Base* > *f* select_domain ************* The argument *select_domain* has prototype ``const`` *BoolVector* & *select_domain* It has size :math:`n` and specifies which components of the diagonal of :math:`D` are non-zero; i.e., *select_domain* [ *j* ] is true if and only if :math:`D_{j,j}` is possibly non-zero. select_range ************ The argument *select_range* has prototype ``const`` *BoolVector* & *select_range* It has size :math:`m` and specifies which components of the vector :math:`s` are non-zero; i.e., *select_range* [ *i* ] is true if and only if :math:`s_i` is possibly non-zero. internal_bool ************* If this is true, calculations are done with sets represented by a vector of boolean values. Otherwise, a vector of sets of integers is used. pattern_out *********** This argument has prototype ``sparse_rc`` < *SizeVector* >& *pattern_out* This input value of *pattern_out* does not matter. Upon return *pattern_out* is a sparsity pattern for :math:`H(x)`. Sparsity for Entire Hessian *************************** Suppose that :math:`D` is the :math:`n \times n` identity matrix. In this case, *pattern_out* is a sparsity pattern for :math:`(s^\R{T} F) F^{(2)} ( x )`. Algorithm ********* See Algorithm II in *Computing sparse Hessians with automatic differentiation* by Andrea Walther. Note that *s* provides the information so that 'dead ends' are not included in the sparsity pattern. Example ******* {xrst_toc_hidden example/sparse/for_hes_sparsity.cpp } The file :ref:`for_hes_sparsity.cpp-name` contains an example and test of this operation. {xrst_end for_hes_sparsity} ----------------------------------------------------------------------------- */ # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! Forward Hessian sparsity patterns. \tparam Base is the base type for this recording. \tparam BoolVector is the simple vector with elements of type bool that is used for sparsity for the vector s. \tparam SizeVector is the simple vector with elements of type size_t that is used for row, column index sparsity patterns. \param select_domain is a sparsity pattern for for the diagonal of D. \param select_range is a sparsity pattern for for s. \param internal_bool If this is true, calculations are done with sets represented by a vector of boolean values. Otherwise, a vector of standard sets is used. \param pattern_out The return value is a sparsity pattern for H(x) where \f[ H(x) = D * F^{(1)} (x) * D \f] Here F is the function corresponding to the operation sequence and x is any argument value. */ template template void ADFun::for_hes_sparsity( const BoolVector& select_domain , const BoolVector& select_range , bool internal_bool , sparse_rc& pattern_out ) { // used to identify the RecBase type in calls to sweeps RecBase not_used_rec_base(0.0); // size_t n = Domain(); size_t m = Range(); // CPPAD_ASSERT_KNOWN( size_t( select_domain.size() ) == n, "for_hes_sparsity: size of select_domain is not equal to " "number of independent variables" ); CPPAD_ASSERT_KNOWN( size_t( select_range.size() ) == m, "for_hes_sparsity: size of select_range is not equal to " "number of dependent variables" ); // do not need transpose or dependency bool transpose = false; bool dependency = false; // local::pod_vector select_domain_pod_vector(n); for(size_t j = 0; j < n; ++j) select_domain_pod_vector[j] = select_domain[j]; // sparse_rc pattern_tmp; if( internal_bool ) { // reverse Jacobian sparsity pattern for select_range local::sparse::pack_setvec internal_rev_jac; internal_rev_jac.resize(num_var_tape_, 1); for(size_t i = 0; i < m; i++) if( select_range[i] ) { CPPAD_ASSERT_UNKNOWN( dep_taddr_[i] < num_var_tape_ ); // Not using post_element because only adding one element per set internal_rev_jac.add_element( dep_taddr_[i] , 0 ); } // reverse Jacobian sparsity for all variables on tape local::sweep::rev_jac( &play_, dependency, n, num_var_tape_, internal_rev_jac, not_used_rec_base ); // internal vector of sets that will hold Hessian local::sparse::pack_setvec internal_for_hes; internal_for_hes.resize(n + 1 + num_var_tape_, n + 1); // // compute forward Hessian sparsity pattern local::sweep::for_hes( &play_, n, num_var_tape_, select_domain_pod_vector, internal_rev_jac, internal_for_hes, not_used_rec_base ); // // put the result in pattern_tmp local::sparse::get_internal_pattern( transpose, ind_taddr_, internal_for_hes, pattern_tmp ); } else { // reverse Jacobian sparsity pattern for select_range // (corresponds to s) local::sparse::list_setvec internal_rev_jac; internal_rev_jac.resize(num_var_tape_, 1); for(size_t i = 0; i < m; i++) if( select_range[i] ) { CPPAD_ASSERT_UNKNOWN( dep_taddr_[i] < num_var_tape_ ); // Not using post_element because only adding one element per set internal_rev_jac.add_element( dep_taddr_[i] , 0 ); } // reverse Jacobian sparsity for all variables on tape local::sweep::rev_jac( &play_, dependency, n, num_var_tape_, internal_rev_jac, not_used_rec_base ); // internal vector of sets that will hold Hessian local::sparse::list_setvec internal_for_hes; internal_for_hes.resize(n + 1 + num_var_tape_, n + 1); // // compute forward Hessian sparsity pattern local::sweep::for_hes( &play_, n, num_var_tape_, select_domain_pod_vector, internal_rev_jac, internal_for_hes, not_used_rec_base ); // // put the result in pattern_tmp local::sparse::get_internal_pattern( transpose, ind_taddr_, internal_for_hes, pattern_tmp ); } // subtract 1 from all column values CPPAD_ASSERT_UNKNOWN( pattern_tmp.nr() == n ); CPPAD_ASSERT_UNKNOWN( pattern_tmp.nc() == n + 1 ); const SizeVector& row( pattern_tmp.row() ); const SizeVector& col( pattern_tmp.col() ); size_t nr = n; size_t nc = n; size_t nnz = pattern_tmp.nnz(); pattern_out.resize(nr, nc, nnz); for(size_t k = 0; k < nnz; k++) { CPPAD_ASSERT_UNKNOWN( 0 < col[k] ); pattern_out.set(k, row[k], col[k] - 1); } return; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/for_jac_sparsity.hpp ================================================ # ifndef CPPAD_CORE_FOR_JAC_SPARSITY_HPP # define CPPAD_CORE_FOR_JAC_SPARSITY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin for_jac_sparsity} Forward Mode Jacobian Sparsity Patterns ####################################### Syntax ****** | *f* . ``for_jac_sparsity`` ( | |tab| *pattern_in* , *transpose* , *dependency* , *internal_bool* , *pattern_out* | ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to the operation sequence stored in *f* . Fix :math:`R \in \B{R}^{n \times \ell}` and define the function .. math:: J(x) = F^{(1)} ( x ) * R Given the :ref:`glossary@Sparsity Pattern` for :math:`R`, ``for_jac_sparsity`` computes a sparsity pattern for :math:`J(x)`. x * Note that the sparsity pattern :math:`J(x)` corresponds to the operation sequence stored in *f* and does not depend on the argument *x* . (The operation sequence may contain :ref:`CondExp-name` and :ref:`VecAD-name` operations.) SizeVector ********** The type *SizeVector* is a :ref:`SimpleVector-name` class with :ref:`elements of type` ``size_t`` . f * The object *f* has prototype ``ADFun`` < *Base* > *f* The :ref:`ADFun-name` object *f* is not ``const`` . After a call to ``for_jac_sparsity`` , a sparsity pattern for each of the variables in the operation sequence is held in *f* for possible later use during reverse Hessian sparsity calculations. size_forward_bool ================= After ``for_jac_sparsity`` , if *k* is a ``size_t`` object, *k* = *f* . ``size_forward_bool`` () sets *k* to the amount of memory (in unsigned character units) used to store the :ref:`glossary@Sparsity Pattern@Boolean Vector` sparsity patterns. If *internal_bool* if false, *k* will be zero. Otherwise it will be non-zero. If you do not need this information for :ref:`RevSparseHes-name` calculations, it can be deleted (and the corresponding memory freed) using *f* . ``size_forward_bool`` (0) after which *f* . ``size_forward_bool`` () will return zero. size_forward_set ================ After ``for_jac_sparsity`` , if *k* is a ``size_t`` object, *k* = *f* . ``size_forward_set`` () sets *k* to the amount of memory (in unsigned character units) used to store the :ref:`glossary@Sparsity Pattern@Vector of Sets` sparsity patterns. If *internal_bool* if true, *k* will be zero. Otherwise it will be non-zero. If you do not need this information for future :ref:`rev_hes_sparsity-name` calculations, it can be deleted (and the corresponding memory freed) using *f* . ``size_forward_set`` (0) after which *f* . ``size_forward_set`` () will return zero. pattern_in ********** The argument *pattern_in* has prototype ``const sparse_rc`` < *SizeVector* >& *pattern_in* see :ref:`sparse_rc-name` . If *transpose* it is false (true), *pattern_in* is a sparsity pattern for :math:`R` (:math:`R^\R{T}`). transpose ********* This argument has prototype ``bool`` *transpose* See :ref:`for_jac_sparsity@pattern_in` above and :ref:`for_jac_sparsity@pattern_out` below. dependency ********** This argument has prototype ``bool`` *dependency* see :ref:`for_jac_sparsity@pattern_out` below. internal_bool ************* This argument has prototype ``bool`` *internal_bool* If this is true, calculations are done with sets represented by a vector of boolean values. Otherwise, a vector of sets of integers is used. pattern_out *********** This argument has prototype ``sparse_rc`` < *SizeVector* >& *pattern_out* This input value of *pattern_out* does not matter. If *transpose* it is false (true), upon return *pattern_out* is a sparsity pattern for :math:`J(x)` (:math:`J(x)^\R{T}`). If *dependency* is true, *pattern_out* is a :ref:`dependency.cpp@Dependency Pattern` instead of sparsity pattern. Sparsity for Entire Jacobian **************************** Suppose that :math:`R` is the :math:`n \times n` identity matrix. In this case, *pattern_out* is a sparsity pattern for :math:`F^{(1)} ( x )` ( :math:`F^{(1)} (x)^\R{T}` ) if *transpose* is false (true). Example ******* {xrst_toc_hidden example/sparse/for_jac_sparsity.cpp } The file :ref:`for_jac_sparsity.cpp-name` contains an example and test of this operation. {xrst_end for_jac_sparsity} ----------------------------------------------------------------------------- */ # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! Forward Jacobian sparsity patterns. \tparam Base is the base type for this recording. \tparam SizeVector is the simple vector with elements of type size_t that is used for row, column index sparsity patterns. \param pattern_in is the sparsity pattern for for R or R^T depending on transpose. \param transpose Is the input and returned sparsity pattern transposed. \param dependency Are the derivatives with respect to left and right of the expression below considered to be non-zero: \code CondExpRel(left, right, if_true, if_false) \endcode This is used by the optimizer to obtain the correct dependency relations. \param internal_bool If this is true, calculations are done with sets represented by a vector of boolean values. Otherwise, a vector of standard sets is used. \param pattern_out The value of transpose is false (true), the return value is a sparsity pattern for J(x) ( J(x)^T ) where \f[ J(x) = F^{(1)} (x) * R \f] Here F is the function corresponding to the operation sequence and x is any argument value. */ template template void ADFun::for_jac_sparsity( const sparse_rc& pattern_in , bool transpose , bool dependency , bool internal_bool , sparse_rc& pattern_out ) { // used to identify the RecBase type in calls to sweeps RecBase not_used_rec_base(0.0); // // number or rows, columns, and non-zeros in pattern_in size_t nr_in = pattern_in.nr(); size_t nc_in = pattern_in.nc(); // size_t n = nr_in; size_t ell = nc_in; if( transpose ) std::swap(n, ell); // CPPAD_ASSERT_KNOWN( n == Domain() , "for_jac_sparsity: number rows in R " "is not equal number of independent variables." ); bool zero_empty = true; bool input_empty = true; if( internal_bool ) { // allocate memory for bool sparsity calculation // (sparsity pattern is empty after a resize) for_jac_sparse_pack_.resize(num_var_tape_, ell); for_jac_sparse_set_.resize(0, 0); // // set sparsity pattern for independent variables local::sparse::set_internal_pattern( zero_empty , input_empty , transpose , ind_taddr_ , for_jac_sparse_pack_ , pattern_in ); // compute sparsity for other variables local::sweep::for_jac( &play_, dependency, n, num_var_tape_, for_jac_sparse_pack_, not_used_rec_base ); // set the output pattern local::sparse::get_internal_pattern( transpose, dep_taddr_, for_jac_sparse_pack_, pattern_out ); } else { // allocate memory for set sparsity calculation // (sparsity pattern is empty after a resize) for_jac_sparse_set_.resize(num_var_tape_, ell); for_jac_sparse_pack_.resize(0, 0); // // set sparsity pattern for independent variables local::sparse::set_internal_pattern( zero_empty , input_empty , transpose , ind_taddr_ , for_jac_sparse_set_ , pattern_in ); // compute sparsity for other variables local::sweep::for_jac( &play_, dependency, n, num_var_tape_, for_jac_sparse_set_, not_used_rec_base ); // get the output pattern local::sparse::get_internal_pattern( transpose, dep_taddr_, for_jac_sparse_set_, pattern_out ); } return; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/for_one.hpp ================================================ # ifndef CPPAD_CORE_FOR_ONE_HPP # define CPPAD_CORE_FOR_ONE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ForOne} First Order Partial Derivative: Driver Routine ############################################## Syntax ****** | *dy* = *f* . ``ForOne`` ( *x* , *j* ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* . The syntax above sets *dy* to the partial of :math:`F` with respect to :math:`x_j`; i.e., .. math:: dy = \D{F}{ x_j } (x) = \left[ \D{ F_0 }{ x_j } (x) , \cdots , \D{ F_{m-1} }{ x_j } (x) \right] f * The object *f* has prototype ``ADFun`` < *Base* > *f* Note that the :ref:`ADFun-name` object *f* is not ``const`` (see :ref:`ForOne@ForOne Uses Forward` below). x * The argument *x* has prototype ``const`` *Vector* & *x* (see :ref:`ForOne@Vector` below) and its size must be equal to *n* , the dimension of the :ref:`fun_property@Domain` space for *f* . It specifies that point at which to evaluate the partial derivative. j * The argument *j* has prototype ``size_t`` *j* an is less than *n* , :ref:`fun_property@Domain` space for *f* . It specifies the component of *F* for which we are computing the partial derivative. dy ** The result *dy* has prototype *Vector* *dy* (see :ref:`ForOne@Vector` below) and its size is :math:`m`, the dimension of the :ref:`fun_property@Range` space for *f* . The value of *dy* is the partial of :math:`F` with respect to :math:`x_j` evaluated at *x* ; i.e., for :math:`i = 0 , \ldots , m - 1` .. math:: dy[i] = \D{ F_i }{ x_j } ( x ) Vector ****** The type *Vector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` *Base* . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. ForOne Uses Forward ******************* After each call to :ref:`Forward-name` , the object *f* contains the corresponding :ref:`Taylor coefficients` . After a call to ``ForOne`` , the zero order Taylor coefficients correspond to *f* . ``Forward`` (0, *x* ) and the other coefficients are unspecified. Example ******* {xrst_toc_hidden example/general/for_one.cpp } The routine :ref:`ForOne` is both an example and test. It returns ``true`` , if it succeeds and ``false`` otherwise. {xrst_end ForOne} ----------------------------------------------------------------------------- */ // BEGIN CppAD namespace namespace CppAD { template template Vector ADFun::ForOne(const Vector &x, size_t j) { size_t j1; size_t n = Domain(); size_t m = Range(); // check Vector is Simple Vector class with Base type elements CheckSimpleVector(); CPPAD_ASSERT_KNOWN( x.size() == n, "ForOne: Length of x not equal domain dimension for f" ); CPPAD_ASSERT_KNOWN( j < n, "ForOne: the index j is not less than domain dimension for f" ); // point at which we are evaluating the second partials Forward(0, x); // direction in which are are taking the derivative Vector dx(n); for(j1 = 0; j1 < n; j1++) dx[j1] = Base(0.0); dx[j] = Base(1.0); // dimension the return value Vector dy(m); // compute the return value dy = Forward(1, dx); return dy; } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/for_sparse_hes.hpp ================================================ # ifndef CPPAD_CORE_FOR_SPARSE_HES_HPP # define CPPAD_CORE_FOR_SPARSE_HES_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ForSparseHes} {xrst_spell walther } Hessian Sparsity Pattern: Forward Mode ###################################### Syntax ****** | *h* = *f* . ``ForSparseHes`` ( *r* , *s* ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* . we define .. math:: :nowrap: \begin{eqnarray} H(x) & = & \partial_x \left[ \partial_u S \cdot F[ x + R \cdot u ] \right]_{u=0} \\ & = & R^\R{T} \cdot (S \cdot F)^{(2)} ( x ) \cdot R \end{eqnarray} Where :math:`R \in \B{R}^{n \times n}` is a diagonal matrix and :math:`S \in \B{R}^{1 \times m}` is a row vector. Given a :ref:`glossary@Sparsity Pattern` for the diagonal of :math:`R` and the vector :math:`S`, ``ForSparseHes`` returns a sparsity pattern for the :math:`H(x)`. f * The object *f* has prototype ``const ADFun`` < *Base* > *f* x * If the operation sequence in *f* is :ref:`glossary@Operation@Independent` of the independent variables in :math:`x \in \B{R}^n`, the sparsity pattern is valid for all values of (even if it has :ref:`CondExp-name` or :ref:`VecAD-name` operations). r * The argument *r* has prototype ``const`` *SetVector* & *r* (see :ref:`ForSparseHes@SetVector` below) If it has elements of type ``bool`` , its size is :math:`n`. If it has elements of type ``std::set`` , its size is one and all the elements of *s* [0] are between zero and :math:`n - 1`. It specifies a :ref:`glossary@Sparsity Pattern` for the diagonal of :math:`R`. The fewer non-zero elements in this sparsity pattern, the faster the calculation should be and the more sparse :math:`H(x)` should be. s * The argument *s* has prototype ``const`` *SetVector* & *s* (see :ref:`ForSparseHes@SetVector` below) If it has elements of type ``bool`` , its size is :math:`m`. If it has elements of type ``std::set`` , its size is one and all the elements of *s* [0] are between zero and :math:`m - 1`. It specifies a :ref:`glossary@Sparsity Pattern` for the vector *S* . The fewer non-zero elements in this sparsity pattern, the faster the calculation should be and the more sparse :math:`H(x)` should be. h * The result *h* has prototype *SetVector* & *h* (see :ref:`ForSparseHes@SetVector` below). If *h* has elements of type ``bool`` , its size is :math:`n * n`. If it has elements of type ``std::set`` , its size is :math:`n` and all the set elements are between zero and *n* ``-1`` inclusive. It specifies a :ref:`glossary@Sparsity Pattern` for the matrix :math:`H(x)`. SetVector ********* The type *SetVector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` ``bool`` or ``std::set`` ; see :ref:`glossary@Sparsity Pattern` for a discussion of the difference. The type of the elements of :ref:`ForSparseHes@SetVector` must be the same as the type of the elements of *r* . Algorithm ********* See Algorithm II in *Computing sparse Hessians with automatic differentiation* by Andrea Walther. Note that *s* provides the information so that 'dead ends' are not included in the sparsity pattern. Example ******* {xrst_toc_hidden example/sparse/for_sparse_hes.cpp } The file :ref:`for_sparse_hes.cpp-name` contains an example and test of this operation. {xrst_end ForSparseHes} ----------------------------------------------------------------------------- */ # include # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file core/for_sparse_hes.hpp Forward mode Hessian sparsity patterns. */ // =========================================================================== // ForSparseHesCase /*! Private helper function for ForSparseHes(q, s) bool sparsity. All of the description in the public member function ForSparseHes(q, s) applies. \param set_type is a bool value. This argument is used to dispatch to the proper source code depending on the value of SetVector::value_type. \param r See ForSparseHes(r, s). \param s See ForSparseHes(r, s). \param h is the return value for the corresponding call to ForSparseJac(q, s). */ template template void ADFun::ForSparseHesCase( bool set_type , const SetVector& r , const SetVector& s , SetVector& h ) { // used to identify the RecBase type in calls to sweeps RecBase not_used_rec_base(0.0); // size_t n = Domain(); size_t m = Range(); // // check Vector is Simple SetVector class with bool elements CheckSimpleVector(); // CPPAD_ASSERT_KNOWN( size_t(r.size()) == n, "ForSparseHes: size of r is not equal to\n" "domain dimension for ADFun object." ); CPPAD_ASSERT_KNOWN( size_t(s.size()) == m, "ForSparseHes: size of s is not equal to\n" "range dimension for ADFun object." ); // // select_domain corresponding to r local::pod_vector select_domain(n); for(size_t j = 0; j < n; ++j) { select_domain[j] = r[j]; CPPAD_ASSERT_UNKNOWN( ind_taddr_[j] == j + 1); CPPAD_ASSERT_UNKNOWN( play_.GetOp(j + 1) == local::InvOp ); } // sparsity pattern corresponding to s local::sparse::pack_setvec rev_jac_pattern; rev_jac_pattern.resize(num_var_tape_, 1); for(size_t i = 0; i < m; i++) { CPPAD_ASSERT_UNKNOWN( dep_taddr_[i] < num_var_tape_ ); // // Not using post_element because only adding one element per set if( s[i] ) rev_jac_pattern.add_element( dep_taddr_[i], 0); } // compute reverse sparsity pattern for dependency analysis // (note that we are only want non-zero derivatives not true dependency) bool dependency = false; local::sweep::rev_jac( &play_, dependency, n, num_var_tape_, rev_jac_pattern, not_used_rec_base ); // vector of sets that will hold the forward Hessain values local::sparse::pack_setvec for_hes_pattern; for_hes_pattern.resize(n+1+num_var_tape_, n+1); // // compute the Hessian sparsity patterns local::sweep::for_hes( &play_, n, num_var_tape_, select_domain, rev_jac_pattern, for_hes_pattern, not_used_rec_base ); // initialize return values corresponding to independent variables h.resize(n * n); for(size_t i = 0; i < n; i++) { for(size_t j = 0; j < n; j++) h[ i * n + j ] = false; } // copy to result pattern CPPAD_ASSERT_UNKNOWN( for_hes_pattern.end() == n+1 ); for(size_t i = 0; i < n; i++) { // ind_taddr_[i] is operator taddr for i-th independent variable CPPAD_ASSERT_UNKNOWN( ind_taddr_[i] == i + 1 ); CPPAD_ASSERT_UNKNOWN( play_.GetOp( ind_taddr_[i] ) == local::InvOp ); // extract the result from for_hes_pattern local::sparse::pack_setvec::const_iterator itr(for_hes_pattern, ind_taddr_[i] ); size_t j = *itr; while( j < for_hes_pattern.end() ) { CPPAD_ASSERT_UNKNOWN( 0 < j ) h[ i * n + (j-1) ] = true; j = *(++itr); } } } /*! Private helper function for ForSparseHes(q, s) set sparsity. All of the description in the public member function ForSparseHes(q, s) applies. \param set_type is a std::set value. This argument is used to dispatch to the proper source code depending on the value of SetVector::value_type. \param r See ForSparseHes(r, s). \param s See ForSparseHes(q, s). \param h is the return value for the corresponding call to ForSparseJac(q, s). */ template template void ADFun::ForSparseHesCase( const std::set& set_type , const SetVector& r , const SetVector& s , SetVector& h ) { // used to identify the RecBase type in calls to sweeps RecBase not_used_rec_base(0.0); // size_t n = Domain(); # ifndef NDEBUG size_t m = Range(); # endif std::set::const_iterator itr_1; // // check SetVector is Simple Vector class with sets for elements CheckSimpleVector, SetVector>( local::one_element_std_set(), local::two_element_std_set() ); CPPAD_ASSERT_KNOWN( r.size() == 1, "ForSparseHes: size of s is not equal to one." ); CPPAD_ASSERT_KNOWN( s.size() == 1, "ForSparseHes: size of s is not equal to one." ); // // select_domain corresponding to r local::pod_vector select_domain(n); for(size_t j = 0; j < n; ++j) { select_domain[j] = false; CPPAD_ASSERT_UNKNOWN( ind_taddr_[j] == j + 1); CPPAD_ASSERT_UNKNOWN( play_.GetOp(j + 1) == local::InvOp ); } itr_1 = r[0].begin(); while( itr_1 != r[0].end() ) { size_t j = *itr_1++; select_domain[j] = true; } // sparsity pattern corresponding to s local::sparse::list_setvec rev_jac_pattern; rev_jac_pattern.resize(num_var_tape_, 1); itr_1 = s[0].begin(); while( itr_1 != s[0].end() ) { size_t i = *itr_1++; CPPAD_ASSERT_KNOWN( i < m, "ForSparseHes: an element of the set s[0] has value " "greater than or equal m" ); CPPAD_ASSERT_UNKNOWN( dep_taddr_[i] < num_var_tape_ ); // // Not using post_element because only adding one element per set rev_jac_pattern.add_element( dep_taddr_[i], 0); } // // compute reverse sparsity pattern for dependency analysis // (note that we are only want non-zero derivatives not true dependency) bool dependency = false; local::sweep::rev_jac( &play_, dependency, n, num_var_tape_, rev_jac_pattern, not_used_rec_base ); // // vector of sets that will hold reverse Hessain values local::sparse::list_setvec for_hes_pattern; for_hes_pattern.resize(n+1+num_var_tape_, n+1); // // compute the Hessian sparsity patterns local::sweep::for_hes( &play_, n, num_var_tape_, select_domain, rev_jac_pattern, for_hes_pattern, not_used_rec_base ); // return values corresponding to independent variables // j is index corresponding to reverse mode partial h.resize(n); CPPAD_ASSERT_UNKNOWN( for_hes_pattern.end() == n+1 ); for(size_t i = 0; i < n; i++) { CPPAD_ASSERT_UNKNOWN( ind_taddr_[i] == i + 1 ); CPPAD_ASSERT_UNKNOWN( play_.GetOp( ind_taddr_[i] ) == local::InvOp ); // extract the result from for_hes_pattern local::sparse::list_setvec::const_iterator itr_2(for_hes_pattern, ind_taddr_[i] ); size_t j = *itr_2; while( j < for_hes_pattern.end() ) { CPPAD_ASSERT_UNKNOWN( 0 < j ) h[i].insert(j-1); j = *(++itr_2); } } } // =========================================================================== // ForSparseHes /*! User API for Hessian sparsity patterns using reverse mode. The C++ source code corresponding to this operation is \verbatim h = f.ForSparseHes(q, r) \endverbatim \tparam Base is the base type for this recording. \tparam SetVector is a simple vector with elements of type bool or std::set. \param r is a vector with size n that specifies the sparsity pattern for the diagonal of the matrix \f$ R \f$, where n is the number of independent variables corresponding to the operation sequence stored in play. \param s is a vector with size m that specifies the sparsity pattern for the vector \f$ S \f$, where m is the number of dependent variables corresponding to the operation sequence stored in play. \return The return vector is a sparsity pattern for \f$ H(x) \f$ \f[ H(x) = R^T ( S * F)^{(2)} (x) R \f] where \f$ F \f$ is the function corresponding to the operation sequence and x is any argument value. */ template template SetVector ADFun::ForSparseHes( const SetVector& r, const SetVector& s ) { SetVector h; typedef typename SetVector::value_type Set_type; // Should check to make sure q is same as in previous call to // forward sparse Jacobian. ForSparseHesCase( Set_type() , r , s , h ); return h; } // =========================================================================== // ForSparseHesCheckpoint /*! Hessian sparsity patterns calculation used by checkpoint functions. \tparam Base is the base type for this recording. \param r is a vector with size n that specifies the sparsity pattern for the diagonal of \f$ R \f$, where n is the number of independent variables corresponding to the operation sequence stored in play_. \param s is a vector with size m that specifies the sparsity pattern for the vector \f$ S \f$, where m is the number of dependent variables corresponding to the operation sequence stored in play_. \param h The input size and elements of h do not matter. On output, h is the sparsity pattern for the matrix \f$ H(x) R \f$. \par Assumptions The forward jacobian sparsity pattern must be currently stored in this ADFUN object. */ // The checkpoint class is not yet using forward sparse Hessians. # ifdef CPPAD_NOT_DEFINED template void ADFun::ForSparseHesCheckpoint( vector& r , vector& s , local::sparse::list_setvec& h ) { // used to identify the RecBase type in calls to sweeps RecBase not_used_rec_base(0.0); // size_t n = Domain(); size_t m = Range(); // checkpoint functions should get this right CPPAD_ASSERT_UNKNOWN( for_jac_sparse_pack_.n_set() == 0 ); CPPAD_ASSERT_UNKNOWN( for_jac_sparse_set_.n_set() == 0 ); CPPAD_ASSERT_UNKNOWN( s.size() == m ); // Array that holds the reverse Jacobiain dependency flags. // Initialize as true for dependent variables, false for others. local::pod_vector RevJac(num_var_tape_); for(size_t i = 0; i < num_var_tape_; i++) RevJac[i] = false; for(size_t i = 0; i < m; i++) { CPPAD_ASSERT_UNKNOWN( dep_taddr_[i] < num_var_tape_ ) RevJac[ dep_taddr_[i] ] = s[i]; } // holds forward Hessian sparsity pattern for all variables local::sparse::list_setvec for_hes_pattern; for_hes_pattern.resize(n+1+num_var_tape_, n+1); // compute Hessian sparsity pattern for all variables local::sweep::for_hes( &play_, n, num_var_tape_, for_jac_sparse_set_, RevJac.data(), for_hes_pattern, not_used_rec_base ); // dimension the return value if( transpose ) h.resize(n, n); else h.resize(n, n); // j is index corresponding to reverse mode partial for(size_t j = 0; j < n; j++) { CPPAD_ASSERT_UNKNOWN( ind_taddr_[j] < num_var_tape_ ); // ind_taddr_[j] is operator taddr for j-th independent variable CPPAD_ASSERT_UNKNOWN( ind_taddr_[j] == j + 1 ); CPPAD_ASSERT_UNKNOWN( play_.GetOp( ind_taddr_[j] ) == local::InvOp ); // extract the result from for_hes_pattern CPPAD_ASSERT_UNKNOWN( for_hes_pattern.end() == q ); local::sparse::list_setvec::const_iterator itr(for_hes_pattern, .j + 1); size_t i = *itr; while( i < q ) { if( transpose ) h.post_element(j, i); else h.post_element(i, j); i = *(++itr); } } // process posts for(size_t i = 0; i < n; ++i) h.process_post(i); } # endif } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/for_sparse_jac.hpp ================================================ # ifndef CPPAD_CORE_FOR_SPARSE_JAC_HPP # define CPPAD_CORE_FOR_SPARSE_JAC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ForSparseJac} Jacobian Sparsity Pattern: Forward Mode ####################################### Syntax ****** | *s* = *f* . ``ForSparseJac`` ( *q* , *r* ) | *s* = *f* . ``ForSparseJac`` ( *q* , *r* , *transpose* , *dependency* ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* . For a fixed :math:`n \times q` matrix :math:`R`, the Jacobian of :math:`F[ x + R * u ]` with respect to :math:`u` at :math:`u = 0` is .. math:: S(x) = F^{(1)} ( x ) * R Given a :ref:`glossary@Sparsity Pattern` for :math:`R`, ``ForSparseJac`` returns a sparsity pattern for the :math:`S(x)`. f * The object *f* has prototype ``ADFun`` < *Base* > *f* Note that the :ref:`ADFun-name` object *f* is not ``const`` . After a call to ``ForSparseJac`` , the sparsity pattern for each of the variables in the operation sequence is held in *f* (for possible later use by :ref:`RevSparseHes-name` ). These sparsity patterns are stored with elements of type ``bool`` or elements of type ``std::set`` (see :ref:`ForSparseJac@SetVector` below). size_forward_bool ================= After ``ForSparseJac`` , if *k* is a ``size_t`` object, *k* = *f* . ``size_forward_bool`` () sets *k* to the amount of memory (in unsigned character units) used to store the sparsity pattern with elements of type ``bool`` in the function object *f* . If the sparsity patterns for the previous ``ForSparseJac`` used elements of type ``bool`` , the return value for ``size_forward_bool`` will be non-zero. Otherwise, its return value will be zero. This sparsity pattern is stored for use by :ref:`RevSparseHes-name` and when it is not longer needed, it can be deleted (and the corresponding memory freed) using *f* . ``size_forward_bool`` (0) After this call, *f* . ``size_forward_bool`` () will return zero. size_forward_set ================ After ``ForSparseJac`` , if *k* is a ``size_t`` object, *k* = *f* . ``size_forward_set`` () sets *k* to the amount of memory (in unsigned character units) used to store the :ref:`glossary@Sparsity Pattern@Vector of Sets` sparsity patterns. If the sparsity patterns for this operation use elements of type ``bool`` , the return value for ``size_forward_set`` will be zero. Otherwise, its return value will be non-zero. This sparsity pattern is stored for use by :ref:`RevSparseHes-name` and when it is not longer needed, it can be deleted (and the corresponding memory freed) using *f* . ``size_forward_set`` (0) After this call, *f* . ``size_forward_set`` () will return zero. x * If the operation sequence in *f* is :ref:`glossary@Operation@Independent` of the independent variables in :math:`x \in \B{R}^n`, the sparsity pattern is valid for all values of (even if it has :ref:`CondExp-name` or :ref:`VecAD-name` operations). q * The argument *q* has prototype ``size_t`` *q* It specifies the number of columns in :math:`R \in \B{R}^{n \times q}` and the Jacobian :math:`S(x) \in \B{R}^{m \times q}`. transpose ********* The argument *transpose* has prototype ``bool`` *transpose* The default value ``false`` is used when *transpose* is not present. dependency ********** The argument *dependency* has prototype ``bool`` *dependency* If *dependency* is true, the :ref:`dependency.cpp@Dependency Pattern` (instead of sparsity pattern) is computed. r * The argument *r* has prototype ``const`` *SetVector* & *r* see :ref:`ForSparseJac@SetVector` below. transpose false =============== If *r* has elements of type ``bool`` , its size is :math:`n * q`. If it has elements of type ``std::set`` , its size is :math:`n` and all the set elements must be between zero and *q* ``-1`` inclusive. It specifies a :ref:`glossary@Sparsity Pattern` for the matrix :math:`R \in \B{R}^{n \times q}`. transpose true ============== If *r* has elements of type ``bool`` , its size is :math:`q * n`. If it has elements of type ``std::set`` , its size is :math:`q` and all the set elements must be between zero and *n* ``-1`` inclusive. It specifies a :ref:`glossary@Sparsity Pattern` for the matrix :math:`R^\R{T} \in \B{R}^{q \times n}`. s * The return value *s* has prototype *SetVector* *s* see :ref:`ForSparseJac@SetVector` below. transpose false =============== If *s* has elements of type ``bool`` , its size is :math:`m * q`. If it has elements of type ``std::set`` , its size is :math:`m` and all its set elements are between zero and *q* ``-1`` inclusive. It specifies a :ref:`glossary@Sparsity Pattern` for the matrix :math:`S(x) \in \B{R}^{m \times q}`. transpose true ============== If *s* has elements of type ``bool`` , its size is :math:`q * m`. If it has elements of type ``std::set`` , its size is :math:`q` and all its set elements are between zero and *m* ``-1`` inclusive. It specifies a :ref:`glossary@Sparsity Pattern` for the matrix :math:`S(x)^\R{T} \in \B{R}^{q \times m}`. SetVector ********* The type *SetVector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` ``bool`` or ``std::set`` ; see :ref:`glossary@Sparsity Pattern` for a discussion of the difference. Entire Sparsity Pattern *********************** Suppose that :math:`q = n` and :math:`R` is the :math:`n \times n` identity matrix. In this case, the corresponding value for *s* is a sparsity pattern for the Jacobian :math:`S(x) = F^{(1)} ( x )`. Example ******* {xrst_toc_hidden example/sparse/for_sparse_jac.cpp } The file :ref:`for_sparse_jac.cpp-name` contains an example and test of this operation. The file :ref:`sparsity_sub.cpp` contains an example and test of using ``ForSparseJac`` to compute the sparsity pattern for a subset of the Jacobian. {xrst_end ForSparseJac} ----------------------------------------------------------------------------- */ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file core/for_sparse_jac.hpp Forward mode Jacobian sparsity patterns. */ // --------------------------------------------------------------------------- /*! Private helper function for ForSparseJac(q, r) boolean sparsity patterns. All of the description in the public member function ForSparseJac(q, r) applies. \param set_type is a bool value. This argument is used to dispatch to the proper source code depending on the value of SetVector::value_type. \param transpose See ForSparseJac(q, r, transpose, dependency). \param dependency See ForSparseJac(q, r, transpose, dependency). \param q See ForSparseJac(q, r, transpose, dependency). \param r See ForSparseJac(q, r, transpose, dependency). \param s is the return value for the corresponding call to ForSparseJac(q, r). */ template template void ADFun::ForSparseJacCase( bool set_type , bool transpose , bool dependency , size_t q , const SetVector& r , SetVector& s ) { // used to identify the RecBase type in calls to sweeps RecBase not_used_rec_base(0.0); // size_t m = Range(); size_t n = Domain(); // check SetVector is Simple Vector class with bool elements CheckSimpleVector(); // dimension size of result vector s.resize( m * q ); CPPAD_ASSERT_KNOWN( q > 0, "ForSparseJac: q is not greater than zero" ); CPPAD_ASSERT_KNOWN( size_t(r.size()) == n * q, "ForSparseJac: size of r is not equal to\n" "q times domain dimension for ADFun object." ); // // allocate memory for the requested sparsity calculation result for_jac_sparse_pack_.resize(num_var_tape_, q); // set values corresponding to independent variables for(size_t i = 0; i < n; i++) { CPPAD_ASSERT_UNKNOWN( ind_taddr_[i] < num_var_tape_ ); // ind_taddr_[i] is operator taddr for i-th independent variable CPPAD_ASSERT_UNKNOWN( play_.GetOp( ind_taddr_[i] ) == local::InvOp ); // set bits that are true if( transpose ) { for(size_t j = 0; j < q; j++) if( r[ j * n + i ] ) for_jac_sparse_pack_.post_element( ind_taddr_[i], j); } else { for(size_t j = 0; j < q; j++) if( r[ i * q + j ] ) for_jac_sparse_pack_.post_element( ind_taddr_[i], j); } } // process posts for(size_t j = 0; j < n; j++) for_jac_sparse_pack_.process_post( ind_taddr_[j] ); // evaluate the sparsity patterns local::sweep::for_jac( &play_, dependency, n, num_var_tape_, for_jac_sparse_pack_, not_used_rec_base ); // return values corresponding to dependent variables CPPAD_ASSERT_UNKNOWN( size_t(s.size()) == m * q ); for(size_t i = 0; i < m; i++) { CPPAD_ASSERT_UNKNOWN( dep_taddr_[i] < num_var_tape_ ); // extract the result from for_jac_sparse_pack_ if( transpose ) { for(size_t j = 0; j < q; j++) s[ j * m + i ] = false; } else { for(size_t j = 0; j < q; j++) s[ i * q + j ] = false; } CPPAD_ASSERT_UNKNOWN( for_jac_sparse_pack_.end() == q ); local::sparse::pack_setvec::const_iterator itr(for_jac_sparse_pack_, dep_taddr_[i] ); size_t j = *itr; while( j < q ) { if( transpose ) s[j * m + i] = true; else s[i * q + j] = true; j = *(++itr); } } } // --------------------------------------------------------------------------- /*! Private helper function for ForSparseJac(q, r) set sparsity. All of the description in the public member function ForSparseJac(q, r) applies. \param set_type is a std::set object. This argument is used to dispatch to the proper source code depending on the value of SetVector::value_type. \param transpose See ForSparseJac(q, r, transpose, dependency). \param dependency See ForSparseJac(q, r, transpose, dependency). \param q See ForSparseJac(q, r, transpose, dependency). \param r See ForSparseJac(q, r, transpose, dependency). \param s is the return value for the corresponding call to ForSparseJac(q, r). */ template template void ADFun::ForSparseJacCase( const std::set& set_type , bool transpose , bool dependency , size_t q , const SetVector& r , SetVector& s ) { // used to identify the RecBase type in calls to sweeps RecBase not_used_rec_base(0.0); // size_t m = Range(); size_t n = Domain(); // check SetVector is Simple Vector class with sets for elements CheckSimpleVector, SetVector>( local::one_element_std_set(), local::two_element_std_set() ); // dimension size of result vector if( transpose ) s.resize(q); else s.resize( m ); // temporary iterator std::set::const_iterator itr_1; CPPAD_ASSERT_KNOWN( q > 0, "ForSparseJac: q is not greater than zero" ); CPPAD_ASSERT_KNOWN( size_t(r.size()) == n || transpose, "ForSparseJac: size of r is not equal to n and transpose is false." ); CPPAD_ASSERT_KNOWN( size_t(r.size()) == q || ! transpose, "ForSparseJac: size of r is not equal to q and transpose is true." ); // // allocate memory for the requested sparsity calculation for_jac_sparse_set_.resize(num_var_tape_, q); // set values corresponding to independent variables if( transpose ) { for(size_t i = 0; i < q; i++) { // add the elements that are present itr_1 = r[i].begin(); while( itr_1 != r[i].end() ) { size_t j = *itr_1++; CPPAD_ASSERT_KNOWN( j < n, "ForSparseJac: transpose is true and element of the set\n" "r[j] has value greater than or equal n." ); CPPAD_ASSERT_UNKNOWN( ind_taddr_[j] < num_var_tape_ ); // operator for j-th independent variable CPPAD_ASSERT_UNKNOWN( play_.GetOp( ind_taddr_[j] ) == local::InvOp ); for_jac_sparse_set_.post_element( ind_taddr_[j], i); } } } else { for(size_t i = 0; i < n; i++) { CPPAD_ASSERT_UNKNOWN( ind_taddr_[i] < num_var_tape_ ); // ind_taddr_[i] is operator taddr for i-th independent variable CPPAD_ASSERT_UNKNOWN( play_.GetOp( ind_taddr_[i] ) == local::InvOp ); // add the elements that are present itr_1 = r[i].begin(); while( itr_1 != r[i].end() ) { size_t j = *itr_1++; CPPAD_ASSERT_KNOWN( j < q, "ForSparseJac: an element of the set r[i] " "has value greater than or equal q." ); for_jac_sparse_set_.post_element( ind_taddr_[i], j); } } } // process posts for(size_t j = 0; j < n; j++) for_jac_sparse_set_.process_post( ind_taddr_[j] ); // evaluate the sparsity patterns local::sweep::for_jac( &play_, dependency, n, num_var_tape_, for_jac_sparse_set_, not_used_rec_base ); // return values corresponding to dependent variables CPPAD_ASSERT_UNKNOWN( size_t(s.size()) == m || transpose ); CPPAD_ASSERT_UNKNOWN( size_t(s.size()) == q || ! transpose ); for(size_t i = 0; i < m; i++) { CPPAD_ASSERT_UNKNOWN( dep_taddr_[i] < num_var_tape_ ); // extract results from for_jac_sparse_set_ // and add corresponding elements to sets in s CPPAD_ASSERT_UNKNOWN( for_jac_sparse_set_.end() == q ); local::sparse::list_setvec::const_iterator itr_2(for_jac_sparse_set_, dep_taddr_[i] ); size_t j = *itr_2; while( j < q ) { if( transpose ) s[j].insert(i); else s[i].insert(j); j = *(++itr_2); } } } // --------------------------------------------------------------------------- /*! User API for Jacobian sparsity patterns using forward mode. The C++ source code corresponding to this operation is \verbatim s = f.ForSparseJac(q, r, transpose, dependency) \endverbatim \tparam Base is the base type for this recording. \tparam SetVector is a simple vector with elements of type bool or std::set. \param q is the number of columns in the matrix \f$ R \f$. \param r is a sparsity pattern for the matrix \f$ R \f$. \param transpose are sparsity patterns for \f$ R \f$ and \f$ S(x) \f$ transposed. \param dependency Are the derivatives with respect to left and right of the expression below considered to be non-zero: \code CondExpRel(left, right, if_true, if_false) \endcode This is used by the optimizer to obtain the correct dependency relations. \return The value of transpose is false (true), the return value is a sparsity pattern for \f$ S(x) \f$ (\f$ S(x)^T \f$) where \f[ S(x) = F^{(1)} (x) * R \f] where \f$ F \f$ is the function corresponding to the operation sequence and x is any argument value. If SetVector::value_type is bool, the return value has size \f$ m * q \f$ (\f$ q * m \f$). where m is the number of dependent variables corresponding to the operation sequence stored in f. If SetVector::value_type is std::set, the return value has size \f$ m \f$ ( \f$ q \f$ ) and with all its elements between zero and \f$ q - 1 \f$ ( \f$ m - 1 \f$). \par Side Effects If SetVector::value_type is bool, the forward sparsity pattern for all of the variables on the tape is stored in for_jac_sparse_pack__. In this case \verbatim for_jac_sparse_pack_.n_set() == num_var_tape_ for_jac_sparse_pack_.end() == q for_jac_sparse_set_.n_set() == 0 for_jac_sparse_set_.end() == 0 \endverbatim \n \n If SetVector::value_type is std::set, the forward sparsity pattern for all of the variables on the tape is stored in for_jac_sparse_set__. In this case \verbatim for_jac_sparse_set_.n_set() == num_var_tape_ for_jac_sparse_set_.end() == q for_jac_sparse_pack_.n_set() == 0 for_jac_sparse_pack_.end() == 0 \endverbatim */ template template SetVector ADFun::ForSparseJac( size_t q , const SetVector& r , bool transpose , bool dependency ) { SetVector s; typedef typename SetVector::value_type Set_type; // free all memory currently in sparsity patterns for_jac_sparse_pack_.resize(0, 0); for_jac_sparse_set_.resize(0, 0); ForSparseJacCase( Set_type() , transpose , dependency , q , r , s ); return s; } // =========================================================================== // ForSparseJacCheckpoint /*! Forward mode Jacobian sparsity calculation used by checkpoint functions. \tparam Base is the base type for this recording. \param transpose is true (false) s is equal to \f$ S(x) \f$ (\f$ S(x)^T \f$) where \f[ S(x) = F^{(1)} (x) * R \f] where \f$ F \f$ is the function corresponding to the operation sequence and \f$ x \f$ is any argument value. \param q is the number of columns in the matrix \f$ R \f$. \param r is a sparsity pattern for the matrix \f$ R \f$. \param transpose are the sparsity patterns for \f$ R \f$ and \f$ S(x) \f$ transposed. \param dependency Are the derivatives with respect to left and right of the expression below considered to be non-zero: \code CondExpRel(left, right, if_true, if_false) \endcode This is used by the optimizer to obtain the correct dependency relations. \param s The input size and elements of s do not matter. On output, s is the sparsity pattern for the matrix \f$ S(x) \f$ or \f$ S(x)^T \f$ depending on transpose. \par Side Effects If SetVector::value_type is bool, the forward sparsity pattern for all of the variables on the tape is stored in for_jac_sparse_pack__. In this case \verbatim for_jac_sparse_pack_.n_set() == num_var_tape_ for_jac_sparse_pack_.end() == q for_jac_sparse_set_.n_set() == 0 for_jac_sparse_set_.end() == 0 \endverbatim \n \n If SetVector::value_type is std::set, the forward sparsity pattern for all of the variables on the tape is stored in for_jac_sparse_set__. In this case \verbatim for_jac_sparse_set_.n_set() == num_var_tape_ for_jac_sparse_set_.end() == q for_jac_sparse_pack_.n_set() == 0 for_jac_sparse_pack_.end() == 0 \endverbatim */ template void ADFun::ForSparseJacCheckpoint( size_t q , const local::sparse::list_setvec& r , bool transpose , bool dependency , local::sparse::list_setvec& s ) { // used to identify the RecBase type in calls to sweeps RecBase not_used_rec_base(0.0); // size_t n = Domain(); size_t m = Range(); # ifndef NDEBUG if( transpose ) { CPPAD_ASSERT_UNKNOWN( r.n_set() == q ); CPPAD_ASSERT_UNKNOWN( r.end() == n ); } else { CPPAD_ASSERT_UNKNOWN( r.n_set() == n ); CPPAD_ASSERT_UNKNOWN( r.end() == q ); } for(size_t j = 0; j < n; j++) { CPPAD_ASSERT_UNKNOWN( ind_taddr_[j] == (j+1) ); CPPAD_ASSERT_UNKNOWN( play_.GetOp( ind_taddr_[j] ) == local::InvOp ); } # endif // free all memory currently in sparsity patterns for_jac_sparse_pack_.resize(0, 0); for_jac_sparse_set_.resize(0, 0); // allocate new sparsity pattern for_jac_sparse_set_.resize(num_var_tape_, q); // set sparsity pattern for dependent variables if( transpose ) { for(size_t i = 0; i < q; i++) { local::sparse::list_setvec::const_iterator itr(r, i); size_t j = *itr; while( j < n ) { for_jac_sparse_set_.post_element( ind_taddr_[j], i ); j = *(++itr); } } } else { for(size_t j = 0; j < n; j++) { local::sparse::list_setvec::const_iterator itr(r, j); size_t i = *itr; while( i < q ) { for_jac_sparse_set_.post_element( ind_taddr_[j], i ); i = *(++itr); } } } // process posts for(size_t j = 0; j < n; j++) for_jac_sparse_set_.process_post( ind_taddr_[j] ); // evaluate the sparsity pattern for all variables local::sweep::for_jac( &play_, dependency, n, num_var_tape_, for_jac_sparse_set_, not_used_rec_base ); // dimension the return value if( transpose ) s.resize(q, m); else s.resize(m, q); // return values corresponding to dependent variables for(size_t i = 0; i < m; i++) { CPPAD_ASSERT_UNKNOWN( dep_taddr_[i] < num_var_tape_ ); // extract the result from for_jac_sparse_set_ CPPAD_ASSERT_UNKNOWN( for_jac_sparse_set_.end() == q ); local::sparse::list_setvec::const_iterator itr(for_jac_sparse_set_, dep_taddr_[i] ); size_t j = *itr; while( j < q ) { if( transpose ) s.post_element(j, i); else s.post_element(i, j); j = *(++itr); } } // process posts for(size_t i = 0; i < s.n_set(); ++i) s.process_post(i); } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/for_two.hpp ================================================ # ifndef CPPAD_CORE_FOR_TWO_HPP # define CPPAD_CORE_FOR_TWO_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ForTwo} {xrst_spell ddy } Forward Mode Second Partial Derivative Driver ############################################# Syntax ****** | *ddy* = *f* . ``ForTwo`` ( *x* , *j* , *k* ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* . The syntax above sets .. math:: ddy [ i * p + \ell ] = \DD{ F_i }{ x_{j[ \ell ]} }{ x_{k[ \ell ]} } (x) for :math:`i = 0 , \ldots , m-1` and :math:`\ell = 0 , \ldots , p`, where :math:`p` is the size of the vectors *j* and *k* . f * The object *f* has prototype ``ADFun`` < *Base* > *f* Note that the :ref:`ADFun-name` object *f* is not ``const`` (see :ref:`ForTwo@ForTwo Uses Forward` below). x * The argument *x* has prototype ``const`` *BaseVector* & *x* (see :ref:`ForTwo@BaseVector` below) and its size must be equal to *n* , the dimension of the :ref:`fun_property@Domain` space for *f* . It specifies that point at which to evaluate the partial derivatives listed above. j * The argument *j* has prototype ``const`` *SizeVector_t* & *j* (see :ref:`ForTwo@SizeVector_t` below) We use *p* to denote the size of the vector *j* . All of the indices in *j* must be less than *n* ; i.e., for :math:`\ell = 0 , \ldots , p-1`, :math:`j[ \ell ] < n`. k * The argument *k* has prototype ``const`` *SizeVector_t* & *k* (see :ref:`ForTwo@SizeVector_t` below) and its size must be equal to *p* , the size of the vector *j* . All of the indices in *k* must be less than *n* ; i.e., for :math:`\ell = 0 , \ldots , p-1`, :math:`k[ \ell ] < n`. ddy *** The result *ddy* has prototype *BaseVector* *ddy* (see :ref:`ForTwo@BaseVector` below) and its size is :math:`m * p`. It contains the requested partial derivatives; to be specific, for :math:`i = 0 , \ldots , m - 1` and :math:`\ell = 0 , \ldots , p - 1` .. math:: ddy [ i * p + \ell ] = \DD{ F_i }{ x_{j[ \ell ]} }{ x_{k[ \ell ]} } (x) BaseVector ********** The type *BaseVector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type Base` . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. SizeVector_t ************ The type *SizeVector_t* must be a :ref:`SimpleVector-name` class with :ref:`elements of type size_t` . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. ForTwo Uses Forward ******************* After each call to :ref:`Forward-name` , the object *f* contains the corresponding :ref:`Taylor coefficients` . After a call to ``ForTwo`` , the zero order Taylor coefficients correspond to *f* . ``Forward`` (0, *x* ) and the other coefficients are unspecified. Examples ******** {xrst_toc_hidden example/general/for_two.cpp } The routine :ref:`ForTwo` is both an example and test. It returns ``true`` , if it succeeds and ``false`` otherwise. {xrst_end ForTwo} ----------------------------------------------------------------------------- */ // BEGIN CppAD namespace namespace CppAD { template template BaseVector ADFun::ForTwo( const BaseVector &x, const SizeVector_t &j, const SizeVector_t &k) { size_t i; size_t j1; size_t k1; size_t l; size_t n = Domain(); size_t m = Range(); size_t p = j.size(); // check BaseVector is Simple Vector class with Base type elements CheckSimpleVector(); // check SizeVector_t is Simple Vector class with size_t elements CheckSimpleVector(); CPPAD_ASSERT_KNOWN( x.size() == n, "ForTwo: Length of x not equal domain dimension for f." ); CPPAD_ASSERT_KNOWN( j.size() == k.size(), "ForTwo: Length of the j and k vectors are not equal." ); // point at which we are evaluating the second partials Forward(0, x); // dimension the return value BaseVector ddy(m * p); // allocate memory to hold all possible diagonal Taylor coefficients // (for large sparse cases, this is not efficient) BaseVector D(m * n); // boolean flag for which diagonal coefficients are computed CppAD::vector c(n); for(j1 = 0; j1 < n; j1++) c[j1] = false; // direction vector in argument space BaseVector dx(n); for(j1 = 0; j1 < n; j1++) dx[j1] = Base(0.0); // result vector in range space BaseVector dy(m); // compute the diagonal coefficients that are needed for(l = 0; l < p; l++) { j1 = j[l]; k1 = k[l]; CPPAD_ASSERT_KNOWN( j1 < n, "ForTwo: an element of j not less than domain dimension for f." ); CPPAD_ASSERT_KNOWN( k1 < n, "ForTwo: an element of k not less than domain dimension for f." ); size_t count = 2; while(count) { count--; if( ! c[j1] ) { // diagonal term in j1 direction c[j1] = true; dx[j1] = Base(1.0); Forward(1, dx); dx[j1] = Base(0.0); dy = Forward(2, dx); for(i = 0; i < m; i++) D[i * n + j1 ] = dy[i]; } j1 = k1; } } // compute all the requested cross partials for(l = 0; l < p; l++) { j1 = j[l]; k1 = k[l]; if( j1 == k1 ) { for(i = 0; i < m; i++) ddy[i * p + l] = Base(2.0) * D[i * n + j1]; } else { // cross term in j1 and k1 directions dx[j1] = Base(1.0); dx[k1] = Base(1.0); Forward(1, dx); dx[j1] = Base(0.0); dx[k1] = Base(0.0); dy = Forward(2, dx); // place result in return value for(i = 0; i < m; i++) ddy[i * p + l] = dy[i] - D[i*n+j1] - D[i*n+k1]; } } return ddy; } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/forward/compare_change.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin compare_change} Comparison Changes Between Taping and Zero Order Forward ######################################################## Syntax ****** | *f* . ``compare_change_count`` ( *count* ) | *number* = *f* . ``compare_change_number`` () | *op_index* = *f* . ``compare_change_op_index`` () See Also ******** :ref:`FunCheck-name` Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* ; i.e, given :math:`x \in \B{R}^n`, :math:`F(x)` is defined by *F* ( *x* ) = *f* . ``Forward`` (0, *x* ) see :ref:`forward_zero-name` . If :math:`x` is such that all the algorithm :ref:`comparison` operations have the same result as when the algorithm was taped, The function :math:`F(x)` and the algorithm will have the same values. (This is a sufficient, but not necessary condition). f * In the ``compare_change_number`` and ``compare_change_op_index`` syntax, the object *f* has prototype ``const ADFun`` < *Base* > *f* In the ``compare_change_count`` syntax, the object *f* has prototype ``ADFun`` < *Base* > *f* count ***** The argument *count* has prototype *size_t* ``count`` It specifies which comparison change should correspond to the information stored in *f* during subsequent calls to :ref:`forward_zero-name` ; i.e., *f* . ``Forward`` (0, *x* ) For example, if *count* == 1 , the operator index corresponding to the first comparison change will be stored. This is the default value used if *count* is not specified. Speed ===== The special case where *count* == 0 , should be faster because the comparisons are not checked during *f* . ``Forward`` (0, *x* ) number ****** The return value *number* has prototype ``size_t`` *number* If *count* is non-zero, *number* is the number of ``AD`` < *Base* > :ref:`comparison` operations, corresponding to the previous call to *f* . ``Forward`` (0, *x* ) that have a different result for this value of *x* than the value used when *f* was created by taping an algorithm. If *count* is zero, or if no calls to *f* . ``Forward`` (0, *x* ) follow the previous setting of *count* , *number* is zero. Discussion ========== If *count* and *number* are non-zero, you may want to re-tape the algorithm with the :ref:`independent variables` equal to the values in *x* , so the AD operation sequence properly represents the algorithm for this value of independent variables. On the other hand, re-taping the AD operation sequence usually takes significantly more time than evaluation using :ref:`forward_zero-name` . If the functions values have not changed (see :ref:`FunCheck-name` ) it may not be worth re-taping a new AD operation sequence. op_index ******** The return value *op_index* has prototype ``size_t`` *op_index* If *count* is non-zero, *op_index* is the operator index corresponding the *count* -th comparison change during the previous call to *f* . ``Forward`` (0, *x* ) If *count* is greater than the corresponding *number* , there is no such comparison change and *op_index* will also be zero. If *count* is zero, if the function *f* has been :ref:`optimized` , or if no calls to *f* . ``Forward`` (0, *x* ) follow the previous setting of *count* , *op_index* is zero. Purpose ======= The operator index can be used to generate an error during the taping process so that the corresponding algorithm can be inspected. In some cases, it is possible to re-design this part of the algorithm to avoid the particular comparison operation. For example, using an :ref:`conditional expression` may be appropriate in some cases. See :ref:`Independent@abort_op_index` in the syntax ``Independent`` ( *x* , *abort_op_index* ) {xrst_toc_hidden example/compare_change/compare_change.cpp } Example ******* :ref:`compare_change.cpp-name` contains an example and test of this operation. {xrst_end compare_change} ================================================ FILE: include/cppad/core/forward/devel.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin devel_forward dev} Devel: Forward Mode ################### Contents ******** {xrst_toc_table include/cppad/core/forward/forward.hpp } {xrst_end devel_forward} ================================================ FILE: include/cppad/core/forward/forward.hpp ================================================ # ifndef CPPAD_CORE_FORWARD_FORWARD_HPP # define CPPAD_CORE_FORWARD_FORWARD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- // documented after Forward but included here so easy to see # include # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /* --------------------------------------- --------------------------------------- {xrst_begin devel_forward_order dev} {xrst_spell pri xq yq } Multiple orders, one direction, forward mode Taylor coefficients ################################################################ Syntax ****** | *yq* = *f* . ``Forward`` ( *q* , *xq* , *s* ) Prototype ********* {xrst_literal // BEGIN_FORWARD_ORDER // END_FORWARD_ORDER } Base **** The type used during the forward mode computations; i.e., the corresponding recording of operations used the type AD. BaseVector ********** is a Simple Vector class with elements of type Base. q * is the highest order for this forward mode computation; i.e., after this calculation there will be *q* +1 Taylor coefficients per variable. xq ** contains Taylor coefficients for the independent variables. The size of *xq* must either be *n* or ( *q* +1)* *n* , We define *p* = *q* + 1 *- xq.size* ()/ *n* . For *j* = 0 , ... , *n-1* , *k* = 0, ... , *q* , *xq* [ ( *q* +1)* *j* + *k - p* ] is the k-th order coefficient for the j-th independent variable. s * Is the stream where output corresponding to PriOp operations will written. yq ** contains Taylor coefficients for the dependent variables. The size of the return value *yq* has size *m* * ( *q* +1 *-p* ) . For *i* = 0, ... , *m-1* , *k* = *p* , ..., *q* , *yq* [( *q* +1 *-p* )* *i* + ( *k-p* )] is the k-th order coefficient for the i-th dependent variable. taylor\_ ******** The Taylor coefficients up to order *p-1* are inputs and the coefficients from order *p* through *q* are outputs. Let *N* = *num_var_tape_* , and *C* = *cap_order_taylor_* . Note that for *i* = 1 , ..., *N-1* , *k* = 0 , ..., *q* , *taylor_* [ *C* * *i* + *k* ] is the k-th order coefficient, for the i-th variable on the tape. (The first independent variable has index one on the tape and there is no variable with index zero.) {xrst_end devel_forward_order} */ // BEGIN_FORWARD_ORDER template template BaseVector ADFun::Forward( size_t q , const BaseVector& xq , std::ostream& s ) // END_FORWARD_ORDER { // used to identify the RecBase type in calls to sweeps RecBase not_used_rec_base(0.0); // temporary indices size_t i, j, k; // number of independent variables size_t n = ind_taddr_.size(); // number of dependent variables size_t m = dep_taddr_.size(); // check Vector is Simple Vector class with Base type elements CheckSimpleVector(); // check size of xq CPPAD_ASSERT_KNOWN( size_t(xq.size()) == n || size_t(xq.size()) == n*(q+1), "Forward(q, xq): xq.size() is not equal n or n*(q+1)" ); // p = lowest order we are computing size_t p = q + 1 - size_t(xq.size()) / n; CPPAD_ASSERT_UNKNOWN( p == 0 || p == q ); // check one order case CPPAD_ASSERT_KNOWN( q <= num_order_taylor_ || p == 0, "Forward(q, xq): Number of Taylor coefficient orders stored in this" " ADFun\nis less than q and xq.size() != n*(q+1)." ); // if p > 1, the previous number of directions must be one CPPAD_ASSERT_KNOWN( p <= 1 || num_direction_taylor_ == 1, "Forward(q, xq): computing order q >= 2" " and number of directions is not one." "\nMust use Forward(q, r, xq) for this case" ); // does taylor_ need more orders or fewer directions if( (cap_order_taylor_ <= q) || (num_direction_taylor_ != 1) ) { if( p == 0 ) { // no need to copy old values during capacity_order num_order_taylor_ = 0; } else num_order_taylor_ = q; size_t c = std::max(q + 1, cap_order_taylor_); size_t r = 1; capacity_order(c, r); } CPPAD_ASSERT_UNKNOWN( cap_order_taylor_ > q ); CPPAD_ASSERT_UNKNOWN( num_direction_taylor_ == 1 ); // short hand notation for order capacity size_t C = cap_order_taylor_; // The optimizer may skip a step that does not affect dependent variables. // Initializing zero order coefficients avoids following valgrind warning: // "Conditional jump or move depends on uninitialised value(s)". for(j = 0; j < num_var_tape_; j++) { for(k = p; k <= q; k++) taylor_[C * j + k] = CppAD::numeric_limits::quiet_NaN(); } // set Taylor coefficients for independent variables # ifndef NDEBUG for(j = 0; j < n; j++) { // ind_taddr_[j] is index of j-th independent variable CPPAD_ASSERT_UNKNOWN( ind_taddr_[j] < num_var_tape_ ); // ind_taddr_[j] is operator taddr for j-th independent variable CPPAD_ASSERT_UNKNOWN( play_.GetOp( ind_taddr_[j] ) == local::InvOp ); } # endif if( p == q ) { CPPAD_ASSERT_UNKNOWN( xq.size() == n ); for(j = 0; j < n; ++j) taylor_[ C * ind_taddr_[j] + q] = xq[j]; } else { CPPAD_ASSERT_UNKNOWN( xq.size() == (q + 1) * n ); for(j = 0; j < n; ++j) { for(k = 0; k <= q; k++) taylor_[ C * ind_taddr_[j] + k] = xq[ (q+1)*j + k]; } } // // evaluate the derivatives CPPAD_ASSERT_UNKNOWN( cskip_op_.size() == play_.num_var_op() ); CPPAD_ASSERT_UNKNOWN( load_op2var_.size() == play_.num_var_load() ); if( q == 0 ) { bool print = true; local::sweep::forward_0( not_used_rec_base, &play_, num_var_tape_, C, cskip_op_.data(), load_op2var_, compare_change_count_, compare_change_number_, compare_change_op_index_, s, print, taylor_.data() ); } else { bool print = true; local::sweep::forward_any( not_used_rec_base, &play_, num_var_tape_, C, cskip_op_.data(), load_op2var_, compare_change_count_, compare_change_number_, compare_change_op_index_, s, print, p, q, taylor_.data() ); } // return Taylor coefficients for dependent variables BaseVector yq; if( p == q ) { yq.resize(m); for(i = 0; i < m; i++) { CPPAD_ASSERT_UNKNOWN( dep_taddr_[i] < num_var_tape_ ); yq[i] = taylor_[ C * dep_taddr_[i] + q]; } } else { yq.resize(m * (q+1) ); for(i = 0; i < m; i++) { for(k = 0; k <= q; k++) yq[ (q+1) * i + k] = taylor_[ C * dep_taddr_[i] + k ]; } } # ifndef NDEBUG if( check_for_nan_ ) { bool ok = true; size_t index = m; if( p == 0 ) { for(i = 0; i < m; i++) { // Visual Studio 2012, CppAD required in front of isnan ? if( CppAD::isnan( yq[ (q+1) * i + 0 ] ) ) { ok = false; if( index == m ) index = i; } } } if( ! ok ) { CPPAD_ASSERT_UNKNOWN( index < m ); // CppAD::vector x0(n); for(j = 0; j < n; j++) x0[j] = taylor_[ C * ind_taddr_[j] + 0 ]; std::string file_name; put_check_for_nan(x0, file_name); std::stringstream ss; ss << "yq = f.Forward(q, xq): a zero order Taylor coefficient is nan.\n" "Corresponding independent variables vector was written " "to binary a file.\n" "vector_size = " << n << "\n" << "file_name = " << file_name << "\n" << "index = " << index << "\n"; // ss.str() returns a string object with a copy of the current // contents in the stream buffer. std::string msg_str = ss.str(); // msg_str.c_str() returns a pointer to the c-string // representation of the string object's value. const char* msg_char_star = msg_str.c_str(); ErrorHandler::Call( true, __LINE__, __FILE__, "if( CppAD::isnan( yq[ (q+1) * index + 0 ] )", msg_char_star ); } CPPAD_ASSERT_KNOWN(ok, "with the value nan." ); if( 0 < q ) { for(i = 0; i < m; i++) { for(k = p; k <= q; k++) { // Studio 2012, CppAD required in front of isnan ? ok &= ! CppAD::isnan( yq[ (q+1-p)*i + k-p ] ); } } } CPPAD_ASSERT_KNOWN(ok, "yq = f.Forward(q, xq): has a non-zero order Taylor coefficient\n" "with the value nan (but zero order coefficients are not nan)." ); } # endif // now we have q + 1 taylor_ coefficient orders per variable num_order_taylor_ = q + 1; return yq; } /* --------------------------------------- --------------------------------------- {xrst_begin devel_forward_dir dev} {xrst_spell xq yq } One order, multiple directions, forward mode Taylor coefficients ################################################################ Syntax ****** *yq* = *f* . ``Forward`` ( *q* , *r* , *xq* ) Prototype ********* {xrst_literal // BEGIN_FORWARD_DIR // END_FORWARD_DIR } Base **** The type used during the forward mode computations; i.e., the corresponding recording of operations used the type AD. BaseVector ********** is a Simple Vector class with elements of type Base. q * is the order for this forward mode computation, *q > 0* . There must be at least *q* Taylor coefficients per variable before this call. After this call there will be *q* +1 Taylor coefficients per variable. r * is the number of directions for this calculation. If *q* != 1 , r must be the same as in the previous call to Forward where q was equal to one. xq ** contains Taylor coefficients for the independent variables. The size of xq must either be *r* * *n* , For *j* = 0 , ... , *n-1* , *ell* = 0, ... , *r-1* , *xq* [ ( *r* * *j* + *ell* ] is the q-th order coefficient for the j-th independent variable and the ell-th direction. yq ** contains Taylor coefficients for the dependent variables. The size of *y* is *r* * *m* . For *i* = 0, ... , *m-1* , *ell* = 0, ... , *r-1* , *yq* [ *r* * *i* + *ell* ] is the q-th order coefficient for the i-th dependent variable and the ell-th direction. taylor\_ ******** The Taylor coefficients up to order *q-1* are inputs and the coefficients of order q are outputs. Let *N* = *num_var_tape_* , and *C* = *cap_order_taylor_* . Note that for *i* = 1 , ..., *N-1* , *taylor_* [ ( *C-1* )* *r* * *i* + *i* + 0 ] is the zero order coefficient, for the i-th variable, and all directions. For *i* = 1 , ..., *N-1* , *k* = 1 , ..., *q* , *ell* = 0 , ..., *r-1* , *taylor_* [ ( *C-1* )* *r* * *i* + *i* + ( *k-1* )* *r* + *ell* + 1 ] is the k-th order coefficient, for the i-th variable, and ell-th direction. (The first independent variable has index one on the tape and there is no variable with index zero.) {xrst_end devel_forward_dir} */ // BEGIN_FORWARD_DIR template template BaseVector ADFun::Forward( size_t q , size_t r , const BaseVector& xq ) // END_FORWARD_DIR { // used to identify the RecBase type in calls to sweeps RecBase not_used_rec_base(0.0); // temporary indices size_t i, j, ell; // number of independent variables size_t n = ind_taddr_.size(); // number of dependent variables size_t m = dep_taddr_.size(); // check Vector is Simple Vector class with Base type elements CheckSimpleVector(); CPPAD_ASSERT_KNOWN( q > 0, "Forward(q, r, xq): q == 0" ); CPPAD_ASSERT_KNOWN( size_t(xq.size()) == r * n, "Forward(q, r, xq): xq.size() is not equal r * n" ); CPPAD_ASSERT_KNOWN( q <= num_order_taylor_ , "Forward(q, r, xq): Number of Taylor coefficient orders stored in" " this ADFun is less than q" ); CPPAD_ASSERT_KNOWN( q == 1 || num_direction_taylor_ == r , "Forward(q, r, xq): q > 1 and number of Taylor directions r" " is not same as previous Forward(1, r, xq)" ); // does taylor_ need more orders or new number of directions if( cap_order_taylor_ <= q || num_direction_taylor_ != r ) { if( num_direction_taylor_ != r ) num_order_taylor_ = 1; size_t c = std::max(q + 1, cap_order_taylor_); capacity_order(c, r); } CPPAD_ASSERT_UNKNOWN( cap_order_taylor_ > q ); CPPAD_ASSERT_UNKNOWN( num_direction_taylor_ == r ) // short hand notation for order capacity size_t c = cap_order_taylor_; // set Taylor coefficients for independent variables for(j = 0; j < n; j++) { CPPAD_ASSERT_UNKNOWN( ind_taddr_[j] < num_var_tape_ ); // ind_taddr_[j] is operator taddr for j-th independent variable CPPAD_ASSERT_UNKNOWN( play_.GetOp( ind_taddr_[j] ) == local::InvOp ); for(ell = 0; ell < r; ell++) { size_t index = ((c-1)*r + 1)*ind_taddr_[j] + (q-1)*r + ell + 1; taylor_[ index ] = xq[ r * j + ell ]; } } // evaluate the derivatives CPPAD_ASSERT_UNKNOWN( cskip_op_.size() == play_.num_var_op() ); CPPAD_ASSERT_UNKNOWN( load_op2var_.size() == play_.num_var_load() ); local::sweep::forward_dir( not_used_rec_base, &play_, num_var_tape_, c, cskip_op_.data(), load_op2var_, q, r, taylor_.data() ); // return Taylor coefficients for dependent variables BaseVector yq; yq.resize(r * m); for(i = 0; i < m; i++) { CPPAD_ASSERT_UNKNOWN( dep_taddr_[i] < num_var_tape_ ); for(ell = 0; ell < r; ell++) { size_t index = ((c-1)*r + 1)*dep_taddr_[i] + (q-1)*r + ell + 1; yq[ r * i + ell ] = taylor_[ index ]; } } # ifndef NDEBUG if( check_for_nan_ ) { bool ok = true; for(i = 0; i < m; i++) { for(ell = 0; ell < r; ell++) { // Studio 2012, CppAD required in front of isnan ? ok &= ! CppAD::isnan( yq[ r * i + ell ] ); } } CPPAD_ASSERT_KNOWN(ok, "yq = f.Forward(q, r, xq): has a non-zero order Taylor coefficient\n" "with the value nan (but zero order coefficients are not nan)." ); } # endif // now we have q + 1 taylor_ coefficient orders per variable num_order_taylor_ = q + 1; return yq; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/forward/forward_dir.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin forward_dir} {xrst_spell xk xq yq } Multiple Directions Forward Mode ################################ Syntax ****** | *yq* = *f* . ``Forward`` ( *q* , *r* , *xq* ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* . Given a function :math:`X : \B{R} \rightarrow \B{R}^n`, defined by its :ref:`Taylor coefficients` , forward mode computes the Taylor coefficients for the function .. math:: Y (t) = F [ X(t) ] This version of forward mode computes multiple directions as the same time (reducing the number of passes through the tape). This requires more memory, but might be faster in some cases. Reverse Mode ************ Reverse mode for multiple directions has not yet been implemented. If you have speed tests that indicate that multiple direction forward mode is faster, and you want to try multiple direction reverse mode, contact the CppAD project manager. Notation ******** n = We use *n* to denote the dimension of the :ref:`fun_property@Domain` space for *f* . m = We use *m* to denote the dimension of the :ref:`fun_property@Range` space for *f* . f * The :ref:`ADFun-name` object *f* has prototype ``ADFun`` < *Base* > *f* Note that the :ref:`ADFun-name` object *f* is not ``const`` . After this call we will have | |tab| *f* . ``size_order`` () == *q* + 1 | |tab| *f* . ``size_direction`` () == *r* q * This argument has prototype ``size_t`` *q* It specifies the order of Taylor Coefficient that we are calculating and must be greater than zero. The zero order coefficients can only have one direction computed and stored in *f* so use :ref:`forward_zero-name` to compute the zero order coefficients. r * This argument has prototype ``size_t`` *r* It specifies the number of directions that are computed together. If ( *r* == 1 ), you are only using one direction and :ref:`forward_order-name` is simpler, and should be faster, than this more general case. xq ** The argument *xq* has prototype ``const`` *Vector* & *xq* and its size must be *n* * *r* (see :ref:`forward_dir@Vector` below). For :math:`\ell = 0 , \ldots , r-1`, :math:`j = 0 , \ldots , n-1`, the *j*-th component of the *q*-th order Taylor coefficient for :math:`X_\ell (t)` is defined by |tab| :math:`x_j^{(q),\ell} =` *xq* [ *r* * *j* + *ell* ] Zero Order ********** For :math:`j = 0 , \ldots , n-1`, the *j*-th component of the zero order Taylor coefficient for :math:`X_\ell (t)` is defined by |tab| :math:`x_j^{(0)} =` *xk* [ *j* ] where *xk* corresponds to the previous call *f* . ``Forward`` ( *k* , *xk* ) with *k* = 0 . Non-Zero Lower Orders ********************* For :math:`\ell = 0 , \ldots , r-1`, :math:`j = 0 , \ldots , n-1`, :math:`k = 1, \ldots , q-1`, the *j*-th component of the *k*-th order Taylor coefficient for :math:`X_\ell (t)` is defined by |tab| :math:`x_j^{(k),\ell} =` *xk* [ *r* * *j* + *ell* ] where *xk* corresponds to the previous call *f* . ``Forward`` ( *k* , *r* , *xk* ) Note that *r* must have the same value in this previous call. X(t) **** For :math:`\ell = 0 , \ldots , r-1`, the function :math:`X_\ell : \B{R} \rightarrow \B{R}^n` is defined using the Taylor coefficients :math:`x^{(k),\ell} \in \B{R}^n`: .. math:: X_\ell (t) = x^{(0)} + x^{(1),\ell} * t^1 + \cdots + x^{(q),\ell} t^q Note that the *k*-th derivative of :math:`X_\ell (t)` is related to its Taylor coefficients by .. math:: :nowrap: \begin{eqnarray} x^{(0)} & = & X_\ell (0) \\ x^{(k), \ell} & = & \frac{1}{k !} X_\ell^{(k)} (0) \end{eqnarray} for :math:`k = 1 , \ldots , q`. Y(t) **** For :math:`\ell = 0 , \ldots , r-1`, the function :math:`Y_\ell : \B{R} \rightarrow \B{R}^m` is defined by :math:`Y_\ell (t) = F[ X_\ell (t) ]`. We use :math:`y^{(0)}` for the zero order coefficient and :math:`y^{(k),\ell} \in \B{R}^m` to denote the higher order coefficients; i.e., .. math:: Y_\ell (t) = y^{(0)} + y^{(1),\ell} * t^1 + \cdots + y^{(q),\ell} * t^q + o( t^q ) where :math:`o( t^q ) * t^{-q} \rightarrow 0` as :math:`t \rightarrow 0`. Note that the *k*-th derivative of :math:`Y_\ell (t)` is related to its Taylor coefficients by .. math:: :nowrap: \begin{eqnarray} y^{(0)} & = & Y_\ell (0) \\ y^{(k), \ell} & = & \frac{1}{k !} Y_\ell^{(k)} (0) \end{eqnarray} for :math:`k = 1 , \ldots , q`. yq ** The argument *yq* has prototype *Vector* *yq* and its size is *m* * *r* (see :ref:`forward_dir@Vector` below). For :math:`\ell = 0 , \ldots , r-1`, :math:`i = 0 , \ldots , m-1`, the *i*-th component of the *q*-th order Taylor coefficient for :math:`Y_\ell (t)` is given by |tab| :math:`y_i^{(q),\ell} =` *yq* [ *r* * *i* + *ell* ] Vector ****** The type *Vector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` *Base* . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. {xrst_toc_hidden example/general/forward_dir.cpp } Example ******* The file :ref:`forward_dir.cpp-name` contains an example and test using one order (multiple orders). They return true if they succeed and false otherwise. {xrst_end forward_dir} ================================================ FILE: include/cppad/core/forward/forward_one.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin forward_one} First Order Forward Mode: Derivative Values ########################################### Syntax ****** | *y1* = *f* . ``Forward`` (1, *x1* ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* . The result of the syntax above is .. math:: y1 = F^{(1)} (x0) * x1 where :math:`F^{(1)} (x0)` is the Jacobian of :math:`F` evaluated at *x0* . f * The object *f* has prototype ``ADFun`` < *Base* > *f* Note that the :ref:`ADFun-name` object *f* is not ``const`` . Before this call to ``Forward`` , the value returned by *f* . ``size_order`` () must be greater than or equal one. After this call it will be will be two (see :ref:`size_order-name` ). x0 ** The vector *x0* in the formula .. math:: y1 = F^{(1)} (x0) * x1 corresponds to the previous call to :ref:`forward_zero-name` using this ADFun object *f* ; i.e., *f* . ``Forward`` (0, *x0* ) If there is no previous call with the first argument zero, the value of the :ref:`Independent-name` variables during the recording of the AD sequence of operations is used for *x0* . x1 ** The argument *x1* has prototype ``const`` *Vector* & *x1* (see :ref:`forward_one@Vector` below) and its size must be equal to *n* , the dimension of the :ref:`fun_property@Domain` space for *f* . Vector ****** The type *Vector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` *Base* . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Example ******* The file :ref:`forward.cpp-name` contains an example and test of this operation. Special Case ************ This is special case of :ref:`forward_order-name` where .. math:: :nowrap: \begin{eqnarray} Y(t) & = & F[ X(t) ] \\ X(t) & = & x^{(0)} t^0 + x^{(1)} * t^1 + \cdots, + x^{(q)} * t^q + o( t^q ) \\ Y(t) & = & y^{(0)} t^0 + y^{(1)} * t^1 + \cdots, + y^{(q)} * t^q + o( t^q ) \end{eqnarray} and :math:`o( t^q ) * t^{-q} \rightarrow 0` as :math:`t \rightarrow 0`. For this special case, :math:`q = 1`, :math:`x^{(0)}` = *x0* , :math:`x^{(1)}` = *x1* , :math:`X(t) = x^{(0)} + x^{(1)} t`, and .. math:: y^{(0)} + y^{(1)} t = F [ x^{(0)} + x^{(1)} t ] + o(t) Taking the derivative with respect to :math:`t`, at :math:`t = 0`, we obtain .. math:: y^{(1)} = F^{(1)} [ x^{(0)} ] x^{(1)} which agrees with the specifications for *y1* in the :ref:`forward_one@Purpose` above. {xrst_end forward_one} ================================================ FILE: include/cppad/core/forward/forward_order.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin forward_order} {xrst_spell cout dx ostream xk xq yq } Multiple Order Forward Mode ########################### Syntax ****** | *yq* = *f* . ``Forward`` ( *q* , *xq* ) | *yq* = *f* . ``Forward`` ( *q* , *xq* , *s* ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* . Given a function :math:`X : \B{R} \rightarrow \B{R}^n`, defined by its :ref:`Taylor coefficients` , forward mode computes the Taylor coefficients for the function .. math:: Y (t) = F [ X(t) ] Function Values =============== If you are using forward mode to compute values for :math:`F(x)`, :ref:`forward_zero-name` is simpler to understand than this explanation of the general case. Derivative Values ================= If you are using forward mode to compute values for :math:`F^{(1)} (x) * dx`, :ref:`forward_one-name` is simpler to understand than this explanation of the general case. Notation ******** n = We use *n* to denote the dimension of the :ref:`fun_property@Domain` space for *f* . m = We use *m* to denote the dimension of the :ref:`fun_property@Range` space for *f* . f * The :ref:`ADFun-name` object *f* has prototype ``ADFun`` < *Base* > *f* Note that the :ref:`ADFun-name` object *f* is not ``const`` . After this call we will have | |tab| *f* . ``size_order`` () == *q* + 1 | |tab| *f* . ``size_direction`` () == 1 One Order ********* If *xq* . ``size`` () == *n* , then we are only computing one order. In this case, before this call we must have | |tab| *f* . ``size_order`` () >= *q* | |tab| *f* . ``size_direction`` () == 1 q * The argument *q* has prototype ``size_t`` *q* and specifies the highest order of the Taylor coefficients to be calculated. xq ** The argument *xq* has prototype ``const`` *BaseVector* & *xq* (see :ref:`forward_order@BaseVector` below). As above, we use *n* to denote the dimension of the :ref:`fun_property@Domain` space for *f* . The size of *xq* must be either *n* or *n* * ( *q* +1) . After this call we will have *f* . ``size_order`` () == *q* + 1 One Order ========= If *xq* . ``size`` () == *n* , the *q*-th order Taylor coefficient for :math:`X(t)` is defined by |tab| :math:`x^{(q)} =` *xq* . For :math:`k = 0 , \ldots , q-1`, the Taylor coefficient :math:`x^{(k)}` is defined by *xk* in the previous call to *f* . ``Forward`` ( *k* , *xk* ) Multiple Orders =============== If *xq* . ``size`` () == *n* * ( *q* +1) , For :math:`k = 0 , \ldots , q`, :math:`j = 0 , \ldots , n-1`, the *j*-th component of the *k*-th order Taylor coefficient for :math:`X(t)` is defined by |tab| :math:`x_j^{(k)} =` *xq* [ ( *q* +1) * *j* + *k* ] Restrictions ============ Note if *f* uses :ref:`atomic_one-name` functions, the size of *xq* must be *n* . s * If the argument *s* is not present, ``std::cout`` is used in its place. Otherwise, this argument has prototype ``std::ostream&`` *s* If order zero is begin calculated, *s* specifies where the output corresponding to :ref:`PrintFor-name` will be written. If order zero is not being calculated, *s* is not used X(t) **** The function :math:`X : \B{R} \rightarrow \B{R}^n` is defined using the Taylor coefficients :math:`x^{(k)} \in \B{R}^n`: .. math:: X(t) = x^{(0)} * t^0 + x^{(1)} * t^1 + \cdots + x^{(q)} * t^q Note that for :math:`k = 0 , \ldots , q`, the *k*-th derivative of :math:`X(t)` is related to the Taylor coefficients by the equation .. math:: x^{(k)} = \frac{1}{k !} X^{(k)} (0) Y(t) **** The function :math:`Y : \B{R} \rightarrow \B{R}^m` is defined by :math:`Y(t) = F[ X(t) ]`. We use :math:`y^{(k)} \in \B{R}^m` to denote the *k*-th order Taylor coefficient of :math:`Y(t)`; i.e., .. math:: Y(t) = y^{(0)} * t^0 + y^{(1)} * t^1 + \cdots + y^{(q)} * t^q + o( t^q ) where :math:`o( t^q ) * t^{-q} \rightarrow 0` as :math:`t \rightarrow 0`. Note that :math:`y^{(k)}` is related to the *k*-th derivative of :math:`Y(t)` by the equation .. math:: y^{(k)} = \frac{1}{k !} Y^{(k)} (0) yq ** The return value *yq* has prototype *BaseVector* *yq* (see :ref:`forward_order@BaseVector` below). One Order ========= If *xq* . ``size`` () == *n* , the vector *yq* has size *m* . The *q*-th order Taylor coefficient for :math:`Y(t)` is returned as *yq* :math:`= y^{(q)}`. Multiple Orders =============== If *xq* . ``size`` () == *n* * ( *q* +1) , the vector *yq* has size *m* * ( *q* +1) . For :math:`k = 0 , \ldots , q`, for :math:`i = 0 , \ldots , m-1`, the *i*-th component of the *k*-th order Taylor coefficient for :math:`Y(t)` is returned as ``yq`` [ ( ``q`` +1) * ``i`` + ``k`` ] :math:`= y_i^{(k)}` BaseVector ********** The type *BaseVector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` *Base* . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Zero Order ********** The case where :math:`q = 0` and *xq* . ``size`` () == *n* , corresponds to the zero order :ref:`forward_zero@Special Case` . First Order *********** The case where :math:`q = 1` and *xq* . ``size`` () == *n* , corresponds to the first order :ref:`forward_one@Special Case` . Second Order ************ The case where :math:`q = 2` and *xq* . ``size`` () == *n* , corresponds to the second order :ref:`forward_two@Special Case` . {xrst_toc_hidden example/general/forward.cpp example/general/forward_order.cpp } Example ******* The files :ref:`forward.cpp-name` and :ref:`forward_order.cpp-name` contain examples and tests of using forward mode with one order and multiple orders respectively. They return true if they succeed and false otherwise. {xrst_end forward_order} ================================================ FILE: include/cppad/core/forward/forward_two.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin forward_two} Second Order Forward Mode: Derivative Values ############################################ Syntax ****** | *y2* = *f* . ``Forward`` (2, *x2* ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* . The result of the syntax above is that for *i* = 0 , ... , *m* ``-1`` , y2 [i] = :math:`F_i^{(1)} (x0) * x2 + \frac{1}{2} x1^T * F_i^{(2)} (x0) * x1` where :math:`F^{(1)} (x0)` is the Jacobian of :math:`F`, and :math:`F_i^{(2)} (x0)` is the Hessian of th *i*-th component of :math:`F`, evaluated at *x0* . f * The object *f* has prototype ``ADFun`` < *Base* > *f* Note that the :ref:`ADFun-name` object *f* is not ``const`` . Before this call to ``Forward`` , the value returned by *f* . ``size_order`` () must be greater than or equal two. After this call it will be will be three (see :ref:`size_order-name` ). x0 ** The vector *x0* in the formula for *y2* [ *i* ] corresponds to the previous call to :ref:`forward_zero-name` using this ADFun object *f* ; i.e., *f* . ``Forward`` (0, *x0* ) If there is no previous call with the first argument zero, the value of the :ref:`Independent-name` variables during the recording of the AD sequence of operations is used for *x0* . x1 ** The vector *x1* in the formula for *y2* [ *i* ] corresponds to the previous call to :ref:`forward_one-name` using this ADFun object *f* ; i.e., *f* . ``Forward`` (1, *x1* ) x2 ** The argument *x2* has prototype ``const`` *Vector* & *x2* (see :ref:`forward_two@Vector` below) and its size must be equal to *n* , the dimension of the :ref:`fun_property@Domain` space for *f* . y2 ** The result *y2* has prototype *Vector* *y2* (see :ref:`forward_two@Vector` below) The size of *y1* is equal to *m* , the dimension of the :ref:`fun_property@Range` space for *f* . Its value is given element-wise by the formula in the :ref:`forward_two@Purpose` above. Vector ****** The type *Vector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` *Base* . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Example ******* The file :ref:`forward.cpp-name` contains an example and test of this operation. Special Case ************ This is special case of :ref:`forward_order-name` where .. math:: :nowrap: \begin{eqnarray} Y(t) & = F[ X(t) ] \\ X(t) & = & x^{(0)} t^0 + x^{(1)} * t^1 + \cdots, + x^{(q)} * t^q + o( t^q ) \\ Y(t) & = & y^{(0)} t^0 + y^{(1)} * t^1 + \cdots, + y^{(q)} * t^q + o( t^q ) \end{eqnarray} and :math:`o( t^q ) * t^{-q} \rightarrow 0` as :math:`t \rightarrow 0`. For this special case, :math:`q = 2`, :math:`x^{(0)}` = *x0* , :math:`x^{(1)}` = *x1* , :math:`x^{(2)}` = *x2* , :math:`X(t) = x^{(0)} + x^{(1)} t + x^{(2)} t^2`, and .. math:: y^{(0)} + y^{(1)} t + y^{(2)} t^2 = F [ x^{(0)} + x^{(1)} t + x^{(2)} t^2 ] + o(t^2) Restricting our attention to the *i*-th component, and taking the derivative with respect to :math:`t`, we obtain .. math:: y_i^{(1)} + 2 y_i^{(2)} t = F_i^{(1)} [ x^{(0)} + x^{(1)} t + x^{(2)} t^2 ] [ x^{(1)} + 2 x^{(2)} t ] + o(t) Taking a second derivative with respect to :math:`t`, and evaluating at :math:`t = 0`, we obtain .. math:: 2 y_i^{(2)} = [ x^{(1)} ]^T F_i^{(2)} [ x^{(0)} ] x^{(1)} + F_i^{(1)} [ x^{(0)} ] 2 x^{(2)} which agrees with the specification for *y2* [ *i* ] in the :ref:`forward_two@Purpose` above. {xrst_end forward_two} ================================================ FILE: include/cppad/core/forward/forward_zero.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin forward_zero} {xrst_spell cout ostream } Zero Order Forward Mode: Function Values ######################################## Syntax ****** | *y0* = *f* . ``Forward`` (0, *x0* ) | *y0* = *f* . ``Forward`` (0, *x0* , *s* ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* . The result of the syntax above is .. math:: y0 = F(x0) See the :ref:`FunCheck discussion` for possible differences between :math:`F(x)` and the algorithm that defined the operation sequence. f * The object *f* has prototype ``ADFun`` < *Base* > *f* Note that the :ref:`ADFun-name` object *f* is not ``const`` . After this call to ``Forward`` , the value returned by *f* . ``size_order`` () will be equal to one (see :ref:`size_order-name` ). x0 ** The argument *x0* has prototype ``const`` *Vector* & *x0* (see :ref:`forward_zero@Vector` below) and its size must be equal to *n* , the dimension of the :ref:`fun_property@Domain` space for *f* . s * If the argument *s* is not present, ``std::cout`` is used in its place. Otherwise, this argument has prototype ``std::ostream&`` *s* It specifies where the output corresponding to :ref:`PrintFor-name` , and this zero order forward mode call, will be written. y0 ** The result *y0* has prototype *Vector* *y0* (see :ref:`forward_zero@Vector` below) and its value is :math:`F(x)` at *x* = *x0* . The size of *y0* is equal to *m* , the dimension of the :ref:`fun_property@Range` space for *f* . Vector ****** The type *Vector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` *Base* . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Example ******* The file :ref:`forward.cpp-name` contains an example and test of this operation. Special Case ************ This is special case of :ref:`forward_order-name` where .. math:: :nowrap: \begin{eqnarray} Y(t) & = & F[ X(t) ] \\ X(t) & = & x^{(0)} t^0 + x^{(1)} * t^1 + \cdots, + x^{(q)} * t^q + o( t^q ) \\ Y(t) & = & y^{(0)} t^0 + y^{(1)} * t^1 + \cdots, + y^{(q)} * t^q + o( t^q ) \end{eqnarray} and :math:`o( t^q ) * t^{-q} \rightarrow 0` as :math:`t \rightarrow 0`. For this special case, :math:`q = 0`, :math:`x^{(0)}` = *x0* , :math:`X(t) = x^{(0)}`, and .. math:: y^{(0)} = Y(t) = F[ X(t) ] = F( x^{(0)} ) which agrees with the specifications for *y0* in the :ref:`forward_zero@Purpose` above. {xrst_end forward_zero} ================================================ FILE: include/cppad/core/forward/size_order.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin size_order} Number Taylor Coefficient Orders Currently Stored ################################################# Syntax ****** | *s* = *f* . ``size_order`` () See Also ======== :ref:`fun_property-name` Purpose ******* Determine the number of Taylor coefficient orders, per variable,direction, currently calculated and stored in the ADFun object *f* . See the discussion under :ref:`size_order@Constructor` , :ref:`size_order@Forward` , and :ref:`size_order@capacity_order` for a description of when this value can change. f * The object *f* has prototype ``const ADFun`` < *Base* > *f* s * The result *s* has prototype ``size_t`` *s* and is the number of Taylor coefficient orders, per variable,direction in the AD operation sequence, currently calculated and stored in the ADFun object *f* . Constructor *********** Directly after the :ref:`fun_construct-name` syntax ``ADFun`` < *Base* > *f* ( *x* , *y* ) the value of *s* returned by ``size_order`` is one. This is because there is an implicit call to ``Forward`` that computes the zero order Taylor coefficients during this constructor. Forward ******* After a call to :ref:`Forward` with the syntax *f* . ``Forward`` ( *q* , *x_q* ) the value of *s* returned by ``size_order`` would be :math:`q + 1`. The call to ``Forward`` above uses the lower order Taylor coefficients to compute and store the *q*-th order Taylor coefficients for all the variables in the operation sequence corresponding to *f* . Thus there are :math:`q + 1` (order zero through *q* ) Taylor coefficients per variable,direction. (You can determine the number of variables in the operation sequence using the :ref:`fun_property@size_var` function.) capacity_order ************** If the number of Taylor coefficient orders currently stored in *f* is less than or equal *c* , a call to :ref:`capacity_order-name` with the syntax *f* . ``capacity_order`` ( *c* ) does not affect the value *s* returned by ``size_order`` . Otherwise, the value *s* returned by ``size_order`` is equal to *c* (only Taylor coefficients of order zero through :math:`c-1` have been retained). Example ******* The file :ref:`forward.cpp-name` contains an example and test of this operation. {xrst_end size_order} ================================================ FILE: include/cppad/core/fun_check.hpp ================================================ # ifndef CPPAD_CORE_FUN_CHECK_HPP # define CPPAD_CORE_FUN_CHECK_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin FunCheck} Check an ADFun Sequence of Operations ##################################### Syntax ****** | *ok* = ``FunCheck`` ( *f* , *g* , *x* , *r* , *a* ) See Also ******** :ref:`FunCheck-name` Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* . We use :math:`G : \B{R}^n \rightarrow \B{R}^m` to denote the function corresponding to the C++ function object *g* . This routine check if .. math:: F(x) = G(x) If :math:`F(x) \neq G(x)`, the :ref:`operation sequence` corresponding to *f* does not represents the algorithm used by *g* to calculate values for :math:`G` (see :ref:`FunCheck@Discussion` below). f * The ``FunCheck`` argument *f* has prototype ``ADFun`` < *Base* > *f* Note that the :ref:`ADFun-name` object *f* is not ``const`` (see :ref:`Forward` below). g * The ``FunCheck`` argument *g* has prototype *Fun* & *g* ( *Fun* is defined the properties of *g* ). The C++ function object *g* supports the syntax *y* = *g* ( *x* ) which computes :math:`y = G(x)`. x = The *g* argument *x* has prototype ``const`` *Vector* & *x* (see :ref:`FunCheck@Vector` below) and its size must be equal to *n* , the dimension of the :ref:`fun_property@Domain` space for *f* . y * The *g* result *y* has prototype *Vector* *y* and its value is :math:`G(x)`. The size of *y* is equal to *m* , the dimension of the :ref:`fun_property@Range` space for *f* . x * The ``FunCheck`` argument *x* has prototype ``const`` *Vector* & *x* and its size must be equal to *n* , the dimension of the :ref:`fun_property@Domain` space for *f* . This specifies that point at which to compare the values calculated by *f* and *G* . r * The ``FunCheck`` argument *r* has prototype ``const`` *Base* & *r* It specifies the relative error the element by element comparison of the value of :math:`F(x)` and :math:`G(x)`. a * The ``FunCheck`` argument *a* has prototype ``const`` *Base* & *a* It specifies the absolute error the element by element comparison of the value of :math:`F(x)` and :math:`G(x)`. ok ** The ``FunCheck`` result *ok* has prototype ``bool`` *ok* It is true, if for :math:`i = 0 , \ldots , m-1` either the relative error bound is satisfied .. math:: | F_i (x) - G_i (x) | \leq r ( | F_i (x) | + | G_i (x) | ) or the absolute error bound is satisfied .. math:: | F_i (x) - G_i (x) | \leq a It is false if for some :math:`(i, j)` neither of these bounds is satisfied. Vector ****** The type *Vector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` *Base* . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. FunCheck Uses Forward ********************* After each call to :ref:`Forward-name` , the object *f* contains the corresponding :ref:`Taylor coefficients` . After ``FunCheck`` , the previous calls to :ref:`Forward-name` are undefined. Discussion ********** Suppose that the algorithm corresponding to *g* contains | |tab| ``if`` ( *x* >= 0 ) | |tab| |tab| *y* = ``exp`` ( *x* ) | |tab| ``else`` | |tab| |tab| *y* = ``exp`` ( ``-`` *x* ) where *x* and *y* are ``AD`` objects. It follows that the AD of ``double`` :ref:`operation sequence` depends on the value of *x* . If the sequence of operations stored in *f* corresponds to *g* with :math:`x \geq 0`, the function values computed using *f* when :math:`x < 0` will not agree with the function values computed by :math:`g`. This is because the operation sequence corresponding to *g* changed (and hence the object *f* does not represent the function :math:`G` for this value of *x* ). In this case, you probably want to re-tape the calculations performed by *g* with the :ref:`independent variables` equal to the values in *x* (so AD operation sequence properly represents the algorithm for this value of independent variables). Example ******* {xrst_toc_hidden example/general/fun_check.cpp } The file :ref:`fun_check.cpp-name` contains an example and test of this function. {xrst_end FunCheck} --------------------------------------------------------------------------- */ namespace CppAD { template bool FunCheck( ADFun &f , Fun &g , const Vector &x , const Base &r , const Base &a ) { bool ok = true; size_t m = f.Range(); Vector yf = f.Forward(0, x); Vector yg = g(x); size_t i; for(i = 0; i < m; i++) ok &= NearEqual(yf[i], yg[i], r, a); return ok; } } # endif ================================================ FILE: include/cppad/core/fun_construct.hpp ================================================ # ifndef CPPAD_CORE_FUN_CONSTRUCT_HPP # define CPPAD_CORE_FUN_CONSTRUCT_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin fun_construct} {xrst_spell versa } Construct an ADFun Object and Stop Recording ############################################ Syntax ****** | ``ADFun`` < *Base* > *f* ( *ax* , *ay* ); | ``ADFun`` < *Base* > *f* | *f* . ``swap`` ( *g* ) | ``f`` = ``g`` Purpose ******* The ``ADFun`` < *Base* > object *f* stores an AD of *Base* :ref:`operation sequence` . It can then be used to calculate derivatives of the corresponding :ref:`glossary@AD Function` .. math:: F : \B{R}^n \rightarrow \B{R}^m where :math:`B` is the space corresponding to objects of type *Base* . ax ** If the argument *ax* is present, it has prototype ``const`` *ADVector* & *ax* It must be the vector argument in the previous call to :ref:`Independent-name` . Neither its size, or any of its values, are allowed to change between calling ``Independent`` ( *ax* ) and ``ADFun`` < *Base* > *f* ( *ax* , *ay* ) ay ** If the argument *ay* is present, it has prototype ``const`` *ADVector* & *ay* The sequence of operations that map *ax* to *ay* are stored in the ADFun object *f* . ADVector ******** The type *ADVector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` ``AD`` < *Base* > . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Default Constructor ******************* The default constructor ``ADFun`` < *Base* > *g* creates an ``AD`` < *Base* > object with no corresponding operation sequence; i.e., *g* . ``size_var`` () returns the value zero (see :ref:`fun_property@size_var` ). Sequence Constructor ******************** The following constructor stores the current ``AD`` < *Base* > operation sequence in *f* : ``ADFun`` < *Base* > *f* ( *ax* , *ay* ) To be specific, it is equivalent to the following steps using the default constructor: #. Create *f* with the default constructor ``ADFun`` < *Base* > *f* ; #. Stop the recording and store the operation sequence using *f* . ``Dependent`` ( *ax* , *ay* ); see :ref:`Independent@Start Recording` , :ref:`Dependent@Stop Recording` , and :ref:`Dependent@Store Operation Sequence` . #. Calculate the zero order Taylor coefficients for all the variables in the operation sequence using *y* = *f* . ``Forward`` ( 0 , *x* ) see :ref:`forward_zero-name`. Here *x* and *y* are :ref:`simple vectors ` with elements of type *Base* and the elements of *x* are equal to the corresponding elements in *ax*. #. If NDEBUG is not defined, *y* is checked to make sure it's elements are nearly equal to the corresponding values in *ay* . Copy Constructor **************** It is an error to attempt to use the ``ADFun`` < *Base* > copy constructor; i.e., the following syntax is not allowed: ``ADFun`` < *Base* > *g* ( *f* ) where *f* is an ``ADFun`` < *Base* > object. Use its :ref:`fun_construct@Default Constructor` instead and its assignment operator. swap **** The swap operation *f* . ``swap`` ( *g* ) exchanges the contents of the two ``ADFun`` < *Base* > functions; i.e., *f* before the swap is identical to *g* after the swap and vise versa. Assignment Operator ******************* The ``ADFun`` < *Base* > assignment operation *g* = *f* makes a copy of the operation sequence currently stored in *f* in the object *g* . The object *f* is not affected by this operation and can be ``const`` . All of information (state) stored in *f* is copied to *g* and any information originally in *g* is lost. Move Semantics ============== In the special case where *f* is a temporary object, this assignment will use move semantics. This avoids the overhead of the copying all the information from *f* to *g* . Taylor Coefficients =================== The Taylor coefficient information currently stored in *f* (computed by :ref:`f.Forward` ) is copied to *g* . Hence, directly after this operation *g* . ``size_order`` () == *f* . ``size_order`` () Sparsity Patterns ================= The forward Jacobian sparsity pattern currently stored in *f* (computed by :ref:`f.ForSparseJac` ) is copied to *g* . Hence, directly after this operation | |tab| *g* . ``size_forward_bool`` () == *f* . ``size_forward_bool`` () | |tab| *g* . ``size_forward_set`` () == *f* . ``size_forward_set`` () Parallel Mode ************* The call to ``Independent`` , and the corresponding call to ``ADFun`` < *Base* > *f* ( *ax* , *ay* ) or *f* . ``Dependent`` ( *ax* , *ay* ) or :ref:`abort_recording-name` , must be preformed by the same thread; i.e., :ref:`thread_alloc::thread_num` must be the same. Example ******* Sequence Constructor ==================== The file :ref:`independent.cpp-name` contains an example and test of the sequence constructor. Default Constructor =================== The files :ref:`fun_check.cpp-name` and :ref:`hes_lagrangian.cpp-name` contain an examples and tests using the default constructor. They return true if they succeed and false otherwise. {xrst_toc_hidden example/general/fun_assign.cpp } Assignment Operator =================== The file :ref:`fun_assign.cpp-name` contains an example and test of the ``ADFun`` < *Base* > assignment operator. {xrst_end fun_construct} ---------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file fun_construct.hpp ADFun function constructors and assignment operator. */ /*! ADFun default constructor The C++ syntax for this operation is \verbatim ADFun f \endverbatim An empty ADFun object is created. The Dependent member function, or the ADFun assignment operator, can then be used to put an operation sequence in this ADFun object. \tparam Base is the base for the recording that can be stored in this ADFun object; i.e., operation sequences that were recorded using the type AD. */ template ADFun::ADFun(void) : function_name_(""), exceed_collision_limit_(false), has_been_optimized_(false), check_for_nan_(true) , compare_change_count_(0), compare_change_number_(0), compare_change_op_index_(0), num_order_taylor_(0), cap_order_taylor_(0), num_direction_taylor_(0), num_var_tape_(0) { } // // move semantics version of constructor // (none of the default constructor values matter to the destructor) template ADFun::ADFun(ADFun&& f) { swap(f); } // // destructor template ADFun::~ADFun(void) { } /*! ADFun assignment operator The C++ syntax for this operation is \verbatim g = f \endverbatim where g and f are ADFun ADFun objects. A copy of the the operation sequence currently stored in f is placed in this ADFun object (called g above). Any information currently stored in this ADFun object is lost. \tparam Base is the base for the recording that can be stored in this ADFun object; i.e., operation sequences that were recorded using the type AD. \param f ADFun object containing the operation sequence to be copied. */ template void ADFun::operator=(const ADFun& f) { // go through member variables in ad_fun.hpp order // // string objects function_name_ = f.function_name_; // // bool objects exceed_collision_limit_ = f.exceed_collision_limit_; has_been_optimized_ = f.has_been_optimized_; check_for_nan_ = f.check_for_nan_; // // size_t objects compare_change_count_ = f.compare_change_count_; compare_change_number_ = f.compare_change_number_; compare_change_op_index_ = f.compare_change_op_index_; num_order_taylor_ = f.num_order_taylor_; cap_order_taylor_ = f.cap_order_taylor_; num_direction_taylor_ = f.num_direction_taylor_; num_var_tape_ = f.num_var_tape_; // // pod_vector objects ind_taddr_ = f.ind_taddr_; dep_taddr_ = f.dep_taddr_; dep_parameter_ = f.dep_parameter_; cskip_op_ = f.cskip_op_; load_op2var_ = f.load_op2var_; // // pod_vector_maybe_vectors taylor_ = f.taylor_; subgraph_partial_ = f.subgraph_partial_; // // player play_ = f.play_; // // subgraph subgraph_info_ = f.subgraph_info_; // // sparse_pack for_jac_sparse_pack_ = f.for_jac_sparse_pack_; // // sparse_list for_jac_sparse_set_ = f.for_jac_sparse_set_; } /// swap template void ADFun::swap(ADFun& f) { // string objects function_name_.swap( f.function_name_ ); // // bool objects std::swap( exceed_collision_limit_ , f.exceed_collision_limit_); std::swap( has_been_optimized_ , f.has_been_optimized_); std::swap( check_for_nan_ , f.check_for_nan_); // // size_t objects std::swap( compare_change_count_ , f.compare_change_count_); std::swap( compare_change_number_ , f.compare_change_number_); std::swap( compare_change_op_index_ , f.compare_change_op_index_); std::swap( num_order_taylor_ , f.num_order_taylor_); std::swap( cap_order_taylor_ , f.cap_order_taylor_); std::swap( num_direction_taylor_ , f.num_direction_taylor_); std::swap( num_var_tape_ , f.num_var_tape_); // // pod_vector objects ind_taddr_.swap( f.ind_taddr_); dep_taddr_.swap( f.dep_taddr_); dep_parameter_.swap( f.dep_parameter_); taylor_.swap( f.taylor_); cskip_op_.swap( f.cskip_op_); load_op2var_.swap( f.load_op2var_); // // player play_.swap(f.play_); // // subgraph_info subgraph_info_.swap(f.subgraph_info_); // // sparse_pack for_jac_sparse_pack_.swap( f.for_jac_sparse_pack_); // // sparse_list for_jac_sparse_set_.swap( f.for_jac_sparse_set_); } /// Move semantics version of constructor and assignment template void ADFun::operator=(ADFun&& f) { swap(f); } /*! ADFun constructor from an operation sequence. The C++ syntax for this operation is \verbatim ADFun f(x, y) \endverbatim The operation sequence that started with the previous call Independent(x), and that ends with this operation, is stored in this ADFun object f. \tparam Base is the base for the recording that will be stored in the object f; i.e., the operations were recorded using the type AD. \tparam ADVector is a simple vector class with elements of typea AD. \param x is the independent variable vector for this ADFun object. The domain dimension of this object will be the size of x. \param y is the dependent variable vector for this ADFun object. The range dimension of this object will be the size of y. \par Taylor Coefficients A zero order forward mode sweep is done, and if NDEBUG is not defined the resulting values for the dependent variables are checked against the values in y. Thus, the zero order Taylor coefficients corresponding to the value of the x vector are stored in this ADFun object. */ template template ADFun::ADFun(const ADVector &x, const ADVector &y) { // used to identify the RecBase type in calls to sweeps RecBase not_used_rec_base(0.0); CPPAD_ASSERT_KNOWN( x.size() > 0, "ADFun: independent variable vector has size zero." ); CPPAD_ASSERT_KNOWN( Variable(x[0]), "ADFun: independent variable vector has been changed." ); local::ADTape* tape = AD::tape_ptr(x[0].tape_id_); CPPAD_ASSERT_KNOWN( tape->size_independent_ == size_t ( x.size() ), "ADFun: independent variable vector has been changed." ); size_t j, n = x.size(); # ifndef NDEBUG size_t i, m = y.size(); for(j = 0; j < n; j++) { CPPAD_ASSERT_KNOWN( size_t(x[j].taddr_) == (j+1), "ADFun: independent variable vector has been changed." ); CPPAD_ASSERT_KNOWN( x[j].tape_id_ == x[0].tape_id_, "ADFun: independent variable vector has been changed." ); } for(i = 0; i < m; i++) { CPPAD_ASSERT_KNOWN( CppAD::Parameter( y[i] ) || (y[i].tape_id_ == x[0].tape_id_) , "ADFun: dependent vector contains variables for" "\na different tape than the independent variables." ); } # endif // stop the tape and store the operation sequence Dependent(tape, y); // This function has not yet been optimized exceed_collision_limit_ = false; // ad_fun.hpp member values not set by dependent check_for_nan_ = true; // allocate memory for one zero order taylor_ coefficient CPPAD_ASSERT_UNKNOWN( num_order_taylor_ == 0 ); CPPAD_ASSERT_UNKNOWN( num_direction_taylor_ == 0 ); size_t c = 1; size_t r = 1; capacity_order(c, r); CPPAD_ASSERT_UNKNOWN( cap_order_taylor_ == c ); CPPAD_ASSERT_UNKNOWN( num_direction_taylor_ == r ); // set zero order coefficients corresponding to independent variables CPPAD_ASSERT_UNKNOWN( n == ind_taddr_.size() ); for(j = 0; j < n; j++) { CPPAD_ASSERT_UNKNOWN( ind_taddr_[j] == (j+1) ); CPPAD_ASSERT_UNKNOWN( size_t(x[j].taddr_) == (j+1) ); taylor_[ ind_taddr_[j] ] = x[j].value_; } // use independent variable values to fill in values for others CPPAD_ASSERT_UNKNOWN( cskip_op_.size() == play_.num_var_op() ); CPPAD_ASSERT_UNKNOWN( load_op2var_.size() == play_.num_var_load() ); bool print = false; local::sweep::forward_0( not_used_rec_base, &play_, num_var_tape_, cap_order_taylor_, cskip_op_.data(), load_op2var_, compare_change_count_, compare_change_number_, compare_change_op_index_, std::cout, print, taylor_.data() ); CPPAD_ASSERT_UNKNOWN( compare_change_count_ == 1 ); CPPAD_ASSERT_UNKNOWN( compare_change_number_ == 0 ); CPPAD_ASSERT_UNKNOWN( compare_change_op_index_ == 0 ); // now set the number of orders stored num_order_taylor_ = 1; # ifndef NDEBUG // on MS Visual Studio 2012, CppAD required in front of isnan ? for(i = 0; i < m; i++) if( taylor_[dep_taddr_[i]] != y[i].value_ || CppAD::isnan( y[i].value_ ) ) { using std::endl; std::ostringstream buf; buf << "A dependent variable value is not equal to " << "its tape evaluation value," << endl << "perhaps it is nan." << endl << "Dependent variable value = " << y[i].value_ << endl << "Tape evaluation value = " << taylor_[dep_taddr_[i]] << endl << "Difference = " << y[i].value_ - taylor_[dep_taddr_[i]] << endl ; // buf.str() returns a string object with a copy of the current // contents in the stream buffer. std::string msg_str = buf.str(); // msg_str.c_str() returns a pointer to the c-string // representation of the string object's value. const char* msg_char_star = msg_str.c_str(); CPPAD_ASSERT_KNOWN( 0, msg_char_star ); } # endif } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/fun_eval.hpp ================================================ # ifndef CPPAD_CORE_FUN_EVAL_HPP # define CPPAD_CORE_FUN_EVAL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # endif ================================================ FILE: include/cppad/core/fun_property.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin fun_property} {xrst_spell dyn } ADFun Function Properties ######################### Syntax ****** | *n* = *f* . ``Domain`` () | *m* = *f* . ``Range`` () | *p* = *f* . ``Parameter`` ( *i* ) | *s* = *f* . ``size_var`` () | *s* = *f* . ``size_par`` () | *s* = *f* . ``size_op`` () | *s* = *f* . ``size_op_arg`` () | *s* = *f* . ``size_text`` () | *s* = *f* . ``size_VecAD`` () | *s* = *f* . ``size_random`` () | *s* = *f* . ``size_dyn_ind`` () | *s* = *f* . ``size_dyn_par`` () | *s* = *f* . ``size_dyn_arg`` () | *s* = *f* . ``size_op_seq`` () See Also ======== :ref:`function_name-name` , :ref:`size_order-name` , :ref:`capacity_order-name` , :ref:`number_skip-name` . Purpose ******* The operations above return properties of the AD of *Base* :ref:`operation sequence` stored in the ADFun object *f* . (If there is no operation sequence stored in *f* , ``size_var`` returns zero.) f * The object *f* has prototype ``const ADFun`` < *Base* > *f* (see ``ADFun`` < *Base* > :ref:`constructor` ). Domain ****** The result *n* has prototype ``size_t`` *n* and is the dimension of the domain space corresponding to *f* . This is equal to the size of the vector *x* in the call ``Independent`` ( *x* ) that starting recording the operation sequence currently stored in *f* (see :ref:`fun_construct-name` and :ref:`Dependent-name` ). Range ***** The result *m* has prototype ``size_t`` *m* and is the dimension of the range space corresponding to *f* . This is equal to the size of the vector *y* in syntax *ADFun* < ``Base>`` *f* ( *x* , *y* ) or *f* . ``Dependent`` ( *y* ) depending on which stored the operation sequence currently in *f* (see :ref:`fun_construct-name` and :ref:`Dependent-name` ). Parameter ********* The argument *i* has prototype ``size_t`` *i* and :math:`0 \leq i < m`. The result *p* has prototype ``bool`` *p* It is true if the *i*-th component of range space for :math:`F` corresponds to a :ref:`glossary@Parameter` in the operation sequence. In this case, the *i*-th component of :math:`F` is constant and .. math:: \D{F_i}{x_j} (x) = 0 for :math:`j = 0 , \ldots , n-1` and all :math:`x \in \B{R}^n`. size_var ******** The result *s* has prototype ``size_t`` *s* and is the number of variables in the operation sequence plus the following: one for a phantom variable with tape address zero, one for each component of the range that is a parameter. The amount of work and memory necessary for computing function values and derivatives using *f* is roughly proportional to *s* . (The function call :ref:`f.size_order()` returns the number of Taylor coefficient orders, per variable,direction, currently stored in *f* .) If there is no operation sequence stored in *f* , ``size_var`` returns zero (see :ref:`fun_construct@Default Constructor` ). size_par ******** The result *s* has prototype ``size_t`` *s* and is the number of parameters in the operation sequence (include a phantom parameter at index zero that is not used). Parameters differ from variables in that only values (and not derivatives) need to be stored for each parameter. These parameters are considered part of the operation sequence, as opposed to the Taylor coefficients which are considered extra data in the function object *f* . Note that one *Base* value is required for each parameter. size_op ******* The result *s* has prototype ``size_t`` *s* and is the number of operations in the operation sequence. Some operators, like comparison operators, do not correspond to a variable. Other operators, like the sine operator, correspond to two variables. Thus, this value will be different from :ref:`fun_property@size_var` . Note that one ``enum`` value is required for each operator. size_op_arg *********** The result *s* has prototype ``size_t`` *s* and is the total number of operator arguments in the operation sequence. For example, Binary operators (e.g. addition) have two arguments. Note that one integer index is stored in the operation sequence for each argument. Also note that, as of 2013-10-20, there is an extra phantom argument with index 0 that is not used. size_text ********* The result *s* has prototype ``size_t`` *s* and is the total characters used in the :ref:`PrintFor-name` commands in this operation sequence. size_VecAD ********** The result *s* has prototype ``size_t`` *s* and is the number of :ref:`VecAD-name` vectors, plus the number of elements in the vectors. Only ``VecAD`` vectors that depend on the independent variables are stored in the operation sequence. size_random *********** The result *s* has prototype ``size_t`` *s* and is the amount of memory currently holding information for randomly access the operator sequence. Random access is only used by the following routines :ref:`subgraph_sparsity-name` , :ref:`subgraph_reverse-name` , and :ref:`optimize-name` . The optimize routine replaces the operation sequence, so the extra memory is automatically dropped. The subgraph routines hold onto this information so that it does not need to be recalculated between calls. The routine :ref:`subgraph_reverse@clear_subgraph` will free this extra memory. size_dyn_ind ************ The result *s* has prototype ``size_t`` *s* and is the number of independent :ref:`glossary@Parameter@Dynamic` parameters in the operation sequence. This is the size of the :ref:`Independent@dynamic` parameter in the corresponding call to ``Independent`` . size_dyn_par ************ The result *s* has prototype ``size_t`` *s* and is the number of :ref:`glossary@Parameter@Dynamic` parameters. The dynamic parameters depend on the value of the independent dynamic parameters but not on the value of the variables. This includes the independent dynamic parameters. size_dyn_arg ************ The result *s* has prototype ``size_t`` *s* and is the total number of dynamic parameter operator arguments in the operation sequence. For example, Binary operators (e.g. addition) have two arguments. Note that one integer index is stored in the operation sequence for each argument. size_op_seq *********** The result *s* has prototype ``size_t`` *s* and is the amount of memory required to store the operation sequence (not counting a small amount of memory required for every operation sequence). For the current version of CppAD, this is given by {xrst_comment see size_t player::Memory(void)} | |tab| *s* = *f* . ``size_op`` () * ``sizeof`` ( ``CPPAD_VEC_ENUM_TYPE`` ) | |tab| |tab| + *f* . ``size_op_arg`` () * ``sizeof`` ( *tape_addr_type* ) | |tab| |tab| + *f* . ``size_par`` () * ``sizeof`` ( *Base* ) | |tab| |tab| + *f* . ``size_par`` () * ``sizeof`` ( ``bool`` ) | |tab| |tab| + *f* . ``size_dyn_par`` () * ``sizeof`` ( ``CPPAD_VEC_ENUM_TYPE`` ) | |tab| |tab| + *f* . ``size_dyn_par`` () * ``sizeof`` ( *tape_addr_type* ) | |tab| |tab| + *f* . ``size_dyn_arg`` () * ``sizeof`` ( *tape_addr_type* ) | |tab| |tab| + *f* . ``size_text`` () * ``sizeof`` ( ``char`` ) | |tab| |tab| + *f* . ``size_VecAD`` () * ``sizeof`` ( *tape_addr_type* ) see :ref:`tape_addr_type` . Note that this is the minimal amount of memory that can hold the information corresponding to an operation sequence. The actual amount of memory allocated (:ref:`inuse` ) for the operations sequence may be larger. Also note that ``CPPAD_VEC_ENUM_TYPE`` is not part of the CppAD API and may change. Example ******* {xrst_toc_hidden example/general/fun_property.cpp } The file :ref:`fun_property.cpp-name` contains an example and test of these operations. {xrst_end fun_property} ================================================ FILE: include/cppad/core/function_name.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin function_name} Setting and Getting a Function's Name ##################################### Syntax ****** | *f* . ``function_name_set`` ( *function_name* ) | *function_name* = *f* . ``function_name_get`` () See Also ******** :ref:`fun_property-name` f * In the set operation, *f* has prototype ``ADFun`` < *Base* > *f* In the get operation, *f* has prototype ``const ADFun`` < *Base* > *f* (see ``ADFun`` < *Base* > :ref:`constructor` ). function_name ************* is the name of the function. In the set operation, *function_name* has prototype ``const std::string&`` *function_name* In the get operation, *function_name* has prototype ``std::string`` *function_name* {xrst_toc_hidden example/general/function_name.cpp } Example ******* The file :ref:`function_name.cpp-name` contains an example and test of these operations. {xrst_end function_name} ================================================ FILE: include/cppad/core/graph/cpp_ad_graph.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin cpp_ad_graph} {xrst_spell notpos nx } C++ Representation of an AD Graph ################################# See Also ******** :ref:`json_ad_graph-title` function ******** This section defines a computational graph representation of a function :math:`y = f(x, p)`. The vector *x* is called the independent variable vector, *p* is called the independent dynamic parameter vector, and *y* is called the dependent variable vector. Node Indices ************ The nodes in an AD graph have the following order: :: p_0 , ... , p_{np-1} , x_0 , ... , x_{nx-1} , c_0 , ... , c_{nc-1} , r_0 , ... , r_{no-1} p = The sub-vector :: p_0, ... , p_{np-1} is the independent dynamic parameter vector; see :ref:`cpp_ad_graph@n_dynamic_ind` . The node index corresponding to *p_0* is ``1`` . x = The sub-vector :: x_1, ... , x_nx is the independent variable vector; see :ref:`cpp_ad_graph@n_variable_ind` . The node index corresponding to *x_0* is the index corresponding to *p_0* plus *np* . c = The sub-vector :: c_1, ... , c_nc is the constant parameter vector; see :ref:`cpp_ad_graph@constant_vec` . The node index corresponding to *c_0* is the index corresponding to *x_0* plus *nx* . r = The sub-vector *r_i* for *i* =0,..., *no* ``-1`` is the result vector for the *i*-th operator; see :ref:`cpp_ad_graph@operator_vec` . All of the node arguments for an the *i*-th operator are nodes that come before the first element of *r_i* . The node index corresponding to the first element of *r_0* is the index corresponding to *c_0* plus *nc* . For *i* > 0 , The node index corresponding to the first element of *r_i* is the index corresponding to the first element of *r_* { *i-1* } plus the number of results for the *i-1*-th operator. function_name ************* is a ``std::string`` containing the name for the function corresponding to this graph. discrete_name_vec ***************** is a vector with elements of type ``std::string`` . A discrete function has one argument, one result, and it derivative is always zero; e.g., the Heaviside function. Calls by this function to discrete functions use the index in this vector to identify the discrete functions; see :ref:`cpp_ad_graph@operator_arg@discrete_graph_op` below. If there are no calls to discrete functions, this vector can be empty. atomic_name_vec *************** is a vector with elements of type ``std::string`` . An atomic function can have any number of arguments and results and non-zero derivatives. Calls by this function to other functions use the index in this vector to identify the other functions; see :ref:`cpp_ad_graph@operator_arg@atom_graph_op` below. If there are no calls to other functions, this vector can be empty. Discrete functions are faster, and simpler to create and use than atomic functions. print_text_vec ************** is a vector with elements of type ``std::string`` . The :ref:`graph_op_enum@Print` operators uses indices in this vector for the corresponding :ref:`PrintFor@before` and :ref:`PrintFor@after` values. If there are no print operators, this vector can be empty. n_dynamic_ind ************* is the number of independent dynamic parameters in the function (called *np* above); see :ref:`Independent@dynamic` . n_variable_ind ************** is the number of independent variables in the function (called *nx* above); see :ref:`Independent@x` . constant_vec ************ is a vector of with elements of type ``double`` and size *nc* that can be used to define this function. operator_vec ************ is a vector with elements of type ``graph_op_enum`` and size *no* (the number of operators in the graph). For *i* = 0, ..., *no* ``-1`` *operator_vec* [ *i* ] contains the instructions for computing the result vector *r_i* . operator_arg ************ is a vector with size equal to the sum of the size of each of its sub-vectors (which are described below). For *i* = 0, ..., *no* ``-1`` , we use *first_node* [ *i* ] to denote the index in *operator_arg* of the first node argument to the *i*-th operator. We use *n_node_arg* [ *i* ] to denote the number of node arguments for the *i*-th operator. For *j* = 0 , ..., *n_node_arg* [ *i* ] ``-1`` , the *j*-th node argument for the *i*-th operator has node index *operator_arg* [ *first_node* [ *i* ] + *j* ] The *operator_arg* sub-vector for the *i*-th operator starts are *first_node* [ *i* ] and has *n_node_arg* [ *i* ] elements except for the following operators: ``sum_graph_op`` , ``discrete_graph_op`` , ``atom_graph_op`` , ``print_graph_op`` . print_graph_op ============== In the case where *operator_vec* [ *i* ]. ``op_enum`` is ``print_graph_op`` : *before* [ *i* ] = *operator_arg* [ *first_node* [ *i* ] ``- 2`` ] is the index in :ref:`cpp_ad_graph@print_text_vec` of the text that is printed before the value and *after* [ *i* ] = *operator_arg* [ *first_node* [ *i* ] ``- 1`` ] is the index in :ref:`cpp_ad_graph@print_text_vec` of the text that is printed after the value. The *operator_arg* sub-vector for the *i*-th operator starts at index *first_node* [ *i* ] ``-2`` and has 4 = *n_node_arg* [ *i* ]+2 elements. The node with index *notpos* [ *i* ] = *operator_arg* [ *first_node* [ *i* ] ] is checked and if it is positive, nothing is printed by this operator. Otherwise, the value corresponding to the following node is printed: *value* [ *i* ] = *operator_arg* [ *first_node* [ *i* ] + 1 ] discrete_graph_op ================= In the case where *operator_vec* [ *i* ]. ``op_enum`` is ``discrete_graph_op`` : *name_index* [ *i* ] = *operator_arg* [ *first_node* [ *i* ] ``- 1`` ] is the index in :ref:`cpp_ad_graph@discrete_name_vec` of the function being called by this operator. For this operator, the *operator_arg* sub-vector for the *i*-th operator starts at index *first_node* [ *i* ] ``-1`` and has 2 = *n_node_arg* [ *i* ]+1 elements. atom_graph_op ============= In the case where *operator_vec* [ *i* ]. ``op_enum`` is ``atom_graph_op`` : *name_index* [ *i* ] = *operator_arg* [ *first_node* [ *i* ] ``- 3`` ] is the index in :ref:`cpp_ad_graph@atomic_name_vec` of the function being called by this operator. *n_result* [ *i* ] = *operator_arg* [ *first_node* [ *i* ] ``- 2`` ] is the number of result nodes for this operator. *n_node_arg* [ *i* ] = *operator_arg* [ *first_node* [ *i* ] ``- 1`` ] is the number of node arguments for this operator. For this operator, the *operator_arg* sub-vector for the *i*-th operator starts at index *first_node* [ *i* ] ``-3`` and has *n_node_arg* [ *i* ]+3 elements. sum_graph_op ============ In the case where *operator_vec* [ *i* ]. ``op_enum`` is ``sum_graph_op`` : *n_node_arg* [ *i* ] = *operator_arg* [ *first_node* [ *i* ] ``- 1`` ] is the number of node arguments for this operator. For this operator, the *operator_arg* sub-vector for the *i*-th operator starts at index *first_node* [ *i* ] ``-1`` and has *n_node_arg* [ *i* ]+1 elements. dependent_vec ************* is a vector with size equal to the number element in *y* . The *i*-th element of *y* corresponds to the node index *dependent_vec* [ *i* ] . cpp_graph ********* The ``cpp_graph`` class implements the data structure above. It is defined by the documentation sections under Contents below: Contents ******** {xrst_toc_table include/cppad/core/graph/graph_op_enum.hpp include/cppad/core/graph/cpp_graph.xrst include/cppad/core/graph/from_graph.hpp include/cppad/core/graph/to_graph.hpp } {xrst_end cpp_ad_graph} ================================================ FILE: include/cppad/core/graph/cpp_graph.hpp ================================================ # ifndef CPPAD_CORE_GRAPH_CPP_GRAPH_HPP # define CPPAD_CORE_GRAPH_CPP_GRAPH_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE class cpp_graph { // BEGIN_CPP_GRAPH_CLASS public: typedef CppAD::graph::graph_op_enum graph_op_enum; private: // std::string function_name_; vector discrete_name_vec_; vector atomic_name_vec_; vector print_text_vec_; size_t n_dynamic_ind_; size_t n_variable_ind_; vector constant_vec_; vector operator_vec_; vector operator_arg_; vector dependent_vec_; public: typedef local::graph::cpp_graph_itr const_iterator; // const_iterator begin(void) const { size_t op_index = 0; return const_iterator(operator_vec_, operator_arg_, op_index); } const_iterator end(void) { size_t op_index = operator_vec_.size(); return const_iterator(operator_vec_, operator_arg_, op_index); } /* ------------------------------------------------------------------------------- {xrst_begin cpp_graph_ctor} C++ AD Graph Constructor ######################## Syntax ****** | ``cpp_graph`` *graph_obj* | *graph_obj* . ``initialize`` () function_name ************* :ref:`cpp_ad_graph@function_name` is initialized to the empty string. n_dynamic_ind ************* :ref:`cpp_ad_graph@n_dynamic_ind` is initialized as zero. n_variable_ind ************** :ref:`cpp_ad_graph@n_variable_ind` is initialized as zero. constant_vec ************ :ref:`cpp_ad_graph@constant_vec` is initialized as empty. operator_vec ************ :ref:`cpp_ad_graph@operator_vec` is initialized as empty. operator_arg ************ :ref:`cpp_ad_graph@operator_arg` is initialized as empty. dependent_vec ************* :ref:`cpp_ad_graph@dependent_vec` is initialized as empty. Parallel Mode ************* The first use of the ``cpp_graph`` constructor cannot be in :ref:`parallel` execution mode. {xrst_end cpp_graph_ctor} -------------------------------------------------------------------------------- */ public: void initialize(void) { function_name_ = ""; n_dynamic_ind_ = 0; n_variable_ind_ = 0; discrete_name_vec_.resize(0); atomic_name_vec_.resize(0); print_text_vec_.resize(0); constant_vec_.resize(0); operator_vec_.resize(0); operator_arg_.resize(0); dependent_vec_.resize(0); return; } cpp_graph(void) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL static bool first = true; if( first ) { first = false; CPPAD_ASSERT_UNKNOWN( local::graph::op_name2enum.size() == 0 ); // initialize cpp_graph global variables in cpp_graph_op.cpp local::graph::set_operator_info(); } initialize(); } /* --------------------------------------------------------------------------------- {xrst_begin cpp_graph_scalar} C++ AD Graph Scalar Values ########################## Syntax ****** Get === | *function_name* = *graph_obj* . ``function_name_get`` () | *n_dynamic_ind* = *graph_obj* . ``n_dynamic_ind_get`` () | *n_variable_ind* = *graph_obj* . ``n_variable_ind_get`` () Set === | *graph_obj* . ``function_name_set`` ( *function_name* ) | *graph_obj* . ``n_dynamic_ind_set`` ( *n_dynamic_ind* ) | *graph_obj* . ``n_variable_ind_set`` ( *n_variable_ind* ) Set *** The argument for all the set operations is const. graph_obj ********* is an ``cpp_graph`` object. It is const for all the get functions. function_name ************* is a ``std::string&`` specifying the name of the function for this graph. n_dynamic_ind ************* is a ``size_t`` specifying the number of independent dynamic parameters. n_variable_ind ************** is a ``size_t`` specifying the number of independent variables. {xrst_end cpp_graph_scalar} */ // function_name const std::string& function_name_get(void) const { return function_name_; } void function_name_set(const std::string& function_name) { function_name_ = function_name; } // // n_dynamic_ind const size_t& n_dynamic_ind_get(void) const { return n_dynamic_ind_; } void n_dynamic_ind_set(const size_t n_dynamic_ind) { n_dynamic_ind_ = n_dynamic_ind; } // // n_variable_ind const size_t& n_variable_ind_get(void) const { return n_variable_ind_; } void n_variable_ind_set(const size_t n_variable_ind) { n_variable_ind_ = n_variable_ind; } /* --------------------------------------------------------------------------------- {xrst_begin cpp_graph_vector} C++ AD Graph Vector Values ########################## Syntax ****** Size ==== | *size* = *graph_obj* . ``discrete_name_vec_size`` () | *size* = *graph_obj* . ``atomic_name_vec_size`` () | *size* = *graph_obj* . ``print_text_vec_size`` () | *size* = *graph_obj* . ``constant_vec_size`` () | *size* = *graph_obj* . ``operator_vec_size`` () | *size* = *graph_obj* . ``operator_arg_size`` () | *size* = *graph_obj* . ``dependent_vec_size`` () Get === | *discrete_name* = *graph_obj* . ``discrete_name_vec_get`` ( *index* ) | *atomic_name* = *graph_obj* . ``atomic_name_vec_get`` ( *index* ) | *print_text* = *graph_obj* . ``print_text_vec_get`` ( *index* ) | *constant* = *graph_obj* . ``constant_vec_get`` ( *index* ) | *op_enum* = *graph_obj* . ``operator_vec_get`` ( *index* ) | *argument* = *graph_obj* . ``operator_arg_get`` ( *index* ) | *node_index* = *graph_obj* . ``dependent_vec_get`` ( *index* ) Push Back ========= | *graph_obj* . ``discrete_name_vec_push_back`` ( *discrete_name* ) | *graph_obj* . ``atomic_name_vec_push_back`` ( *atomic_name* ) | *graph_obj* . ``print_text_vec_push_back`` ( *print_text* ) | *graph_obj* . ``constant_vec_push_back`` ( *constant* ) | *graph_obj* . ``operator_vec_push_back`` ( *op_enum* ) | *graph_obj* . ``operator_arg_push_back`` ( *argument* ) | *graph_obj* . ``dependent_vec_push_back`` ( *node_index* ) Find ==== | *discrete_index* = *graph_obj* . ``discrete_name_vec_find`` ( *discrete_name* ) | *atomic_index* = *graph_obj* . ``atomic_name_vec_find`` ( *atomic_name* ) | *print_index* = *graph_obj* . ``print_text_vec_find`` ( *print_text* ) Arguments ********* All of the member function arguments are either call by value or const. size **** is a ``size_t`` value equal to the current size of the specified vector. index ***** is a ``size_t`` value that must be less than the current size of the specified vector. push_back ********* The arguments for all the push_back functions are const. The size of the specified vector before a push_back, is the index in the vector corresponding to the argument value. The size of the vector after the push_back is the size before plus one. graph_obj ********* is an ``cpp_graph`` object. It is const for the size, get, and find functions and not const for the push_back functions. discrete_name ************* is a ``std::string`` equal to the name of a :ref:`discrete-name` function. atomic_name *********** is a ``std::string`` equal to the name of an :ref:`atomic_three-name` function. print_text ********** is a ``std::string`` equal to the text to be printed. constant ******** is a ``double`` equal to the constant with the corresponding index in ``constant_vec`` . op_enum ******* is the :ref:`graph_op_enum-name` for corresponding operator. argument ******** is the ``size_t`` value for corresponding operator argument. node_index ********** is the node index for the corresponding dependent variable with the corresponding index in :ref:`cpp_ad_graph@dependent_vec` . discrete_index ************** is the index such that *discrete_name* == *graph_obj* . ``discrete_name_vec_get`` ( *discrete_index* ) If there is no such index, *discrete_index* == *graph_obj* . ``discrete_name_vec_size`` () atomic_index ************ is the index such that *atomic_name* == *graph_obj* . ``atomic_name_vec_get`` ( *atomic_index* ) If there is no such index, *atomic_index* == *graph_obj* . ``atomic_name_vec_size`` () print_index *********** is the index such that *print_text* == *graph_obj* . ``print_text_vec_get`` ( *print_index* ) If there is no such index, *print_index* == *graph_obj* . ``print_text_vec_size`` () {xrst_end cpp_graph_vector} */ // discrete_name_vec const std::string& discrete_name_vec_get(size_t index) const { return discrete_name_vec_[index]; } size_t discrete_name_vec_size(void) const { return discrete_name_vec_.size(); } void discrete_name_vec_push_back(const std::string& discrete_name) { discrete_name_vec_.push_back(discrete_name); } size_t discrete_name_vec_find(const std::string& discrete_name) const { for(size_t i = 0; i < discrete_name_vec_.size(); ++i) if( discrete_name == discrete_name_vec_[i] ) return i; return discrete_name_vec_.size(); } // // atomic_name_vec const std::string& atomic_name_vec_get(size_t index) const { return atomic_name_vec_[index]; } size_t atomic_name_vec_size(void) const { return atomic_name_vec_.size(); } void atomic_name_vec_push_back(const std::string& atomic_name) { atomic_name_vec_.push_back(atomic_name); } size_t atomic_name_vec_find(const std::string& atomic_name) const { for(size_t i = 0; i < atomic_name_vec_.size(); ++i) if( atomic_name == atomic_name_vec_[i] ) return i; return atomic_name_vec_.size(); } // // print_text_vec const std::string& print_text_vec_get(size_t index) const { return print_text_vec_[index]; } size_t print_text_vec_size(void) const { return print_text_vec_.size(); } void print_text_vec_push_back(const std::string& atomic_name) { print_text_vec_.push_back(atomic_name); } size_t print_text_vec_find(const std::string& print_text) const { for(size_t i = 0; i < print_text_vec_.size(); ++i) if( print_text == print_text_vec_[i] ) return i; return print_text_vec_.size(); } // // constant_vec const double& constant_vec_get(size_t index) const { return constant_vec_[index]; } size_t constant_vec_size(void) const { return constant_vec_.size(); } void constant_vec_push_back(const double& constant) { constant_vec_.push_back(constant); } // // oerator_vec const graph_op_enum& operator_vec_get(size_t index) const { return operator_vec_[index]; } size_t operator_vec_size(void) const { return operator_vec_.size(); } void operator_vec_push_back(const graph_op_enum op_enum) { operator_vec_.push_back(op_enum); } // // operator_arg const size_t& operator_arg_get(size_t index) const { return operator_arg_[index]; } size_t operator_arg_size(void) const { return operator_arg_.size(); } void operator_arg_push_back(const size_t argument) { operator_arg_.push_back(argument); } // // dependent_vec const size_t& dependent_vec_get(size_t index) const { return dependent_vec_[index]; } size_t dependent_vec_size(void) const { return dependent_vec_.size(); } void dependent_vec_push_back(const size_t node_index) { dependent_vec_.push_back(node_index); } /* {xrst_begin cpp_graph_print} {xrst_spell ostream } Print A C++ AD Graph #################### Syntax ****** *graph_obj* . ``print`` ( *os* ) graph_obj ********* is an const ``cpp_graph`` object. os ** Is the ``std::ostream`` where the graph is printed. Discussion ********** This function is included to help with using the ``cpp_graph`` class. The formatting of it's output is not part of the API; i.e., it may change in the future. {xrst_toc_hidden example/graph/print_graph.cpp } Example ******* The file :ref:`print_graph.cpp-name` contains an example and test of this operation. {xrst_end cpp_graph_print} */ void print(std::ostream& os) const { using std::setw; using std::string; // // function name if( function_name_ != "" ) os << function_name_ << "\n"; // // initialize node index size_t node_index = 1; // // text vector size_t n_text = print_text_vec_.size(); for(size_t i = 0; i < n_text; ++i) { string s_i = "c[" + std::to_string(i) + "]"; // os << setw(11) << ""; os << setw(10) << s_i; os << "'" << print_text_vec_[i] << "'"; os << "\n"; } // // parameter vector for(size_t i = 0; i < n_dynamic_ind_; i++) { string p_i = "p[" + std::to_string(i) + "]"; // os << setw(11) << node_index; os << setw(10) << p_i; os << "\n"; ++node_index; } // // variable vector for(size_t i = 0; i < n_variable_ind_; i++) { string x_i = "x[" + std::to_string(i) + "]"; // os << setw(11) << node_index; os << setw(10) << x_i; os << "\n"; ++node_index; } // // constant vector size_t n_constant = constant_vec_.size(); for(size_t i = 0; i < n_constant; i++) { string c_i = "c[" + std::to_string(i) + "]"; // os << setw(11) << node_index; os << setw(10) << c_i; os << setw(20) << constant_vec_[i]; os << "\n"; ++node_index; } size_t n_op = operator_vec_.size(); cpp_graph::const_iterator itr; for(size_t op_index = 0; op_index < n_op; ++op_index) { if( op_index == 0 ) itr = begin(); else ++itr; cpp_graph::const_iterator::value_type itr_value = *itr; // const vector& str_index( *itr_value.str_index_ptr ); const vector& arg( *itr_value.arg_node_ptr ); graph_op_enum op_enum = itr_value.op_enum; size_t n_result = itr_value.n_result; size_t n_arg = arg.size(); CPPAD_ASSERT_UNKNOWN( n_arg > 0 ); // string op_name = local::graph::op_enum2name[ op_enum ]; // if( n_result == 0 ) os << setw(11) << ""; else if( n_result == 1 ) os << setw(11) << node_index; else { string first = std::to_string(node_index); string last = std::to_string(node_index + n_result - 1); os << setw(11) << first + "-" + last; } os << setw(10) << op_name; for(size_t i = 0; i < n_arg; ++i) os << setw(5) << arg[i]; switch( op_enum ) { case graph::discrete_graph_op: CPPAD_ASSERT_UNKNOWN( str_index.size() == 1 ); os << discrete_name_vec_get( str_index[0] ); break; case graph::atom_graph_op: CPPAD_ASSERT_UNKNOWN( str_index.size() == 1 ); os << atomic_name_vec_get( str_index[0] ); break; case graph::print_graph_op: CPPAD_ASSERT_UNKNOWN( str_index.size() == 2 ); os << print_text_vec_get( str_index[0] ) << ","; os << print_text_vec_get( str_index[1] ); break; default: CPPAD_ASSERT_UNKNOWN( str_index.size() == 0 ); break; } os << "\n"; node_index += n_result; } // // dependent vector size_t n_dependent = dependent_vec_.size(); os << "y nodes = "; for(size_t i = 0; i < n_dependent; i++) { os << dependent_vec_[i]; if( i + 1 < n_dependent ) os << ", "; } os << "\n"; } }; // END CPP_GRAPH_CLASS } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/graph/cpp_graph.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin cpp_graph} A C++ AD Graph Class #################### Contents ******** {xrst_toc_table include/cppad/core/graph/cpp_graph.hpp } {xrst_end cpp_graph} ================================================ FILE: include/cppad/core/graph/from_graph.hpp ================================================ # ifndef CPPAD_CORE_GRAPH_FROM_GRAPH_HPP # define CPPAD_CORE_GRAPH_FROM_GRAPH_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /* {xrst_begin from_graph} ADFun Object Corresponding to a CppAD Graph ########################################### Syntax ****** | |tab| ``cpp_graph`` *graph_obj* | |tab| ``ADFun`` < *Base* > *fun* | |tab| *fun* . ``from_graph`` ( *graph_obj* ) | |tab| *fun* . ``from_graph`` ( *graph_obj* , *dyn2var* , *var2dyn* ) Prototype ********* {xrst_literal // BEGIN_ONE_ARGUMENT // END_ONE_ARGUMENT } {xrst_literal // BEGIN_WITH_IS_DYNAMIC // END_WITH_IS_DYNAMIC } Base **** is the type corresponding to this :ref:`adfun-name` object; i.e., its calculations are done using the type *Base* . RecBase ******* in the prototype above, *RecBase* is the same type as *Base* . graph_obj ********* is a :ref:`cpp_ad_graph-name` representation of this function. dyn2var ******* is a vector with size equal to the number of independent dynamic parameters in the graph; i.e., the size of :ref:`cpp_ad_graph@Node Indices@p` . It specifies which independent dynamic parameters in the graph are independent variables in the function *fun* . var2dyn ******* is a vector with size equal to the number of independent variables in the graph; i.e., the size of :ref:`cpp_ad_graph@Node Indices@x` . It specifies which independent variables in the graph are independent dynamic parameters in the function *fun* . fun *** It *dyn2var* and *var2dyn* are not present, the independent dynamic parameters and independent variables in *fun* are the same as for the graph. Otherwise, they are described below. m_true, m_false =============== Let *m_true* ( *m_false* ) be the number of true (false) elements of *dyn2var* . n_true, n_false =============== Let *n_true* ( *n_false* ) be the number of true (false) elements of *var2dyn* . Independent Dynamic Parameters ============================== The first *m_false* independent dynamic parameters in *fun* correspond to the false components of *dyn2var* and have the same order as in the graph. The next *n_true* independent dynamic parameters in *fun* correspond to the true components of *var2dyn* and have the same order as in the graph. Independent Variables ===================== The first *m_true* independent variables in *fun* correspond to the true components of *dyn2var* and have the same order as in the graph. The next *n_false* independent variables in *fun* correspond to the false components of *var2dyn* and have the same order as in the graph. {xrst_toc_hidden example/graph/switch_var_dyn.cpp } Examples ******** The file :ref:`switch_var_dyn.cpp-name` contains an example and test of this routine. For simpler examples, that do not change the dynamic parameters and variables; see :ref:`graph_op_enum examples` . {xrst_end from_graph} */ // BEGIN_WITH_IS_DYNAMIC template void CppAD::ADFun::from_graph( const CppAD::cpp_graph& graph_obj , const CppAD::vector& dyn2var , const CppAD::vector& var2dyn ) // END_WITH_IS_DYNAMIC { using CppAD::isnan; using namespace CppAD::graph; // // some sizes const std::string function_name = graph_obj.function_name_get(); const size_t n_dynamic_ind = graph_obj.n_dynamic_ind_get(); const size_t n_variable_ind = graph_obj.n_variable_ind_get(); const size_t n_constant = graph_obj.constant_vec_size(); const size_t n_usage = graph_obj.operator_vec_size(); const size_t n_dependent = graph_obj.dependent_vec_size(); // // n_dynamic_ind_fun // n_variable_ind_fun CPPAD_ASSERT_KNOWN( n_variable_ind == var2dyn.size(), "from_graph: size of var2dyn not equal " "number of independent variables in graph" ); CPPAD_ASSERT_KNOWN( n_dynamic_ind == dyn2var.size(), "from_graph: size of dyn2val not equal " "number of independent dynamic parameters in graph" ); size_t n_dynamic_ind_fun = 0; size_t n_variable_ind_fun = 0; for(size_t i = 0; i < n_dynamic_ind; ++i) { if( dyn2var[i] ) ++n_variable_ind_fun; else ++n_dynamic_ind_fun; } for(size_t i = 0; i < n_variable_ind; ++i) { if( var2dyn[i] ) ++n_dynamic_ind_fun; else ++n_variable_ind_fun; } // // Start of node indices # ifndef NDEBUG size_t start_dynamic_ind = 1; size_t start_independent = start_dynamic_ind + n_dynamic_ind; size_t start_constant = start_independent + n_variable_ind; size_t start_operator = start_constant + n_constant; # endif // // initialize mappings from node index as empty // (there is no node zero) vector node_type( 1 ); local::pod_vector node2fun( 1 ); node_type[0] = number_ad_type_enum; // invalid value node2fun[0] = 0; // invalid value // // discrete_index // mapping from index in discrete_name_vec to discrete index size_t n_list_discrete = discrete::list_size(); size_t n_graph_discrete = graph_obj.discrete_name_vec_size(); vector discrete_index( n_graph_discrete ); for(size_t i = 0; i < n_graph_discrete; ++i) discrete_index[i] = n_list_discrete; // invalid discrete index for(size_t index = 0; index < n_list_discrete; ++index) { const std::string& name( discrete::name(index) ); size_t graph_index = graph_obj.discrete_name_vec_find(name); if( graph_index != n_graph_discrete ) { if( discrete_index[graph_index] != n_list_discrete ) { std::string msg = "from_graph: error in call to "; msg += name; msg += ".\nThere is more than one discrete "; msg += "function with this name"; // // use this source code as point of detection bool known = true; int line = __LINE__; const char* file = __FILE__; const char* exp = "discrete_index[i] == n_list_discrete"; // // CppAD error handler ErrorHandler::Call( known, line, file, exp, msg.c_str() ); } discrete_index[graph_index] = index; } } // // atomic_name2index // mapping from index in atomic_name_vec to atomic three index size_t n_graph_atomic = graph_obj.atomic_name_vec_size(); vector atomic_name2index( n_graph_atomic ); for(size_t index = 0; index < n_graph_atomic; ++index) atomic_name2index[index] = 0; // invalid atomic index { bool set_null = true; size_t index_in = 0; size_t type; std::string name; void* ptr; size_t n_atomic = CppAD::local::atomic_index( set_null, index_in, type, &name, ptr ); set_null = false; for(index_in = 1; index_in <= n_atomic; ++index_in) { CppAD::local::atomic_index( set_null, index_in, type, &name, ptr ); size_t graph_index = graph_obj.atomic_name_vec_find(name); if( graph_index != n_graph_atomic ) { if( atomic_name2index[graph_index] != 0 ) { std::string msg = "from_graph: error in call to "; msg += name + ".\n"; msg += "There is more than one atomic "; msg += "function with this name"; // // use this source code as point of detection bool known = true; int line = __LINE__; const char* file = __FILE__; const char* exp = "atomic_index[index] == 0"; // // CppAD error handler ErrorHandler::Call( known, line, file, exp, msg.c_str() ); } atomic_name2index[graph_index] = index_in; } } } // ---------------------------------------------------------------------- // Create a recording for this function // ---------------------------------------------------------------------- // start a recording local::recorder rec; CPPAD_ASSERT_UNKNOWN( rec.num_var_op() == 0 ); rec.set_n_dyn_independent(n_dynamic_ind_fun); rec.set_abort_op_index(0); rec.set_record_compare(false); // rec_text_index // mapping from print_text_vec index to recording index vector rec_text_index( graph_obj.print_text_vec_size() ); for(size_t i = 0; i < graph_obj.print_text_vec_size(); ++i) { const std::string& text = graph_obj.print_text_vec_get(i); rec_text_index[i] = rec.PutTxt( text.c_str() ); } // nan Base nan = CppAD::numeric_limits::quiet_NaN(); // Place the parameter with index 0 in the tape const local::pod_vector_maybe& parameter( rec.par_all()); CPPAD_ASSERT_UNKNOWN( parameter.size() == 0 ); addr_t i_par = rec.put_con_par(nan); CPPAD_ASSERT_UNKNOWN( i_par == 0 ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_par] ) ); // // Place the variable with index 0 in the tape CPPAD_ASSERT_NARG_NRES(local::BeginOp, 1, 1); rec.PutOp(local::BeginOp); rec.PutArg(0); // // Next come the independent dynamic parameters in the graph addr_t i_var = 0; for(size_t i = 0; i < n_dynamic_ind; ++i) { if( dyn2var[i] ) { i_var = rec.PutOp( local::InvOp ); node_type.push_back(variable_enum);; node2fun.push_back(i_var); } else { i_par = rec.put_dyn_par(nan, local::ind_dyn ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_par] ) ); node_type.push_back(dynamic_enum); node2fun.push_back(i_par); } } // Next come the independent variables in the graph CPPAD_ASSERT_NARG_NRES(local::InvOp, 0, 1); for(size_t i = 0; i < n_variable_ind; ++i) { if( var2dyn[i] ) { i_par = rec.put_dyn_par(nan, local::ind_dyn ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_par] ) ); node_type.push_back(dynamic_enum); node2fun.push_back(i_par); } else { i_var = rec.PutOp( local::InvOp ); node_type.push_back(variable_enum);; node2fun.push_back(i_var); } } CPPAD_ASSERT_UNKNOWN( size_t( i_par ) == n_dynamic_ind_fun ); CPPAD_ASSERT_UNKNOWN( size_t( i_var ) == n_variable_ind_fun ); // Next come the constant parameters for(size_t i = 0; i < n_constant; ++i) { Base par = Base( graph_obj.constant_vec_get(i) ); i_par = rec.put_con_par(par); CPPAD_ASSERT_UNKNOWN( parameter[i_par] == par ); // node_type.push_back(constant_enum);; node2fun.push_back(i_par); } // // local arrays used to avoid reallocating memory local::pod_vector temporary; vector type_x; vector arg; vector arg_node; // // arrays only used by atom_graph_op, atom4_graph_op vector parameter_x, taylor_y; vector type_y; vector< AD > ax, ay; vector select_y; // // define here because not using as loop index cpp_graph::const_iterator graph_itr; // // loop over operators in the recording # ifndef NDEBUG size_t start_result = start_operator; # endif for(size_t op_index = 0; op_index < n_usage; ++op_index) { // op_enum, str_index, n_result, arg_node if( op_index == 0 ) graph_itr = graph_obj.begin(); else ++graph_itr; cpp_graph::const_iterator::value_type itr_value = *graph_itr; const vector& str_index(*itr_value.str_index_ptr ); graph_op_enum op_enum = itr_value.op_enum; size_t n_result = itr_value.n_result; size_t call_id = itr_value.call_id; size_t n_arg = itr_value.arg_node_ptr->size(); arg.resize(n_arg); // // make sure type_x is large enough type_x.resize(n_arg); # ifndef NDEBUG addr_t n_con_arg = 0; # endif addr_t n_dyn_arg = 0; addr_t n_var_arg = 0; for(size_t j = 0; j < n_arg; ++j) { size_t node_index = (*itr_value.arg_node_ptr)[j]; // // argument to graph operator CPPAD_ASSERT_KNOWN( node_index < start_result, "from_graph op argument index is greater or equal\n" "the starting index for the next result" ); // // type of argument type_x[j] = node_type[ node_index ]; // // argument to function operator arg[j] = node2fun[ node_index ]; CPPAD_ASSERT_UNKNOWN( arg[j] != 0 ); // // count number of arguments of different types # ifndef NDEBUG n_con_arg += addr_t( type_x[j] == constant_enum ); # endif n_dyn_arg += addr_t( type_x[j] == dynamic_enum ); n_var_arg += addr_t( type_x[j] == variable_enum ); } CPPAD_ASSERT_UNKNOWN( n_arg == size_t(n_con_arg + n_dyn_arg + n_var_arg) ); // addr_t i_result = 0; // invalid value // ------------------------------------------------------------------- // conditional expressions // ------------------------------------------------------------------- if( op_enum == cexp_eq_graph_op || op_enum == cexp_le_graph_op || op_enum == cexp_lt_graph_op ) { CPPAD_ASSERT_UNKNOWN( n_result == 1 && n_arg == 4 ); // cop CompareOp cop; if( op_enum == cexp_eq_graph_op ) cop = CompareEq; else if ( op_enum == cexp_le_graph_op ) cop = CompareLe; else cop = CompareLt; // if( n_var_arg == 0 ) { if( n_dyn_arg == 0 ) { // result is a constant parameter Base result = CondExpOp(cop, parameter[arg[0]], // left parameter[arg[1]], // right parameter[arg[2]], // if_true parameter[arg[3]] // if_false ); i_result = rec.put_con_par(result); } else { i_result = rec.put_dyn_cond_exp( nan, cop, arg[0], arg[1], arg[2], arg[3] ); } } else { // flag marking which arguments are variables addr_t flag = 0; addr_t bit = 1; for(size_t j = 0; j < 4; ++j) { if( type_x[j] == variable_enum ) flag |= bit; bit = 2 * bit; } CPPAD_ASSERT_UNKNOWN( flag != 0 ); rec.PutArg(addr_t(cop), flag, arg[0], arg[1], arg[2], arg[3]); i_result = rec.PutOp(local::CExpOp); } } // ------------------------------------------------------------------- // compare operators // ------------------------------------------------------------------- else if( op_enum == comp_eq_graph_op || op_enum == comp_le_graph_op || op_enum == comp_lt_graph_op || op_enum == comp_ne_graph_op ) { CPPAD_ASSERT_UNKNOWN( n_result == 0 && n_arg == 2 ); // bool var_left = type_x[0] == variable_enum; bool var_right = type_x[1] == variable_enum; bool dyn_left = type_x[0] == dynamic_enum; bool dyn_right = type_x[1] == dynamic_enum; // ax.resize(n_arg); // ax[0] if( var_left | dyn_left ) ax[0].taddr_ = arg[0]; else ax[0].value_ = parameter_x[0]; // ax[1] if( var_right | dyn_right ) ax[1].taddr_ = arg[1]; else ax[1].value_ = parameter_x[1]; // bool result; switch( op_enum ) { case comp_eq_graph_op: result = true; rec.comp_eq( var_left, var_right, dyn_left, dyn_right, ax[0], ax[1], result ); break; case comp_le_graph_op: result = true; rec.comp_le( var_left, var_right, dyn_left, dyn_right, ax[0], ax[1], result ); break; case comp_lt_graph_op: result = true; rec.comp_lt( var_left, var_right, dyn_left, dyn_right, ax[0], ax[1], result ); break; case comp_ne_graph_op: result = false; rec.comp_eq( var_left, var_right, dyn_left, dyn_right, ax[0], ax[1], result ); break; default: CPPAD_ASSERT_UNKNOWN(false); } } // ------------------------------------------------------------------- // sum operator // ------------------------------------------------------------------- else if( op_enum == sum_graph_op ) { CPPAD_ASSERT_KNOWN( n_result == 1 , "AD graph sum operator: n_result is not 1" ); if( n_var_arg == 0 ) { // result of the sum is a parameter Base sum_constant = 0.0; temporary.resize(0); for(size_t j = 0; j < n_arg; j++) { if( type_x[j] == constant_enum ) sum_constant += parameter[ arg[j] ]; else { CPPAD_ASSERT_UNKNOWN( type_x[j] == dynamic_enum ); temporary.push_back( arg[j] ); } } CPPAD_ASSERT_UNKNOWN( temporary.size() == size_t(n_dyn_arg) ); // // start with constant parameter i_result = rec.put_con_par(sum_constant); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == sum_constant ); // // sum the dynamic parameters for(addr_t j = 0; j < n_dyn_arg; ++j) { i_result = rec.put_dyn_par( nan, local::add_dyn, i_result, temporary[j] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); } } else { // result of the sum is a variable size_t n_temporary = 6 + size_t(n_var_arg + n_dyn_arg); if( temporary.size() < n_temporary ) temporary.resize( n_temporary ); Base sum_constant = 0.0; addr_t j_variable = 5 ; addr_t j_dynamic = 5 + n_var_arg; for(size_t j = 0; j < n_arg; j++) { if( type_x[j] == constant_enum ) sum_constant += parameter[ arg[j] ]; if( type_x[j] == variable_enum ) temporary[ j_variable++ ] = arg[j]; if( type_x[j] == dynamic_enum ) temporary[ j_dynamic++ ] = arg[j]; } // number of arguments to this operator temporary[j_dynamic] = j_dynamic + 1; // temporary[0] = rec.put_con_par(sum_constant); CPPAD_ASSERT_UNKNOWN(parameter[temporary[0]] == sum_constant); // temporary[1] = 5 + n_var_arg; temporary[2] = 5 + n_var_arg; temporary[3] = temporary[2] + n_dyn_arg; temporary[4] = temporary[2] + n_dyn_arg; // i_result = rec.PutOp(local::CSumOp); for(size_t j = 0; j < n_temporary; ++j) rec.PutArg( temporary[j] ); CPPAD_ASSERT_UNKNOWN( local::NumRes(local::CSumOp) == 1 ); } } // ------------------------------------------------------------------- // print operator // ------------------------------------------------------------------- else if( op_enum == print_graph_op ) { CPPAD_ASSERT_UNKNOWN( n_arg == 2 && n_result == 0 ); // // before size_t before_graph = str_index[0]; addr_t before_rec = rec_text_index[before_graph]; // // after size_t after_graph = str_index[1]; addr_t after_rec = rec_text_index[after_graph]; // // base 2 representation of [ Var(notpos), Var(value) ] addr_t is_var = 0; if( type_x[0] == variable_enum ) is_var += 1; if( type_x[1] == variable_enum ) is_var += 2; // // record this print operator addr_t notpos = arg[0]; addr_t value = arg[1]; rec.PutOp(local::PriOp); rec.PutArg(is_var, notpos, before_rec, value, after_rec); } // ------------------------------------------------------------------- // discrete operator // ------------------------------------------------------------------- else if( op_enum == discrete_graph_op ) { CPPAD_ASSERT_UNKNOWN( n_arg == 1 && n_result == 1 ); size_t name_index = str_index[0]; size_t function_index = discrete_index[name_index]; if( function_index == n_list_discrete ) { std::string msg = "from_graph: error in call to "; msg += graph_obj.discrete_name_vec_get(name_index); msg += ".\nNo previously defined discrete function "; msg += "has this name"; // // use this source code as point of detection bool known = true; int line = __LINE__; const char* file = __FILE__; const char* exp = "discrete_index[name_index] != n_list_discrete"; // // CppAD error handler ErrorHandler::Call(known, line, file, exp, msg.c_str()); } if( type_x[0] == variable_enum ) { CPPAD_ASSERT_NARG_NRES(local::DisOp, 2, 1); i_result = rec.PutOp(local::DisOp); rec.PutArg( addr_t(function_index) ); rec.PutArg( arg[0] ); } else if( type_x[0] == dynamic_enum ) { i_result = rec.put_dyn_par( nan, local::dis_dyn, addr_t(function_index), arg[0] ); } else { Base result = discrete::eval( function_index, parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); } } // ------------------------------------------------------------------- // atomic operator // ------------------------------------------------------------------- else if( op_enum == atom_graph_op || op_enum == atom4_graph_op ) { size_t name_index = str_index[0]; // // atomic_index CPPAD_ASSERT_UNKNOWN( name_index < atomic_name2index.size() ); size_t atomic_index = atomic_name2index[name_index]; if( atomic_index == 0 ) { std::string msg = "from_graph: error in call to "; msg += graph_obj.atomic_name_vec_get(name_index); msg += ".\n"; msg += "No previously defined atomic function "; msg += "has this name"; // // use this source code as point of detection bool known = true; int line = __LINE__; const char* file = __FILE__; const char* exp = "atomic_index != 0"; // // CppAD error handler ErrorHandler::Call(known, line, file, exp, msg.c_str()); } // // parameter_x parameter_x.resize(n_arg); for(size_t j = 0; j < n_arg; ++j) { if( type_x[j] == constant_enum ) parameter_x[j] = parameter[ arg[j] ]; else parameter_x[j] = nan; } // // type, name, v_ptr bool set_null = false; size_t type; std::string* name = nullptr; void* v_ptr; CppAD::local::atomic_index( set_null, atomic_index, type, name, v_ptr ); CPPAD_ASSERT_KNOWN( 2 < type, "from_graph: attempt to use an atomic function with < 3" ); // // type_y, taylor_y type_y.resize(n_result); size_t order_low = 0; size_t order_up = 0; if( type == 3 ) { // afun atomic_three* afun = reinterpret_cast< atomic_three* >( v_ptr ); // // type_y afun->for_type(parameter_x, type_x, type_y); // // taylor_y size_t need_y = size_t(constant_enum); taylor_y.resize(n_result); afun->forward( parameter_x , type_x , need_y , order_low , order_up , parameter_x , taylor_y ); } else { CPPAD_ASSERT_UNKNOWN( type == 4 ); // afun atomic_four* afun = reinterpret_cast< atomic_four* >( v_ptr ); // // type_x for(size_t j = 0; j < n_arg; ++j) { if( type_x[j] == constant_enum ) if( parameter[ arg[j] ] == Base(0) ) type_x[j] = identical_zero_enum; } // // type_y afun->for_type(call_id, type_x, type_y); for(size_t i = 0; i < n_result; ++i) { if( type_y[i] == identical_zero_enum ) type_y[i] = constant_enum; } // // type_x for(size_t j = 0; j < n_arg; ++j) if( type_x[j] == identical_zero_enum ) type_x[j] = constant_enum; // // select_y select_y.resize(n_result); for(size_t i = 0; i < n_result; ++i) select_y[i] = type_y[i] == constant_enum; // // taylor_y taylor_y.resize(n_result); afun->forward( call_id , select_y , order_low , order_up , parameter_x , taylor_y ); } // // record_dynamic, record_variable bool record_dynamic = false; bool record_variable = false; for(size_t i = 0; i < n_result; ++i) { CPPAD_ASSERT_UNKNOWN( type_y[i] <= variable_enum ); record_dynamic |= type_y[i] == dynamic_enum; record_variable |= type_y[i] == variable_enum; } // tape_id is zero because not a true recording tape_id_t tape_id = 0; // // ax, ay if( record_dynamic || record_variable ) { // tape_id (not a recording AD operations) // ax ax.resize(n_arg); for(size_t j = 0; j < n_arg; ++j) { ax[j].value_ = parameter_x[j]; ax[j].taddr_ = arg[j]; } // ay ay.resize(n_result); for(size_t i = 0; i < n_result; ++i) { ay[i].value_ = nan; // not_used ay[i].taddr_ = 0; // not used } } if( record_dynamic ) rec.put_dyn_atomic( tape_id, atomic_index, call_id, type_x, type_y, ax, ay ); if( record_variable ) rec.put_var_atomic( tape_id, atomic_index, call_id, type_x, type_y, ax, ay ); // // node_type, node2fun for(size_t i = 0; i < n_result; ++i) { node_type.push_back(type_y[i]); switch( type_y[i] ) { case constant_enum: node2fun.push_back(rec.put_con_par(taylor_y[i])); break; case dynamic_enum: case variable_enum: node2fun.push_back(ay[i].taddr_); break; default: CPPAD_ASSERT_UNKNOWN(false); break; } } } // ------------------------------------------------------------------- // unary operators // ------------------------------------------------------------------- else if( n_arg == 1 ) { CPPAD_ASSERT_UNKNOWN( n_arg == 1 && n_result == 1 ); Base result; // used in cases argument is a constant if( type_x[0] == variable_enum ) switch( op_enum ) { case abs_graph_op: i_result = rec.PutOp(local::AbsOp); rec.PutArg( arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::AbsOp) == 1 ); break; case acosh_graph_op: i_result = rec.PutOp(local::AcoshOp); rec.PutArg( arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::AcoshOp) == 1 ); break; case asinh_graph_op: i_result = rec.PutOp(local::AsinhOp); rec.PutArg( arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::AsinhOp) == 1 ); break; case atanh_graph_op: i_result = rec.PutOp(local::AtanhOp); rec.PutArg( arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::AtanhOp) == 1 ); break; case erf_graph_op: i_result = rec.PutOp(local::ErfOp); CPPAD_ASSERT_UNKNOWN( NumArg(local::ErfOp) == 3 ); // // arg[0] = variable index for function argument rec.PutArg( arg[0] ); // // parameter[ arg[1] ] = 0.0 i_par = rec.put_con_par( Base(0.0) ); rec.PutArg( i_par ); // // parameter[ arg[2] ] = 2 / sqrt(pi) i_par = rec.put_con_par(Base( 1.0 / std::sqrt( std::atan(1.0) ) )); rec.PutArg( i_par ); // break; case erfc_graph_op: i_result = rec.PutOp(local::ErfcOp); CPPAD_ASSERT_UNKNOWN( NumArg(local::ErfcOp) == 3 ); // // arg[0] = variable index for function argument rec.PutArg( arg[0] ); // // parameter[ arg[1] ] = 0.0 i_par = rec.put_con_par( Base(0.0) ); rec.PutArg( i_par ); // // parameter[ arg[2] ] = 2 / sqrt(pi) i_par = rec.put_con_par(Base( 1.0 / std::sqrt( std::atan(1.0) ) )); rec.PutArg( i_par ); // break; case expm1_graph_op: i_result = rec.PutOp(local::Expm1Op); rec.PutArg( arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::Expm1Op) == 1 ); break; case log1p_graph_op: i_result = rec.PutOp(local::Log1pOp); rec.PutArg( arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::Log1pOp) == 1 ); break; case acos_graph_op: i_result = rec.PutOp(local::AcosOp); rec.PutArg( arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::AcosOp) == 1 ); break; case asin_graph_op: i_result = rec.PutOp(local::AsinOp); rec.PutArg( arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::AsinOp) == 1 ); break; case atan_graph_op: i_result = rec.PutOp(local::AtanOp); rec.PutArg( arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::AtanOp) == 1 ); break; case cosh_graph_op: i_result = rec.PutOp(local::CoshOp); rec.PutArg( arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::CoshOp) == 1 ); break; case cos_graph_op: i_result = rec.PutOp(local::CosOp); rec.PutArg( arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::CosOp) == 1 ); break; case exp_graph_op: i_result = rec.PutOp(local::ExpOp); rec.PutArg( arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::ExpOp) == 1 ); break; case log_graph_op: i_result = rec.PutOp(local::LogOp); rec.PutArg( arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::LogOp) == 1 ); break; case neg_graph_op: i_result = rec.PutOp(local::NegOp); rec.PutArg( arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::NegOp) == 1 ); break; case sign_graph_op: i_result = rec.PutOp(local::SignOp); rec.PutArg( arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::SignOp) == 1 ); break; case sinh_graph_op: i_result = rec.PutOp(local::SinhOp); rec.PutArg( arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::SinhOp) == 1 ); break; case sin_graph_op: i_result = rec.PutOp(local::SinOp); rec.PutArg( arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::SinOp) == 1 ); break; case sqrt_graph_op: i_result = rec.PutOp(local::SqrtOp); rec.PutArg( arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::SqrtOp) == 1 ); break; case tanh_graph_op: i_result = rec.PutOp(local::TanhOp); rec.PutArg( arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::TanhOp) == 1 ); break; case tan_graph_op: i_result = rec.PutOp(local::TanOp); rec.PutArg( arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::TanOp) == 1 ); break; default: CPPAD_ASSERT_UNKNOWN( false ); break; } else if( type_x[0] == dynamic_enum ) switch( op_enum ) { case abs_graph_op: i_result = rec.put_dyn_par(nan, local::abs_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case acosh_graph_op: i_result = rec.put_dyn_par(nan, local::acosh_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case asinh_graph_op: i_result = rec.put_dyn_par(nan, local::asinh_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case atanh_graph_op: i_result = rec.put_dyn_par(nan, local::atanh_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case erf_graph_op: i_result = rec.put_dyn_par(nan, local::erf_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case erfc_graph_op: i_result = rec.put_dyn_par(nan, local::erfc_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case expm1_graph_op: i_result = rec.put_dyn_par(nan, local::expm1_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case log1p_graph_op: i_result = rec.put_dyn_par(nan, local::log1p_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case acos_graph_op: i_result = rec.put_dyn_par(nan, local::acos_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case asin_graph_op: i_result = rec.put_dyn_par(nan, local::asin_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case atan_graph_op: i_result = rec.put_dyn_par(nan, local::atan_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case cosh_graph_op: i_result = rec.put_dyn_par(nan, local::cosh_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case cos_graph_op: i_result = rec.put_dyn_par(nan, local::cos_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case exp_graph_op: i_result = rec.put_dyn_par(nan, local::exp_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case log_graph_op: i_result = rec.put_dyn_par(nan, local::log_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case neg_graph_op: i_result = rec.put_dyn_par(nan, local::neg_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case sign_graph_op: i_result = rec.put_dyn_par(nan, local::sign_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case sinh_graph_op: i_result = rec.put_dyn_par(nan, local::sinh_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case sin_graph_op: i_result = rec.put_dyn_par(nan, local::sin_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case sqrt_graph_op: i_result = rec.put_dyn_par(nan, local::sqrt_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case tanh_graph_op: i_result = rec.put_dyn_par(nan, local::tanh_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case tan_graph_op: i_result = rec.put_dyn_par(nan, local::tan_dyn, arg[0] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; default: CPPAD_ASSERT_UNKNOWN( false ); break; } else switch( op_enum ) { case abs_graph_op: result = CppAD::abs( parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case acosh_graph_op: result = CppAD::acosh( parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case asinh_graph_op: result = CppAD::asinh( parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case atanh_graph_op: result = CppAD::atanh( parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case erf_graph_op: result = CppAD::erf( parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case erfc_graph_op: result = CppAD::erfc( parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case expm1_graph_op: result = CppAD::expm1( parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case log1p_graph_op: result = CppAD::log1p( parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case acos_graph_op: result = CppAD::acos( parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case asin_graph_op: result = CppAD::asin( parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case atan_graph_op: result = CppAD::atan( parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case cosh_graph_op: result = CppAD::cosh( parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case cos_graph_op: result = CppAD::cos( parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case exp_graph_op: result = CppAD::exp( parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case log_graph_op: result = CppAD::log( parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case neg_graph_op: result = - parameter[ arg[0] ]; i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case sign_graph_op: result = CppAD::sign( parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case sinh_graph_op: result = CppAD::sinh( parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case sin_graph_op: result = CppAD::sin( parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case sqrt_graph_op: result = CppAD::sqrt( parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case tanh_graph_op: result = CppAD::tanh( parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case tan_graph_op: result = CppAD::tan( parameter[ arg[0] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; default: CPPAD_ASSERT_UNKNOWN( false ); break; } } // ------------------------------------------------------------------- // binary operators // ------------------------------------------------------------------- else { CPPAD_ASSERT_UNKNOWN( n_arg == 2 && n_result == 1 ); Base result; // used in cases where both arguments are constants if( type_x[0] == variable_enum && type_x[1] == variable_enum ) switch( op_enum ) { case add_graph_op: i_result = rec.PutOp(local::AddvvOp); rec.PutArg( arg[0], arg[1] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::AddvvOp) == 2 ); break; case azmul_graph_op: i_result = rec.PutOp(local::ZmulvvOp); rec.PutArg( arg[0], arg[1] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::ZmulvvOp) == 2 ); break; case div_graph_op: i_result = rec.PutOp(local::DivvvOp); rec.PutArg( arg[0], arg[1] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::DivvvOp) == 2 ); break; case mul_graph_op: i_result = rec.PutOp(local::MulvvOp); rec.PutArg( arg[0], arg[1] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::MulvvOp) == 2 ); break; case pow_graph_op: i_result = rec.PutOp(local::PowvvOp); rec.PutArg( arg[0], arg[1] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::PowvvOp) == 2 ); break; case sub_graph_op: i_result = rec.PutOp(local::SubvvOp); rec.PutArg( arg[0], arg[1] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::SubvvOp) == 2 ); break; default: CPPAD_ASSERT_UNKNOWN( false ); break; } else if( type_x[0] == variable_enum ) switch( op_enum ) { // addition is communitative, so use Addpv case add_graph_op: i_result = rec.PutOp(local::AddpvOp); rec.PutArg( arg[1], arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::AddpvOp) == 2 ); break; case azmul_graph_op: i_result = rec.PutOp(local::ZmulvpOp); rec.PutArg( arg[0], arg[1] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::ZmulvpOp) == 2 ); break; case div_graph_op: i_result = rec.PutOp(local::DivvpOp); rec.PutArg( arg[0], arg[1] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::DivvpOp) == 2 ); break; // multiplication is communitative, so use Mulpv case mul_graph_op: i_result = rec.PutOp(local::MulpvOp); rec.PutArg( arg[1], arg[0] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::MulpvOp) == 2 ); break; case pow_graph_op: i_result = rec.PutOp(local::PowvpOp); rec.PutArg( arg[0], arg[1] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::PowvpOp) == 2 ); break; case sub_graph_op: i_result = rec.PutOp(local::SubvpOp); rec.PutArg( arg[0], arg[1] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::SubvpOp) == 2 ); break; default: CPPAD_ASSERT_UNKNOWN( false ); break; } else if( type_x[1] == variable_enum ) switch( op_enum ) { case add_graph_op: i_result = rec.PutOp(local::AddpvOp); rec.PutArg( arg[0], arg[1] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::AddpvOp) == 2 ); break; case azmul_graph_op: i_result = rec.PutOp(local::ZmulpvOp); rec.PutArg( arg[0], arg[1] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::ZmulpvOp) == 2 ); break; case div_graph_op: i_result = rec.PutOp(local::DivpvOp); rec.PutArg( arg[0], arg[1] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::DivpvOp) == 2 ); break; case mul_graph_op: i_result = rec.PutOp(local::MulpvOp); rec.PutArg( arg[0], arg[1] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::MulpvOp) == 2 ); break; case pow_graph_op: i_result = rec.PutOp(local::PowpvOp); rec.PutArg( arg[0], arg[1] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::PowpvOp) == 2 ); break; case sub_graph_op: i_result = rec.PutOp(local::SubpvOp); rec.PutArg( arg[0], arg[1] ); CPPAD_ASSERT_UNKNOWN( NumArg(local::SubpvOp) == 2 ); break; default: CPPAD_ASSERT_UNKNOWN( false ); break; } else if( type_x[0] == dynamic_enum || type_x[1] == dynamic_enum ) switch( op_enum ) { case add_graph_op: i_result = rec.put_dyn_par(nan, local::add_dyn, arg[0], arg[1]); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case azmul_graph_op: i_result = rec.put_dyn_par(nan, local::zmul_dyn, arg[0], arg[1]); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case div_graph_op: i_result = rec.put_dyn_par(nan, local::div_dyn, arg[0], arg[1]); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case mul_graph_op: i_result = rec.put_dyn_par(nan, local::mul_dyn, arg[0], arg[1]); break; case pow_graph_op: i_result = rec.put_dyn_par(nan, local::pow_dyn, arg[0], arg[1]); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; case sub_graph_op: i_result = rec.put_dyn_par(nan, local::sub_dyn, arg[0], arg[1]); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[i_result] ) ); break; default: CPPAD_ASSERT_UNKNOWN( false ); break; } else switch( op_enum ) { case add_graph_op: result = parameter[ arg[0] ] + parameter[ arg[1] ]; i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case azmul_graph_op: result = azmul( parameter[ arg[0] ] , parameter[ arg[1] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case div_graph_op: result = parameter[ arg[0] ] / parameter[ arg[1] ]; i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case mul_graph_op: result = parameter[ arg[0] ] * parameter[ arg[1] ]; i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case pow_graph_op: result = pow( parameter[ arg[0] ], parameter[ arg[1] ] ); i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; case sub_graph_op: result = parameter[ arg[0] ] - parameter[ arg[1] ]; i_result = rec.put_con_par(result); CPPAD_ASSERT_UNKNOWN( parameter[i_result] == result ); break; default: CPPAD_ASSERT_UNKNOWN( false ); break; } } // Exclude cases where node_type and node2fun for the results // have already been set if( n_result != 0 ) if( op_enum != atom_graph_op && op_enum != atom4_graph_op ) { // set node_type and node2fun for result // CPPAD_ASSERT_UNKNOWN( i_result != 0 ); CPPAD_ASSERT_UNKNOWN( n_result == 1 ); if( n_var_arg > 0 ) node_type.push_back(variable_enum); else if( n_dyn_arg > 0 ) node_type.push_back(dynamic_enum); else node_type.push_back(constant_enum); node2fun.push_back(i_result); } // # ifndef NDEBUG start_result += n_result; # endif CPPAD_ASSERT_UNKNOWN( node2fun.size() == start_result ); CPPAD_ASSERT_UNKNOWN( node_type.size() == start_result ); } // set this->dep_parameter_, set this->dep_taddr_ // CPPAD_ASSERT_NARG_NRES(local::ParOp, 1, 1); dep_parameter_.resize( n_dependent ); dep_taddr_.resize( n_dependent ); for(size_t i = 0; i < n_dependent; ++i) { CPPAD_ASSERT_UNKNOWN( node_type[ graph_obj.dependent_vec_get(i) ] != number_ad_type_enum ); if( node_type[ graph_obj.dependent_vec_get(i) ] == variable_enum ) { dep_parameter_[i] = false; dep_taddr_[i] = size_t( node2fun[ graph_obj.dependent_vec_get(i) ] ); } else { dep_parameter_[i] = true; dep_taddr_[i] = size_t( rec.PutOp(local::ParOp) ); rec.PutArg( node2fun[ graph_obj.dependent_vec_get(i) ] ); } } rec.PutOp(local::EndOp); // ---------------------------------------------------------------------- // End recording, set private member data except for // dep__parameter_ and dep_taddr_ // ---------------------------------------------------------------------- // // bool values in this object except check_for_nan_ has_been_optimized_ = false; // // size_t values in this object compare_change_count_ = 1; compare_change_number_ = 0; compare_change_op_index_ = 0; num_order_taylor_ = 0; cap_order_taylor_ = 0; num_direction_taylor_ = 0; num_var_tape_ = rec.num_var(); // // taylor_ taylor_.resize(0); // // cskip_op_ cskip_op_.resize( rec.num_var_op() ); // // load_op2var_ load_op2var_.resize( rec.num_var_load() ); // // play_ // Now that each dependent variable has a place in the recording, // and there is a EndOp at the end of the record, we can transfer the // recording to the player and and erase the recording. play_.get_recording(rec, n_variable_ind_fun); // // ind_taddr_ // Note that play_ has been set, we can use it to check operators ind_taddr_.resize(n_variable_ind_fun); CPPAD_ASSERT_UNKNOWN( n_variable_ind_fun < num_var_tape_); for(size_t j = 0; j < n_variable_ind_fun; j++) { CPPAD_ASSERT_UNKNOWN( play_.GetOp(j+1) == local::InvOp ); ind_taddr_[j] = j+1; } // // for_jac_sparse_pack_, for_jac_sparse_set_ for_jac_sparse_pack_.resize(0, 0); for_jac_sparse_set_.resize(0,0); // // resize subgraph_info_ subgraph_info_.resize( ind_taddr_.size(), // n_dep dep_taddr_.size(), // n_ind play_.num_var_op(), // n_op play_.num_var() // n_var ); // // set the function name function_name_ = function_name; // return; } // BEGIN_ONE_ARGUMENT template void CppAD::ADFun::from_graph( const CppAD::cpp_graph& graph_obj ) // END_ONE_ARGUMENT { size_t n_variable_ind = graph_obj.n_variable_ind_get(); size_t n_dynamic_ind = graph_obj.n_dynamic_ind_get(); CppAD::vector dyn2var(n_dynamic_ind), var2dyn(n_variable_ind); for(size_t j = 0; j < n_dynamic_ind; ++j) dyn2var[j] = false; for(size_t j = 0; j < n_variable_ind; ++j) var2dyn[j] = false; // from_graph(graph_obj, dyn2var, var2dyn); } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/graph/from_json.hpp ================================================ # ifndef CPPAD_CORE_GRAPH_FROM_JSON_HPP # define CPPAD_CORE_GRAPH_FROM_JSON_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include /* {xrst_begin from_json} ADFun Object Corresponding to a Json AD Graph ############################################# Syntax ****** | |tab| ``ADFun`` < *Base* > *fun* | |tab| *fun* . ``from_json`` ( *json* ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } json **** is a :ref:`json_ad_graph-name` . Base **** is the type corresponding to this :ref:`adfun-name` object; i.e., its calculations are done using the type *Base* . RecBase ******* in the prototype above, *RecBase* is the same type as *Base* . {xrst_toc_hidden example/json/from_json.cpp } Example ******* The file :ref:`from_json.cpp-name` is an example and test of this operation. {xrst_end from_json} */ // BEGIN_PROTOTYPE template void CppAD::ADFun::from_json(const std::string& json) // END_PROTOTYPE { using CppAD::isnan; // // // C++ graph object cpp_graph graph_obj; // // convert json to graph representation local::graph::json_parser(json, graph_obj); // // convert the graph representation to a function from_graph(graph_obj); // return; } # endif ================================================ FILE: include/cppad/core/graph/graph_op_enum.hpp ================================================ # ifndef CPPAD_CORE_GRAPH_GRAPH_OP_ENUM_HPP # define CPPAD_CORE_GRAPH_GRAPH_OP_ENUM_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include /* {xrst_begin graph_op_enum} C++ AD Graph Operator Enum Type ############################### {xrst_comment The following headings are referenced by comments in cpp_graph_op.cpp: Atomic Function, Comparison, Discrete Function, Print, Summation } Unary ***** The unary operators have one argument and one result node. The argument is a node index and the result is the next node. Binary ****** The binary operators have two arguments and one result node. The arguments are node indices and the result is the next node. The first (second) argument is the left (right) operand node index. Conditional Expression ********************** The conditional expression operators have four arguments and one result node. The arguments are node indices and the result is the next node. The first argument is :ref:`CondExp@left` , the second is :ref:`CondExp@right` , the third is :ref:`CondExp@if_true` , the fourth is :ref:`CondExp@if_false` , the result is given by | |tab| ``if`` ( *left* *cop* *right* ) | |tab| |tab| *result* = *if_true* ; | |tab| ``else`` | |tab| |tab| *result* = *if_false* ; where *cop* is given in the comment after the enum type values below. Other Comparisons ================= Note that ``CondExpGt`` ( *left* , *right* , *if_true* , *if_false* ) is equivalent to ``CondExpLe`` ( *left* , *right* , *if_false* , *if_true* ) Similar conversions can be used for all the possible :ref:`conditional expressions` . Comparison ********** The comparison operators have two arguments and no result node. The first (second) argument is the left (right) operand node index. The comparison result was true for the value of the independent dynamic parameters and independent variables at which this graph was created. Other Comparisons ================= The comparison result true for *left* > *right* is equivalent to the comparison result true for *right* < *left* . The comparison result false for *left* > *right* is equivalent to the comparison result true for *left* <= *right* . In a similar fashion, all the possible comparisons results can be converted to a true result for one of the comparisons above. Summation ********* The summation operator has one node result and a variable number of arguments. The first argument is the number of nodes in the summation, and the other arguments are the indices of the nodes to be summed. The total number of arguments for this operator is one plus the number of nodes in the summation. Discrete Function ***************** The discrete function operator has two arguments and one node result. The first argument is the index in :ref:`cpp_ad_graph@discrete_name_vec` for the :ref:`discrete@name` of the discrete function that is called. The second argument is the index of the node that is the argument to the discrete function. Atomic Function *************** The atomic function operator has a variable number of arguments and a variable number of result nodes. There are three extra arguments for :ref:`atomic_three-name` functions and four extra arguments for :ref:`atomic_four-name` functions. The total number of operator arguments is the number of extra arguments plus the number of arguments for the function being called. The extra arguments come before the function arguments. #. The first operator argument is function name represented by it's index in the :ref:`cpp_ad_graph@atomic_name_vec` . #. If this is an atomic four function call, the second operator argument is the :ref:`atomic_four_call@call_id` . #. In the atomic three (atomic four) case, second (third) operator argument is the number of results for this function call. The order of the function results is determined by the function being called. #. In the atomic three (atomic four) case, the third (fourth) operator argument is the number of arguments for this function call. #. The rest of the operator arguments are the node indices for each of the function arguments. The order of the function arguments is determined by function being called. Print ***** The print operator has four arguments. #. The first argument is the index in :ref:`cpp_ad_graph@print_text_vec` for the :ref:`PrintFor@before` text for this print operator. #. The second argument is the index in :ref:`cpp_ad_graph@print_text_vec` for the :ref:`PrintFor@after` text for this print operator. #. The third argument is the node corresponding to :ref:`PrintFor@notpos` for this print operator. #. The fourth argument is the node corresponding to :ref:`PrintFor@value` for this print operator. Missing Operators ***************** As of yet the following :ref:`ADFun-name` operators do not have a corresponding graph operator: #. Operators to load and store :ref:`VecAD-name` elements. #. An operator for the :ref:`atomic_two-name` interface. Enum Values *********** {xrst_literal SORT_THIS_LINE_PLUS_3 SORT_THIS_LINE_MINUS_4 } Examples ******** Contents ******** {xrst_toc_table example/graph/azmul_op.cpp example/graph/add_op.cpp example/graph/div_op.cpp example/graph/mul_op.cpp example/graph/pow_op.cpp example/graph/sub_op.cpp example/graph/unary_op.cpp example/graph/sum_op.cpp example/graph/comp_op.cpp example/graph/cexp_op.cpp example/graph/discrete_op.cpp example/graph/atom_op.cpp example/graph/atom4_op.cpp example/graph/print_op.cpp } {xrst_end graph_op_enum} */ // BEGIN_SORT_THIS_LINE_PLUS_3 namespace CppAD { namespace graph { enum graph_op_enum { abs_graph_op, // unary: absolute value acos_graph_op, // unary: inverse cosine acosh_graph_op, // unary: inverse hyperbolic cosine add_graph_op, // binary: addition asin_graph_op, // unary: inverse sine asinh_graph_op, // unary: inverse hyperbolic sine atan_graph_op, // unary: inverse tangent atanh_graph_op, // unary: inverse hyperbolic tangent atom4_graph_op, // atomic four function call atom_graph_op, // atomic three function call azmul_graph_op, // binary: absolute zero multiplication cexp_eq_graph_op, // conditional expression: == cexp_le_graph_op, // conditional expression: <= cexp_lt_graph_op, // conditional expression: < comp_eq_graph_op, // comparison: == comp_le_graph_op, // comparison: <= comp_lt_graph_op, // comparison: < comp_ne_graph_op, // comparison: != cos_graph_op, // unary: cosine cosh_graph_op, // unary: hyperbolic cosine discrete_graph_op, // discrete function div_graph_op, // binary: division erf_graph_op, // unary: error function erfc_graph_op, // unary: complementary error function exp_graph_op, // unary: exponential expm1_graph_op, // unary: exponential minus one log1p_graph_op, // unary: logarithm of one plus argument log_graph_op, // unary: logarithm mul_graph_op, // binary: multiplication neg_graph_op, // unary: minus pow_graph_op, // binary: first argument raised to second argument print_graph_op, // print during zero order forward sign_graph_op, // unary: sign of argument sin_graph_op, // unary: sine sinh_graph_op, // unary: hyperbolic sine sqrt_graph_op, // unary: square root sub_graph_op, // binary: subtraction sum_graph_op, // summation tan_graph_op, // unary: tangent tanh_graph_op, // unary: hyperbolic tangent n_graph_op // number of graph_op_enum operators }; } } // END_SORT_THIS_LINE_MINUS_4 # endif ================================================ FILE: include/cppad/core/graph/json_ad_graph.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin json_ad_graph} {xrst_spell nx str } Json Representation of an AD Graph ################################## See Also ******** :ref:`cpp_ad_graph-title`. Node Indices ************ The nodes in an AD graph have the following order: :: p_0 , ... , p_{np-1} , x_0 , ... , x_{nx-1} , c_0 , ... , c_{nc-1} , r_0 , ... , r_{no-1} p = The sub-vector :: p_0, ... , p_{np-1} is the independent dynamic parameter vector; see :ref:`json_ad_graph@dynamic_ind_vec@n_dynamic_ind` . The node index corresponding to *p_0* is ``1`` . x = The sub-vector :: x_0, ... , x_{nx-1} is the independent variable vector; see :ref:`json_ad_graph@variable_ind_vec@n_variable_ind` . The node index corresponding to *x_0* is the index corresponding to *p_0* plus *np* . c = The sub-vector :: c_0, ... , c_{nc-1} is the constant parameter vector; see :ref:`json_ad_graph@constant_vec` . The node index corresponding to *c_0* is the index corresponding to *x_0* plus *nx* . r_i === For *i* =0,..., *no* ``-1`` the sub-vector *r_i* is the result vector for the *i*-th operator usage; see :ref:`json_ad_graph@op_usage_vec` . The value *no* is the number of operator usages; see :ref:`json_ad_graph@op_usage_vec@n_usage` below. All of the arguments for an the *i*-th operator are nodes that come before the first element of *r_i* . The node index corresponding to the first element of *r_0* is the index corresponding to *c_0* plus *nc* . For *i* > 0 , The node index corresponding to the first element of *r_i* is the index corresponding to the first element of *r_* { *i-1* } plus the number of results for the *i-1*-th operator. Format ****** A complete description of the format for an AD graph is given below. For a general description of Json format see `Json Data types `_. Token ***** White Space =========== Any sequence of white space, except within a string, terminates the current token and is otherwise ignored. Non-Negative Integer ==================== A non-negative integer is a non-empty sequence of the following characters: ``0123456789`` . Floating Point Number ===================== A floating point number is a non-empty sequence of the following characters: ``0123456789+-eE.`` . Note that there can't be any white space between a leading plus or minus sign and the rest of the number. String ====== A string starts with the double quote character ``"`` and includes all the characters until the next double quote. The value of a string is the sequence of characters between the double quotes. Currently there is no support using the double quote as part of the value of a string. Single Character ================ The following is a list of the single character tokens: .. list-table:: :widths: auto * - Token - Usage * - ``,`` - separates entries in a list * - ``:`` - separates name from corresponding value * - ``{`` - starts a list of name ``:`` value pairs * - ``}`` - ends a list of name ``:`` value pairs * - ``[`` - starts a list of values * - ``]`` - ends a list of values op_define_vec ************* This vector has the following Json format: [ *n_define* , [ *first_op_define* , ..., *last_op_define* ] ] where the non-negative integer *n_define* is the number of operator definitions in this vector. op_define ========= The possible operator definitions *op_define* are listed in section :ref:`json_graph_op-name` . If an operator has a fixed number of arguments, one result, and only node indices for arguments, its definition has the form | { | |tab| ``"op_code"`` : *op_code* , | |tab| ``"name"`` : *name* , | |tab| ``"n_arg"`` : *n_arg* | } Otherwise the operator definition has the form | { | |tab| ``"op_code"`` : *op_code* , | |tab| ``"name"`` : *name* | } For example, the following is the *op_define* corresponding to the :ref:`json_graph_op@Binary Operators@add` operator: | { | |tab| ``"op_code"`` : *op_code* , | |tab| ``"name"`` : ``"add"`` , | |tab| ``"n_arg"`` : 2 | } op_code ======= Note that every operator definition has a *op_code* value. These values must start at one and increment by one for each operator definition; i.e., the *op_code* for in *first_op_define* is ``1`` , the value in the next definition is ``2`` , and so on. The order of the definitions determines the *op_code* values used to specify operators for this computational graph. n_arg ===== This is the number of argument values for the operator; i.e., the dimension of its domain space. If it is present in an operator definition, it is the same value for every usage of the operator. Otherwise it is specified by the operator usage. n_result ======== This is the number of results for the operator; i.e., the dimension of its range space. If *n_arg* is present in an operator definition, *n_result* is one for every usage of the operator. Otherwise it is specified by the operator usage. op_usage ******** Each operation usage has the following information: n_arg In Definition =================== If *n_arg* is present in an operator definition, it is not in a corresponding *op_usage* which as the form [ *op_code* , *first_arg* , ..., *last_arg* ] n_arg Not In Definition ======================= If *n_arg* is not in an operator definition, it is in a corresponding *op_usage* . If there are no strings in a corresponding usage, it has the form [ *op_code* , *n_result* , *n_arg* , [ *first_arg* , ..., *last_arg* ] ] Strings In Usage ================ If *n_arg* is not in an operator definition, and there are strings in a corresponding usage, a corresponding usage has the form | |tab| [ *op_code* , *first_str* , ..., *last_str* , *n_result* , *n_arg* , | |tab| |tab| [ *first_arg* , ..., *last_arg* ] | |tab| ] where *first_str* ..., *last_str* , are a fixed number of strings that are part of the corresponding operator. first_arg, ..., last_arg ======================== The values *first_arg* , ..., *last_arg* , are the node indices corresponding to each of the arguments for this operator. They must be less than the node index corresponding to the first result for this operator; see :ref:`json_ad_graph@Node Indices@r_i` above. They specify which previous results (results before this operator in the graph) correspond to each of the arguments to this operator. As a consequence, there cannot be any cycles in the graph where the operators are represented by arcs from the argument to the result nodes; i.e., the graph is acyclic. dynamic_ind_vec *************** This is the independent dynamic parameter vector (called *p* above); see :ref:`Independent@dynamic` . The function can depend on these parameters, but no derivatives are computed with respect to these parameters. n_dynamic_ind ============= We use the non-negative integer *n_dynamic_ind* for the number of elements in this vector (called *np* above). variable_ind_vec **************** This is the independent variable vector (called *x* above); see :ref:`Independent@x` . The function can depend on these variable and derivatives can be computed with respect to these variables. n_variable_ind ============== We use the non-negative integer *n_variable_ind* for the number of element in this vector (called *nx* above). constant_vec ************ This is the constant parameter vector (called *c* above). These parameters can be used to define the function and cannot change. The Json format for *constant_vec* is [ *n_constant* , [ *first_constant* , ..., *last_constant* ] ] Each of the elements of this vector, e.g., *first_constant* , is a :ref:`json_ad_graph@Token@Floating Point Number` specifying the value for the corresponding node. n_constant ========== The non-negative integer *n_constant* is the number of elements in this vector (called *nc* above). op_usage_vec ************ The Jason format for an *op_usage_vec* is [ *n_usage* , [ *first_op_usage* , ..., *last_op_usage* ] ] Each of the elements of this vector, e.g. *first_op_usage* , is an :ref:`json_ad_graph@op_usage` . n_usage ======= The non-negative integer *n_usage* is the number of operator usages (called *no* above). dependent_vec ************* This is the vector of dependent variable node indices. This identifies which nodes in the graph correspond to dependent variables. The Json format for *dependent_vec* is [ *n_dependent* , [ *first_dependent* , ..., *last_dependent* ] ] Each of the elements of this vector, e.g. *first_dependent* , is a :ref:`json_ad_graph@Token@Non-Negative Integer` corresponding to a node index in the graph. n_dependent =========== The non-negative integer *n_dependent* is the number of elements in this vector. AD Graph ******** Each operator corresponds to a set of arcs from its argument nodes to its result nodes. The graph is acyclic; see :ref:`json_ad_graph@op_usage@first_arg, ..., last_arg` . function_name ============= A *function_name* is a :ref:`json_ad_graph@Token@String` that is used to identify the function. function ======== The Json AD graph representation of a function is | { | |tab| ``"function_name"`` : *function_name* , | |tab| ``"op_define_vec"`` : *op_define_vec* , | |tab| ``"n_dynamic_ind"`` : *n_dynamic_ind* , | |tab| ``"n_variable_ind"`` : *n_variable_ind* , | |tab| ``"constant_vec"`` : *constant_vec* , | |tab| ``"op_usage_vec"`` : *op_usage_vec* , | |tab| ``"dependent_vec"`` : *dependent_vec* | } This represents a the :math:`y = f(x, p)` where :math:`p` is the dynamic parameter vector, :math:`x` is the independent variable vector, and :math:`y` is the dependent variable vector. Contents ******** {xrst_toc_table include/cppad/core/graph/json_graph_op.xrst include/cppad/core/graph/from_json.hpp include/cppad/core/graph/to_json.hpp example/json/get_started.cpp example/json/sparse.cpp } {xrst_end json_ad_graph} ================================================ FILE: include/cppad/core/graph/json_graph_op.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin json_graph_op} {xrst_spell acosh asinh chkpoint erfc expm neg notpos } Json AD Graph Operator Definitions ################################## Notation ******** op_code ======= Each operator definition has a *op_code* value that is used to identify it for a particular *json_ad_graph* . Arguments ========= The values *first_arg* , ... , :ref:`last_arg` are the node indices for arguments to an operator. Unary Operators *************** All these operations create one result node and have the following Json definition: | { | |tab| ``"op_code"`` : *op_code* , | |tab| ``"name"`` : *name* , | |tab| ``"n_arg"`` : 1 | } where *name* is a :ref:`json_ad_graph@Token@String` . A corresponding *op_usage* has the form [ *op_code* , *arg* ] The possible values for the string *name* are listed in the table below. The corresponding result is the node value as a function of the argument value. .. csv-table:: :widths: auto {xrst_comment BEGIN_SORT_THIS_LINE_PLUS_2} *name*,result ``abs``,absolute value ``acos``,inverse cosine ``acosh``,inverse hyperbolic cosine ``asin``,inverse sine ``asinh``,inverse hyperbolic sine ``atan``,inverse tangent ``atanh``,inverse hyperbolic sine ``cos``,cosine ``cosh``,hyperbolic cosine ``erf``,error functions ``erfc``,complementary error function ``exp``,exponential ``expm1``,minus one plus the exponential ``log1p``,log plus one ``log``,logarithm ``neg``,negative ``sign``,sign function ``sin``,sine ``sinh``,hyperbolic sine ``sqrt``,square root ``tan``,tangent ``tanh``,hyperbolic tangent {xrst_comment END_SORT_THIS_LINE_MINUS_1} Example ======= The file :ref:`json_unary_op.cpp-name` is an example and test for one of these operators. Binary Operators **************** All these operations create one result node and have the following Json definition: | { | |tab| ``"op_code"`` : *op_code* , | |tab| ``"name"`` : *name* , | |tab| ``"n_arg"`` : 2 | } where *name* is a :ref:`json_ad_graph@Token@String` . A corresponding *op_usage* has the form [ *op_code* , *first_arg* , *second_arg* ] The possible values for the string *name* are listed below: add === The result is the first argument value plus the second argument value; see the example and test :ref:`json_add_op.cpp-name` . azmul ===== If the first argument value is zero, the result is zero (even if the second argument value is nan). Otherwise the result is the first argument value times the second argument value; see the example and test :ref:`json_azmul_op.cpp-name` . div === The result is the first argument value divided by the second argument value; see the example and test :ref:`json_div_op.cpp-name` . mul === The result is the first argument value times the second argument value; see the example and test :ref:`json_mul_op.cpp-name` . pow === The result is the first argument value raised to the second argument value; see the example and test :ref:`json_pow_op.cpp-name` . sub === The result is the first argument value minus the second argument value; see the example and test :ref:`json_sub_op.cpp-name` . sum *** This operator has the following Json definition: | { | |tab| ``"op_code"`` : *op_code* , | |tab| ``"name"`` : ``"sum"`` | } A corresponding *op_usage* has the form [ *op_code* , *n_result* , *n_arg* , [ *first_arg* , ..., *last_arg* ] ] where *n_result* is always ``1`` . This operation creates one node with value equal to the sum of values corresponding to all of its argument nodes. Example ======= The file :ref:`json_sum_op.cpp-name` is an example and test of this operation. Conditional Expressions *********************** These operators are :ref:`conditional expressions` and have the following Json definition: | { | |tab| ``"op_code"`` : *op_code* , | |tab| ``"name"`` : " ``cexp_`` *rel* , | |tab| ``"n_arg"`` : 4 | } where *rel* is ``eq`` (equal), ``le`` (less than or equal), or ``lt`` (less than). The first argument is :ref:`CondExp@left` , the second is :ref:`CondExp@right` , the third is :ref:`CondExp@if_true` , the fourth is :ref:`CondExp@if_false` , the result is given by | |tab| ``if`` ( *left* *cop* *right* ) | |tab| |tab| *result* = *if_true* ; | |tab| ``else`` | |tab| |tab| *result* = *if_false* ; where the comparison *cop* is define by the cases below: cexp_eq ======= For this operator *cop* is ``==`` cexp_le ======= For this operator *cop* is ``<=`` cexp_lt ======= For this operator *cop* is ``<`` Other Comparisons ================= Note that ``CondExpGt`` ( *left* , *right* , *if_true* , *if_false* ) is equivalent to ``CondExpLe`` ( *left* , *right* , *if_false* , *if_true* ) Similar conversions can be used for all the possible :ref:`conditional expressions` . Example ======= The file :ref:`json_cexp_op.cpp-name` is an example and test for one of these operators. Compare Operators ***************** These are :ref:`comparison` operators and have the following Json definition: | { | |tab| ``"op_code"`` : *op_code* , | |tab| ``"name"`` : " ``comp_`` *rel* " | } where *rel* is ``eq`` (equal), ``ne`` (not equal), ``le`` (less than or equal), or ``lt`` (less than). A corresponding *op_usage* has the form [ *op_code* , *n_result* , *n_arg* , [ *left* , *right* ] ] n_result ======== This is always zero because a comparison operator does not create any new nodes. n_arg ===== This is always two because a comparison operator has two argument nodes corresponding to the left and right operands. left, right =========== The logical comparison is defined as the logical expression *left* *cop* *right* The comparison *cop* is define by the cases below. The Json graph corresponds to the comparison being true. If, for a value of the independent parameters and variables, the comparison is false, the Json graph may no longer be valid. For example, the Json graph may only contain the code for the true case below: | |tab| ``if`` ( *left* *cop* *right* ) | |tab| { *source code when result is true* } | |tab| ``else`` | |tab| { *source code when result is false* } Including this operator enables CppAD to detect when the graph may no longer be a valid representation of the intended function. comp_eq ======= For this operator *cop* is ``==`` comp_le ======= For this operator *cop* is ``<=`` comp_lt ======= For this operator *cop* is ``<`` comp_ne ======= For this operator *cop* is ``!=`` Other Comparisons ================= The comparison result true for *left* > *right* is equivalent to the comparison result true for *right* < *left* . The comparison result false for *left* > *right* is equivalent to the comparison result true for *left* <= *right* . In a similar fashion, all the possible comparisons results can be converted to a true result for one of the comparisons above. Example ======= The file :ref:`json_comp_op.cpp-name` is an example and test for one of these operators. Discrete Functions ****************** This operator has the following Json definition: | { | |tab| ``"op_code"`` : *op_code* , | |tab| ``"name"`` : ``"discrete"`` | } A corresponding op_usage has the form [ *op_code* , *name* , *n_result* , *n_arg* , [ *arg* ] ] name ==== The value *name* is a :ref:`json_ad_graph@Token@String` specifying the :ref:`discrete@name` of the discrete function that is called. n_result ======== This is always ``1`` because a discrete function creates one new node. The result node value is the specified discrete function of the argument value. n_arg ===== This is always ``1`` because a discrete function has one argument node. arg === is the node index for the argument to the discrete function. Example ======= the example and test :ref:`json_discrete_op.cpp-name` . Atomic Functions **************** These operators create *n_result* nodes with values determined by an evaluation of the an :ref:`atomic_three-name` or :ref:`atomic_four-name` function. Atomic Three ============ This operator has the following Json definition: | { | |tab| ``"op_code"`` : *op_code* , | |tab| ``"name"`` : ``"atom"`` | } A corresponding *op_usage* has the form | |tab| [ *op_code* , *name* , *n_result* , *n_arg* , | |tab| |tab| [ *first_arg* , ..., *last_arg* ] | |tab| ] Atomic Four =========== This operator has the following Json definition: | { | |tab| ``"op_code"`` : *op_code* , | |tab| ``"name"`` : ``"atom4"`` | } A corresponding *op_usage* has the form | |tab| [ *op_code* , *name* , *call_id* , *n_result* , *n_arg* , | |tab| |tab| [ *first_arg* , ..., *last_arg* ] | |tab| ] name ==== The value *name* is a :ref:`json_ad_graph@Token@String` specifying the :ref:`atomic_three_ctor@atomic_three@name` of the ``atomic_three`` function that is called. call_id ======= is a :ref:`json_ad_graph@Token@Non-Negative Integer` specifying the :ref:`atomic_four_call@call_id` for an atomic four function. n_result ======== is the number of results for this function; i.e., its range space dimension. n_arg ===== is the number of arguments to this function; i.e., its domain space dimension. first_arg, ..., last_arg ======================== The values corresponding to the node indices *first_arg* , ..., *last_arg* are the arguments (independent variables) for the atomic function evaluation. In the case where the atomic function is a ``chkpoint_two`` function, the independent dynamic parameters are specified by calling its :ref:`chkpoint_two@Syntax@new_dynamic` routine. Example ======= the example and test :ref:`json_atom_op.cpp-name` . Print ***** This operator has the following Json definition: | { | |tab| ``"op_code"`` : *op_code* , | |tab| ``"name"`` : ``"print"`` | } A corresponding *op_usage* has the form [ *op_code* , *before* , *after* , *n_result* , *n_arg* , [ *notpos* , *value* ] ] before ====== is a :ref:`json_ad_graph@Token@String` that is printed :ref:`PrintFor@before` the value for this operator. after ===== is a :ref:`json_ad_graph@Token@String` that is printed :ref:`PrintFor@after` the value for this operator. n_result ======== This is always zero because a print operator does not create any new nodes. n_arg ===== This is always two because a print operator has two argument nodes. notpos ====== This is :ref:`PrintFor@notpos` which determines if the value is printed. value ===== This is the :ref:`PrintFor@value` that is printed. Example ======= The file :ref:`json_print_op.cpp-name` is an example and test of this operator. Contents ******** {xrst_toc_table example/json/unary_op.cpp example/json/add_op.cpp example/json/azmul_op.cpp example/json/div_op.cpp example/json/mul_op.cpp example/json/pow_op.cpp example/json/sub_op.cpp example/json/sum_op.cpp example/json/cexp_op.cpp example/json/comp_op.cpp example/json/discrete_op.cpp example/json/atom_op.cpp example/json/atom4_op.cpp example/json/print_op.cpp } {xrst_end json_graph_op} ================================================ FILE: include/cppad/core/graph/to_graph.hpp ================================================ # ifndef CPPAD_CORE_GRAPH_TO_GRAPH_HPP # define CPPAD_CORE_GRAPH_TO_GRAPH_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include /* ------------------------------------------------------------------------------ {xrst_begin to_graph} Create a C++ AD Graph Corresponding to an ADFun Object ###################################################### Syntax ****** | |tab| ``cpp_graph`` *graph_obj* | |tab| ``ADFun`` < *Base* > *fun* | |tab| *fun* . ``to_graph`` ( *graph_obj* ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } Base **** is the type corresponding to this :ref:`adfun-name` object; i.e., its calculations are done using the type *Base* . RecBase ******* in the prototype above, *RecBase* is the same type as *Base* . graph_obj ********* This is a ``cpp_graph`` object. The input value of the object does not matter. Upon return it is a :ref:`cpp_ad_graph-name` representation of this function. Restrictions ************ The ``to_graph`` routine is not yet implement for some possible :ref:`ADFun-name` operators; see :ref:`graph_op_enum@Missing Operators` . Examples ******** See :ref:`graph_op_enum examples` . {xrst_end to_graph} */ // BEGIN_PROTOTYPE template void CppAD::ADFun::to_graph( CppAD::cpp_graph& graph_obj ) // END_PROTOTYPE { using local::pod_vector; using local::opcode_t; using namespace CppAD::graph; // # ifndef NDEBUG # endif graph_obj.initialize(); // // -------------------------------------------------------------------- // some constants // -------------------------------------------------------------------- // // output: function_name graph_obj.function_name_set(function_name_); // // dynamic parameter information const pod_vector& dyn_par_op ( play_.dyn_par_op() ); const pod_vector& dyn_par_arg( play_.dyn_par_arg() ); const pod_vector& dyn2par_index ( play_.dyn2par_index() ); const pod_vector& par_is_dyn( play_.par_is_dyn() ); // // number of dynamic parameters const size_t n_dynamic = dyn2par_index.size(); // // output: n_dynamic_ind size_t n_dynamic_ind = play_.n_dyn_independent(); graph_obj.n_dynamic_ind_set(n_dynamic_ind); // // number of parameters const size_t n_parameter = play_.num_par_all(); // // number of constant parameters # ifndef NDEBUG const size_t n_constant = n_parameter - n_dynamic - 1; # endif // // output: n_variable_ind size_t n_variable_ind = ind_taddr_.size(); graph_obj.n_variable_ind_set(n_variable_ind); // // value of parameters const Base* parameter = play_.par_ptr(); // // number of variables const size_t n_variable = play_.num_var(); // // some checks CPPAD_ASSERT_UNKNOWN( n_dynamic_ind <= n_dynamic ); CPPAD_ASSERT_UNKNOWN( par_is_dyn.size() == n_parameter ); CPPAD_ASSERT_UNKNOWN( n_parameter > 0 ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[0] ) ); CPPAD_ASSERT_UNKNOWN( ! par_is_dyn[0] ); // -------------------------------------------------------------------- // par2node pod_vector par2node(n_parameter); par2node[0] = 0; // invalid value for(size_t i = 1; i <= n_dynamic_ind; ++i) par2node[i] = i; // independent dynamic parameters for(size_t i = n_dynamic_ind + 1; i < n_parameter; ++i) par2node[i] = 0; // will be set later // ---------------------------------------------------------------------- // // initialize index of previous node in the graph size_t previous_node = 0; // // n_dynamic_ind previous_node += n_dynamic_ind; // // n_variable_ind previous_node += n_variable_ind; // -------------------------------------------------------------------- // // output: constant_vec // constant_vec and par2node for constants for(size_t i = 1; i < n_parameter; ++i) { if( ! par_is_dyn[i] ) { // this is a constant node graph_obj.constant_vec_push_back( parameter[i] ); par2node[i] = ++previous_node; } } CPPAD_ASSERT_UNKNOWN( n_constant == graph_obj.constant_vec_size() ); // ---------------------------------------------------------------------- // output: initialize atomic_name_vec, operator_vec, operator_arg // temporary used for elements of operator_vec // // Json operators are dynamic operators plus variables operators. // Skip BeginOp, EndOp, and independent variables. // // dynamic parameter operations and par2node // for dynamic parameters that are not constants or independent CPPAD_ASSERT_UNKNOWN( num_arg_dyn(local::ind_dyn) == 0 ); CPPAD_ASSERT_UNKNOWN( num_arg_dyn(local::atom_dyn) == 0 ); size_t i_arg = 0; pod_vector node_arg; for(size_t i_dyn = n_dynamic_ind; i_dyn < n_dynamic; ++i_dyn) { // operator for this dynamic parameter local::op_code_dyn dyn_op = local::op_code_dyn( dyn_par_op[i_dyn] ); // // parameter index for this dynamic parameter size_t i_par = size_t( dyn2par_index[i_dyn] ); CPPAD_ASSERT_UNKNOWN( par2node[i_par] == 0 ); par2node[i_par] = ++previous_node; // // number of arguments for operators with exception of atom_dyn size_t n_arg = num_arg_dyn(dyn_op); if( n_arg > node_arg.size() ) node_arg.resize(n_arg); // // parameter arguments in graph node space (except for atom_dyn) if( dyn_op != local::atom_dyn ) { size_t offset_par = num_non_par_arg_dyn(dyn_op); for(size_t i = offset_par; i < n_arg; ++i) { node_arg[i] = par2node[ dyn_par_arg[i_arg + i] ]; CPPAD_ASSERT_UNKNOWN( node_arg[i] > 0 ); } } // // invalid value graph_op_enum graph_op = n_graph_op; switch(dyn_op) { // --------------------------------------------------------------- // unary operators case local::abs_dyn: case local::fabs_dyn: graph_op = abs_graph_op; break; case local::acosh_dyn: graph_op = acosh_graph_op; break; case local::asinh_dyn: graph_op = asinh_graph_op; break; case local::atanh_dyn: graph_op = atanh_graph_op; break; case local::erf_dyn: graph_op = erf_graph_op; break; case local::erfc_dyn: graph_op = erfc_graph_op; break; case local::expm1_dyn: graph_op = expm1_graph_op; break; case local::log1p_dyn: graph_op = log1p_graph_op; break; case local::acos_dyn: graph_op = acos_graph_op; break; case local::asin_dyn: graph_op = asin_graph_op; break; case local::atan_dyn: graph_op = atan_graph_op; break; case local::cosh_dyn: graph_op = cosh_graph_op; break; case local::cos_dyn: graph_op = cos_graph_op; break; case local::exp_dyn: graph_op = exp_graph_op; break; case local::log_dyn: graph_op = log_graph_op; break; case local::neg_dyn: graph_op = neg_graph_op; break; case local::sign_dyn: graph_op = sign_graph_op; break; case local::sinh_dyn: graph_op = sinh_graph_op; break; case local::sin_dyn: graph_op = sin_graph_op; break; case local::sqrt_dyn: graph_op = sqrt_graph_op; break; case local::tanh_dyn: graph_op = tanh_graph_op; break; case local::tan_dyn: graph_op = tan_graph_op; break; // --------------------------------------------------------------- // binary operators case local::add_dyn: graph_op = add_graph_op; break; case local::div_dyn: graph_op = div_graph_op; break; case local::mul_dyn: graph_op = mul_graph_op; break; case local::pow_dyn: graph_op = pow_graph_op; break; case local::sub_dyn: graph_op = sub_graph_op; break; case local::zmul_dyn: graph_op = azmul_graph_op; break; // --------------------------------------------------------------- // graph_op determined later for these cases case local::atom_dyn: case local::cond_exp_dyn: case local::dis_dyn: case local::result_dyn: break; // --------------------------------------------------------------- default: // This error should have been reported above CPPAD_ASSERT_UNKNOWN( false ); break; } switch( dyn_op ) { // -------------------------------------------------------------- case local::result_dyn: // setting par2node[i_dyn] above is all that is necessary CPPAD_ASSERT_UNKNOWN( n_arg == 0 ); break; // -------------------------------------------------------------- case local::dis_dyn: { // arg[0]: discrete function index size_t discrete_index = size_t( dyn_par_arg[i_arg + 0] ); // get the name for this discrete function std::string name = discrete::name( discrete_index ); // // set graph index for this discrete function call size_t name_index = graph_obj.discrete_name_vec_find(name); if( name_index == graph_obj.discrete_name_vec_size() ) graph_obj.discrete_name_vec_push_back(name); // graph_op = discrete_graph_op; graph_obj.operator_vec_push_back( graph_op ); graph_obj.operator_arg_push_back( name_index ); graph_obj.operator_arg_push_back( node_arg[1] ); } break; // -------------------------------------------------------------- case local::atom_dyn: { // arg[0]: atomic function index size_t atom_index = size_t( dyn_par_arg[i_arg + 0] ); // // arg[1]: call_id size_t call_id = size_t( dyn_par_arg[i_arg + 1] ); // // arg[2]: number of arguments to function n_arg = size_t( dyn_par_arg[i_arg + 2] ); // arg[3]: number of results from function size_t n_result = size_t( dyn_par_arg[i_arg + 3] ); // // get the name and type for this atomic function std::string name; size_t type; { bool set_null = false; void* ptr; CppAD::local::atomic_index( set_null, atom_index, type, &name, ptr ); } // set graph index for this atomic function call size_t name_index = graph_obj.atomic_name_vec_find(name); if( name_index == graph_obj.atomic_name_vec_size() ) graph_obj.atomic_name_vec_push_back(name); // // atom_graph_op: // name_index, n_result, n_arg (before first_node) // // atom4_graph_op: // name_index, call_id, n_result, n_arg (before first_node) graph_obj.operator_arg_push_back(name_index); if( type == 4 ) graph_obj.operator_arg_push_back(call_id); graph_obj.operator_arg_push_back(n_result); graph_obj.operator_arg_push_back(n_arg); // if( type == 3 ) graph_op = atom_graph_op; else { CPPAD_ASSERT_UNKNOWN( type == 4 ); graph_op = atom4_graph_op; } // graph_obj.operator_vec_push_back( graph_op ); // for(size_t j = 0; j < n_arg; ++j) { // arg[5 + j] is j-th argument to the function size_t node_j = par2node[ dyn_par_arg[i_arg + 5 + j] ]; CPPAD_ASSERT_UNKNOWN( node_j < i_par ); graph_obj.operator_arg_push_back( node_j ); } } break; // -------------------------------------------------------------- case local::cond_exp_dyn: { CompareOp cop = CompareOp( dyn_par_arg[i_arg + 0] ); size_t left = node_arg[1]; size_t right = node_arg[2]; size_t if_true = node_arg[3]; size_t if_false = node_arg[4]; switch( cop ) { case CompareLt: graph_op = cexp_lt_graph_op; break; case CompareLe: graph_op = cexp_le_graph_op; break; case CompareEq: graph_op = cexp_eq_graph_op; break; case CompareGe: graph_op = cexp_lt_graph_op; std::swap(if_true, if_false); break; case CompareGt: graph_op = cexp_le_graph_op; std::swap(if_true, if_false); break; case CompareNe: graph_op = cexp_eq_graph_op; std::swap(if_true, if_false); break; default: CPPAD_ASSERT_UNKNOWN(false); break; } graph_obj.operator_vec_push_back( graph_op ); graph_obj.operator_arg_push_back( left ); graph_obj.operator_arg_push_back( right ); graph_obj.operator_arg_push_back( if_true ); graph_obj.operator_arg_push_back( if_false ); } break; // -------------------------------------------------------------- // unary or binary default: CPPAD_ASSERT_UNKNOWN((n_arg == 1) || (n_arg == 2)); // graph_obj.operator_vec_push_back( graph_op ); for(size_t i = 0; i < n_arg; ++i) graph_obj.operator_arg_push_back( node_arg[i] ); break; } i_arg += n_arg; } // ---------------------------------------------------------------------- // variable operators pod_vector var2node(n_variable); var2node[0] = 0; // invalid node value for(size_t i = 1; i <= n_variable_ind; ++i) var2node[i] = n_dynamic_ind + i; for(size_t i = n_variable_ind + 1; i < n_variable; ++i) var2node[i] = 0; // invalid node value // local::play::const_sequential_iterator itr = play_.begin(); local::op_code_var var_op; const addr_t* arg; size_t i_var; pod_vector is_var(2); vector atom_node_arg; bool in_atomic_call = false; bool more_operators = true; while(more_operators) { // if non-zero, this is a fixed size operator with this many arguments // and implemented after the switch. In additionk, is_var is set for // each of the at most 2 arguments. size_t fixed_n_arg = 0; // invalid value graph_op_enum graph_op = n_graph_op; // next op (++itr).op_info(var_op, arg, i_var); // ------------------------------------------------------------------- // Cases with fixed number of arguments, one or two arguments, and // operator is not ignored. // ------------------------------------------------------------------- switch( var_op ) { // 2DO: some of these cases can be joined // ------------------------------------------------------------- // unary operators case local::AbsOp: fixed_n_arg = 1; is_var[0] = true; break; case local::AcoshOp: fixed_n_arg = 1; is_var[0] = true; break; case local::AsinhOp: fixed_n_arg = 1; is_var[0] = true; break; case local::AtanhOp: fixed_n_arg = 1; is_var[0] = true; break; case local::ErfOp: fixed_n_arg = 1; is_var[0] = true; break; case local::ErfcOp: fixed_n_arg = 1; is_var[0] = true; break; case local::Expm1Op: fixed_n_arg = 1; is_var[0] = true; break; case local::Log1pOp: fixed_n_arg = 1; is_var[0] = true; break; case local::AcosOp: fixed_n_arg = 1; is_var[0] = true; break; case local::AsinOp: fixed_n_arg = 1; is_var[0] = true; break; case local::AtanOp: fixed_n_arg = 1; is_var[0] = true; break; case local::CoshOp: fixed_n_arg = 1; is_var[0] = true; break; case local::CosOp: fixed_n_arg = 1; is_var[0] = true; break; case local::ExpOp: fixed_n_arg = 1; is_var[0] = true; break; case local::LogOp: fixed_n_arg = 1; is_var[0] = true; break; case local::NegOp: fixed_n_arg = 1; is_var[0] = true; break; case local::SignOp: fixed_n_arg = 1; is_var[0] = true; break; case local::SinhOp: fixed_n_arg = 1; is_var[0] = true; break; case local::SinOp: fixed_n_arg = 1; is_var[0] = true; break; case local::SqrtOp: fixed_n_arg = 1; is_var[0] = true; break; case local::TanhOp: fixed_n_arg = 1; is_var[0] = true; break; case local::TanOp: fixed_n_arg = 1; is_var[0] = true; break; // --------------------------------------------------------------- // binary operators // --------------------------------------------------------------- // first argument a parameter, second argument a variable case local::AddpvOp: case local::DivpvOp: case local::MulpvOp: case local::PowpvOp: case local::SubpvOp: case local::ZmulpvOp: fixed_n_arg = 2; is_var[0] = false; is_var[1] = true; break; // first argument a variable, second argument a parameter case local::DivvpOp: case local::PowvpOp: case local::SubvpOp: case local::ZmulvpOp: fixed_n_arg = 2; is_var[0] = true; is_var[1] = false; break; // first argument a variable, second argument a variable case local::AddvvOp: case local::DivvvOp: case local::MulvvOp: case local::PowvvOp: case local::SubvvOp: case local::ZmulvvOp: fixed_n_arg = 2; is_var[0] = true; is_var[1] = true; break; // -------------------------------------------------------------- default: break; } if( fixed_n_arg > 0 ) { // Set graph_op switch( var_op ) { // ---------------------------------------------------------- // unary operators case local::AbsOp: graph_op = abs_graph_op; break; case local::AcoshOp: graph_op = acosh_graph_op; break; case local::AsinhOp: graph_op = asinh_graph_op; break; case local::AtanhOp: graph_op = atanh_graph_op; break; case local::ErfOp: graph_op = erf_graph_op; break; case local::ErfcOp: graph_op = erfc_graph_op; break; case local::Expm1Op: graph_op = expm1_graph_op; break; case local::Log1pOp: graph_op = log1p_graph_op; break; case local::AcosOp: graph_op = acos_graph_op; break; case local::AsinOp: graph_op = asin_graph_op; break; case local::AtanOp: graph_op = atan_graph_op; break; case local::CoshOp: graph_op = cosh_graph_op; break; case local::CosOp: graph_op = cos_graph_op; break; case local::ExpOp: graph_op = exp_graph_op; break; case local::LogOp: graph_op = log_graph_op; break; case local::NegOp: graph_op = neg_graph_op; break; case local::SignOp: graph_op = sign_graph_op; break; case local::SinhOp: graph_op = sinh_graph_op; break; case local::SinOp: graph_op = sin_graph_op; break; case local::SqrtOp: graph_op = sqrt_graph_op; break; case local::TanhOp: graph_op = tanh_graph_op; break; case local::TanOp: graph_op = tan_graph_op; break; // ----------------------------------------------------------- // binary operators case local::AddpvOp: case local::AddvvOp: graph_op = add_graph_op; break; case local::DivpvOp: case local::DivvpOp: case local::DivvvOp: graph_op = div_graph_op; break; case local::MulpvOp: case local::MulvvOp: graph_op = mul_graph_op; break; case local::PowpvOp: case local::PowvpOp: case local::PowvvOp: graph_op = pow_graph_op; break; case local::SubpvOp: case local::SubvpOp: case local::SubvvOp: graph_op = sub_graph_op; break; case local::ZmulpvOp: case local::ZmulvpOp: case local::ZmulvvOp: graph_op = azmul_graph_op; break; // ----------------------------------------------------------- default: // This should be one of the cases above CPPAD_ASSERT_UNKNOWN(false); break; } // // var2node and previous_node for this operator var2node[i_var] = ++previous_node; // // graph_obj.operator_vec_push_back( graph_op ); for(size_t i = 0; i < fixed_n_arg; ++i) { if( is_var[i] ) graph_obj.operator_arg_push_back( var2node[ arg[i] ] ); else graph_obj.operator_arg_push_back( par2node[ arg[i] ] ); } } // ------------------------------------------------------------------- // Other cases // ------------------------------------------------------------------- else switch( var_op ) { // ------------------------------------------------------------- // comparison operators case local::EqppOp: case local::EqpvOp: case local::EqvvOp: case local::NeppOp: case local::NepvOp: case local::NevvOp: case local::LtppOp: case local::LtpvOp: case local::LtvpOp: case local::LtvvOp: case local::LeppOp: case local::LepvOp: case local::LevpOp: case local::LevvOp: { // node_0, node_1 size_t node_0, node_1; switch( var_op ) { // both nodes parameters case local::EqppOp: case local::NeppOp: case local::LtppOp: case local::LeppOp: node_0 = par2node[arg[0]]; node_1 = par2node[arg[1]]; break; // first node parameter, second variable case local::EqpvOp: case local::NepvOp: case local::LtpvOp: case local::LepvOp: node_0 = par2node[arg[0]]; node_1 = var2node[arg[1]]; break; // first node variable, second parameter case local::LtvpOp: case local::LevpOp: node_0 = var2node[arg[0]]; node_1 = par2node[arg[1]]; break; // both nodes variables case local::EqvvOp: case local::NevvOp: case local::LtvvOp: case local::LevvOp: node_0 = var2node[arg[0]]; node_1 = var2node[arg[1]]; break; // should never get here default: CPPAD_ASSERT_UNKNOWN(false); node_0 = 0; // to avoid compiler warning node_1 = 0; break; } // Set graph_op switch( var_op ) { case local::EqppOp: case local::EqpvOp: case local::EqvvOp: graph_op = comp_eq_graph_op; break; case local::NeppOp: case local::NepvOp: case local::NevvOp: graph_op = comp_ne_graph_op; break; case local::LtppOp: case local::LtpvOp: case local::LtvpOp: case local::LtvvOp: graph_op = comp_lt_graph_op; break; case local::LeppOp: case local::LepvOp: case local::LevpOp: case local::LevvOp: graph_op = comp_le_graph_op; break; // should never get here default: CPPAD_ASSERT_UNKNOWN(false); graph_op = n_graph_op; // invalid values break; } graph_obj.operator_vec_push_back( graph_op ); graph_obj.operator_arg_push_back( node_0 ); graph_obj.operator_arg_push_back( node_1 ); } break; // -------------------------------------------------------------- // CSumOp case local::CSumOp: { // does this case have subtraction terms bool has_subtract = (arg[1] != arg[2]) || (arg[3] != arg[4]); // // var2node for this operator if( has_subtract ) { // two cumulative sum and one subtract operators var2node[i_var] = previous_node + 3; } else { // one cumulative sum operator var2node[i_var] = previous_node + 1; } // // previous_node + 1 = sum corresponding to addition terms // graph_op = sum_graph_op; CPPAD_ASSERT_UNKNOWN( 5 <= arg[1] ); CPPAD_ASSERT_UNKNOWN( arg[2] <= arg[3] ); size_t n_arg = size_t(1 + arg[1] - 5 + arg[3] - arg[2]); // // n_arg comes befrore first_node graph_obj.operator_arg_push_back(n_arg); // // graph_op for addition terms graph_obj.operator_vec_push_back( graph_op ); // // argument nodes size_t arg_node = par2node[ arg[0] ]; graph_obj.operator_arg_push_back( arg_node ); # ifndef NDEBUG size_t j_arg = 1; # endif for(addr_t i = 5; i < arg[1]; ++i) { arg_node = var2node[ arg[i] ]; CPPAD_ASSERT_UNKNOWN( arg_node > 0 ); graph_obj.operator_arg_push_back( arg_node ); # ifndef NDEBUG ++j_arg; # endif } for(addr_t i = arg[2]; i < arg[3]; ++i) { arg_node = par2node[ arg[i] ]; CPPAD_ASSERT_UNKNOWN( arg_node > 0 ); graph_obj.operator_arg_push_back( arg_node ); # ifndef NDEBUG ++j_arg; # endif } CPPAD_ASSERT_UNKNOWN( j_arg == n_arg ); if( has_subtract ) { // previous_node + 2 = sum corresponding to subtract terms CPPAD_ASSERT_UNKNOWN( arg[1] <= arg[2] ); CPPAD_ASSERT_UNKNOWN( arg[3] <= arg[4] ); n_arg = size_t(arg[2] - arg[1] + arg[4] - arg[3]); // // n_arg comes before first_node graph_obj.operator_arg_push_back(n_arg); // // graph_op for subtraction terms graph_op = sum_graph_op; graph_obj.operator_vec_push_back( graph_op ); // // argument nodes # ifndef NDEBUG j_arg = 0; # endif for(addr_t i = arg[1]; i < arg[2]; ++i) { arg_node = var2node[ arg[i] ]; CPPAD_ASSERT_UNKNOWN( arg_node > 0 ); graph_obj.operator_arg_push_back( arg_node ); # ifndef NDEBUG ++j_arg; # endif } for(addr_t i = arg[3]; i < arg[4]; ++i) { arg_node = par2node[ arg[i] ]; CPPAD_ASSERT_UNKNOWN( arg_node > 0 ); graph_obj.operator_arg_push_back( arg_node ); # ifndef NDEBUG ++j_arg; # endif } CPPAD_ASSERT_UNKNOWN( j_arg == n_arg ); // // previous_node + 3 = first sum minus second sum graph_op = sub_graph_op; graph_obj.operator_vec_push_back( graph_op ); graph_obj.operator_arg_push_back( previous_node + 1 ); graph_obj.operator_arg_push_back( previous_node + 2 ); } // previous node if( has_subtract ) previous_node += 3; else previous_node += 1; } itr.correct_before_increment(); break; // -------------------------------------------------------------- case local::DisOp: { // discrete function index size_t discrete_index = size_t( arg[0] ); // name of this discrete function std::string name = discrete::name( discrete_index ); // // set graph index for this discrete function call size_t name_index = graph_obj.discrete_name_vec_find(name); if( name_index == graph_obj.discrete_name_vec_size() ) graph_obj.discrete_name_vec_push_back(name); // graph_op = discrete_graph_op; graph_obj.operator_vec_push_back( graph_op ); graph_obj.operator_arg_push_back( name_index ); graph_obj.operator_arg_push_back( var2node[arg[1]] ); // var2node[i_var] = ++previous_node; } break; // -------------------------------------------------------------- case local::PriOp: { // before std::string before( play_.GetTxt( size_t(arg[2]) ) ); size_t before_index = graph_obj.print_text_vec_find(before); if( before_index == graph_obj.print_text_vec_size() ) graph_obj.print_text_vec_push_back(before); // after std::string after( play_.GetTxt( size_t(arg[4]) ) ); size_t after_index = graph_obj.print_text_vec_find(after); if( after_index == graph_obj.print_text_vec_size() ) graph_obj.print_text_vec_push_back(after); // notpos size_t notpos_node; if( arg[0] & 1 ) notpos_node = var2node[ arg[1] ]; else notpos_node = par2node[ arg[1] ]; // value size_t value_node; if( arg[0] & 1 ) value_node = var2node[ arg[3] ]; else value_node = par2node[ arg[3] ]; // graph_op = print_graph_op; graph_obj.operator_vec_push_back( graph_op ); graph_obj.operator_arg_push_back( before_index ); graph_obj.operator_arg_push_back( after_index ); graph_obj.operator_arg_push_back( notpos_node ); graph_obj.operator_arg_push_back( value_node ); } break; // -------------------------------------------------------------- case local::FunapOp: atom_node_arg.push_back( par2node[arg[0]] ); break; case local::FunavOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) <= i_var ); atom_node_arg.push_back( var2node[arg[0]] ); break; case local::FunrpOp: par2node[arg[0]] = ++previous_node; break; case local::FunrvOp: var2node[i_var] = ++previous_node; break; case local::AFunOp: in_atomic_call = ! in_atomic_call; if( in_atomic_call ) { atom_node_arg.resize(0); } else { // This is the AFunOp at the end of the call size_t atom_index = size_t( arg[0] ); // size_t call_id = size_t( arg[1] ); size_t n_arg = size_t( arg[2] ); size_t n_result = size_t( arg[3] ); CPPAD_ASSERT_UNKNOWN( atom_node_arg.size() == n_arg ); // // get the name and type for this atomic function std::string name; size_t type; { bool set_null = false; void* ptr; CppAD::local::atomic_index( set_null, atom_index, type, &name, ptr ); } // set graph index for this atomic function size_t name_index = graph_obj.atomic_name_vec_find(name); if( name_index == graph_obj.atomic_name_vec_size() ) graph_obj.atomic_name_vec_push_back(name); // // for atom_graph_op: // name_index, n_result, n_arg (before first_node) // // for atom4_graph_op: // name_index, call_id, n_result, n_arg (before first_node) graph_obj.operator_arg_push_back(name_index); if( type == 4 ) graph_obj.operator_arg_push_back(call_id); graph_obj.operator_arg_push_back(n_result); graph_obj.operator_arg_push_back(n_arg); if( type == 3 ) graph_op = atom_graph_op; else { CPPAD_ASSERT_UNKNOWN( type == 4 ); graph_op = atom4_graph_op; } graph_obj.operator_vec_push_back( graph_op ); for(size_t i = 0; i < n_arg; ++i) graph_obj.operator_arg_push_back( atom_node_arg[i] ); } break; // -------------------------------------------------------------- // CExpOp: case local::CExpOp: { CompareOp cop = CompareOp( arg[0] ); size_t left, right, if_true, if_false; if( arg[1] & 1 ) left = var2node[ arg[2] ]; else left = par2node[ arg[2] ]; if( arg[1] & 2 ) right = var2node[ arg[3] ]; else right = par2node[ arg[3] ]; if( arg[1] & 4 ) if_true = var2node[ arg[4] ]; else if_true = par2node[ arg[4] ]; if( arg[1] & 8 ) if_false = var2node[ arg[5] ]; else if_false = par2node[ arg[5] ]; switch( cop ) { case CompareLt: graph_op = cexp_lt_graph_op; break; case CompareLe: graph_op = cexp_le_graph_op; break; case CompareEq: graph_op = cexp_eq_graph_op; break; case CompareGe: graph_op = cexp_lt_graph_op; std::swap(if_true, if_false); break; case CompareGt: graph_op = cexp_le_graph_op; std::swap(if_true, if_false); break; case CompareNe: graph_op = cexp_eq_graph_op; std::swap(if_true, if_false); break; default: CPPAD_ASSERT_UNKNOWN(false); break; } // var2node and previous_node for this operator var2node[i_var] = ++previous_node; // graph_obj.operator_vec_push_back( graph_op ); graph_obj.operator_arg_push_back( left ); graph_obj.operator_arg_push_back( right ); graph_obj.operator_arg_push_back( if_true ); graph_obj.operator_arg_push_back( if_false ); } break; // -------------------------------------------------------------- // EndOp: case local::EndOp: more_operators = false; break; // -------------------------------------------------------------- // InvOp: independent variables case local::InvOp: // no graph operators for independent variables break; // -------------------------------------------------------------- // ParOp: case local::ParOp: // no need for a graph operator, just map variable to parameter var2node[i_var] = par2node[arg[0]]; break; // -------------------------------------------------------------- default: // This error should have been reported above CPPAD_ASSERT_UNKNOWN(false); break; } } // ---------------------------------------------------------------------- // output: dependent_vec size_t n_dependent = dep_taddr_.size(); for(size_t i = 0; i < n_dependent; ++i) graph_obj.dependent_vec_push_back( var2node[ dep_taddr_[i] ] ); // return; } # endif ================================================ FILE: include/cppad/core/graph/to_json.hpp ================================================ # ifndef CPPAD_CORE_GRAPH_TO_JSON_HPP # define CPPAD_CORE_GRAPH_TO_JSON_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include /* ------------------------------------------------------------------------------ {xrst_begin to_json} Json AD Graph Corresponding to an ADFun Object ############################################## Syntax ****** | *json* = *fun* . ``to_json`` () Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } fun *** is the :ref:`adfun-name` object. json **** The return value of *json* is a :ref:`json_ad_graph-name` representation of the corresponding function. Base **** is the type corresponding to this :ref:`adfun-name` object; i.e., its calculations are done using the type *Base* . RecBase ******* in the prototype above, *RecBase* is the same type as *Base* . Restrictions ************ The ``to_json`` routine is not yet implement for some possible :ref:`ADFun-name` operators; see :ref:`graph_op_enum@Missing Operators` . {xrst_toc_hidden example/json/to_json.cpp } Example ******* The file :ref:`to_json.cpp-name` is an example and test of this operation. {xrst_end to_json} */ // BEGIN_PROTOTYPE template std::string CppAD::ADFun::to_json(void) // END_PROTOTYPE { // // to_graph return values cpp_graph graph_obj; // // graph corresponding to this function to_graph(graph_obj); // // convert to json std::string json; local::graph::json_writer(json, graph_obj); // return json; } # endif ================================================ FILE: include/cppad/core/hash_code.hpp ================================================ # ifndef CPPAD_CORE_HASH_CODE_HPP # define CPPAD_CORE_HASH_CODE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /*! \file core/hash_code.hpp CppAD hashing utility. */ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! General purpose hash code for an arbitrary value. \tparam Value is the type of the argument being hash coded. It should be a plain old data class; i.e., the values included in the equality operator in the object and not pointed to by the object. \param value the value that we are generating a hash code for. All of the fields in value should have been set before the hash code is computed (otherwise undefined values are used). \return is a hash code that is between zero and CPPAD_HASH_TABLE_SIZE - 1. \par Checked Assertions \li std::numeric_limits::max() >= CPPAD_HASH_TABLE_SIZE \li sizeof(value) is even \li sizeof(unsigned short) == 2 */ template unsigned short hash_code(const Value& value) { return local::local_hash_code(value); } /*! hash code for an AD object. \tparam Base is the base type for this AD value. \param u the AD value that we are generating a hash code for. \return is a hash code that is between zero and CPPAD_HASH_TABLE_SIZE - 1. */ template unsigned short hash_code(const AD& u) { size_t code = hash_code(u.value_); code += size_t(u.taddr_); code += size_t(u.ad_type_ == dynamic_enum); return (unsigned short)(code % CPPAD_HASH_TABLE_SIZE); } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/hessian.hpp ================================================ # ifndef CPPAD_CORE_HESSIAN_HPP # define CPPAD_CORE_HESSIAN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin Hessian} Hessian: Easy Driver #################### Syntax ****** | *hes* = *f* . ``Hessian`` ( *x* , *w* ) | *hes* = *f* . ``Hessian`` ( *x* , *l* ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* . The syntax above sets *hes* to the Hessian The syntax above sets *h* to the Hessian .. math:: hes = \dpow{2}{x} \sum_{i=1}^m w_i F_i (x) The routine :ref:`sparse_hessian-name` may be faster in the case where the Hessian is sparse. f * The object *f* has prototype ``ADFun`` < *Base* > *f* Note that the :ref:`ADFun-name` object *f* is not ``const`` (see :ref:`Hessian@Hessian Uses Forward` below). x * The argument *x* has prototype ``const`` *Vector* & *x* (see :ref:`Hessian@Vector` below) and its size must be equal to *n* , the dimension of the :ref:`fun_property@Domain` space for *f* . It specifies that point at which to evaluate the Hessian. l * If the argument *l* is present, it has prototype ``size_t`` *l* and is less than *m* , the dimension of the :ref:`fun_property@Range` space for *f* . It specifies the component of *F* for which we are evaluating the Hessian. To be specific, in the case where the argument *l* is present, .. math:: w_i = \left\{ \begin{array}{ll} 1 & i = l \\ 0 & {\rm otherwise} \end{array} \right. w * If the argument *w* is present, it has prototype ``const`` *Vector* & *w* and size :math:`m`. It specifies the value of :math:`w_i` in the expression for *h* . hes *** The result *hes* has prototype *Vector* *hes* (see :ref:`Hessian@Vector` below) and its size is :math:`n * n`. For :math:`j = 0 , \ldots , n - 1` and :math:`\ell = 0 , \ldots , n - 1` .. math:: hes [ j * n + \ell ] = \DD{ w^{\rm T} F }{ x_j }{ x_\ell } ( x ) Vector ****** The type *Vector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` *Base* . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Hessian Uses Forward ******************** After each call to :ref:`Forward-name` , the object *f* contains the corresponding :ref:`Taylor coefficients` . After a call to ``Hessian`` , the zero order Taylor coefficients correspond to *f* . ``Forward`` (0, *x* ) and the other coefficients are unspecified. Example ******* {xrst_toc_hidden example/general/hessian.cpp example/general/hes_lagrangian.cpp } The routines :ref:`hessian.cpp-name` and :ref:`hes_lagrangian.cpp-name` are examples and tests of ``Hessian`` . They return ``true`` , if they succeed and ``false`` otherwise. {xrst_end Hessian} ----------------------------------------------------------------------------- */ // BEGIN CppAD namespace namespace CppAD { template template Vector ADFun::Hessian(const Vector &x, size_t l) { size_t i, m = Range(); CPPAD_ASSERT_KNOWN( l < m, "Hessian: index i is not less than range dimension for f" ); Vector w(m); for(i = 0; i < m; i++) w[i] = Base(0.0); w[l] = Base(1.0); return Hessian(x, w); } template template Vector ADFun::Hessian(const Vector &x, const Vector &w) { size_t j; size_t k; size_t n = Domain(); // check Vector is Simple Vector class with Base type elements CheckSimpleVector(); CPPAD_ASSERT_KNOWN( size_t(x.size()) == n, "Hessian: length of x not equal domain dimension for f" ); CPPAD_ASSERT_KNOWN( size_t(w.size()) == Range(), "Hessian: length of w not equal range dimension for f" ); // point at which we are evaluating the Hessian Forward(0, x); // define the return value Vector hes(n * n); // direction vector for calls to forward Vector u(n); for(j = 0; j < n; j++) u[j] = Base(0.0); // location for return values from Reverse Vector ddw(n * 2); // loop over forward directions for(j = 0; j < n; j++) { // evaluate partials of entire function w.r.t. j-th coordinate u[j] = Base(1.0); Forward(1, u); u[j] = Base(0.0); // evaluate derivative of partial corresponding to F_i ddw = Reverse(2, w); // return desired components for(k = 0; k < n; k++) hes[k * n + j] = ddw[k * 2 + 1]; } return hes; } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/identical.hpp ================================================ # ifndef CPPAD_CORE_IDENTICAL_HPP # define CPPAD_CORE_IDENTICAL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file identical.hpp Check if certain properties is true for any possible AD tape play back. */ // --------------------------------------------------------------------------- /*! Determine if an AD object is a parameter, and could never have a different value during any tape playback. An AD object x is identically a parameter if and only if all of the objects in the following chain are parameters: \code x , x.value , x.value.value , ... \endcode In such a case, the value of the object will always be the same no matter what the independent variable values are at any level. \param x values that we are checking for identically a pamameter. \return returns true iff x is identically a parameter. */ template bool IdenticalCon(const AD &x) { return Constant(x) && IdenticalCon(x.value_); } // Zero ============================================================== /*! Determine if an AD is equal to zero, and must be equal zero during any tape playback. \param x object that we are checking. \return returns true if and only if x is equals zero and is identically a parameter \ref CppAD::IdenticalCon. */ template bool IdenticalZero(const AD &x) { return Constant(x) && IdenticalZero(x.value_); } // One ============================================================== /*! Determine if an AD is equal to one, and must be equal one during any tape playback. \param x object that we are checking. \return returns true if and only if x is equals one and is identically a parameter \ref CppAD::IdenticalCon. */ template bool IdenticalOne(const AD &x) { return Constant(x) && IdenticalOne(x.value_); } // Equal =================================================================== /*! Determine if two AD objects are equal, and must be equal during any tape playback. \param x first of two objects we are checking for equal. \param y second of two objects we are checking for equal. \return returns true if and only if the arguments are equal and both identically parameters \ref CppAD::IdenticalCon. */ template bool IdenticalEqualCon (const AD &x, const AD &y) { bool constant; constant = Constant(x) && Constant(y); return constant & IdenticalEqualCon(x.value_, y.value_); } // ========================================================================== } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/independent/devel.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin devel_independent dev} Developer Documentation for Independent ####################################### Contents ******** {xrst_toc_table include/cppad/core/independent/independent.hpp } {xrst_end devel_independent} ================================================ FILE: include/cppad/core/independent/independent.hpp ================================================ # ifndef CPPAD_CORE_INDEPENDENT_INDEPENDENT_HPP # define CPPAD_CORE_INDEPENDENT_INDEPENDENT_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /* {xrst_begin independent_all dev} Independent: All Arguments Present ################################## Purpose ******* This implements :ref:`Independent-name` with all the possible arguments present. Syntax ****** | ``Independent`` ( *x* , *abort_op_index* , *record_compare* , *dynamic* ) Prototype ********* {xrst_literal // BEGIN_ALL_ARGUMENT // END_ALL_ARGUMENT } Base **** The base type the recording started by this operation. ADVector ******** is simple vector type with elements of type ``AD`` < *Base* > . x * is the vector of the independent variables. abort_op_index ************** operator index at which execution will be aborted (during the recording of operations). The value zero corresponds to not aborting (will not match). record_compare ************** should comparison operators be recorded. dynamic ******* is the independent dynamic parameter vector. {xrst_end independent_all} */ // BEGIN_ALL_ARGUMENT template void Independent( ADVector& x , size_t abort_op_index , bool record_compare , ADVector& dynamic ) // END_ALL_ARGUMENT { CPPAD_ASSERT_KNOWN( abort_op_index == 0 || record_compare, "Independent: abort_op_index is non-zero and record_compare is false." ); typedef typename ADVector::value_type ADBase; typedef typename ADBase::value_type Base; CPPAD_ASSERT_KNOWN( ADBase::tape_ptr() == nullptr, "Independent: cannot create a new tape because\n" "a previous tape is still active (for this thread).\n" "AD::abort_recording() would abort this previous recording." ); local::ADTape* tape = ADBase::tape_manage(new_tape_manage); tape->Independent(x, abort_op_index, record_compare, dynamic); } /* ---------------------------------------------------------------------------- {xrst_begin independent_x_abort_record dev} Independent: Default For dynamic ################################ Purpose ******* This implements :ref:`Independent-name` using the default for the dynamic argument. Syntax ****** ``Independent`` ( *x* , *abort_op_index* , *record_compare* ) Prototype ********* {xrst_literal // BEGIN_THREE_ARGUMENT // END_THREE_ARGUMENT } Base **** The base type the recording started by this operation. ADVector ******** is simple vector type with elements of type ``AD`` < *Base* > . x * is the vector of the independent variables. abort_op_index ************** operator index at which execution will be aborted (during the recording of operations). The value zero corresponds to not aborting (will not match). record_compare ************** should comparison operators be recorded. {xrst_end independent_x_abort_record} */ // BEGIN_THREE_ARGUMENT template void Independent(ADVector &x, size_t abort_op_index, bool record_compare) // END_THREE_ARGUMENT { ADVector dynamic(0); // empty vector Independent(x, abort_op_index, record_compare, dynamic); } /* ------------------------------------------------------------------------------ {xrst_begin independent_x_abort_op_index dev} Independent: Default For record_compare, dynamic ################################################ Purpose ******* This implements :ref:`Independent-name` using the default for the record_compare and dynamic arguments. Syntax ****** ``Independent`` ( *x* , *abort_op_index* ) Prototype ********* {xrst_literal // BEGIN_X_ABORT_OP_INDEX // END_X_ABORT_OP_INDEX } Base **** The base type the recording started by this operation. ADVector ******** is simple vector type with elements of type ``AD`` < *Base* > . x * is the vector of the independent variables. abort_op_index ************** operator index at which execution will be aborted (during the recording of operations). The value zero corresponds to not aborting (will not match). {xrst_end independent_x_abort_op_index} */ // BEGIN_X_ABORT_OP_INDEX template void Independent(ADVector &x, size_t abort_op_index) // END_X_ABORT_OP_INDEX { bool record_compare = true; ADVector dynamic(0); // empty vector Independent(x, abort_op_index, record_compare, dynamic); } /* ------------------------------------------------------------------------------ {xrst_begin independent_x_dynamic dev} Independent: Default For abort_op_index, record_compare ####################################################### Purpose ******* This implements :ref:`Independent-name` using the default for the abort_op_index and record_compare. Syntax ****** ``Independent`` ( *x* , *dynamic* ) Prototype ********* {xrst_literal // BEGIN_X_DYNAMIC // END_X_DYNAMIC } Base **** The base type the recording started by this operation. ADVector ******** is simple vector type with elements of type ``AD`` < *Base* > . x * is the vector of the independent variables. dynamic ******* is the independent dynamic parameter vector. {xrst_end independent_x_dynamic} */ // BEGIN_X_DYNAMIC template void Independent(ADVector& x, ADVector& dynamic) // END_X_DYNAMIC { size_t abort_op_index = 0; bool record_compare = true; Independent(x, abort_op_index, record_compare, dynamic); } /* ------------------------------------------------------------------------------ {xrst_begin independent_x dev} Independent: Default For abort_op_index, record_compare, dynamic ################################################################ Purpose ******* This implements :ref:`Independent-name` using the default for the abort_op_index, record_compare and dynamic arguments. Syntax ****** ``Independent`` ( *x* ) Prototype ********* {xrst_literal // BEGIN_ONE_ARGUMENT // END_ONE_ARGUMENT } Base **** The base type the recording started by this operation. ADVector ******** is simple vector type with elements of type ``AD`` < *Base* > . x * is the vector of the independent variables. {xrst_end independent_x} */ // BEGIN_ONE_ARGUMENT template void Independent(ADVector &x) // END_ONE_ARGUMENT { size_t abort_op_index = 0; bool record_compare = true; ADVector dynamic(0); // empty vector Independent(x, abort_op_index, record_compare, dynamic); } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/independent/user.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin Independent} Declare Independent Variables and Start Recording ################################################# Syntax ****** | ``Independent`` ( *x* ) | ``Independent`` ( *x* , *dynamic* ) | ``Independent`` ( *x* , *abort_op_index* ) | ``Independent`` ( *x* , *abort_op_index* , *record_compare* ) | ``Independent`` ( *x* , *abort_op_index* , *record_compare* , *dynamic* ) Start Recording *************** The syntax above starts recording :ref:`glossary@AD of Base` operations with *x* as the independent variable vector. Once the :ref:`operation sequence` is completed, it must be transferred to a function object or aborted; see below. Stop Recording ************** The recording is stopped, and the operation sequence is transferred to the AD function object *f* , using either the :ref:`function constructor` ``ADFun`` < *Base* > *f* ( *x* , *y* ) or the :ref:`dependent variable specifier` *f* . ``Dependent`` ( *x* , *y* ) The only other way to stop a recording is using :ref:`abort_recording-name` . Between when the recording is started and when it stopped, we refer to the elements of *x* , and the values that depend on the elements of *x* , as ``AD`` < *Base* > variables. x * The vector *x* has prototype *ADVector* & *x* (see *ADVector* below). The size of the vector *x* , must be greater than zero, and is the number of independent variables for this AD operation sequence. abort_op_index ************** If this argument has prototype ``size_t`` *abort_op_index* If it is present, it specifies the operator index at which the execution will aborted by calling the CppAD :ref:`error handler` . When this error handler leads to an assert, the user can inspect the call stack to see the source code corresponding to this operator index; see :ref:`compare_change@op_index@Purpose` for *op_index* and :ref:`Faq@Speed@NDEBUG` . No abort will occur if *abort_op_index* is zero. If this argument is not present, the default value zero is used for *abort_index* . record_compare ************** This argument has prototype ``bool`` *record_compare* If it is present, it specifies if AD :ref:`compare-name` operations are recorded. It takes extra time and memory to record these operations. On the other hand, they can be useful for detecting when and why a functions recording would change; see *abort_op_index* above and :ref:`compare_change-name` . If this argument is not present, the default value ``true`` is used for *record_compare* . If this argument is false, *abort_op_index* must be zero. dynamic ******* If this argument is present, it has prototype ``const`` *ADVector* & *dynamic* (see *Vector* below). It specifies the independent :ref:`glossary@Parameter@Dynamic` parameters. The value of these parameters, in the :ref:`ADFun-name` object *f* , that can be changed using :ref:`new_dynamic-name` . Efficiency ========== Any operations that use dynamic parameters will be recorded. We use other dynamic parameters to denote parameters that depend on the independent dynamic parameters *dynamic* , and do not depend on *x* . It is more efficient to compute other dynamic parameters before calling ``Independent`` and include them in the independent dynamic parameter vector *dynamic* . ADVector ******** The type *ADVector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` ``AD`` < *Base* > . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Parallel Mode ************* Each thread can have one, and only one, active recording. A call to ``Independent`` starts the recording for the current thread. The recording must be stopped by a corresponding call to ``ADFun`` < *Base* > *f* ( *x* , *y* ) or *f* . ``Dependent`` ( *x* , *y* ) or :ref:`abort_recording-name` preformed by the same thread; i.e., :ref:`thread_alloc::thread_num` must be the same. Example ******* {xrst_toc_hidden example/general/independent.cpp } The file :ref:`independent.cpp-name` contains an example and test of this operation. {xrst_end Independent} ================================================ FILE: include/cppad/core/integer.hpp ================================================ # ifndef CPPAD_CORE_INTEGER_HPP # define CPPAD_CORE_INTEGER_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* ------------------------------------------------------------------------------ {xrst_begin Integer} Convert From AD to Integer ########################## Syntax ****** | *i* = ``Integer`` ( *x* ) Purpose ******* Converts from an AD type to the corresponding integer value. i * The result *i* has prototype ``int`` *i* x * Real Types ========== If the argument *x* has either of the following prototypes: | |tab| ``const float`` & *x* | |tab| ``const double`` & *x* the fractional part is dropped to form the integer value. For example, if *x* is 1.5, *i* is 1. In general, if :math:`x \geq 0`, *i* is the greatest integer less than or equal *x* . If :math:`x \leq 0`, *i* is the smallest integer greater than or equal *x* . Complex Types ============= If the argument *x* has either of the following prototypes: | |tab| ``const std::complex`` & *x* | |tab| ``const std::complex`` & *x* The result *i* is given by *i* = ``Integer`` ( *x* . ``real`` ()) AD Types ======== If the argument *x* has either of the following prototypes: | |tab| ``const AD`` < *Base* > & *x* | |tab| ``const VecAD`` < *Base* >:: ``reference &`` *x* *Base* must support the ``Integer`` function and the conversion has the same meaning as for *Base* . Operation Sequence ****************** The result of this operation is not an :ref:`glossary@AD of Base` object. Thus it will not be recorded as part of an AD of *Base* :ref:`operation sequence` . Example ******* {xrst_toc_hidden example/general/integer.cpp } The file :ref:`integer.cpp-name` contains an example and test of this operation. {xrst_end Integer} ------------------------------------------------------------------------------ */ namespace CppAD { template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION int Integer(const AD &x) { return Integer(x.value_); } template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION int Integer(const VecAD_reference &x) { return Integer( x.ADBase() ); } } # endif ================================================ FILE: include/cppad/core/jacobian.hpp ================================================ # ifndef CPPAD_CORE_JACOBIAN_HPP # define CPPAD_CORE_JACOBIAN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin Jacobian} Jacobian: Driver Routine ######################## Syntax ****** | *jac* = *f* . ``Jacobian`` ( *x* ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* . The syntax above sets *jac* to the Jacobian of *F* evaluated at *x* ; i.e., .. math:: jac = F^{(1)} (x) f * The object *f* has prototype ``ADFun`` < *Base* > *f* Note that the :ref:`ADFun-name` object *f* is not ``const`` (see :ref:`Jacobian@Forward or Reverse` below). x * The argument *x* has prototype ``const`` *Vector* & *x* (see :ref:`Jacobian@Vector` below) and its size must be equal to *n* , the dimension of the :ref:`fun_property@Domain` space for *f* . It specifies that point at which to evaluate the Jacobian. jac *** The result *jac* has prototype *Vector* *jac* (see :ref:`Jacobian@Vector` below) and its size is :math:`m * n`; i.e., the product of the :ref:`fun_property@Domain` and :ref:`fun_property@Range` dimensions for *f* . For :math:`i = 0 , \ldots , m - 1` and :math:`j = 0 , \ldots , n - 1` .. math:: jac[ i * n + j ] = \D{ F_i }{ x_j } ( x ) Vector ****** The type *Vector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` *Base* . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Forward or Reverse ****************** This will use order zero Forward mode and either order one Forward or order one Reverse to compute the Jacobian (depending on which it estimates will require less work). After each call to :ref:`Forward-name` , the object *f* contains the corresponding :ref:`Taylor coefficients` . After a call to ``Jacobian`` , the zero order Taylor coefficients correspond to *f* . ``Forward`` (0, *x* ) and the other coefficients are unspecified. Example ******* {xrst_toc_hidden example/general/jacobian.cpp } The routine :ref:`Jacobian` is both an example and test. It returns ``true`` , if it succeeds and ``false`` otherwise. {xrst_end Jacobian} ----------------------------------------------------------------------------- */ // BEGIN CppAD namespace namespace CppAD { template void JacobianFor(ADFun &f, const Vector &x, Vector &jac) { size_t i; size_t j; size_t n = f.Domain(); size_t m = f.Range(); // check Vector is Simple Vector class with Base type elements CheckSimpleVector(); CPPAD_ASSERT_UNKNOWN( size_t(x.size()) == f.Domain() ); CPPAD_ASSERT_UNKNOWN( size_t(jac.size()) == f.Range() * f.Domain() ); // argument and result for forward mode calculations Vector u(n); Vector v(m); // initialize all the components for(j = 0; j < n; j++) u[j] = Base(0.0); // loop through the different coordinate directions for(j = 0; j < n; j++) { // set u to the j-th coordinate direction u[j] = Base(1.0); // compute the partial of f w.r.t. this coordinate direction v = f.Forward(1, u); // reset u to vector of all zeros u[j] = Base(0.0); // return the result for(i = 0; i < m; i++) jac[ i * n + j ] = v[i]; } } template void JacobianRev(ADFun &f, const Vector &x, Vector &jac) { size_t i; size_t j; size_t n = f.Domain(); size_t m = f.Range(); CPPAD_ASSERT_UNKNOWN( size_t(x.size()) == f.Domain() ); CPPAD_ASSERT_UNKNOWN( size_t(jac.size()) == f.Range() * f.Domain() ); // argument and result for reverse mode calculations Vector u(n); Vector v(m); // initialize all the components for(i = 0; i < m; i++) v[i] = Base(0.0); // loop through the different coordinate directions for(i = 0; i < m; i++) { if( f.Parameter(i) ) { // return zero for this component of f for(j = 0; j < n; j++) jac[ i * n + j ] = Base(0.0); } else { // set v to the i-th coordinate direction v[i] = Base(1.0); // compute the derivative of this component of f u = f.Reverse(1, v); // reset v to vector of all zeros v[i] = Base(0.0); // return the result for(j = 0; j < n; j++) jac[ i * n + j ] = u[j]; } } } template template Vector ADFun::Jacobian(const Vector &x) { size_t i; size_t n = Domain(); size_t m = Range(); CPPAD_ASSERT_KNOWN( size_t(x.size()) == n, "Jacobian: length of x not equal domain dimension for F" ); // point at which we are evaluating the Jacobian Forward(0, x); // work factor for forward mode size_t workForward = n; // work factor for reverse mode size_t workReverse = 0; for(i = 0; i < m; i++) { if( ! Parameter(i) ) ++workReverse; } // choose the method with the least work Vector jac( n * m ); # ifdef CPPAD_FOR_TMB if( workForward < workReverse ) # else if( workForward <= workReverse ) # endif JacobianFor(*this, x, jac); else JacobianRev(*this, x, jac); return jac; } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/lu_ratio.hpp ================================================ # ifndef CPPAD_CORE_LU_RATIO_HPP # define CPPAD_CORE_LU_RATIO_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin LuRatio app} {xrst_spell ip jp xk } LU Factorization of A Square Matrix and Stability Calculation ############################################################# Syntax ****** | ``# include `` | *sign* = ``LuRatio`` ( *ip* , *jp* , *LU* , *ratio* ) Description *********** Computes an LU factorization of the matrix *A* where *A* is a square matrix. A measure of the numerical stability called *ratio* is calculated. This ratio is useful when the results of ``LuRatio`` are used as part of an :ref:`ADFun-name` object. Include ******* This routine is designed to be used with AD objects and requires the ``cppad/cppad.hpp`` file to be included. Matrix Storage ************** All matrices are stored in row major order. To be specific, if :math:`Y` is a vector that contains a :math:`p` by :math:`q` matrix, the size of :math:`Y` must be equal to :math:`p * q` and for :math:`i = 0 , \ldots , p-1`, :math:`j = 0 , \ldots , q-1`, .. math:: Y_{i,j} = Y[ i * q + j ] sign **** The return value *sign* has prototype ``int`` *sign* If *A* is invertible, *sign* is plus or minus one and is the sign of the permutation corresponding to the row ordering *ip* and column ordering *jp* . If *A* is not invertible, *sign* is zero. ip ** The argument *ip* has prototype *SizeVector* & *ip* (see description of :ref:`LuFactor@SizeVector` below). The size of *ip* is referred to as *n* in the specifications below. The input value of the elements of *ip* does not matter. The output value of the elements of *ip* determine the order of the rows in the permuted matrix. jp ** The argument *jp* has prototype *SizeVector* & *jp* (see description of :ref:`LuFactor@SizeVector` below). The size of *jp* must be equal to *n* . The input value of the elements of *jp* does not matter. The output value of the elements of *jp* determine the order of the columns in the permuted matrix. LU ** The argument *LU* has the prototype *ADvector* & *LU* and the size of *LU* must equal :math:`n * n` (see description of :ref:`LuRatio@ADvector` below). A = We define *A* as the matrix corresponding to the input value of *LU* . P = We define the permuted matrix *P* in terms of *A* by *P* ( *i* , *j* ) = *A* [ *ip* [ *i* ] * *n* + *jp* [ *j* ] ] L = We define the lower triangular matrix *L* in terms of the output value of *LU* . The matrix *L* is zero above the diagonal and the rest of the elements are defined by *L* ( *i* , *j* ) = *LU* [ *ip* [ *i* ] * *n* + *jp* [ *j* ] ] for :math:`i = 0 , \ldots , n-1` and :math:`j = 0 , \ldots , i`. U = We define the upper triangular matrix *U* in terms of the output value of *LU* . The matrix *U* is zero below the diagonal, one on the diagonal, and the rest of the elements are defined by *U* ( *i* , *j* ) = *LU* [ *ip* [ *i* ] * *n* + *jp* [ *j* ] ] for :math:`i = 0 , \ldots , n-2` and :math:`j = i+1 , \ldots , n-1`. Factor ====== If the return value *sign* is non-zero, *L* * *U* = *P* If the return value of *sign* is zero, the contents of *L* and *U* are not defined. Determinant =========== If the return value *sign* is zero, the determinant of *A* is zero. If *sign* is non-zero, using the output value of *LU* the determinant of the matrix *A* is equal to *sign* * *LU* [ *ip* [0], *jp* [0]] * ... * *LU* [ *ip* [ *n* ``-1`` ], *jp* [ *n* ``-1`` ]] ratio ***** The argument *ratio* has prototype ``AD`` < *Base* > & *ratio* On input, the value of *ratio* does not matter. On output it is a measure of how good the choice of pivots is. For :math:`p = 0 , \ldots , n-1`, the *p*-th pivot element is the element of maximum absolute value of a :math:`(n-p) \times (n-p)` sub-matrix. The ratio of each element of sub-matrix divided by the pivot element is computed. The return value of *ratio* is the maximum absolute value of such ratios over with respect to all elements and all the pivots. Purpose ======= Suppose that the execution of a call to ``LuRatio`` is recorded in the ``ADFun`` < *Base* > object *F* . Then a call to :ref:`Forward-name` of the form *F* . ``Forward`` ( *k* , *xk* ) with *k* equal to zero will revaluate this Lu factorization with the same pivots and a new value for *A* . In this case, the resulting *ratio* may not be one. If *ratio* is too large (the meaning of too large is up to you), the current pivots do not yield a stable LU factorization of *A* . A better choice for the pivots (for this value of *A* ) will be made if you recreate the ``ADFun`` object starting with the :ref:`Independent-name` variable values that correspond to the vector *xk* . SizeVector ********** The type *SizeVector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type size_t` . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. ADvector ******** The type *ADvector* must be a :ref:`simple vector class` with elements of type ``AD`` < *Base* > . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Example ******* {xrst_toc_hidden example/general/lu_ratio.cpp } The file :ref:`lu_ratio.cpp-name` contains an example and test of using ``LuRatio`` . {xrst_end LuRatio} -------------------------------------------------------------------------- */ namespace CppAD { // BEGIN CppAD namespace // Lines different from the code in cppad/lu_factor.hpp end with // template // int LuRatio(SizeVector &ip, SizeVector &jp, ADvector &LU, AD &ratio) // { typedef ADvector FloatVector; // typedef AD Float; // // check numeric type specifications CheckNumericType(); // check simple vector class specifications CheckSimpleVector(); CheckSimpleVector(); size_t i, j; // some temporary indices const Float zero( 0 ); // the value zero as a Float object size_t imax; // row index of maximum element size_t jmax; // column index of maximum element Float emax; // maximum absolute value size_t p; // count pivots int sign; // sign of the permutation Float etmp; // temporary element Float pivot; // pivot element // ------------------------------------------------------- size_t n = size_t(ip.size()); CPPAD_ASSERT_KNOWN( size_t(jp.size()) == n, "Error in LuFactor: jp must have size equal to n" ); CPPAD_ASSERT_KNOWN( size_t(LU.size()) == n * n, "Error in LuFactor: LU must have size equal to n * m" ); // ------------------------------------------------------- // initialize row and column order in matrix not yet pivoted for(i = 0; i < n; i++) { ip[i] = i; jp[i] = i; } // initialize the sign of the permutation sign = 1; // initialize the ratio // ratio = Float(1); // // --------------------------------------------------------- // Reduce the matrix P to L * U using n pivots for(p = 0; p < n; p++) { // determine row and column corresponding to element of // maximum absolute value in remaining part of P imax = jmax = n; emax = zero; for(i = p; i < n; i++) { for(j = p; j < n; j++) { CPPAD_ASSERT_UNKNOWN( (ip[i] < n) && (jp[j] < n) ); etmp = LU[ ip[i] * n + jp[j] ]; // check if maximum absolute value so far if( AbsGeq (etmp, emax) ) { imax = i; jmax = j; emax = etmp; } } } for(i = p; i < n; i++) // { for(j = p; j < n; j++) // { etmp = fabs(LU[ ip[i] * n + jp[j] ] / emax); // ratio = // CondExpGt(etmp, ratio, etmp, ratio); // } // } // CPPAD_ASSERT_KNOWN( (imax < n) && (jmax < n) , "AbsGeq must return true when second argument is zero" ); if( imax != p ) { // switch rows so max absolute element is in row p i = ip[p]; ip[p] = ip[imax]; ip[imax] = i; sign = -sign; } if( jmax != p ) { // switch columns so max absolute element is in column p j = jp[p]; jp[p] = jp[jmax]; jp[jmax] = j; sign = -sign; } // pivot using the max absolute element pivot = LU[ ip[p] * n + jp[p] ]; // check for determinant equal to zero if( pivot == zero ) { // abort the mission return 0; } // Reduce U by the elementary transformations that maps // LU( ip[p], jp[p] ) to one. Only need transform elements // above the diagonal in U and LU( ip[p] , jp[p] ) is // corresponding value below diagonal in L. for(j = p+1; j < n; j++) LU[ ip[p] * n + jp[j] ] /= pivot; // Reduce U by the elementary transformations that maps // LU( ip[i], jp[p] ) to zero. Only need transform elements // above the diagonal in U and LU( ip[i], jp[p] ) is // corresponding value below diagonal in L. for(i = p+1; i < n; i++ ) { etmp = LU[ ip[i] * n + jp[p] ]; for(j = p+1; j < n; j++) { LU[ ip[i] * n + jp[j] ] -= etmp * LU[ ip[p] * n + jp[j] ]; } } } return sign; } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/mul.hpp ================================================ # ifndef CPPAD_CORE_MUL_HPP # define CPPAD_CORE_MUL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN CppAD namespace namespace CppAD { template AD operator * (const AD &left , const AD &right) { // compute the Base part AD result; result.value_ = left.value_ * right.value_; CPPAD_ASSERT_UNKNOWN( Parameter(result) ); // check if there is a recording in progress local::ADTape* tape = AD::tape_ptr(); if( tape == nullptr ) return result; tape_id_t tape_id = tape->id_; // tape_id cannot match the default value for tape_id_; i.e., 0 CPPAD_ASSERT_UNKNOWN( tape_id > 0 ); // check if left and right tapes match bool match_left = left.tape_id_ == tape_id; bool match_right = right.tape_id_ == tape_id; // check if left and right are dynamic parameters bool dyn_left = match_left & (left.ad_type_ == dynamic_enum); bool dyn_right = match_right & (right.ad_type_ == dynamic_enum); // check if left and right are variables bool var_left = match_left & (left.ad_type_ != dynamic_enum); bool var_right = match_right & (right.ad_type_ != dynamic_enum); CPPAD_ASSERT_KNOWN( left.tape_id_ == right.tape_id_ || ! match_left || ! match_right , "Multiply: AD variables or dynamic parameters on different threads." ); if( var_left ) { if( var_right ) { // result = variable * variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::MulvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::MulvvOp) == 2 ); // put operand addresses in tape tape->Rec_.PutArg(left.taddr_, right.taddr_); // put operator in the tape result.taddr_ = tape->Rec_.PutOp(local::MulvvOp); // make result a variable result.tape_id_ = tape_id; result.ad_type_ = variable_enum; } else if( (! dyn_right) && IdenticalZero(right.value_) ) { // result = variable * 0 result.value_ = Base(0.0); // incase left.value_ is nan } else if( (! dyn_right) && IdenticalOne(right.value_) ) { // result = variable * 1 result.make_variable(left.tape_id_, left.taddr_); } else { // result = variable * parameter CPPAD_ASSERT_UNKNOWN( local::NumRes(local::MulpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::MulpvOp) == 2 ); // put operand addresses in tape addr_t p = right.taddr_; if( ! dyn_right ) p = tape->Rec_.put_con_par(right.value_); tape->Rec_.PutArg(p, left.taddr_); // put operator in the tape result.taddr_ = tape->Rec_.PutOp(local::MulpvOp); // make result a variable result.tape_id_ = tape_id; result.ad_type_ = variable_enum; } } else if( var_right ) { if( (! dyn_left) && IdenticalZero(left.value_) ) { // result = 0 * variable result.value_ = Base(0.0); // incase right.value_ is nan } else if( (! dyn_left) && IdenticalOne(left.value_) ) { // result = 1 * variable result.make_variable(right.tape_id_, right.taddr_); } else { // result = parameter * variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::MulpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::MulpvOp) == 2 ); // put operand addresses in tape addr_t p = left.taddr_; if( ! dyn_left ) p = tape->Rec_.put_con_par(left.value_); tape->Rec_.PutArg(p, right.taddr_); // put operator in the tape result.taddr_ = tape->Rec_.PutOp(local::MulpvOp); // make result a variable result.tape_id_ = tape_id; result.ad_type_ = variable_enum; } } else if( dyn_left | dyn_right ) { if ( (!dyn_left) && IdenticalZero(left.value_)) { // 0 * dynamic result.value_ = Base(0.0); } else if( (!dyn_right) && IdenticalZero(right.value_)) { // dynamic * 0 result.value_ = Base(0.0); } else if( (!dyn_left) && IdenticalOne(left.value_)) { // 1 * dynamic result.make_dynamic(right.tape_id_, right.taddr_); } else if( (!dyn_right) && IdenticalOne(right.value_)) { // dynamic * 1 result.make_dynamic(left.tape_id_, left.taddr_); } else { addr_t arg0 = left.taddr_; addr_t arg1 = right.taddr_; if( ! dyn_left ) arg0 = tape->Rec_.put_con_par(left.value_); if( ! dyn_right ) arg1 = tape->Rec_.put_con_par(right.value_); // // parameters with a dynamic parameter result result.taddr_ = tape->Rec_.put_dyn_par( result.value_, local::mul_dyn, arg0, arg1 ); result.tape_id_ = tape_id; result.ad_type_ = dynamic_enum; } } return result; } // convert other cases into the case above CPPAD_FOLD_AD_VALUED_BINARY_OPERATOR(*) } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/mul_eq.hpp ================================================ # ifndef CPPAD_CORE_MUL_EQ_HPP # define CPPAD_CORE_MUL_EQ_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN CppAD namespace namespace CppAD { template AD& AD::operator *= (const AD &right) { // compute the Base part Base left; left = value_; value_ *= right.value_; // check if there is a recording in progress local::ADTape* tape = AD::tape_ptr(); if( tape == nullptr ) return *this; tape_id_t tape_id = tape->id_; // tape_id cannot match the default value for tape_id_; i.e., 0 CPPAD_ASSERT_UNKNOWN( tape_id > 0 ); // check if left and right tapes match bool match_left = tape_id_ == tape_id; bool match_right = right.tape_id_ == tape_id; // check if left and right are dynamic parameters bool dyn_left = match_left & (ad_type_ == dynamic_enum); bool dyn_right = match_right & (right.ad_type_ == dynamic_enum); // check if left and right are variables bool var_left = match_left & (ad_type_ != dynamic_enum); bool var_right = match_right & (right.ad_type_ != dynamic_enum); CPPAD_ASSERT_KNOWN( tape_id_ == right.tape_id_ || ! match_left || ! match_right , "*= : AD variables or dynamic parameters on different threads." ); if( var_left ) { if( var_right ) { // this = variable * variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::MulvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::MulvvOp) == 2 ); // put operand addresses in tape tape->Rec_.PutArg(taddr_, right.taddr_); // put operator in the tape taddr_ = tape->Rec_.PutOp(local::MulvvOp); // check that this is a variable CPPAD_ASSERT_UNKNOWN( tape_id_ == tape_id ); CPPAD_ASSERT_UNKNOWN( ad_type_ == variable_enum); } else if( (! dyn_right) && IdenticalOne(right.value_) ) { // this = variable * 1 } else if( (! dyn_right) && IdenticalZero(right.value_) ) { // this = variable * 0 tape_id_ = 0; // not in current tape } else { // this = variable * parameter // = parameter * variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::MulpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::MulpvOp) == 2 ); // put operand addresses in tape addr_t p = right.taddr_; if( ! dyn_right ) p = tape->Rec_.put_con_par(right.value_); tape->Rec_.PutArg(p, taddr_); // put operator in the tape taddr_ = tape->Rec_.PutOp(local::MulpvOp); // check that this is a variable CPPAD_ASSERT_UNKNOWN( tape_id_ == tape_id ); CPPAD_ASSERT_UNKNOWN( ad_type_ == variable_enum); } } else if( var_right ) { if( (! dyn_left) && IdenticalZero(left) ) { // this = 0 * right } else if( (! dyn_left) && IdenticalOne(left) ) { // this = 1 * right make_variable(right.tape_id_, right.taddr_); } else { // this = parameter * variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::MulpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::MulpvOp) == 2 ); // put operand addresses in tape addr_t p = taddr_; if( ! dyn_left ) p = tape->Rec_.put_con_par(left); tape->Rec_.PutArg(p, right.taddr_); // put operator in the tape taddr_ = tape->Rec_.PutOp(local::MulpvOp); // make this a variable tape_id_ = tape_id; ad_type_ = variable_enum; } } else if( dyn_left | dyn_right ) { if( (!dyn_right) && IdenticalOne(right.value_)) { // dynamic *= 1, so do nothing } else if( (!dyn_right) && IdenticalZero(right.value_)) { // dynamic *= 0, so remove from tape tape_id_ = 0; } else if( (!dyn_left) && IdenticalOne(left)) { // 1 *= dynamic make_dynamic(right.tape_id_, right.taddr_); } else if( (!dyn_left) && IdenticalZero(left)) { // 0 *= dynamic, so do nothing } else { addr_t arg0 = taddr_; addr_t arg1 = right.taddr_; if( ! dyn_left ) arg0 = tape->Rec_.put_con_par(left); if( ! dyn_right ) arg1 = tape->Rec_.put_con_par(right.value_); // // parameters with a dynamic parameter results taddr_ = tape->Rec_.put_dyn_par( value_, local::mul_dyn, arg0, arg1 ); tape_id_ = tape_id; ad_type_ = dynamic_enum; } } return *this; } CPPAD_FOLD_ASSIGNMENT_OPERATOR(*=) } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/near_equal_ext.hpp ================================================ # ifndef CPPAD_CORE_NEAR_EQUAL_EXT_HPP # define CPPAD_CORE_NEAR_EQUAL_EXT_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin near_equal_ext} Compare AD and Base Objects for Nearly Equal ############################################ Syntax ****** | *b* = ``NearEqual`` ( *x* , *y* , *r* , *a* ) Purpose ******* The routine :ref:`NearEqual-name` determines if two objects of the same type are nearly. This routine is extended to the case where one object can have type *Type* while the other can have type ``AD`` < *Type* > or ``AD< std::complex<`` *Type* > > . x * The arguments *x* has one of the following possible prototypes: | |tab| ``const`` *Type* & *x* | |tab| ``const AD`` < *Type* > & *x* | |tab| ``const AD< std::complex<`` *Type* > > & *x* y * The arguments *y* has one of the following possible prototypes: | |tab| ``const`` *Type* & *y* | |tab| ``const AD`` < *Type* > & *y* | |tab| ``const AD< std::complex<`` *Type* > > & *x* r * The relative error criteria *r* has prototype ``const`` *Type* & *r* It must be greater than or equal to zero. The relative error condition is defined as: .. math:: \frac{ | x - y | } { |x| + |y| } \leq r a * The absolute error criteria *a* has prototype ``const`` *Type* & *a* It must be greater than or equal to zero. The absolute error condition is defined as: .. math:: | x - y | \leq a b * The return value *b* has prototype ``bool`` *b* If either *x* or *y* is infinite or not a number, the return value is false. Otherwise, if either the relative or absolute error condition (defined above) is satisfied, the return value is true. Otherwise, the return value is false. Type **** The type *Type* must be a :ref:`NumericType-name` . The routine :ref:`CheckNumericType-name` will generate an error message if this is not the case. If *a* and *b* have type *Type* , the following operation must be defined .. list-table:: :widths: auto * - **Operation** - **Description** * - *a* <= *b* - less that or equal operator (returns a ``bool`` object) Operation Sequence ****************** The result of this operation is not an :ref:`glossary@AD of Base` object. Thus it will not be recorded as part of an AD of *Base* :ref:`operation sequence` . Example ******* {xrst_toc_hidden example/general/near_equal_ext.cpp } The file :ref:`near_equal_ext.cpp-name` contains an example and test of this extension of :ref:`NearEqual-name` . It return true if it succeeds and false otherwise. {xrst_end near_equal_ext} */ // BEGIN CppAD namespace namespace CppAD { // ------------------------------------------------------------------------ // fold into base type and then use template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION bool NearEqual( const AD &x, const AD &y, const Base &r, const Base &a) { return NearEqual(x.value_, y.value_, r, a); } template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION bool NearEqual( const Base &x, const AD &y, const Base &r, const Base &a) { return NearEqual(x, y.value_, r, a); } template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION bool NearEqual( const AD &x, const Base &y, const Base &r, const Base &a) { return NearEqual(x.value_, y, r, a); } // fold into AD type and then use cases above template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION bool NearEqual( const VecAD_reference &x, const VecAD_reference &y, const Base &r, const Base &a) { return NearEqual(x.ADBase(), y.ADBase(), r, a); } template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION bool NearEqual(const VecAD_reference &x, const AD &y, const Base &r, const Base &a) { return NearEqual(x.ADBase(), y, r, a); } template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION bool NearEqual(const VecAD_reference &x, const Base &y, const Base &r, const Base &a) { return NearEqual(x.ADBase(), y, r, a); } template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION bool NearEqual(const AD &x, const VecAD_reference &y, const Base &r, const Base &a) { return NearEqual(x, y.ADBase(), r, a); } template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION bool NearEqual(const Base &x, const VecAD_reference &y, const Base &r, const Base &a) { return NearEqual(x, y.ADBase(), r, a); } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/new_dynamic.hpp ================================================ # ifndef CPPAD_CORE_NEW_DYNAMIC_HPP # define CPPAD_CORE_NEW_DYNAMIC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin new_dynamic} Change the Dynamic Parameters ############################# Syntax ****** | *f* . ``new_dynamic`` ( *dynamic* ) Purpose ******* Often one is only interested in computing derivatives with respect to a subset of arguments to a function. In this case, it is easier to make all the arguments to the function :ref:`independent variables` . It is more efficient, will use less memory and be faster, if the only the argument were are computing derivatives with respect to are independent variables and the other arguments are :ref:`glossary@Parameter@Dynamic` parameters. The ``new_dynamic`` method is used to change the value of the dynamic parameters in *f* . f * The object *f* has prototype ``ADFun`` < *Base* > *f* Note that the :ref:`ADFun-name` object *f* is not ``const`` . dynamic ******* This argument has prototype ``const`` *BaseVector* & *dynamic* (see *BaseVector* below). It specifies a new value for the independent :ref:`glossary@Parameter@Dynamic` parameters. It size must be the same as the size of the independent :ref:`Independent@dynamic` parameter vector in the call to ``Independent`` that started the recording for *f* ; see :ref:`fun_property@size_dyn_ind` . BaseVector ********** The type *BaseVector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` *Base* . Taylor Coefficients ******************* The Taylor coefficients computed by previous calls to :ref:`f.Forward` are lost after this operation; including the order zero coefficients (because they may depend on the dynamic parameters). In order words; :ref:`f.size_order` returns zero directly after *f* . ``new_dynamic`` is called. {xrst_toc_hidden example/general/new_dynamic.cpp } Example ******* The file :ref:`new_dynamic.cpp-name` contains an example and test of this operation. {xrst_end new_dynamic} */ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file new_dynamic.hpp User interface to ADFun dynamic_parameter member function. */ /*! Change the dynamic parameters in this ADFun object \param dynamic is the vector of new values for the dynamic parameters. */ template template void ADFun::new_dynamic(const BaseVector& dynamic) { using local::pod_vector; CPPAD_ASSERT_KNOWN( size_t( dynamic.size() ) == play_.n_dyn_independent() , "f.new_dynamic: dynamic.size() different from corresponding " "call to Independent" ); // check BaseVector is Simple Vector class with Base elements CheckSimpleVector(); // retrieve player information about the dynamic parameters local::pod_vector_maybe& par_all( play_.par_all() ); const pod_vector& par_is_dyn ( play_.par_is_dyn() ); const pod_vector& dyn_par_op ( play_.dyn_par_op() ); const pod_vector& dyn_par_arg( play_.dyn_par_arg() ); const pod_vector& dyn2par_index ( play_.dyn2par_index() ); // set the dependent dynamic parameters RecBase not_used_rec_base(0.0); local::sweep::dynamic( par_all , dynamic , par_is_dyn , dyn2par_index , dyn_par_op , dyn_par_arg , not_used_rec_base ); // the existing Taylor coefficients are no longer valid num_order_taylor_ = 0; return; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/num_skip.hpp ================================================ # ifndef CPPAD_CORE_NUM_SKIP_HPP # define CPPAD_CORE_NUM_SKIP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin number_skip} Number of Variables that Can be Skipped ####################################### Syntax ****** | *n* = *f* . ``number_skip`` () See Also ======== :ref:`fun_property-name` Purpose ******* The :ref:`conditional expressions` use either the :ref:`if_true` or :ref:`if_false` . Hence, some terms only need to be evaluated depending on the value of the comparison in the conditional expression. The :ref:`optimize-name` option is capable of detecting some of these case and determining variables that can be skipped. This routine returns the number such variables. n * The return value *n* has type ``size_t`` is the number of variables that the optimizer has determined can be skipped (given the independent variable values specified by the previous call to :ref:`f.Forward` for order zero). f * The object *f* has prototype ``ADFun`` < *Base* > *f* {xrst_toc_hidden example/general/number_skip.cpp } Example ******* The file :ref:`number_skip.cpp-name` contains an example and test of this function. {xrst_end number_skip} ----------------------------------------------------------------------------- */ # include // BEGIN CppAD namespace namespace CppAD { // This routine is not const because it runs through the operations sequence // 2DO: compute this value during zero order forward operations. template size_t ADFun::number_skip(void) { // must pass through operation sequence to map operations to variables // information defined by atomic forward size_t atom_index=0, atom_old=0, atom_m=0, atom_n=0; // number of variables skipped size_t num_var_skip = 0; // start playback local::play::const_sequential_iterator itr = play_.begin(); local::op_code_var op; size_t i_var; const addr_t* arg; itr.op_info(op, arg, i_var); CPPAD_ASSERT_UNKNOWN(op == local::BeginOp) while(op != local::EndOp) { // next op (++itr).op_info(op, arg, i_var); // if( op == local::AFunOp ) { // skip only appears at front or back AFunOp of atomic function call bool skip_call = cskip_op_[ itr.op_index() ]; local::play::atom_op_info( op, arg, atom_index, atom_old, atom_m, atom_n ); CPPAD_ASSERT_UNKNOWN( NumRes(op) == 0 ); size_t num_op = atom_m + atom_n + 1; for(size_t i = 0; i < num_op; i++) { CPPAD_ASSERT_UNKNOWN( op != local::CSkipOp && op != local::CSumOp ); (++itr).op_info(op, arg, i_var); if( skip_call ) num_var_skip += NumRes(op); } CPPAD_ASSERT_UNKNOWN( op == local::AFunOp ); } else { if( cskip_op_[ itr.op_index() ] ) num_var_skip += NumRes(op); // if( (op == local::CSkipOp) || (op == local::CSumOp) ) itr.correct_before_increment(); } } return num_var_skip; } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/numeric_limits.hpp ================================================ # ifndef CPPAD_CORE_NUMERIC_LIMITS_HPP # define CPPAD_CORE_NUMERIC_LIMITS_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* ------------------------------------------------------------------------------ {xrst_begin numeric_limits} {xrst_spell eps isnan } Numeric Limits For an AD and Base Types ####################################### Syntax ****** | *eps* = ``numeric_limits`` < *Float* >:: ``epsilon`` () | *min* = ``numeric_limits`` < *Float* >:: ``min`` () | *max* = ``numeric_limits`` < *Float* >:: ``max`` () | *nan* = ``numeric_limits`` < *Float* >:: ``quiet_NaN`` () | *inf* = ``numeric_limits`` < *Float* >:: ``infinity`` () | ``numeric_limits`` < *Float* >:: ``digits10`` | ``numeric_limits`` < *Float* >:: ``max_digits10`` CppAD::numeric_limits ********************* The functions above and have the prototype ``static`` *Float* ``CppAD::numeric_limits<`` *Float* >:: *fun* ( *void* ) where *fun* is ``epsilon`` , ``min`` , ``max`` , ``quiet_NaN`` , and ``infinity`` . The values ``digits10`` and ``max_digits10`` are member variable and not a functions. std::numeric_limits ******************* CppAD does not use a specialization of ``std::numeric_limits`` because this would be to restrictive. The C++ standard specifies that Non-fundamental standard types, such as :ref:`std::complex\` shall not have specializations of ``std::numeric_limits`` ; see Section 18.2 of ISO/IEC 14882:1998(E). In addition, since C++11, a only literal types can have a specialization of ``std::numeric_limits`` . Float ***** These functions are defined for all ``AD`` < *Base* > , and for all corresponding *Base* types; see *Base* type :ref:`base_limits-name` . epsilon ******* The result *eps* is equal to machine epsilon and has prototype *Float* *eps* The file :ref:`num_limits.cpp-name` tests the value *eps* by checking that the following are true | |tab| 1 != 1 + *eps* | |tab| 1 == 1 + *eps* / 2 where all the values, and calculations, are done with the precision corresponding to *Float* . min *** The result *min* is equal to the minimum positive normalized value and has prototype *Float* *min* The file :ref:`num_limits.cpp-name` tests the value *min* by checking that the following are true | |tab| ``abs`` ( (( *min* / 100) * 100) / *min* ``- 1`` ) > 3 * *eps* | |tab| ``abs`` ( (( *min* * 100) / 100) / *min* ``- 1`` ) < 3 * *eps* where all the values, and calculations, are done with the precision corresponding to *Float* . max *** The result *max* is equal to the maximum finite value and has prototype *Float* *max* The file :ref:`num_limits.cpp-name` tests the value *max* by checking that the following are true | |tab| ``abs`` ( (( *max* * 100) / 100) / *max* ``- 1`` ) > 3 * *eps* | |tab| ``abs`` ( (( *max* / 100) * 100) / *max* ``- 1`` ) < 3 * *eps* where all the values, and calculations, are done with the precision corresponding to *Float* . quiet_NaN ********* The result *nan* is not a number and has prototype *Float* *nan* The file :ref:`num_limits.cpp-name` tests the value *nan* by checking that the following is true *nan* != *nan* infinity ******** The result *inf* is equal to the positive infinite value and has prototype *Float* *inf* The file :ref:`num_limits.cpp-name` tests the value *inf* by checking that the following are true | |tab| *inf* + 100 == *inf* | |tab| ``isnan`` ( *inf* ``-`` *inf* ) digits10 ******** The member variable ``digits10`` has prototype ``static const int numeric_limits`` < *Float* >:: ``digits10`` It is the number of decimal digits that can be represented by a *Float* value. A number with this many decimal digits can be converted to *Float* and back to a string, without change due to rounding or overflow. max_digits10 ************ The member variable ``max_digits10`` has prototype ``static const int numeric_limits`` < *Float* >:: ``max_digits10`` is the number of decimal digits that are necessary to uniquely represent all distinct values of the type *Float* . For example, the number of digits necessary to convert to text and back and get the exact same result. Example ******* {xrst_toc_hidden example/general/num_limits.cpp } The file :ref:`num_limits.cpp-name` contains an example and test of these functions. {xrst_end numeric_limits} ------------------------------------------------------------------------------ */ # include # include # include # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file numeric_limits.hpp File that defines CppAD numeric_limits for AD types */ /// All these defaults correspond to errors template class numeric_limits { public: /// machine epsilon static Float epsilon(void) { CPPAD_ASSERT_KNOWN( false, "numeric_limits::epsilon() is not specialized for this Float" ); return Float(0); } /// minimum positive normalized value static Float min(void) { CPPAD_ASSERT_KNOWN( false, "numeric_limits::min() is not specialized for this Float" ); return Float(0); } /// maximum finite value static Float max(void) { CPPAD_ASSERT_KNOWN( false, "numeric_limits::max() is not specialized for this Float" ); return Float(0); } /// not a number static Float quiet_NaN(void) { CPPAD_ASSERT_KNOWN( false, "numeric_limits::quiet_NaN() is not specialized for this Float" ); return Float(0); } /// positive infinite value static Float infinity(void) { CPPAD_ASSERT_KNOWN( false, "numeric_limits::infinity() is not specialized for this Float" ); return Float(0); } /// number of decimal digits static const int digits10 = -1; }; /// Partial specialization that defines limits for for all AD types template class numeric_limits< AD > { public: /// machine epsilon static AD epsilon(void) { return AD( numeric_limits::epsilon() ); } /// minimum positive normalized value static AD min(void) { return AD( numeric_limits::min() ); } /// maximum finite value static AD max(void) { return AD( numeric_limits::max() ); } /// not a number static AD quiet_NaN(void) { return AD( numeric_limits::quiet_NaN() ); } /// positive infinite value static AD infinity(void) { return AD( numeric_limits::infinity() ); } /// number of decimal digits static const int digits10 = numeric_limits::digits10; static const int max_digits10 = numeric_limits::max_digits10; }; } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/omp_max_thread.hpp ================================================ # ifndef CPPAD_CORE_OMP_MAX_THREAD_HPP # define CPPAD_CORE_OMP_MAX_THREAD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin omp_max_thread app} {xrst_spell mp } OpenMP Parallel Setup ##################### Deprecated 2011-06-23 ********************* Use :ref:`thread_alloc::parallel_setup` to set the number of threads. Syntax ****** | ``AD`` < *Base* >:: ``omp_max_thread`` ( *number* ) Purpose ******* By default, for each ``AD`` < *Base* > class there is only one tape that records :ref:`glossary@AD of Base` operations. This tape is a global variable and hence it cannot be used by multiple OpenMP threads at the same time. The ``omp_max_thread`` function is used to set the maximum number of OpenMP threads that can be active. In this case, there is a different tape corresponding to each ``AD`` < *Base* > class and thread pair. number ****** The argument *number* has prototype ``size_t`` *number* It must be greater than zero and specifies the maximum number of OpenMp threads that will be active at one time. Independent *********** Each call to :ref:`Independent(x)` creates a new :ref:`glossary@Tape@Active` tape. All of the operations with the corresponding variables must be preformed by the same OpenMP thread. This includes the corresponding call to :ref:`f.Dependent(x,y)` or the :ref:`ADFun f(x, y)` during which the tape stops recording and the variables become parameters. Restriction *********** No tapes can be :ref:`glossary@Tape@Active` when this function is called. {xrst_end omp_max_thread} ----------------------------------------------------------------------------- */ // BEGIN CppAD namespace namespace CppAD { template void AD::omp_max_thread(size_t number) { # ifdef _OPENMP thread_alloc::parallel_setup( number, omp_alloc::in_parallel, omp_alloc::get_thread_num ); # else CPPAD_ASSERT_KNOWN( number == 1, "omp_max_thread: number > 1 and _OPENMP is not defined" ); # endif parallel_ad(); } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/opt_val_hes.hpp ================================================ # ifndef CPPAD_CORE_OPT_VAL_HES_HPP # define CPPAD_CORE_OPT_VAL_HES_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin opt_val_hes app} {xrst_spell signdet sy yy } Jacobian and Hessian of Optimal Values ###################################### Syntax ****** | *signdet* = ``opt_val_hes`` ( *x* , *y* , *fun* , *jac* , *hes* ) See Also ******** :ref:`BenderQuad-name` Reference ********* Algorithmic differentiation of implicit functions and optimal values, Bradley M. Bell and James V. Burke, Advances in Automatic Differentiation, 2008, Springer. Purpose ******* We are given a function :math:`S : \B{R}^n \times \B{R}^m \rightarrow \B{R}^\ell` and we define :math:`F : \B{R}^n \times \B{R}^m \rightarrow \B{R}` and :math:`V : \B{R}^n \rightarrow \B{R}` by .. math:: :nowrap: \begin{eqnarray} F(x, y) & = & \sum_{k=0}^{\ell-1} S_k ( x , y) \\ V(x) & = & F [ x , Y(x) ] \\ 0 & = & \partial_y F [x , Y(x) ] \end{eqnarray} We wish to compute the Jacobian and possibly also the Hessian, of :math:`V (x)`. BaseVector ********** The type *BaseVector* must be a :ref:`SimpleVector-name` class. We use *Base* to refer to the type of the elements of *BaseVector* ; i.e., *BaseVector* :: ``value_type`` x * The argument *x* has prototype ``const`` *BaseVector* & *x* and its size must be equal to *n* . It specifies the point at which we evaluating the Jacobian :math:`V^{(1)} (x)` (and possibly the Hessian :math:`V^{(2)} (x)`). y * The argument *y* has prototype ``const`` *BaseVector* & *y* and its size must be equal to *m* . It must be equal to :math:`Y(x)`; i.e., it must solve the implicit equation .. math:: 0 = \partial_y F ( x , y) Fun *** The argument *fun* is an object of type *Fun* which must support the member functions listed below. CppAD will may be recording operations of the type ``AD`` < *Base* > when these member functions are called. These member functions must not stop such a recording; e.g., they must not call :ref:`AD\::abort_recording` . Fun::ad_vector ============== The type *Fun* :: ``ad_vector`` must be a :ref:`SimpleVector-name` class with elements of type ``AD`` < *Base* > ; i.e. *Fun* :: ``ad_vector::value_type`` is equal to ``AD`` < *Base* > . fun.ell ======= The type *Fun* must support the syntax *ell* = *fun* . ``ell`` () where *ell* has prototype ``size_t`` *ell* and is the value of :math:`\ell`; i.e., the number of terms in the summation. One can choose *ell* equal to one, and have :math:`S(x,y)` the same as :math:`F(x, y)`. Each of the functions :math:`S_k (x , y)`, (in the summation defining :math:`F(x, y)`) is differentiated separately using AD. For very large problems, breaking :math:`F(x, y)` into the sum of separate simpler functions may reduce the amount of memory necessary for algorithmic differentiation and there by speed up the process. fun.s ===== The type *Fun* must support the syntax *s_k* = *fun* . ``s`` ( *k* , *x* , *y* ) The *fun* . ``s`` argument *k* has prototype ``size_t`` *k* and is between zero and *ell* ``- 1`` . The argument *x* to *fun* . ``s`` has prototype ``const`` *Fun* :: ``ad_vector&`` *x* and its size must be equal to *n* . The argument *y* to *fun* . ``s`` has prototype ``const`` *Fun* :: ``ad_vector&`` *y* and its size must be equal to *m* . The *fun* . ``s`` result *s_k* has prototype ``AD`` < *Base* > *s_k* and its value must be given by :math:`s_k = S_k ( x , y )`. fun.sy ====== The type *Fun* must support the syntax *sy_k* = *fun* . ``sy`` ( *k* , *x* , *y* ) The argument *k* to *fun* . ``sy`` has prototype ``size_t`` *k* The argument *x* to *fun* . ``sy`` has prototype ``const`` *Fun* :: ``ad_vector&`` *x* and its size must be equal to *n* . The argument *y* to *fun* . ``sy`` has prototype ``const`` *Fun* :: ``ad_vector&`` *y* and its size must be equal to *m* . The *fun* . ``sy`` result *sy_k* has prototype *Fun* :: ``ad_vector`` *sy_k* its size must be equal to *m* , and its value must be given by :math:`sy_k = \partial_y S_k ( x , y )`. jac *** The argument *jac* has prototype *BaseVector* & *jac* and has size *n* or zero. The input values of its elements do not matter. If it has size zero, it is not affected. Otherwise, on output it contains the Jacobian of :math:`V (x)`; i.e., for :math:`j = 0 , \ldots , n-1`, .. math:: jac[ j ] = V^{(1)} (x)_j where *x* is the first argument to ``opt_val_hes`` . hes *** The argument *hes* has prototype *BaseVector* & *hes* and has size *n* * *n* or zero. The input values of its elements do not matter. If it has size zero, it is not affected. Otherwise, on output it contains the Hessian of :math:`V (x)`; i.e., for :math:`i = 0 , \ldots , n-1`, and :math:`j = 0 , \ldots , n-1`, .. math:: hes[ i * n + j ] = V^{(2)} (x)_{i,j} signdet ******* If *hes* has size zero, *signdet* is not defined. Otherwise the return value *signdet* is the sign of the determinant for :math:`\partial_{yy}^2 F(x , y)`. If it is zero, then the matrix is singular and the Hessian is not computed ( *hes* is not changed). Example ******* {xrst_toc_hidden example/general/opt_val_hes.cpp } The file :ref:`opt_val_hes.cpp-name` contains an example and test of this operation. {xrst_end opt_val_hes} ----------------------------------------------------------------------------- */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file opt_val_hes.hpp \brief Computing Jabobians and Hessians of Optimal Values */ /*! Computing Jabobians and Hessians of Optimal Values We are given a function \f$ S : {\rm R}^n \times {\rm R}^m \rightarrow {\rm R}^\ell \f$ and we define \f$ F : {\rm R}^n \times {\rm R}^m \rightarrow {\rm R} \f$ and \f$ V : {\rm R}^n \rightarrow {\rm R} \f$ by \f[ \begin{array}{rcl} F(x, y) & = & \sum_{k=0}^{\ell-1} S_k ( x , y) \\ V(x) & = & F [ x , Y(x) ] \\ 0 & = & \partial_y F [x , Y(x) ] \end{array} \f] We wish to compute the Jacobian and possibly also the Hessian, of \f$ V (x) \f$. \tparam BaseVector The type BaseVector must be a SimpleVector class. We use Base to refer to the type of the elements of BaseVector; i.e., BaseVector::value_type. \param x is a vector with size n. It specifies the point at which we evaluating the Jacobian \f$ V^{(1)} (x) \f$ (and possibly the Hessian \f$ V^{(2)} (x) \f$). \param y is a vector with size m. It must be equal to \f$ Y(x) \f$; i.e., it must solve the implicit equation \f[ 0 = \partial_y F ( x , y) \f] \param fun The argument fun is an object of type Fun which must support the member functions listed below. CppAD will may be recording operations of the type AD when these member functions are called. These member functions must not stop such a recording; e.g., they must not call AD::abort_recording. \par Fun::ad_vector The type Fun::ad_vector must be a SimpleVector class with elements of type AD; i.e. Fun::ad_vector::value_type is equal to AD. \par fun.ell the type Fun must support the syntax \verbatim ell = fun.ell() \endverbatim where ell is a size_t value that is set to \f$ \ell \f$; i.e., the number of terms in the summation. \par fun.s The type Fun must support the syntax \verbatim s_k = fun.s(k, x, y) \endverbatim The argument k has prototype size_t k. The argument x has prototype const Fun::ad_vector& x and its size must be equal to n. The argument y has prototype const Fun::ad_vector& y and its size must be equal to m. The return value s_k has prototype AD s_k and its value must be given by \f$ s_k = S_k ( x , y ) \f$. \par fun.sy The type Fun must support the syntax \verbatim sy_k = fun.sy(k, x, y) \endverbatim The argument k has prototype size_t k. The argument x has prototype const Fun::ad_vector& x and its size must be equal to n. The argument y has prototype const Fun::ad_vector& y and its size must be equal to m. The return value sy_k has prototype Fun::ad_vector& sy_k, its size is m and its value must be given by \f$ sy_k = \partial_y S_k ( x , y ) \f$. \param jac is a vector with size n or zero. The input values of its elements do not matter. If it has size zero, it is not affected. Otherwise, on output it contains the Jacobian of \f$ V (x) \f$; i.e., for \f$ j = 0 , \ldots , n-1 \f$, \f[ jac[ j ] = V^{(1)} (x)_j \f] $$ where x is the first argument to opt_val_hes. \param hes is a vector with size n * n or zero. The input values of its elements do not matter. If it has size zero, it is not affected. Otherwise, on output it contains the Hessian of \f$ V (x) \f$; i.e., for \f$ i = 0 , \ldots , n-1 \f$, and \f$ j = 0 , \ldots , n-1 \f$, \f[ hes[ i * n + j ] = V^{(2)} (x)_{i,j} \f] \return If hes.size() == 0, the return value is not defined. Otherwise, the return value is the sign of the determinant for \f$ \partial_{yy}^2 F(x , y) \f$$. If it is zero, then the matrix is singular and hes is not set to its specified value. */ template int opt_val_hes( const BaseVector& x , const BaseVector& y , Fun fun , BaseVector& jac , BaseVector& hes ) { // determine the base type typedef typename BaseVector::value_type Base; // check that BaseVector is a SimpleVector class with Base elements CheckSimpleVector(); // determine the AD vector type typedef typename Fun::ad_vector ad_vector; // check that ad_vector is a SimpleVector class with AD elements CheckSimpleVector< AD , ad_vector >(); // size of the x and y spaces size_t n = size_t(x.size()); size_t m = size_t(y.size()); // number of terms in the summation size_t ell = fun.ell(); // check size of return values CPPAD_ASSERT_KNOWN( size_t(jac.size()) == n || jac.size() == 0, "opt_val_hes: size of the vector jac is not equal to n or zero" ); CPPAD_ASSERT_KNOWN( size_t(hes.size()) == n * n || hes.size() == 0, "opt_val_hes: size of the vector hes is not equal to n * n or zero" ); // some temporary indices size_t i, j, k; // AD version of S_k(x, y) ad_vector s_k(1); // ADFun version of S_k(x, y) ADFun S_k; // AD version of x ad_vector a_x(n); // AD version of y ad_vector a_y(n); if( jac.size() > 0 ) { // this is the easy part, computing the V^{(1)} (x) which is equal // to \partial_x F (x, y) (see Theorem 2 of the reference). // copy x and y to AD version for(j = 0; j < n; j++) a_x[j] = x[j]; for(j = 0; j < m; j++) a_y[j] = y[j]; // initialize summation for(j = 0; j < n; j++) jac[j] = Base(0.); // add in \partial_x S_k (x, y) for(k = 0; k < ell; k++) { // start recording Independent(a_x); // record s_k[0] = fun.s(k, a_x, a_y); // stop recording and store in S_k S_k.Dependent(a_x, s_k); // compute partial of S_k with respect to x BaseVector jac_k = S_k.Jacobian(x); // add \partial_x S_k (x, y) to jac for(j = 0; j < n; j++) jac[j] += jac_k[j]; } } // check if we are done if( hes.size() == 0 ) return 0; /* In this case, we need to compute the Hessian. Using Theorem 1 of the reference: Y^{(1)}(x) = - F_yy (x, y)^{-1} F_yx (x, y) Using Theorem 2 of the reference: V^{(2)}(x) = F_xx (x, y) + F_xy (x, y) Y^{(1)}(x) */ // Base and AD version of xy BaseVector xy(n + m); ad_vector a_xy(n + m); for(j = 0; j < n; j++) a_xy[j] = xy[j] = x[j]; for(j = 0; j < m; j++) a_xy[n+j] = xy[n+j] = y[j]; // Initialization summation for Hessian of F size_t nm_sq = (n + m) * (n + m); BaseVector F_hes(nm_sq); for(j = 0; j < nm_sq; j++) F_hes[j] = Base(0.); BaseVector hes_k(nm_sq); // add in Hessian of S_k to hes for(k = 0; k < ell; k++) { // start recording Independent(a_xy); // split out x for(j = 0; j < n; j++) a_x[j] = a_xy[j]; // split out y for(j = 0; j < m; j++) a_y[j] = a_xy[n+j]; // record s_k[0] = fun.s(k, a_x, a_y); // stop recording and store in S_k S_k.Dependent(a_xy, s_k); // when computing the Hessian it pays to optimize the tape S_k.optimize(); // compute Hessian of S_k hes_k = S_k.Hessian(xy, 0); // add \partial_x S_k (x, y) to jac for(j = 0; j < nm_sq; j++) F_hes[j] += hes_k[j]; } // Extract F_yx BaseVector F_yx(m * n); for(i = 0; i < m; i++) { for(j = 0; j < n; j++) F_yx[i * n + j] = F_hes[ (i+n)*(n+m) + j ]; } // Extract F_yy BaseVector F_yy(n * m); for(i = 0; i < m; i++) { for(j = 0; j < m; j++) F_yy[i * m + j] = F_hes[ (i+n)*(n+m) + j + n ]; } // compute - Y^{(1)}(x) = F_yy (x, y)^{-1} F_yx (x, y) BaseVector neg_Y_x(m * n); Base logdet; int signdet = CppAD::LuSolve(m, n, F_yy, F_yx, neg_Y_x, logdet); if( signdet == 0 ) return signdet; // compute hes = F_xx (x, y) + F_xy (x, y) Y^{(1)}(x) for(i = 0; i < n; i++) { for(j = 0; j < n; j++) { hes[i * n + j] = F_hes[ i*(n+m) + j ]; for(k = 0; k < m; k++) hes[i*n+j] -= F_hes[i*(n+m) + k+n] * neg_Y_x[k*n+j]; } } return signdet; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/optimize.hpp ================================================ # ifndef CPPAD_CORE_OPTIMIZE_HPP # define CPPAD_CORE_OPTIMIZE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # define CPPAD_CORE_OPTIMIZE_PRINT_RESULT 0 /* {xrst_begin optimize} {xrst_spell onetape substring } Optimize an ADFun Object Tape ############################# Syntax ****** | *f* . ``optimize`` () | *f* . ``optimize`` ( *options* ) | *flag* = *f* . ``exceed_collision_limit`` () Purpose ******* The operation sequence corresponding to an :ref:`ADFun-name` object can be very large and involve many operations; see the size functions in :ref:`fun_property-name` . The *f* . ``optimize`` procedure reduces the number of operations, and thereby the time and the memory, required to compute function and derivative values. f * The object *f* has prototype ``ADFun`` < *Base* > *f* options ******* This argument has prototype ``const std::string&`` *options* The default for *options* is the empty string. If it is present, it must consist of one or more of the options below separated by a single space character. no_conditional_skip =================== The ``optimize`` function can create conditional skip operators to improve the speed of conditional expressions; see :ref:`CondExp@Optimize` . If the sub-string ``no_conditional_skip`` appears in *options* , conditional skip operations are not be generated. This may make the optimize routine use significantly less memory and take less time to optimize *f* . If conditional skip operations are generated, it may save a significant amount of time when using *f* for :ref:`forward-name` or :ref:`reverse-name` mode calculations; see :ref:`number_skip-name` . no_compare_op ============= If the sub-string ``no_compare_op`` appears in *options* , comparison operators will be removed from the optimized function. These operators are necessary for the :ref:`compare_change-name` functions to be meaningful. On the other hand, they are not necessary, and take extra time, when the compare_change functions are not used. no_print_for_op =============== If the sub-string ``no_compare_op`` appears in *options* , :ref:`PrintFor-name` operations will be removed form the optimized function. These operators are useful for reporting problems evaluating derivatives at independent variable values different from those used to record a function. no_cumulative_sum_op ==================== If this sub-string appears, no cumulative sum operations will be generated during the optimization; see :ref:`optimize_cumulative_sum.cpp-name` . collision_limit=value ===================== If this substring appears, where *value* is a sequence of decimal digits, the optimizer's hash code collision limit will be set to *value* . When the collision limit is reached, the expressions with that hash code are removed and a new lists of expressions with that has code is started. The larger *value* , the more identical expressions the optimizer can recognize, but the slower the optimizer may run. The default for *value* is ``10`` . val_graph ========= If the sub-string ``val_graph`` appears in *options* , the value graph optimizer is used. This is a new (experimental) CppAD operation sequence optimizer. #. The val_graph optimizer has a much simpler implementation. #. It has better developer documentation #. It has examples and tests at the val_graph level. This makes it easy to change the val_graph optimizer. #. The optimized tape has very similar speed to the old optimizer; i.e., when the :ref:`speed_main@Global Options@onetape` option is present. For some of the :ref:`speed-name` test case the val_graph optimized tape is significantly faster. #. The val_graph optimizer take much longer to run. This is probably due to the conversion to and from a val_graph. no_conditional_skip ------------------- If the sub-string ``val_graph`` is present, the ``no_conditional_skip`` sub-string must also appear. collision_limit=value --------------------- If the sub-string ``val_graph`` is present, the ``collision_limit=value`` sub-string must **not** appear. Currently, there is no collision limit for the new optimizer. Re-Optimize *********** Before 2019-06-28, optimizing twice was not supported and would fail if cumulative sum operators were present after the first optimization. This is now supported but it is not expected to have much benefit. If you find a case where it does have a benefit, please inform the CppAD developers of this. Efficiency ********** If a :ref:`zero order forward` calculation is done during the construction of *f* , it will require more memory and time than required after the optimization procedure. In addition, it will need to be redone. For this reason, it is more efficient to use | |tab| ``ADFun`` < *Base* > *f* ; | |tab| *f* . ``Dependent`` ( *x* , *y* ); | |tab| *f* . ``optimize`` (); instead of | |tab| ``ADFun`` < *Base* > *f* ( *x* , *y* ) | |tab| *f* . ``optimize`` (); See the discussion about :ref:`sequence constructors` . Taylor Coefficients ******************* Any Taylor coefficients in the function object are lost; i.e., :ref:`f.size_order()` after the optimization is zero. (See the discussion about efficiency above.) Speed Testing ************* You can run the CppAD :ref:`speed` tests and see the corresponding changes in number of variables and execution time. Note that there is an interaction between using :ref:`speed_main@Global Options@optimize` and :ref:`speed_main@Global Options@onetape` . If *onetape* is true and *optimize* is true, the optimized tape will be reused many times. If *onetape* is false and *optimize* is true, the tape will be re-optimized for each test. Atomic Functions **************** There are some subtitle issue with optimized :ref:`atomic-name` functions :math:`v = g(u)`: rev_sparse_jac ============== The :ref:`atomic_two_rev_sparse_jac-name` function is be used to determine which components of *u* affect the dependent variables of *f* . For each atomic operation, the current :ref:`atomic_two_option@atomic_sparsity` setting is used to determine if ``pack_sparsity_enum`` , ``bool_sparsity_enum`` , or ``set_sparsity_enum`` is used to determine dependency relations between argument and result variables. nan === If *u* [ *i* ] does not affect the value of the dependent variables for *f* , the value of *u* [ *i* ] is set to :ref:`nan-name` . Checking Optimization ********************* If :ref:`Faq@Speed@NDEBUG` is not defined, and :ref:`f.size_order()` is greater than zero, a :ref:`forward_zero-name` calculation is done using the optimized version of *f* and the results are checked to see that they are the same as before. If they are not the same, the :ref:`ErrorHandler-name` is called with a known error message related to *f* . ``optimize`` () . exceed_collision_limit ********************** If the return value *flag* is true (false), the previous call to *f* . ``optimize`` exceed the :ref:`collision_limit` . Examples ******** {xrst_comment childtable without Example instead of Contents for header} {xrst_toc_hidden example/optimize/optimize_twice.cpp example/optimize/forward_active.cpp example/optimize/reverse_active.cpp example/optimize/compare_op.cpp example/optimize/print_for.cpp example/optimize/conditional_skip.cpp example/optimize/nest_conditional.cpp example/optimize/cumulative_sum.cpp } .. csv-table:: :widths: auto optimize_twice.cpp,:ref:`optimize_twice.cpp-title` optimize_forward_active.cpp,:ref:`optimize_forward_active.cpp-title` optimize_reverse_active.cpp,:ref:`optimize_reverse_active.cpp-title` optimize_compare_op.cpp,:ref:`optimize_compare_op.cpp-title` optimize_print_for.cpp,:ref:`optimize_print_for.cpp-title` optimize_conditional_skip.cpp,:ref:`optimize_conditional_skip.cpp-title` optimize_nest_conditional.cpp,:ref:`optimize_nest_conditional.cpp-title` optimize_cumulative_sum.cpp,:ref:`optimize_cumulative_sum.cpp-title` {xrst_end optimize} ----------------------------------------------------------------------------- */ # include /*! \file optimize.hpp Optimize a player object operation sequence */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! Optimize a player object operation sequence The operation sequence for this object is replaced by one with fewer operations but the same function and derivative values. \tparam Base base type for the operator; i.e., this operation was recorded using AD and computations by this routine are done using type Base. \param options \li If the sub-string "no_conditional_skip" appears, conditional skip operations will not be generated. This may make the optimize routine use significantly less memory and take significantly less time. \li If the sub-string "no_compare_op" appears, then comparison operators will be removed from the optimized tape. These operators are necessary for the compare_change function to be be meaningful in the resulting recording. On the other hand, they are not necessary and take extra time when compare_change is not used. */ template void ADFun::optimize(const std::string& options) { # if CPPAD_CORE_OPTIMIZE_PRINT_RESULT // size of operation sequence before optimizatiton size_t size_op_before = size_op(); # endif // number of independent variables size_t n_ind_var = ind_taddr_.size(); # ifndef NDEBUG // n_ind_dyn, ind_dynamic size_t n_ind_dyn = play_.n_dyn_independent(); CppAD::vector ind_dynamic(n_ind_dyn); // // n_dep_var, x, y, check, max_taylor, check_zero_order size_t n_dep_var = dep_taddr_.size(); CppAD::vector x(n_ind_var), y(n_dep_var), check(n_dep_var); Base max_taylor(0); bool check_zero_order = num_order_taylor_ > 0; if( check_zero_order ) { // // ind_dynamic for(size_t j = 0; j < n_ind_dyn; ++j) { const addr_t par_ind = play_.dyn2par_index()[j]; ind_dynamic[j] = play_.par_all()[par_ind]; } // // x // zero order coefficients for independent vars for(size_t j = 0; j < n_ind_var; j++) { CPPAD_ASSERT_UNKNOWN( play_.GetOp(j+1) == local::InvOp ); CPPAD_ASSERT_UNKNOWN( ind_taddr_[j] == j+1 ); x[j] = taylor_[ ind_taddr_[j] * cap_order_taylor_ + 0]; } // y // zero order coefficients for dependent vars for(size_t i = 0; i < n_dep_var; i++) { CPPAD_ASSERT_UNKNOWN( dep_taddr_[i] < num_var_tape_ ); y[i] = taylor_[ dep_taddr_[i] * cap_order_taylor_ + 0]; } // max_taylor // maximum zero order coefficient not counting BeginOp at beginning // (which is corresponds to uninitialized memory). for(size_t i = 1; i < num_var_tape_; i++) { if( abs_geq(taylor_[i*cap_order_taylor_+0] , max_taylor) ) max_taylor = taylor_[i*cap_order_taylor_+0]; } } # endif // // val_graph bool val_graph = options.find("val_graph") != std::string::npos; // if( val_graph ) { val_optimize(options); exceed_collision_limit_ = false; } else { // place to store the optimized version of the recording local::recorder rec; // create the optimized recording size_t exceed = false; switch( play_.address_type() ) { case local::play::unsigned_short_enum: exceed = local::optimize::optimize_run( options, n_ind_var, dep_taddr_, &play_, &rec ); break; case local::play::addr_t_enum: exceed = local::optimize::optimize_run( options, n_ind_var, dep_taddr_, &play_, &rec ); break; case local::play::size_t_enum: exceed = local::optimize::optimize_run( options, n_ind_var, dep_taddr_, &play_, &rec ); break; default: CPPAD_ASSERT_UNKNOWN(false); } exceed_collision_limit_ = exceed; // now replace the recording play_.get_recording(rec, n_ind_var); } // number of variables in the recording num_var_tape_ = play_.num_var(); // set flag so this function knows it has been optimized has_been_optimized_ = true; // free memory allocated for sparse Jacobian calculation // (the results are no longer valid) for_jac_sparse_pack_.resize(0, 0); for_jac_sparse_set_.resize(0,0); // free old Taylor coefficient memory taylor_.clear(); num_order_taylor_ = 0; cap_order_taylor_ = 0; // resize and initialize conditional skip vector // (must use player size because it now has the recoreder information) cskip_op_.resize( play_.num_var_op() ); // resize subgraph_info_ subgraph_info_.resize( ind_taddr_.size(), // n_ind dep_taddr_.size(), // n_dep play_.num_var_op(), // n_op play_.num_var() // n_var ); # ifndef NDEBUG if( check_zero_order ) { std::stringstream s; // // zero order forward calculation using new operation sequence new_dynamic(ind_dynamic); check = Forward(0, x, s); // check results Base eps99 = Base(99) * CppAD::numeric_limits::epsilon(); for(size_t i = 0; i < n_dep_var; i++) if( ! abs_geq( eps99 * max_taylor , check[i] - y[i] ) ) { std::string msg = "Error during check of f.optimize()."; msg += "\neps99 * max_taylor = " + to_string(eps99 * max_taylor); msg += "\ncheck[i] = " + to_string(check[i]); msg += "\ny[i] = " + to_string(y[i]); CPPAD_ASSERT_KNOWN( abs_geq( eps99 * max_taylor , check[i] - y[i] ) , msg.c_str() ); } // Erase memory that this calculation was done so NDEBUG gives // same final state for this object (from users perspective) num_order_taylor_ = 0; } # endif # if CPPAD_CORE_OPTIMIZE_PRINT_RESULT // size of operation sequence after optimizatiton size_t size_op_after = size_op(); std::cout << "optimize: size_op: before = " << size_op_before << ", after = " << size_op_after << "\n"; # endif } } // END_CPPAD_NAMESPACE # undef CPPAD_CORE_OPTIMIZE_PRINT_RESULT # endif ================================================ FILE: include/cppad/core/ordered.hpp ================================================ # ifndef CPPAD_CORE_ORDERED_HPP # define CPPAD_CORE_ORDERED_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file ordered.hpp Check and AD values ordering properties relative to zero. */ // GreaterThanZero ============================================================ /*! Check if an AD is greater than zero. \param x value we are checking. \return returns true iff the x is greater than zero. */ template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION bool GreaterThanZero(const AD &x) { return GreaterThanZero(x.value_); } // GreaterThanOrZero ========================================================= /*! Check if an AD is greater than or equal zero. \param x value we are checking. \return returns true iff the x is greater than or equal zero. */ template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION bool GreaterThanOrZero(const AD &x) { return GreaterThanOrZero(x.value_); } // LessThanZero ============================================================ /*! Check if an AD is less than zero. \param x value we are checking. \return returns true iff the x is less than zero. */ template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION bool LessThanZero(const AD &x) { return LessThanZero(x.value_); } // LessThanOrZero ========================================================= /*! Check if an AD is less than or equal zero. \param x value we are checking. \return returns true iff the x is less than or equal zero. */ template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION bool LessThanOrZero(const AD &x) { return LessThanOrZero(x.value_); } // abs_geq ========================================================= /*! Check if absolute value of one AD is greater or equal another. \param x value we are checking if it is greater than or equal other. \param y value we are checking if it is less than other. \return returns true iff the absolute value of x is greater than or equal absolute value of y. */ template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION bool abs_geq(const AD& x, const AD& y) { return abs_geq(x.value_, y.value_); } // ============================================================================ } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/parallel_ad.hpp ================================================ # ifndef CPPAD_CORE_PARALLEL_AD_HPP # define CPPAD_CORE_PARALLEL_AD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin parallel_ad} {xrst_spell rosen runge teardown } Enable AD Calculations During Parallel Mode ########################################### Syntax ****** | ``parallel_ad`` < *Base* >() Purpose ******* The function ``parallel_ad`` < *Base* >() must be called before any ``AD`` < *Base>* objects are used in :ref:`parallel` mode. In addition, if this routine is called after one is done using parallel mode, it will free extra memory used to keep track of the multiple ``AD`` < *Base* > tapes required for parallel execution. Discussion ********** By default, for each ``AD`` < *Base* > class there is only one tape that records :ref:`glossary@AD of Base` operations. This tape is a global variable and hence it cannot be used by multiple threads at the same time. The :ref:`parallel_setup` function informs CppAD of the maximum number of threads that can be active in parallel mode. This routine does extra setup (and teardown) for the particular *Base* type. CheckSimpleVector ***************** This routine has the side effect of calling ``CheckSimpleVector`` for some of the possible :ref:`CheckSimpleVector@Scalar` and :ref:`CheckSimpleVector@Vector` cases. The set of these cases may increase in the future and currently includes the following: .. csv-table:: :header: Scalar, Vector ``bool`` , ``CppAD::vectorBool`` ``size_t`` , ``CppAD::vector`` *Base* , *vector* < *Base* > ``AD`` < *Base* > , *vector* ``AD`` < *Base* > Where *vector* above is ``CppAD::vector`` , ``std::vector`` , and the :ref:`cppad_testvector-name` . Example ******* The files :ref:`openmp_get_started.cpp-name` , :ref:`bthread_get_started.cpp-name` , and :ref:`pthread_get_started.cpp-name` , contain examples and tests that implement this function. Restriction *********** This routine cannot be called in parallel mode or while there is a tape recording ``AD`` < *Base* > operations. Other Initialization ******************** If the following routines have static memory and must be called once before being used in parallel mode: #. :ref:`CheckSimpleVector ` #. :ref:`thread_alloc, memory_leak ` #. :ref:`Rosen34 ` #. :ref:`Runge45 ` #. :ref:`discrete ` #. :ref:`atomic_one ` {xrst_end parallel_ad} ----------------------------------------------------------------------------- */ # include # include # include # include // BEGIN CppAD namespace namespace CppAD { /*! Enable parallel execution mode with AD by initializing static variables that my be used. */ template void parallel_ad(void) { CPPAD_ASSERT_KNOWN( ! thread_alloc::in_parallel() , "parallel_ad must be called before entering parallel execution mode." ); CPPAD_ASSERT_KNOWN( AD::tape_ptr() == nullptr , "parallel_ad cannot be called while a tape recording is in progress" ); // ensure statics in following functions are initialized ErrorHandler::Current(); // error_handler.hpp elapsed_seconds(); // elapsed_seconds.hpp local::num_arg_dyn(local::abs_dyn); // op_code_dyn.hpp local::op_name_dyn(local::abs_dyn); // op_code_dyn.hpp local::NumArg(local::BeginOp); // op_code_var.hpp local::NumRes(local::BeginOp); // op_code_var.hpp local::one_element_std_set(); // std_set.hpp local::two_element_std_set(); // std_set.hpp // the sparse_pack class has member functions with static data local::sparse::pack_setvec sp; sp.resize(1, 1); // so can call add_element sp.add_element(0, 0); // has static data sp.clear(0); // has static data sp.is_element(0, 0); // has static data local::sparse::pack_setvec::const_iterator itr(sp, 0); // has static data ++itr; // has static data // statics that depend on the value of Base AD::tape_id_ptr(0); // tape_link.hpp AD::tape_handle(0); // tape_link.hpp local::val_graph::enable_parallel(); // val_graph/*_op.hpp discrete::List(); // discrete.hpp // Some check_simple_vector.hpp cases // CheckSimpleVector< bool, CppAD::vectorBool >(); CheckSimpleVector< size_t, CppAD::vector >(); CheckSimpleVector< Base, CppAD::vector >(); CheckSimpleVector< AD, CppAD::vector< AD > >(); // CheckSimpleVector< Base, std::vector >(); CheckSimpleVector< AD, std::vector< AD > >(); // # if CPPAD_BOOSTVECTOR CheckSimpleVector< Base, boost::numeric::ublas::vector >(); CheckSimpleVector< AD, boost::numeric::ublas::vector< AD > >(); # endif // # if CPPAD_EIGENVECTOR CheckSimpleVector< Base, CppAD::eigen_vector >(); CheckSimpleVector< AD, CppAD::eigen_vector< AD > > (); # endif } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/pow.hpp ================================================ # ifndef CPPAD_CORE_POW_HPP # define CPPAD_CORE_POW_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin pow} The AD Power Function ##################### Syntax ****** | *z* = ``pow`` ( *x* , *y* ) See Also ******** :ref:`pow_int-name` Purpose ******* Determines the value of the power function which is defined by .. math:: {\rm pow} (x, y) = x^y If y is a Variable ================== If *y* is a variable, the ``pow`` function may use logarithms and exponentiation to compute derivatives. This will not work if *x* is less than or equal zero. If y is a Parameter =================== If *y* is a parameter, a different method is used to compute the derivatives; see :ref:`pow_forward-name` . In the special case where *x* is zero, zero is returned as the derivative. This is correct when *y* minus the order of the derivative is greater than zero. If *y* minus the order of the derivative is zero, then *y* is an integer. If *y* minus the order of the derivative is less than zero, the actual derivative is infinite. If y is an Integer ================== If the value of *y* is an integer, the :ref:`pow_int-name` function can be used to compute this value using only multiplication (and division if *y* is negative). This will work even if *x* is less than or equal zero. x * The argument *x* has one of the following prototypes | |tab| ``const`` *Base* & *x* | |tab| ``const AD`` < *Base* >& *x* | |tab| ``const VecAD`` < *Base* >:: ``reference&`` *x* y * The argument *y* has one of the following prototypes | |tab| ``const`` *Base* & *y* | |tab| ``const AD`` < *Base* >& *y* | |tab| ``const VecAD`` < *Base* >:: ``reference&`` *y* z * If both *x* and *y* are *Base* objects, the result *z* is also a *Base* object. Otherwise, it has prototype ``AD`` < *Base* > *z* Operation Sequence ****************** This is an AD of *Base* :ref:`atomic operation` and hence is part of the current AD of *Base* :ref:`operation sequence` . Example ******* {xrst_toc_hidden example/general/pow.cpp example/general/pow_nan.cpp } The files :ref:`pow.cpp-name` and :ref:`pow_nan.cpp-name` are examples tests of this function. {xrst_end pow} ------------------------------------------------------------------------------- */ // BEGIN CppAD namespace namespace CppAD { // case where x and y are AD ----------------------------------------- template AD pow(const AD& x, const AD& y) { // compute the Base part AD result; result.value_ = pow(x.value_, y.value_); CPPAD_ASSERT_UNKNOWN( Parameter(result) ); // check if there is a recording in progress local::ADTape* tape = AD::tape_ptr(); if( tape == nullptr ) return result; tape_id_t tape_id = tape->id_; // tape_id cannot match the default value for tape_id_; i.e., 0 CPPAD_ASSERT_UNKNOWN( tape_id > 0 ); // check if x and y tapes match bool match_x = x.tape_id_ == tape_id; bool match_y = y.tape_id_ == tape_id; // check if x and y are dynamic parameters bool dyn_x = match_x & (x.ad_type_ == dynamic_enum); bool dyn_y = match_y & (y.ad_type_ == dynamic_enum); // check if x and y are variables bool var_x = match_x & (x.ad_type_ != dynamic_enum); bool var_y = match_y & (y.ad_type_ != dynamic_enum); CPPAD_ASSERT_KNOWN( x.tape_id_ == y.tape_id_ || ! match_x || ! match_y , "pow: AD variables or dynamic parameters on different threads." ); if( var_x ) { if( var_y ) { // result = variable^variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::PowvvOp) == 3 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::PowvvOp) == 2 ); // put operand addresses in tape tape->Rec_.PutArg(x.taddr_, y.taddr_); // put operator in the tape result.taddr_ = tape->Rec_.PutOp(local::PowvvOp); // make result a variable result.tape_id_ = tape_id; result.ad_type_ = variable_enum; } else if( (! dyn_y) && IdenticalZero( y.value_ ) ) { // result = variable^0 } else { // result = variable^parameter CPPAD_ASSERT_UNKNOWN( local::NumRes(local::PowvpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::PowvpOp) == 2 ); // put operand addresses in tape addr_t p = y.taddr_; if( ! dyn_y ) p = tape->Rec_.put_con_par(y.value_); tape->Rec_.PutArg(x.taddr_, p); // put operator in the tape result.taddr_ = tape->Rec_.PutOp(local::PowvpOp); // make result a variable result.tape_id_ = tape_id; result.ad_type_ = variable_enum; } } else if( var_y ) { if( (! dyn_x) && IdenticalZero(x.value_) ) { // result = 0^variable } else { // result = parameter^variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::PowpvOp) == 3 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::PowpvOp) == 2 ); // put operand addresses in tape addr_t p = x.taddr_; if( ! dyn_x ) p = tape->Rec_.put_con_par(x.value_); tape->Rec_.PutArg(p, y.taddr_); // put operator in the tape result.taddr_ = tape->Rec_.PutOp(local::PowpvOp); // make result a variable result.tape_id_ = tape_id; result.ad_type_ = variable_enum; } } else if( dyn_x | dyn_y ) { if( (!dyn_x) && IdenticalZero(x.value_) ) { // result = 0^dynamic } else if( (! dyn_y) && IdenticalZero( y.value_ ) ) { // result = dynamic^0 } else { addr_t arg0 = x.taddr_; addr_t arg1 = y.taddr_; if( ! dyn_x ) arg0 = tape->Rec_.put_con_par(x.value_); if( ! dyn_y ) arg1 = tape->Rec_.put_con_par(y.value_); // // parameters with a dynamic parameter result result.taddr_ = tape->Rec_.put_dyn_par( result.value_, local::pow_dyn, arg0, arg1 ); result.tape_id_ = tape_id; result.ad_type_ = dynamic_enum; } } else { CPPAD_ASSERT_KNOWN( ! (dyn_x | dyn_y) , "pow: one operand is a dynamic parameter and other not a variable" ); } return result; } // ========================================================================= // Fold operations in same way as CPPAD_FOLD_AD_VALUED_BINARY_OPERATOR(Op) // ------------------------------------------------------------------------- // Operations with VecAD_reference and AD only template AD pow(const AD& x, const VecAD_reference& y) { return pow(x, y.ADBase()); } template AD pow(const VecAD_reference& x, const VecAD_reference& y) { return pow(x.ADBase(), y.ADBase()); } template AD pow(const VecAD_reference& x, const AD& y) { return pow(x.ADBase(), y); } // ------------------------------------------------------------------------- // Operations with Base template AD pow(const Base& x, const AD& y) { return pow(AD(x), y); } template AD pow(const Base& x, const VecAD_reference& y) { return pow(AD(x), y.ADBase()); } template AD pow(const AD& x, const Base& y) { return pow(x, AD(y)); } template AD pow(const VecAD_reference& x, const Base& y) { return pow(x.ADBase(), AD(y)); } // ------------------------------------------------------------------------- // Operations with double template AD pow(const double& x, const AD& y) { return pow(AD(x), y); } template AD pow(const double& x, const VecAD_reference& y) { return pow(AD(x), y.ADBase()); } template AD pow(const AD& x, const double& y) { return pow(x, AD(y)); } template AD pow(const VecAD_reference& x, const double& y) { return pow(x.ADBase(), AD(y)); } // ------------------------------------------------------------------------- // Special case to avoid ambiguity when Base is double inline AD pow(const double& x, const AD& y) { return pow(AD(x), y); } inline AD pow(const double& x, const VecAD_reference& y) { return pow(AD(x), y.ADBase()); } inline AD pow(const AD& x, const double& y) { return pow(x, AD(y)); } inline AD pow(const VecAD_reference& x, const double& y) { return pow(x.ADBase(), AD(y)); } // ========================================================================= // Fold operations for the cases where x is an int, // but let cppad/utility/pow_int.hpp handle the cases where y is an int. // ------------------------------------------------------------------------- template AD pow (const int& x, const VecAD_reference& y) { return pow(AD(x), y.ADBase()); } template AD pow (const int& x, const AD& y) { return pow(AD(x), y); } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/print_for.hpp ================================================ # ifndef CPPAD_CORE_PRINT_FOR_HPP # define CPPAD_CORE_PRINT_FOR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin PrintFor} {xrst_spell notpos } Printing AD Values During Forward Mode ###################################### Syntax ****** | *f* . ``Forward`` (0, *x* ) | *f* . ``Forward`` (0, *x* , *s* ) | ``PrintFor`` ( *before* , *value* ) | ``PrintFor`` ( *notpos* , *before* , *value* , *after* ) See Also ******** :ref:`ad_output-name` Purpose ******* The :ref:`zero order forward` mode command *f* . ``Forward`` (0, *x* ) sets the :ref:`glossary@Tape@Independent Variable` vector equal to *x* . It then computes a value for all of the dependent variables in the :ref:`operation sequence` corresponding to *f* . Putting a ``PrintFor`` in the operation sequence, prints *value* , corresponding to *x* , to be printed during zero order forward operations. f.Forward(0, x) *************** The objects *f* , *x* , and the purpose for this operation, are documented in :ref:`Forward-name` . notpos ****** If present, the argument *notpos* has one of the following prototypes | |tab| ``const AD`` < *Base* >& *notpos* | |tab| ``const VecAD`` < *Base* >:: ``reference&`` *notpos* In this case the text and *value* will be printed if and only if *notpos* is not positive (greater than zero) and a finite number. before ****** The argument *before* has prototype ``const char`` * *before* This text is written to ``std::cout`` before *value* . value ***** The argument *value* has one of the following prototypes | |tab| ``const AD`` < *Base* >& *value* | |tab| ``const VecAD`` < *Base* >:: ``reference&`` *value* The *value* , that corresponds to *x* , is written to ``std::cout`` during the execution of *f* . ``Forward`` (0, *x* ) Note that *value* may be a :ref:`glossary@Variable` or :ref:`glossary@Parameter` . If a parameter is :ref:`glossary@Parameter@Dynamic` its value will depend on the previous call to :ref:`new_dynamic-name` . after ***** The argument *after* has prototype ``const char`` * *after* This text is written to ``std::cout`` after *value* . s * You can redirect this output to any standard output stream using the syntax *f* . ``Forward`` (0, *x* , *s* ) see :ref:`forward_zero@s` in the zero order forward mode documentation. Discussion ********** This is helpful for understanding why tape evaluations have trouble. For example, if one of the operations in *f* is ``log`` ( *value* ) and *value* < 0 , the corresponding result will :ref:`nan-name` . Example ******* {xrst_toc_hidden example/print_for/print_for.cpp example/general/print_for.cpp } The program :ref:`print_for_cout.cpp-name` is an example and test that prints to standard output. The output of this program states the conditions for passing and failing the test. The function :ref:`print_for_string.cpp-name` is an example and test that prints to an standard string stream. This function automatically check for correct output. {xrst_end PrintFor} ------------------------------------------------------------------------------ */ # include namespace CppAD { template void PrintFor( const AD& notpos , const char* before , const AD& value , const char* after ) { CPPAD_ASSERT_NARG_NRES(local::PriOp, 5, 0); // check for case where we are not recording operations local::ADTape* tape = AD::tape_ptr(); if( tape == nullptr ) return; CPPAD_ASSERT_KNOWN( std::strlen(before) <= 1000 , "PrintFor: length of before is greater than 1000 characters" ); CPPAD_ASSERT_KNOWN( std::strlen(after) <= 1000 , "PrintFor: length of after is greater than 1000 characters" ); addr_t arg0, arg1, arg2, arg3, arg4; // arg[0] = base 2 representation of [Var(notpos), Var(value)] arg0 = 0; // arg[1] = address for notpos if( Constant(notpos) ) arg1 = tape->Rec_.put_con_par(notpos.value_); else if( Dynamic(notpos) ) arg1 = notpos.taddr_; else { arg0 += 1; arg1 = notpos.taddr_; } // arg[2] = address of before arg2 = tape->Rec_.PutTxt(before); // arg[3] = address for value if( Constant(value) ) arg3 = tape->Rec_.put_con_par(value.value_); else if( Dynamic(value) ) arg3 = value.taddr_; else { arg0 += 2; arg3 = value.taddr_; } // arg[4] = address of after arg4 = tape->Rec_.PutTxt(after); // put the operator in the tape tape->Rec_.PutArg(arg0, arg1, arg2, arg3, arg4); tape->Rec_.PutOp(local::PriOp); } // Fold all other cases into the case above template void PrintFor(const char* before, const AD& value) { PrintFor(AD(0), before, value, "" ); } // template void PrintFor(const char* before, const VecAD_reference& value) { PrintFor(AD(0), before, value.ADBase(), "" ); } // template void PrintFor( const VecAD_reference& notpos , const char *before , const VecAD_reference& value , const char *after ) { PrintFor(notpos.ADBase(), before, value.ADBase(), after); } // template void PrintFor( const VecAD_reference& notpos , const char *before , const AD& value , const char *after ) { PrintFor(notpos.ADBase(), before, value, after); } // template void PrintFor( const AD& notpos , const char *before , const VecAD_reference& value , const char *after ) { PrintFor(notpos, before, value.ADBase(), after); } } # endif ================================================ FILE: include/cppad/core/rev_hes_sparsity.hpp ================================================ # ifndef CPPAD_CORE_REV_HES_SPARSITY_HPP # define CPPAD_CORE_REV_HES_SPARSITY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin rev_hes_sparsity} {xrst_spell rc } Reverse Mode Hessian Sparsity Patterns ###################################### Syntax ****** | *f* . ``rev_hes_sparsity`` ( | |tab| *select_range* , *transpose* , *internal_bool* , *pattern_out* | ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to the operation sequence stored in *f* . Fix :math:`R \in \B{R}^{n \times \ell}`, :math:`s \in \B{R}^m` and define the function .. math:: H(x) = ( s^\R{T} F )^{(2)} ( x ) R Given a :ref:`glossary@Sparsity Pattern` for :math:`R` and for the vector :math:`s`, ``rev_hes_sparsity`` computes a sparsity pattern for :math:`H(x)`. x * Note that the sparsity pattern :math:`H(x)` corresponds to the operation sequence stored in *f* and does not depend on the argument *x* . BoolVector ********** The type *BoolVector* is a :ref:`SimpleVector-name` class with :ref:`elements of type` ``bool`` . SizeVector ********** The type *SizeVector* is a :ref:`SimpleVector-name` class with :ref:`elements of type` ``size_t`` . f * The object *f* has prototype ``ADFun`` < *Base* > *f* R * The sparsity pattern for the matrix :math:`R` is specified by :ref:`for_jac_sparsity@pattern_in` in the previous call | |tab| *f* . ``for_jac_sparsity`` ( | |tab| |tab| *pattern_in* , *transpose* , *dependency* , *internal_bool* , *pattern_out* | ) select_range ************ The argument *select_range* has prototype ``const`` *BoolVector* & *select_range* It has size :math:`m` and specifies which components of the vector :math:`s` are non-zero; i.e., *select_range* [ *i* ] is true if and only if :math:`s_i` is possibly non-zero. transpose ********* This argument has prototype ``bool`` *transpose* See :ref:`rev_hes_sparsity@pattern_out` below. internal_bool ************* If this is true, calculations are done with sets represented by a vector of boolean values. Otherwise, a vector of sets of integers is used. This must be the same as in the previous call to *f* . ``for_jac_sparsity`` . pattern_out *********** This argument has prototype ``sparse_rc`` < *SizeVector* >& *pattern_out* This input value of *pattern_out* does not matter. If *transpose* it is false (true), upon return *pattern_out* is a sparsity pattern for :math:`H(x)` (:math:`H(x)^\R{T}`). Sparsity for Entire Hessian *************************** Suppose that :math:`R` is the :math:`n \times n` identity matrix. In this case, *pattern_out* is a sparsity pattern for :math:`(s^\R{T} F)^{(2)} ( x )`. Example ******* {xrst_toc_hidden example/sparse/rev_hes_sparsity.cpp } The file :ref:`rev_hes_sparsity.cpp-name` contains an example and test of this operation. {xrst_end rev_hes_sparsity} ----------------------------------------------------------------------------- */ # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! Reverse Hessian sparsity patterns. \tparam Base is the base type for this recording. \tparam BoolVector is the simple vector with elements of type bool that is used for sparsity for the vector s. \tparam SizeVector is the simple vector with elements of type size_t that is used for row, column index sparsity patterns. \param select_range is a sparsity pattern for for s. \param transpose Is the returned sparsity pattern transposed. \param internal_bool If this is true, calculations are done with sets represented by a vector of boolean values. Otherwise, a vector of standard sets is used. \param pattern_out The value of transpose is false (true), the return value is a sparsity pattern for H(x) ( H(x)^T ) where \f[ H(x) = R * F^{(1)} (x) \f] Here F is the function corresponding to the operation sequence and x is any argument value. */ template template void ADFun::rev_hes_sparsity( const BoolVector& select_range , bool transpose , bool internal_bool , sparse_rc& pattern_out ) { // used to identify the RecBase type in calls to sweeps RecBase not_used_rec_base(0.0); // size_t m = Range(); // CPPAD_ASSERT_KNOWN( size_t( select_range.size() ) == m, "rev_hes_sparsity: size of select_range is not equal to " "number of dependent variables" ); // // vector that holds reverse Jacobian sparsity flag local::pod_vector rev_jac_pattern(num_var_tape_); for(size_t i = 0; i < num_var_tape_; i++) rev_jac_pattern[i] = false; // // initialize rev_jac_pattern for dependent variables for(size_t i = 0; i < m; i++) rev_jac_pattern[ dep_taddr_[i] ] = select_range[i]; // // if( internal_bool ) { CPPAD_ASSERT_KNOWN( for_jac_sparse_pack_.n_set() > 0, "rev_hes_sparsity: previous call to for_jac_sparsity did not " "use bool for internal sparsity patterns." ); // column dimension of internal sparstiy pattern size_t ell = for_jac_sparse_pack_.end(); // // allocate memory for bool sparsity calculation // (sparsity pattern is empty after a resize) local::sparse::pack_setvec internal_hes; internal_hes.resize(num_var_tape_, ell); // // compute the Hessian sparsity pattern local::sweep::rev_hes( &play_, num_var_tape_, for_jac_sparse_pack_, rev_jac_pattern.data(), internal_hes, not_used_rec_base ); // get sparstiy pattern for independent variables local::sparse::get_internal_pattern( transpose, ind_taddr_, internal_hes, pattern_out ); } else { CPPAD_ASSERT_KNOWN( for_jac_sparse_set_.n_set() > 0, "rev_hes_sparsity: previous call to for_jac_sparsity did not " "use bool for internal sparsity patterns." ); // column dimension of internal sparstiy pattern size_t ell = for_jac_sparse_set_.end(); // // allocate memory for bool sparsity calculation // (sparsity pattern is empty after a resize) local::sparse::list_setvec internal_hes; internal_hes.resize(num_var_tape_, ell); // // compute the Hessian sparsity pattern local::sweep::rev_hes( &play_, num_var_tape_, for_jac_sparse_set_, rev_jac_pattern.data(), internal_hes, not_used_rec_base ); // get sparstiy pattern for independent variables local::sparse::get_internal_pattern( transpose, ind_taddr_, internal_hes, pattern_out ); } return; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/rev_jac_sparsity.hpp ================================================ # ifndef CPPAD_CORE_REV_JAC_SPARSITY_HPP # define CPPAD_CORE_REV_JAC_SPARSITY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin rev_jac_sparsity} Reverse Mode Jacobian Sparsity Patterns ####################################### Syntax ****** | *f* . ``rev_jac_sparsity`` ( | |tab| *pattern_in* , *transpose* , *dependency* , *internal_bool* , *pattern_out* | ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to the operation sequence stored in *f* . Fix :math:`R \in \B{R}^{\ell \times m}` and define the function .. math:: J(x) = R * F^{(1)} ( x ) Given the :ref:`glossary@Sparsity Pattern` for :math:`R`, ``rev_jac_sparsity`` computes a sparsity pattern for :math:`J(x)`. x * Note that the sparsity pattern :math:`J(x)` corresponds to the operation sequence stored in *f* and does not depend on the argument *x* . (The operation sequence may contain :ref:`CondExp-name` and :ref:`VecAD-name` operations.) SizeVector ********** The type *SizeVector* is a :ref:`SimpleVector-name` class with :ref:`elements of type` ``size_t`` . f * The object *f* has prototype ``ADFun`` < *Base* > *f* pattern_in ********** The argument *pattern_in* has prototype ``const sparse_rc`` < *SizeVector* >& *pattern_in* see :ref:`sparse_rc-name` . If *transpose* it is false (true), *pattern_in* is a sparsity pattern for :math:`R` (:math:`R^\R{T}`). transpose ********* This argument has prototype ``bool`` *transpose* See :ref:`rev_jac_sparsity@pattern_in` above and :ref:`rev_jac_sparsity@pattern_out` below. dependency ********** This argument has prototype ``bool`` *dependency* see :ref:`rev_jac_sparsity@pattern_out` below. internal_bool ************* If this is true, calculations are done with sets represented by a vector of boolean values. Otherwise, a vector of sets of integers is used. pattern_out *********** This argument has prototype ``sparse_rc`` < *SizeVector* >& *pattern_out* This input value of *pattern_out* does not matter. If *transpose* it is false (true), upon return *pattern_out* is a sparsity pattern for :math:`J(x)` (:math:`J(x)^\R{T}`). If *dependency* is true, *pattern_out* is a :ref:`dependency.cpp@Dependency Pattern` instead of sparsity pattern. Sparsity for Entire Jacobian **************************** Suppose that :math:`R` is the :math:`m \times m` identity matrix. In this case, *pattern_out* is a sparsity pattern for :math:`F^{(1)} ( x )` ( :math:`F^{(1)} (x)^\R{T}` ) if *transpose* is false (true). Example ******* {xrst_toc_hidden example/sparse/rev_jac_sparsity.cpp } The file :ref:`rev_jac_sparsity.cpp-name` contains an example and test of this operation. {xrst_end rev_jac_sparsity} ----------------------------------------------------------------------------- */ # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! Reverse Jacobian sparsity patterns. \tparam Base is the base type for this recording. \tparam SizeVector is the simple vector with elements of type size_t that is used for row, column index sparsity patterns. \param pattern_in is the sparsity pattern for for R or R^T depending on transpose. \param transpose Is the input and returned sparsity pattern transposed. \param dependency Are the derivatives with respect to left and right of the expression below considered to be non-zero: \code CondExpRel(left, right, if_true, if_false) \endcode This is used by the optimizer to obtain the correct dependency relations. \param internal_bool If this is true, calculations are done with sets represented by a vector of boolean values. Otherwise, a vector of standard sets is used. \param pattern_out The value of transpose is false (true), the return value is a sparsity pattern for J(x) ( J(x)^T ) where \f[ J(x) = R * F^{(1)} (x) \f] Here F is the function corresponding to the operation sequence and x is any argument value. */ template template void ADFun::rev_jac_sparsity( const sparse_rc& pattern_in , bool transpose , bool dependency , bool internal_bool , sparse_rc& pattern_out ) { // used to identify the RecBase type in calls to sweeps RecBase not_used_rec_base(0.0); // // number or rows, columns, and non-zeros in pattern_in size_t nr_in = pattern_in.nr(); size_t nc_in = pattern_in.nc(); // size_t ell = nr_in; size_t m = nc_in; if( transpose ) std::swap(ell, m); // CPPAD_ASSERT_KNOWN( m == Range() , "rev_jac_sparsity: number columns in R " "is not equal number of dependent variables." ); // number of independent variables size_t n = Domain(); // bool zero_empty = true; bool input_empty = true; if( internal_bool ) { // allocate memory for bool sparsity calculation // (sparsity pattern is empty after a resize) local::sparse::pack_setvec internal_jac; internal_jac.resize(num_var_tape_, ell); // // set sparsity pattern for dependent variables local::sparse::set_internal_pattern( zero_empty , input_empty , ! transpose , dep_taddr_ , internal_jac , pattern_in ); // compute sparsity for other variables local::sweep::rev_jac( &play_, dependency, n, num_var_tape_, internal_jac, not_used_rec_base ); // get sparstiy pattern for independent variables local::sparse::get_internal_pattern( ! transpose, ind_taddr_, internal_jac, pattern_out ); } else { // allocate memory for bool sparsity calculation // (sparsity pattern is empty after a resize) local::sparse::list_setvec internal_jac; internal_jac.resize(num_var_tape_, ell); // // set sparsity pattern for dependent variables local::sparse::set_internal_pattern( zero_empty , input_empty , ! transpose , dep_taddr_ , internal_jac , pattern_in ); // compute sparsity for other variables local::sweep::rev_jac( &play_, dependency, n, num_var_tape_, internal_jac, not_used_rec_base ); // get sparstiy pattern for independent variables local::sparse::get_internal_pattern( ! transpose, ind_taddr_, internal_jac, pattern_out ); } return; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/rev_one.hpp ================================================ # ifndef CPPAD_CORE_REV_ONE_HPP # define CPPAD_CORE_REV_ONE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin RevOne} {xrst_spell dw } First Order Derivative: Driver Routine ###################################### Syntax ****** | *dw* = *f* . ``RevOne`` ( *x* , *i* ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* . The syntax above sets *dw* to the derivative of :math:`F_i` with respect to :math:`x`; i.e., .. math:: dw = F_i^{(1)} (x) = \left[ \D{ F_i }{ x_0 } (x) , \cdots , \D{ F_i }{ x_{n-1} } (x) \right] f * The object *f* has prototype ``ADFun`` < *Base* > *f* Note that the :ref:`ADFun-name` object *f* is not ``const`` (see :ref:`RevOne@RevOne Uses Forward` below). x * The argument *x* has prototype ``const`` *Vector* & *x* (see :ref:`RevOne@Vector` below) and its size must be equal to *n* , the dimension of the :ref:`fun_property@Domain` space for *f* . It specifies that point at which to evaluate the derivative. i * The index *i* has prototype ``size_t`` *i* and is less than :math:`m`, the dimension of the :ref:`fun_property@Range` space for *f* . It specifies the component of :math:`F` that we are computing the derivative of. dw ** The result *dw* has prototype *Vector* *dw* (see :ref:`RevOne@Vector` below) and its size is *n* , the dimension of the :ref:`fun_property@Domain` space for *f* . The value of *dw* is the derivative of :math:`F_i` evaluated at *x* ; i.e., for :math:`j = 0 , \ldots , n - 1` .. math:: dw[ j ] = \D{ F_i }{ x_j } ( x ) Vector ****** The type *Vector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` *Base* . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. RevOne Uses Forward ******************* After each call to :ref:`Forward-name` , the object *f* contains the corresponding :ref:`Taylor coefficients` . After a call to ``RevOne`` , the zero order Taylor coefficients correspond to *f* . ``Forward`` (0, *x* ) and the other coefficients are unspecified. Example ******* {xrst_toc_hidden example/general/rev_one.cpp } The routine :ref:`RevOne` is both an example and test. It returns ``true`` , if it succeeds and ``false`` otherwise. {xrst_end RevOne} ----------------------------------------------------------------------------- */ // BEGIN CppAD namespace namespace CppAD { template template Vector ADFun::RevOne(const Vector &x, size_t i) { size_t i1; size_t n = Domain(); size_t m = Range(); // check Vector is Simple Vector class with Base type elements CheckSimpleVector(); CPPAD_ASSERT_KNOWN( x.size() == n, "RevOne: Length of x not equal domain dimension for f" ); CPPAD_ASSERT_KNOWN( i < m, "RevOne: the index i is not less than range dimension for f" ); // point at which we are evaluating the derivative Forward(0, x); // component which are are taking the derivative of Vector w(m); for(i1 = 0; i1 < m; i1++) w[i1] = 0.; w[i] = Base(1.0); // dimension the return value Vector dw(n); // compute the return value dw = Reverse(1, w); return dw; } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/rev_sparse_hes.hpp ================================================ # ifndef CPPAD_CORE_REV_SPARSE_HES_HPP # define CPPAD_CORE_REV_SPARSE_HES_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin RevSparseHes} Hessian Sparsity Pattern: Reverse Mode ###################################### Syntax ****** | *h* = *f* . ``RevSparseHes`` ( *q* , *s* ) | *h* = *f* . ``RevSparseHes`` ( *q* , *s* , *transpose* ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* . For a fixed matrix :math:`R \in \B{R}^{n \times q}` and a fixed vector :math:`S \in \B{R}^{1 \times m}`, we define .. math:: :nowrap: \begin{eqnarray} H(x) & = & \partial_x \left[ \partial_u S * F[ x + R * u ] \right]_{u=0} \\ & = & R^\R{T} * (S * F)^{(2)} ( x ) \\ H(x)^\R{T} & = & (S * F)^{(2)} ( x ) * R \end{eqnarray} Given a :ref:`glossary@Sparsity Pattern` for the matrix :math:`R` and the vector :math:`S`, ``RevSparseHes`` returns a sparsity pattern for the :math:`H(x)`. f * The object *f* has prototype ``const ADFun`` < *Base* > *f* x * If the operation sequence in *f* is :ref:`glossary@Operation@Independent` of the independent variables in :math:`x \in \B{R}^n`, the sparsity pattern is valid for all values of (even if it has :ref:`CondExp-name` or :ref:`VecAD-name` operations). q * The argument *q* has prototype ``size_t`` *q* It specifies the number of columns in :math:`R \in \B{R}^{n \times q}` and the number of rows in :math:`H(x) \in \B{R}^{q \times n}`. It must be the same value as in the previous :ref:`ForSparseJac-name` call *f* . ``ForSparseJac`` ( *q* , *r* , *r_transpose* ) Note that if *r_transpose* is true, *r* in the call above corresponding to :math:`R^\R{T} \in \B{R}^{q \times n}` transpose ********* The argument *transpose* has prototype ``bool`` *transpose* The default value ``false`` is used when *transpose* is not present. r * The matrix :math:`R` is specified by the previous call *f* . ``ForSparseJac`` ( *q* , *r* , *transpose* ) see :ref:`ForSparseJac@r` . The type of the elements of :ref:`RevSparseHes@SetVector` must be the same as the type of the elements of *r* . s * The argument *s* has prototype ``const`` *SetVector* & *s* (see :ref:`RevSparseHes@SetVector` below) If it has elements of type ``bool`` , its size is :math:`m`. If it has elements of type ``std::set`` , its size is one and all the elements of *s* [0] are between zero and :math:`m - 1`. It specifies a :ref:`glossary@Sparsity Pattern` for the vector *S* . h * The result *h* has prototype *SetVector* & *h* (see :ref:`RevSparseHes@SetVector` below). transpose false =============== If *h* has elements of type ``bool`` , its size is :math:`q * n`. If it has elements of type ``std::set`` , its size is :math:`q` and all the set elements are between zero and *n* ``-1`` inclusive. It specifies a :ref:`glossary@Sparsity Pattern` for the matrix :math:`H(x)`. transpose true ============== If *h* has elements of type ``bool`` , its size is :math:`n * q`. If it has elements of type ``std::set`` , its size is :math:`n` and all the set elements are between zero and *q* ``-1`` inclusive. It specifies a :ref:`glossary@Sparsity Pattern` for the matrix :math:`H(x)^\R{T}`. SetVector ********* The type *SetVector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` ``bool`` or ``std::set`` ; see :ref:`glossary@Sparsity Pattern` for a discussion of the difference. The type of the elements of :ref:`RevSparseHes@SetVector` must be the same as the type of the elements of *r* . Entire Sparsity Pattern *********************** Suppose that :math:`q = n` and :math:`R \in \B{R}^{n \times n}` is the :math:`n \times n` identity matrix. Further suppose that the :math:`S` is the *k*-th :ref:`glossary@Elementary Vector` ; i.e. .. math:: S_j = \left\{ \begin{array}{ll} 1 & {\rm if} \; j = k \\ 0 & {\rm otherwise} \end{array} \right. In this case, the corresponding value *h* is a sparsity pattern for the Hessian matrix :math:`F_k^{(2)} (x) \in \B{R}^{n \times n}`. Example ******* {xrst_toc_hidden example/sparse/rev_sparse_hes.cpp example/sparse/sparsity_sub.cpp } The file :ref:`rev_sparse_hes.cpp-name` contains an example and test of this operation. The file :ref:`sparsity_sub.cpp` contains an example and test of using ``RevSparseHes`` to compute the sparsity pattern for a subset of the Hessian. {xrst_end RevSparseHes} ----------------------------------------------------------------------------- */ # include # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file core/rev_sparse_hes.hpp Reverse mode Hessian sparsity patterns. */ // =========================================================================== // RevSparseHesCase /*! Private helper function for RevSparseHes(q, s) bool sparsity. All of the description in the public member function RevSparseHes(q, s) applies. \param set_type is a bool value. This argument is used to dispatch to the proper source code depending on the value of SetVector::value_type. \param transpose See RevSparseHes(q, s). \param q See RevSparseHes(q, s). \param s See RevSparseHes(q, s). \param h is the return value for the corresponding call to RevSparseJac(q, s). */ template template void ADFun::RevSparseHesCase( bool set_type , bool transpose , size_t q , const SetVector& s , SetVector& h ) { // used to identify the RecBase type in calls to sweeps RecBase not_used_rec_base(0.0); // size_t n = Domain(); size_t m = Range(); // h.resize(q * n ); CPPAD_ASSERT_KNOWN( for_jac_sparse_pack_.n_set() > 0, "RevSparseHes: previous stored call to ForSparseJac did not " "use bool for the elements of r." ); CPPAD_ASSERT_UNKNOWN( for_jac_sparse_set_.n_set() == 0 ); CPPAD_ASSERT_UNKNOWN( for_jac_sparse_pack_.n_set() == num_var_tape_ ); // // temporary indices size_t i, j; // check Vector is Simple SetVector class with bool elements CheckSimpleVector(); CPPAD_ASSERT_KNOWN( q == for_jac_sparse_pack_.end(), "RevSparseHes: q is not equal to its value\n" "in the previous call to ForSparseJac with this ADFun object." ); CPPAD_ASSERT_KNOWN( size_t(s.size()) == m, "RevSparseHes: size of s is not equal to\n" "range dimension for ADFun object." ); // Array that will hold reverse Jacobian dependency flag. // Initialize as true for the dependent variables. local::pod_vector RevJac(num_var_tape_); for(i = 0; i < num_var_tape_; i++) RevJac[i] = false; for(i = 0; i < m; i++) { CPPAD_ASSERT_UNKNOWN( dep_taddr_[i] < num_var_tape_ ); RevJac[ dep_taddr_[i] ] = s[i]; } // vector of sets that will hold reverse Hessain values local::sparse::pack_setvec rev_hes_pattern; rev_hes_pattern.resize(num_var_tape_, q); // compute the Hessian sparsity patterns local::sweep::rev_hes( &play_, num_var_tape_, for_jac_sparse_pack_, RevJac.data(), rev_hes_pattern, not_used_rec_base ); // return values corresponding to independent variables CPPAD_ASSERT_UNKNOWN( size_t(h.size()) == n * q ); for(j = 0; j < n; j++) { for(i = 0; i < q; i++) { if( transpose ) h[ j * q + i ] = false; else h[ i * n + j ] = false; } } // j is index corresponding to reverse mode partial for(j = 0; j < n; j++) { CPPAD_ASSERT_UNKNOWN( ind_taddr_[j] < num_var_tape_ ); // ind_taddr_[j] is operator taddr for j-th independent variable CPPAD_ASSERT_UNKNOWN( ind_taddr_[j] == j + 1 ); CPPAD_ASSERT_UNKNOWN( play_.GetOp( ind_taddr_[j] ) == local::InvOp ); // extract the result from rev_hes_pattern CPPAD_ASSERT_UNKNOWN( rev_hes_pattern.end() == q ); local::sparse::pack_setvec::const_iterator itr(rev_hes_pattern, j + 1); i = *itr; while( i < q ) { if( transpose ) h[ j * q + i ] = true; else h[ i * n + j ] = true; i = *(++itr); } } } /*! Private helper function for RevSparseHes(q, s) set sparsity. All of the description in the public member function RevSparseHes(q, s) applies. \param set_type is a std::set value. This argument is used to dispatch to the proper source code depending on the value of SetVector::value_type. \param transpose See RevSparseHes(q, s). \param q See RevSparseHes(q, s). \param s See RevSparseHes(q, s). \param h is the return value for the corresponding call to RevSparseJac(q, s). */ template template void ADFun::RevSparseHesCase( const std::set& set_type , bool transpose , size_t q , const SetVector& s , SetVector& h ) { // used to identify the RecBase type in calls to sweeps RecBase not_used_rec_base(0.0); // size_t n = Domain(); # ifndef NDEBUG size_t m = Range(); # endif // if( transpose ) h.resize(n); else h.resize(q); CPPAD_ASSERT_KNOWN( for_jac_sparse_set_.n_set() > 0, "RevSparseHes: previous stored call to ForSparseJac did not " "use std::set for the elements of r." ); CPPAD_ASSERT_UNKNOWN( for_jac_sparse_pack_.n_set() == 0 ); CPPAD_ASSERT_UNKNOWN( for_jac_sparse_set_.n_set() == num_var_tape_ ); // // temporary indices size_t i, j; std::set::const_iterator itr_1; // check SetVector is Simple Vector class with sets for elements CheckSimpleVector, SetVector>( local::one_element_std_set(), local::two_element_std_set() ); CPPAD_ASSERT_KNOWN( q == for_jac_sparse_set_.end(), "RevSparseHes: q is not equal to its value\n" "in the previous call to ForSparseJac with this ADFun object." ); CPPAD_ASSERT_KNOWN( s.size() == 1, "RevSparseHes: size of s is not equal to one." ); // Array that will hold reverse Jacobian dependency flag. // Initialize as true for the dependent variables. local::pod_vector RevJac(num_var_tape_); for(i = 0; i < num_var_tape_; i++) RevJac[i] = false; itr_1 = s[0].begin(); while( itr_1 != s[0].end() ) { i = *itr_1++; CPPAD_ASSERT_KNOWN( i < m, "RevSparseHes: an element of the set s[0] has value " "greater than or equal m" ); CPPAD_ASSERT_UNKNOWN( dep_taddr_[i] < num_var_tape_ ); RevJac[ dep_taddr_[i] ] = true; } // vector of sets that will hold reverse Hessain values local::sparse::list_setvec rev_hes_pattern; rev_hes_pattern.resize(num_var_tape_, q); // compute the Hessian sparsity patterns local::sweep::rev_hes( &play_, num_var_tape_, for_jac_sparse_set_, RevJac.data(), rev_hes_pattern, not_used_rec_base ); // return values corresponding to independent variables // j is index corresponding to reverse mode partial CPPAD_ASSERT_UNKNOWN( size_t(h.size()) == q || transpose ); CPPAD_ASSERT_UNKNOWN( size_t(h.size()) == n || ! transpose ); for(j = 0; j < n; j++) { CPPAD_ASSERT_UNKNOWN( ind_taddr_[j] < num_var_tape_ ); CPPAD_ASSERT_UNKNOWN( ind_taddr_[j] == j + 1 ); CPPAD_ASSERT_UNKNOWN( play_.GetOp( ind_taddr_[j] ) == local::InvOp ); // extract the result from rev_hes_pattern // and add corresponding elements to result sets in h CPPAD_ASSERT_UNKNOWN( rev_hes_pattern.end() == q ); local::sparse::list_setvec::const_iterator itr_2(rev_hes_pattern, j+1); i = *itr_2; while( i < q ) { if( transpose ) h[j].insert(i); else h[i].insert(j); i = *(++itr_2); } } } // =========================================================================== // RevSparseHes /*! User API for Hessian sparsity patterns using reverse mode. The C++ source code corresponding to this operation is \verbatim h = f.RevSparseHes(q, r) \endverbatim \tparam Base is the base type for this recording. \tparam SetVector is a simple vector with elements of type bool or std::set. \param transpose is true (false) if is is equal to \f$ H(x) \f$ (\f$ H(x)^T \f$) where \f[ H(x) = R^T (S * F)^{(2)} (x) \f] where \f$ F \f$ is the function corresponding to the operation sequence and x is any argument value. \param q is the value of q in the by the previous call of the form \verbatim f.ForSparseJac(q, r, packed) \endverbatim The value r in this call is a sparsity pattern for the matrix \f$ R \f$. The type of the element of r for the previous call to ForSparseJac must be the same as the type of the elements of s. \param s is a vector with size m that specifies the sparsity pattern for the vector \f$ S \f$, where m is the number of dependent variables corresponding to the operation sequence stored in play. \return If transpose is false (true), the return vector is a sparsity pattern for \f$ H(x) \f$ (\f$ H(x)^T \f$). \f[ H(x) = R^T ( S * F)^{(2)} (x) \f] where \f$ F \f$ is the function corresponding to the operation sequence and x is any argument value. */ template template SetVector ADFun::RevSparseHes( size_t q, const SetVector& s, bool transpose ) { SetVector h; typedef typename SetVector::value_type Set_type; // Should check to make sure q is same as in previous call to // forward sparse Jacobian. RevSparseHesCase( Set_type() , transpose , q , s , h ); return h; } // =========================================================================== // RevSparseHesCheckpoint /*! Hessian sparsity patterns calculation used by checkpoint functions. \tparam Base is the base type for this recording. \param transpose is true (false) h is equal to \f$ H(x) \f$ (\f$ H(x)^T \f$) where \f[ H(x) = R^T (S * F)^{(2)} (x) \f] where \f$ F \f$ is the function corresponding to the operation sequence and \f$ x \f$ is any argument value. \param q is the value of q in the by the previous call of the form \verbatim f.ForSparseJac(q, r) \endverbatim The value r in this call is a sparsity pattern for the matrix \f$ R \f$. \param s is a vector with size m that specifies the sparsity pattern for the vector \f$ S \f$, where m is the number of dependent variables corresponding to the operation sequence stored in play_. \param h The input size and elements of h do not matter. On output, h is the sparsity pattern for the matrix \f$ H(x) \f$ or \f$ H(x)^T \f$ depending on transpose. \par Assumptions The forward jacobian sparsity pattern must be currently stored in this ADFUN object. */ template void ADFun::RevSparseHesCheckpoint( size_t q , vector& s , bool transpose , local::sparse::list_setvec& h ) { // used to identify the RecBase type in calls to sweeps RecBase not_used_rec_base(0.0); // size_t n = Domain(); size_t m = Range(); // checkpoint functions should get this right CPPAD_ASSERT_UNKNOWN( for_jac_sparse_pack_.n_set() == 0 ); CPPAD_ASSERT_UNKNOWN( for_jac_sparse_set_.n_set() == num_var_tape_ ); CPPAD_ASSERT_UNKNOWN( for_jac_sparse_set_.end() == q ); CPPAD_ASSERT_UNKNOWN( s.size() == m ); // Array that holds the reverse Jacobiain dependency flags. // Initialize as true for dependent variables, false for others. local::pod_vector RevJac(num_var_tape_); for(size_t i = 0; i < num_var_tape_; i++) RevJac[i] = false; for(size_t i = 0; i < m; i++) { CPPAD_ASSERT_UNKNOWN( dep_taddr_[i] < num_var_tape_ ) RevJac[ dep_taddr_[i] ] = s[i]; } // holds reverse Hessian sparsity pattern for all variables local::sparse::list_setvec rev_hes_pattern; rev_hes_pattern.resize(num_var_tape_, q); // compute Hessian sparsity pattern for all variables local::sweep::rev_hes( &play_, num_var_tape_, for_jac_sparse_set_, RevJac.data(), rev_hes_pattern, not_used_rec_base ); // dimension the return value if( transpose ) h.resize(n, q); else h.resize(q, n); // j is index corresponding to reverse mode partial for(size_t j = 0; j < n; j++) { CPPAD_ASSERT_UNKNOWN( ind_taddr_[j] < num_var_tape_ ); // ind_taddr_[j] is operator taddr for j-th independent variable CPPAD_ASSERT_UNKNOWN( ind_taddr_[j] == j + 1 ); CPPAD_ASSERT_UNKNOWN( play_.GetOp( ind_taddr_[j] ) == local::InvOp ); // extract the result from rev_hes_pattern CPPAD_ASSERT_UNKNOWN( rev_hes_pattern.end() == q ); local::sparse::list_setvec::const_iterator itr(rev_hes_pattern, j + 1); size_t i = *itr; while( i < q ) { if( transpose ) h.post_element(j, i); else h.post_element(i, j); i = *(++itr); } } for(size_t i = 0; i < h.n_set(); ++i) h.process_post(i); } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/rev_sparse_jac.hpp ================================================ # ifndef CPPAD_CORE_REV_SPARSE_JAC_HPP # define CPPAD_CORE_REV_SPARSE_JAC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin RevSparseJac} Jacobian Sparsity Pattern: Reverse Mode ####################################### Syntax ****** | *s* = *f* . ``RevSparseJac`` ( *q* , *r* ) | *s* = *f* . ``RevSparseJac`` ( *q* , *r* , *transpose* , *dependency* ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* . For a fixed matrix :math:`R \in \B{R}^{q \times m}`, the Jacobian of :math:`R * F( x )` with respect to :math:`x` is .. math:: S(x) = R * F^{(1)} ( x ) Given a :ref:`glossary@Sparsity Pattern` for :math:`R`, ``RevSparseJac`` returns a sparsity pattern for the :math:`S(x)`. f * The object *f* has prototype ``ADFun`` < *Base* > *f* x * If the operation sequence in *f* is :ref:`glossary@Operation@Independent` of the independent variables in :math:`x \in \B{R}^n`, the sparsity pattern is valid for all values of (even if it has :ref:`CondExp-name` or :ref:`VecAD-name` operations). q * The argument *q* has prototype ``size_t`` *q* It specifies the number of rows in :math:`R \in \B{R}^{q \times m}` and the Jacobian :math:`S(x) \in \B{R}^{q \times n}`. transpose ********* The argument *transpose* has prototype ``bool`` *transpose* The default value ``false`` is used when *transpose* is not present. dependency ********** The argument *dependency* has prototype ``bool`` *dependency* If *dependency* is true, the :ref:`dependency.cpp@Dependency Pattern` (instead of sparsity pattern) is computed. r * The argument *s* has prototype ``const`` *SetVector* & *r* see :ref:`RevSparseJac@SetVector` below. transpose false =============== If *r* has elements of type ``bool`` , its size is :math:`q * m`. If it has elements of type ``std::set`` , its size is *q* and all its set elements are between zero and :math:`m - 1`. It specifies a :ref:`glossary@Sparsity Pattern` for the matrix :math:`R \in \B{R}^{q \times m}`. transpose true ============== If *r* has elements of type ``bool`` , its size is :math:`m * q`. If it has elements of type ``std::set`` , its size is *m* and all its set elements are between zero and :math:`q - 1`. It specifies a :ref:`glossary@Sparsity Pattern` for the matrix :math:`R^\R{T} \in \B{R}^{m \times q}`. s * The return value *s* has prototype *SetVector* *s* see :ref:`RevSparseJac@SetVector` below. transpose false =============== If it has elements of type ``bool`` , its size is :math:`q * n`. If it has elements of type ``std::set`` , its size is *q* and all its set elements are between zero and :math:`n - 1`. It specifies a :ref:`glossary@Sparsity Pattern` for the matrix :math:`S(x) \in {q \times n}`. transpose true ============== If it has elements of type ``bool`` , its size is :math:`n * q`. If it has elements of type ``std::set`` , its size is *n* and all its set elements are between zero and :math:`q - 1`. It specifies a :ref:`glossary@Sparsity Pattern` for the matrix :math:`S(x)^\R{T} \in {n \times q}`. SetVector ********* The type *SetVector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` ``bool`` or ``std::set`` ; see :ref:`glossary@Sparsity Pattern` for a discussion of the difference. Entire Sparsity Pattern *********************** Suppose that :math:`q = m` and :math:`R` is the :math:`m \times m` identity matrix. In this case, the corresponding value for *s* is a sparsity pattern for the Jacobian :math:`S(x) = F^{(1)} ( x )`. Example ******* {xrst_toc_hidden example/sparse/rev_sparse_jac.cpp } The file :ref:`rev_sparse_jac.cpp-name` contains an example and test of this operation. {xrst_end RevSparseJac} ----------------------------------------------------------------------------- */ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file core/rev_sparse_jac.hpp Reverse mode Jacobian sparsity patterns. */ // ========================================================================= // RevSparseJacCase /*! Private helper function for RevSparseJac(q, r, transpose) boolean sparsity. All of the description in the public member function RevSparseJac(q, r, transpose) apply. \param set_type is a bool value. This argument is used to dispatch to the proper source code depending on the value of SetVector::value_type. \param transpose See RevSparseJac(q, r, transpose, dependency) \param dependency See RevSparseJac(q, r, transpose, dependency) \param q See RevSparseJac(q, r, transpose, dependency) \param r See RevSparseJac(q, r, transpose, dependency) \param s is the return value for the corresponding call to RevSparseJac(q, r, transpose). */ template template void ADFun::RevSparseJacCase( bool set_type , bool transpose , bool dependency , size_t q , const SetVector& r , SetVector& s ) { // used to identify the RecBase type in calls to sweeps RecBase not_used_rec_base(0.0); // size_t n = Domain(); size_t m = Range(); // dimension of the result vector s.resize( q * n ); // check SetVector is Simple Vector class with bool elements CheckSimpleVector(); // CPPAD_ASSERT_KNOWN( q > 0, "RevSparseJac: q is not greater than zero" ); CPPAD_ASSERT_KNOWN( size_t(r.size()) == q * m, "RevSparseJac: size of r is not equal to\n" "q times range dimension for ADFun object." ); // // vector of sets that will hold the results local::sparse::pack_setvec var_sparsity; var_sparsity.resize(num_var_tape_, q); // The sparsity pattern corresponding to the dependent variables for(size_t i = 0; i < m; i++) { CPPAD_ASSERT_UNKNOWN( dep_taddr_[i] < num_var_tape_ ); if( transpose ) { for(size_t j = 0; j < q; j++) if( r[ i * q + j ] ) var_sparsity.post_element( dep_taddr_[i], j ); } else { for(size_t j = 0; j < q; j++) if( r[ j * m + i ] ) var_sparsity.post_element( dep_taddr_[i], j ); } } // process posts for(size_t i = 0; i < m; i++) var_sparsity.process_post( dep_taddr_[i] ); // evaluate the sparsity patterns local::sweep::rev_jac( &play_, dependency, n, num_var_tape_, var_sparsity, not_used_rec_base ); // return values corresponding to dependent variables CPPAD_ASSERT_UNKNOWN( size_t(s.size()) == q * n ); for(size_t j = 0; j < n; j++) { CPPAD_ASSERT_UNKNOWN( ind_taddr_[j] == (j+1) ); // ind_taddr_[j] is operator taddr for j-th independent variable CPPAD_ASSERT_UNKNOWN( play_.GetOp( ind_taddr_[j] ) == local::InvOp ); // extract the result from var_sparsity if( transpose ) { for(size_t i = 0; i < q; i++) s[ j * q + i ] = false; } else { for(size_t i = 0; i < q; i++) s[ i * n + j ] = false; } CPPAD_ASSERT_UNKNOWN( var_sparsity.end() == q ); local::sparse::pack_setvec::const_iterator itr(var_sparsity, j+1); size_t i = *itr; while( i < q ) { if( transpose ) s[ j * q + i ] = true; else s[ i * n + j ] = true; i = *(++itr); } } } /*! Private helper function for RevSparseJac(q, r, transpose) set sparsity All of the description in the public member function RevSparseJac(q, r, transpose) apply. \param set_type is a std::set object. This argument is used to dispatch to the proper source code depending on the value of SetVector::value_type. \param transpose See RevSparseJac(q, r, transpose, dependency) \param dependency See RevSparseJac(q, r, transpose, dependency) \param q See RevSparseJac(q, r, transpose, dependency) \param r See RevSparseJac(q, r, transpose, dependency) \param s is the return value for the corresponding call to RevSparseJac(q, r, transpose) */ template template void ADFun::RevSparseJacCase( const std::set& set_type , bool transpose , bool dependency , size_t q , const SetVector& r , SetVector& s ) { // used to identify the RecBase type in calls to sweeps RecBase not_used_rec_base(0.0); // // dimension of the result vector if( transpose ) s.resize( Domain() ); else s.resize( q ); // temporary indices std::set::const_iterator itr_1; // check SetVector is Simple Vector class with sets for elements CheckSimpleVector, SetVector>( local::one_element_std_set(), local::two_element_std_set() ); // domain dimensions for F size_t n = ind_taddr_.size(); size_t m = dep_taddr_.size(); CPPAD_ASSERT_KNOWN( q > 0, "RevSparseJac: q is not greater than zero" ); CPPAD_ASSERT_KNOWN( size_t(r.size()) == q || transpose, "RevSparseJac: size of r is not equal to q and transpose is false." ); CPPAD_ASSERT_KNOWN( size_t(r.size()) == m || ! transpose, "RevSparseJac: size of r is not equal to m and transpose is true." ); // vector of lists that will hold the results local::sparse::list_setvec var_sparsity; var_sparsity.resize(num_var_tape_, q); // The sparsity pattern corresponding to the dependent variables if( transpose ) { for(size_t i = 0; i < m; i++) { itr_1 = r[i].begin(); while(itr_1 != r[i].end()) { size_t j = *itr_1++; CPPAD_ASSERT_KNOWN( j < q, "RevSparseJac: transpose is true and element of the set\n" "r[i] has value greater than or equal q." ); CPPAD_ASSERT_UNKNOWN( dep_taddr_[i] < num_var_tape_ ); var_sparsity.post_element( dep_taddr_[i], j ); } } } else { for(size_t i = 0; i < q; i++) { itr_1 = r[i].begin(); while(itr_1 != r[i].end()) { size_t j = *itr_1++; CPPAD_ASSERT_KNOWN( j < m, "RevSparseJac: transpose is false and element of the set\n" "r[i] has value greater than or equal range dimension." ); CPPAD_ASSERT_UNKNOWN( dep_taddr_[j] < num_var_tape_ ); var_sparsity.post_element( dep_taddr_[j], i ); } } } // process posts for(size_t i = 0; i < m; i++) var_sparsity.process_post( dep_taddr_[i] ); // evaluate the sparsity patterns local::sweep::rev_jac( &play_, dependency, n, num_var_tape_, var_sparsity, not_used_rec_base ); // return values corresponding to dependent variables CPPAD_ASSERT_UNKNOWN( size_t(s.size()) == q || transpose ); CPPAD_ASSERT_UNKNOWN( size_t(s.size()) == n || ! transpose ); for(size_t j = 0; j < n; j++) { CPPAD_ASSERT_UNKNOWN( ind_taddr_[j] == (j+1) ); // ind_taddr_[j] is operator taddr for j-th independent variable CPPAD_ASSERT_UNKNOWN( play_.GetOp( ind_taddr_[j] ) == local::InvOp ); CPPAD_ASSERT_UNKNOWN( var_sparsity.end() == q ); local::sparse::list_setvec::const_iterator itr_2(var_sparsity, j+1); size_t i = *itr_2; while( i < q ) { if( transpose ) s[j].insert(i); else s[i].insert(j); i = *(++itr_2); } } } // ========================================================================= // RevSparseJac /*! User API for Jacobian sparsity patterns using reverse mode. The C++ source code corresponding to this operation is \verbatim s = f.RevSparseJac(q, r, transpose, dependency) \endverbatim \tparam Base is the base type for this recording. \tparam SetVector is a simple vector with elements of type bool. or std::set. \param q is the number of rows in the matrix \f$ R \f$. \param r is a sparsity pattern for the matrix \f$ R \f$. \param transpose are the sparsity patterns for \f$ R \f$ and \f$ S(x) \f$ transposed. \param dependency Are the derivatives with respect to left and right of the expression below considered to be non-zero: \code CondExpRel(left, right, if_true, if_false) \endcode This is used by the optimizer to obtain the correct dependency relations. \return If transpose is false (true), the return value is a sparsity pattern for \f$ S(x) \f$ (\f$ S(x)^T \f$) where \f[ S(x) = R * F^{(1)} (x) \f] and \f$ F \f$ is the function corresponding to the operation sequence and x is any argument value. If SetVector::value_type is bool, the return value has size \f$ q * n \f$ ( \f$ n * q \f$). If SetVector::value_type is std::set, the return value has size \f$ q \f$ ( \f$ n \f$) and with all its elements between zero and \f$ n - 1 \f$ (\f$ q - 1 \f$). */ template template SetVector ADFun::RevSparseJac( size_t q , const SetVector& r , bool transpose , bool dependency ) { SetVector s; typedef typename SetVector::value_type Set_type; RevSparseJacCase( Set_type() , transpose , dependency , q , r , s ); return s; } // =========================================================================== // RevSparseJacCheckpoint /*! Reverse mode Jacobian sparsity calculation used by checkpoint functions. \tparam Base is the base type for this recording. \param transpose is true (false) s is equal to \f$ S(x) \f$ (\f$ S(x)^T \f$) where \f[ S(x) = R * F^{(1)} (x) \f] where \f$ F \f$ is the function corresponding to the operation sequence and \f$ x \f$ is any argument value. \param q is the number of rows in the matrix \f$ R \f$. \param r is a sparsity pattern for the matrix \f$ R \f$. \param transpose are the sparsity patterns for \f$ R \f$ and \f$ S(x) \f$ transposed. \param dependency Are the derivatives with respect to left and right of the expression below considered to be non-zero: \code CondExpRel(left, right, if_true, if_false) \endcode This is used by the optimizer to obtain the correct dependency relations. \param s The input size and elements of s do not matter. On output, s is the sparsity pattern for the matrix \f$ S(x) \f$ or \f$ S(x)^T \f$ depending on transpose. */ template void ADFun::RevSparseJacCheckpoint( size_t q , const local::sparse::list_setvec& r , bool transpose , bool dependency , local::sparse::list_setvec& s ) { // used to identify the RecBase type in calls to sweeps RecBase not_used_rec_base(0.0); // size_t n = Domain(); size_t m = Range(); # ifndef NDEBUG if( transpose ) { CPPAD_ASSERT_UNKNOWN( r.n_set() == m ); CPPAD_ASSERT_UNKNOWN( r.end() == q ); } else { CPPAD_ASSERT_UNKNOWN( r.n_set() == q ); CPPAD_ASSERT_UNKNOWN( r.end() == m ); } for(size_t i = 0; i < m; i++) CPPAD_ASSERT_UNKNOWN( dep_taddr_[i] < num_var_tape_ ); # endif // holds reverse Jacobian sparsity pattern for all variables local::sparse::list_setvec var_sparsity; var_sparsity.resize(num_var_tape_, q); // set sparsity pattern for dependent variables if( transpose ) { for(size_t i = 0; i < m; i++) { local::sparse::list_setvec::const_iterator itr(r, i); size_t j = *itr; while( j < q ) { var_sparsity.post_element( dep_taddr_[i], j ); j = *(++itr); } } } else { for(size_t j = 0; j < q; j++) { local::sparse::list_setvec::const_iterator itr(r, j); size_t i = *itr; while( i < m ) { var_sparsity.post_element( dep_taddr_[i], j ); i = *(++itr); } } } // process posts for(size_t i = 0; i < m; i++) var_sparsity.process_post( dep_taddr_[i] ); // evaluate the sparsity pattern for all variables local::sweep::rev_jac( &play_, dependency, n, num_var_tape_, var_sparsity, not_used_rec_base ); // dimension the return value if( transpose ) s.resize(n, m); else s.resize(m, n); // return values corresponding to independent variables for(size_t j = 0; j < n; j++) { CPPAD_ASSERT_UNKNOWN( ind_taddr_[j] == (j+1) ); // ind_taddr_[j] is operator taddr for j-th independent variable CPPAD_ASSERT_UNKNOWN( play_.GetOp( ind_taddr_[j] ) == local::InvOp ); // extract the result from var_sparsity CPPAD_ASSERT_UNKNOWN( var_sparsity.end() == q ); local::sparse::list_setvec::const_iterator itr(var_sparsity, j+1); size_t i = *itr; while( i < q ) { if( transpose ) s.post_element(j, i); else s.post_element(i, j); i = *(++itr); } } // process posts for(size_t i = 0; i < s.n_set(); i++) s.process_post(i); } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/rev_two.hpp ================================================ # ifndef CPPAD_CORE_REV_TWO_HPP # define CPPAD_CORE_REV_TWO_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin RevTwo} {xrst_spell ddw } Reverse Mode Second Partial Derivative Driver ############################################# Syntax ****** | *ddw* = *f* . ``RevTwo`` ( *x* , *i* , *j* ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* . The syntax above sets .. math:: ddw [ k * p + \ell ] = \DD{ F_{i[ \ell ]} }{ x_{j[ \ell ]} }{ x_k } (x) for :math:`k = 0 , \ldots , n-1` and :math:`\ell = 0 , \ldots , p`, where :math:`p` is the size of the vectors *i* and *j* . f * The object *f* has prototype ``ADFun`` < *Base* > *f* Note that the :ref:`ADFun-name` object *f* is not ``const`` (see :ref:`RevTwo@RevTwo Uses Forward` below). x * The argument *x* has prototype ``const`` *BaseVector* & *x* (see :ref:`RevTwo@BaseVector` below) and its size must be equal to *n* , the dimension of the :ref:`fun_property@Domain` space for *f* . It specifies that point at which to evaluate the partial derivatives listed above. i * The argument *i* has prototype ``const`` *SizeVector_t* & *i* (see :ref:`RevTwo@SizeVector_t` below) We use *p* to denote the size of the vector *i* . All of the indices in *i* must be less than *m* , the dimension of the :ref:`fun_property@Range` space for *f* ; i.e., for :math:`\ell = 0 , \ldots , p-1`, :math:`i[ \ell ] < m`. j * The argument *j* has prototype ``const`` *SizeVector_t* & *j* (see :ref:`RevTwo@SizeVector_t` below) and its size must be equal to *p* , the size of the vector *i* . All of the indices in *j* must be less than *n* ; i.e., for :math:`\ell = 0 , \ldots , p-1`, :math:`j[ \ell ] < n`. ddw *** The result *ddw* has prototype *BaseVector* *ddw* (see :ref:`RevTwo@BaseVector` below) and its size is :math:`n * p`. It contains the requested partial derivatives; to be specific, for :math:`k = 0 , \ldots , n - 1` and :math:`\ell = 0 , \ldots , p - 1` .. math:: ddw [ k * p + \ell ] = \DD{ F_{i[ \ell ]} }{ x_{j[ \ell ]} }{ x_k } (x) BaseVector ********** The type *BaseVector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type Base` . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. SizeVector_t ************ The type *SizeVector_t* must be a :ref:`SimpleVector-name` class with :ref:`elements of type size_t` . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. RevTwo Uses Forward ******************* After each call to :ref:`Forward-name` , the object *f* contains the corresponding :ref:`Taylor coefficients` . After a call to ``RevTwo`` , the zero order Taylor coefficients correspond to *f* . ``Forward`` (0, *x* ) and the other coefficients are unspecified. Examples ******** {xrst_toc_hidden example/general/rev_two.cpp } The routine :ref:`RevTwo` is both an example and test. It returns ``true`` , if it succeeds and ``false`` otherwise. {xrst_end RevTwo} ----------------------------------------------------------------------------- */ // BEGIN CppAD namespace namespace CppAD { template template BaseVector ADFun::RevTwo( const BaseVector &x, const SizeVector_t &i, const SizeVector_t &j) { size_t i1; size_t j1; size_t k; size_t l; size_t n = Domain(); size_t m = Range(); size_t p = i.size(); // check BaseVector is Simple Vector class with Base elements CheckSimpleVector(); // check SizeVector_t is Simple Vector class with size_t elements CheckSimpleVector(); CPPAD_ASSERT_KNOWN( x.size() == n, "RevTwo: Length of x not equal domain dimension for f." ); CPPAD_ASSERT_KNOWN( i.size() == j.size(), "RevTwo: Length of the i and j vectors are not equal." ); // point at which we are evaluating the second partials Forward(0, x); // dimension the return value BaseVector ddw(n * p); // direction vector in argument space BaseVector dx(n); for(j1 = 0; j1 < n; j1++) dx[j1] = Base(0.0); // direction vector in range space BaseVector w(m); for(i1 = 0; i1 < m; i1++) w[i1] = Base(0.0); // place to hold the results of a reverse calculation BaseVector r(n * 2); // check the indices in i and j for(l = 0; l < p; l++) { i1 = i[l]; j1 = j[l]; CPPAD_ASSERT_KNOWN( i1 < m, "RevTwo: an element of i not less than range dimension for f." ); CPPAD_ASSERT_KNOWN( j1 < n, "RevTwo: an element of j not less than domain dimension for f." ); } // loop over all forward directions for(j1 = 0; j1 < n; j1++) { // first order forward mode calculation done bool first_done = false; for(l = 0; l < p; l++) if( j[l] == j1 ) { if( ! first_done ) { first_done = true; // first order forward mode in j1 direction dx[j1] = Base(1.0); Forward(1, dx); dx[j1] = Base(0.0); } // execute a reverse in this component direction i1 = i[l]; w[i1] = Base(1.0); r = Reverse(2, w); w[i1] = Base(0.0); // place the reverse result in return value for(k = 0; k < n; k++) ddw[k * p + l] = r[k * 2 + 1]; } } return ddw; } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/reverse.hpp ================================================ # ifndef CPPAD_CORE_REVERSE_HPP # define CPPAD_CORE_REVERSE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file core/reverse.hpp Compute derivatives using reverse mode. */ /*! Use reverse mode to compute derivative of forward mode Taylor coefficients. The function \f$ X : {\bf R} \times {\bf R}^{n \times q} \rightarrow {\bf R} \f$ is defined by \f[ X(t , u) = \sum_{k=0}^{q-1} u^{(k)} t^k \f] The function \f$ Y : {\bf R} \times {\bf R}^{n \times q} \rightarrow {\bf R} \f$ is defined by \f[ Y(t , u) = F[ X(t, u) ] \f] The function \f$ W : {\bf R}^{n \times q} \rightarrow {\bf R} \f$ is defined by \f[ W(u) = \sum_{k=0}^{q-1} ( w^{(k)} )^{\rm T} \frac{1}{k !} \frac{ \partial^k } { t^k } Y(0, u) \f] \tparam Base base type for the operator; i.e., this operation sequence was recorded using AD< Base > and computations by this routine are done using type Base. \tparam BaseVector is a Simple Vector class with elements of type Base. \param q is the number of the number of Taylor coefficients that are being differentiated (per variable). \param w is the weighting for each of the Taylor coefficients corresponding to dependent variables. If the argument w has size m * q , for \f$ k = 0 , \ldots , q-1 \f$ and \f$ i = 0, \ldots , m-1 \f$, \f[ w_i^{(k)} = w [ i * q + k ] \f] If the argument w has size m , for \f$ k = 0 , \ldots , q-1 \f$ and \f$ i = 0, \ldots , m-1 \f$, \f[ w_i^{(k)} = \left\{ \begin{array}{ll} w [ i ] & {\rm if} \; k = q-1 \\ 0 & {\rm otherwise} \end{array} \right. \f] \return Is a vector \f$ dw \f$ such that for \f$ j = 0 , \ldots , n-1 \f$ and \f$ k = 0 , \ldots , q-1 \f$ \f[ dw[ j * q + k ] = W^{(1)} ( x )_{j,k} \f] where the matrix \f$ x \f$ is the value for \f$ u \f$ that corresponding to the forward mode Taylor coefficients for the independent variables as specified by previous calls to Forward. */ template template BaseVector ADFun::Reverse(size_t q, const BaseVector &w) { // used to identify the RecBase type in calls to sweeps RecBase not_used_rec_base(0.0); // constants const Base zero(0); // temporary indices size_t i, j, k; // number of independent variables size_t n = ind_taddr_.size(); // number of dependent variables size_t m = dep_taddr_.size(); // check BaseVector is Simple Vector class with Base type elements CheckSimpleVector(); CPPAD_ASSERT_KNOWN( size_t(w.size()) == m || size_t(w.size()) == (m * q), "Argument w to Reverse does not have length equal to\n" "the dimension of the range or dimension of range times q." ); CPPAD_ASSERT_KNOWN( q > 0, "The first argument to Reverse must be greater than zero." ); CPPAD_ASSERT_KNOWN( num_order_taylor_ >= q, "Less than q Taylor coefficients are currently stored" " in this ADFun object." ); // special case where multiple forward directions have been computed, // but we are only using the one direction zero order results if( (q == 1) && (num_direction_taylor_ > 1) ) { num_order_taylor_ = 1; // number of orders to copy size_t c = cap_order_taylor_; // keep the same capacity setting size_t r = 1; // only keep one direction capacity_order(c, r); } CPPAD_ASSERT_KNOWN( num_direction_taylor_ == 1, "Reverse mode for Forward(q, r, xq) with more than one direction" "\n(r > 1) is not yet supported for q > 1." ); // initialize entire Partial matrix to zero local::pod_vector_maybe Partial(num_var_tape_ * q); for(i = 0; i < num_var_tape_; i++) for(j = 0; j < q; j++) Partial[i * q + j] = zero; // set the dependent variable direction // (use += because two dependent variables can point to same location) if( size_t(w.size()) == m ) { for(i = 0; i < m; ++i) { CPPAD_ASSERT_UNKNOWN( dep_taddr_[i] < num_var_tape_ ); Partial[dep_taddr_[i] * q + q - 1] += w[i]; } } else { CPPAD_ASSERT_UNKNOWN( size_t(w.size()) == m * q ); for(i = 0; i < m; i++) { CPPAD_ASSERT_UNKNOWN( dep_taddr_[i] < num_var_tape_ ); for(k = 0; k < q; k++) Partial[ dep_taddr_[i] * q + k ] += w[i * q + k ]; } } // evaluate the derivatives CPPAD_ASSERT_UNKNOWN( cskip_op_.size() == play_.num_var_op() ); CPPAD_ASSERT_UNKNOWN( load_op2var_.size() == play_.num_var_load() ); local::play::const_sequential_iterator play_itr = play_.end(); local::sweep::reverse( num_var_tape_, &play_, cap_order_taylor_, taylor_.data(), q, Partial.data(), cskip_op_.data(), load_op2var_, play_itr, not_used_rec_base ); // return the derivative values BaseVector value(n * q); for(j = 0; j < n; j++) { CPPAD_ASSERT_UNKNOWN( ind_taddr_[j] < num_var_tape_ ); // independent variable taddr equals its operator taddr CPPAD_ASSERT_UNKNOWN( play_.GetOp( ind_taddr_[j] ) == local::InvOp ); // by the Reverse Identity Theorem // partial of y^{(k)} w.r.t. u^{(0)} is equal to // partial of y^{(q-1)} w.r.t. u^{(q - 1 - k)} if( size_t(w.size()) == m ) { for(k = 0; k < q; k++) value[j * q + k ] = Partial[ind_taddr_[j] * q + q - 1 - k]; } else { for(k = 0; k < q; k++) value[j * q + k ] = Partial[ind_taddr_[j] * q + k]; } } CPPAD_ASSERT_KNOWN( ! ( hasnan(value) && check_for_nan_ ) , "dw = f.Reverse(q, w): has a nan,\n" "but none of its Taylor coefficients are nan." ); return value; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/sign.hpp ================================================ # ifndef CPPAD_CORE_SIGN_HPP # define CPPAD_CORE_SIGN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sign} {xrst_spell rl } The Sign: sign ############## Syntax ****** | *y* = ``sign`` ( *x* ) Description *********** Evaluates the ``sign`` function which is defined by .. math:: {\rm sign} (x) = \left\{ \begin{array}{rl} +1 & {\rm if} \; x > 0 \\ 0 & {\rm if} \; x = 0 \\ -1 & {\rm if} \; x < 0 \end{array} \right. x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Atomic ****** This is an :ref:`atomic operation` . Derivative ********** CppAD computes the derivative of the ``sign`` function as zero for all argument values *x* . The correct mathematical derivative is different and is given by .. math:: {\rm sign}^{(1)} (x) = 2 \delta (x) where :math:`\delta (x)` is the Dirac Delta function. Example ******* {xrst_toc_hidden example/general/sign.cpp } The file :ref:`sign.cpp-name` contains an example and test of this function. {xrst_end sign} ------------------------------------------------------------------------------- */ // BEGIN CppAD namespace namespace CppAD { template AD AD::sign_me (void) const { AD result; result.value_ = sign(value_); CPPAD_ASSERT_UNKNOWN( Parameter(result) ); // check if there is a recording in progress local::ADTape* tape = AD::tape_ptr(); if( tape == nullptr ) return result; // check if operand is a constant parameter if( tape_id_ != tape->id_ ) return result; if(ad_type_ == dynamic_enum) { // dynamic parameter argument result.taddr_ = tape->Rec_.put_dyn_par( result.value_, local::sign_dyn, taddr_ ); result.tape_id_ = tape_id_; result.ad_type_ = dynamic_enum; } else { // variable argument CPPAD_ASSERT_UNKNOWN( local::NumRes(local::SignOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::SignOp) == 1 ); // corresponding operand address tape->Rec_.PutArg(taddr_); // put operator in the tape result.taddr_ = tape->Rec_.PutOp(local::SignOp); // make result a variable result.tape_id_ = tape->id_; result.ad_type_ = variable_enum; } return result; } template AD sign(const AD &x) { return x.sign_me(); } template AD sign(const VecAD_reference &x) { return x.ADBase().sign_me(); } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/sparse.hpp ================================================ # ifndef CPPAD_CORE_SPARSE_HPP # define CPPAD_CORE_SPARSE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // # include # include // # include # include // # include # include // # include # include // # include # include // # include # include // # include # include # include # endif ================================================ FILE: include/cppad/core/sparse_hes.hpp ================================================ # ifndef CPPAD_CORE_SPARSE_HES_HPP # define CPPAD_CORE_SPARSE_HES_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sparse_hes} {xrst_spell nr rc rcv } Computing Sparse Hessians ######################### Syntax ****** | *n_sweep* = *f* . ``sparse_hes`` ( | |tab| *x* , *w* , *subset* , *pattern* , *coloring* , *work* | ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the function corresponding to *f* . Here *n* is the :ref:`fun_property@Domain` size, and *m* is the :ref:`fun_property@Range` size, or *f* . The syntax above takes advantage of sparsity when computing the Hessian .. math:: H(x) = \dpow{2}{x} \sum_{i=0}^{m-1} w_i F_i (x) In the sparse case, this should be faster and take less memory than :ref:`Hessian-name` . The matrix element :math:`H_{i,j} (x)` is the second partial of :math:`w^\R{T} F (x)` with respect to :math:`x_i` and :math:`x_j`. SizeVector ********** The type *SizeVector* is a :ref:`SimpleVector-name` class with :ref:`elements of type` ``size_t`` . BaseVector ********** The type *BaseVector* is a :ref:`SimpleVector-name` class with :ref:`elements of type` ``size_t`` . f * This object has prototype ``ADFun`` < *Base* > *f* Note that the Taylor coefficients stored in *f* are affected by this operation; see :ref:`sparse_hes@Uses Forward` below. x * This argument has prototype ``const`` *BaseVector* & *x* and its size is *n* . It specifies the point at which to evaluate the Hessian :math:`H(x)`. w * This argument has prototype ``const`` *BaseVector* & *w* and its size is *m* . It specifies the weight for each of the components of :math:`F(x)`; i.e. :math:`w_i` is the weight for :math:`F_i (x)`. subset ****** This argument has prototype ``sparse_rcv`` < *SizeVector* , *BaseVector* >& *subset* Its row size and column size is *n* ; i.e., *subset* . ``nr`` () == *n* and *subset* . ``nc`` () == *n* . It specifies which elements of the Hessian are computed. #. The input value of its value vector *subset* . ``val`` () does not matter. Upon return it contains the value of the corresponding elements of the Hessian. #. All of the row, column pairs in *subset* must also appear in *pattern* ; i.e., they must be possibly non-zero. #. The Hessian is symmetric, so one has a choice as to which off diagonal elements to put in *subset* . It will probably be more efficient if one makes this choice so that the there are more entries in each non-zero column of *subset* ; see :ref:`sparse_hes@n_sweep` below. pattern ******* This argument has prototype ``const sparse_rc`` < *SizeVector* >& *pattern* Its row size and column size is *n* ; i.e., *pattern* . ``nr`` () == *n* and *pattern* . ``nc`` () == *n* . It is a sparsity pattern for the Hessian :math:`H(x)`. If the *i*-th row (*j*-th column) does not appear in *subset* , the *i*-th row (*j*-th column) of *pattern* does not matter and need not be computed. This argument is not used (and need not satisfy any conditions), when :ref:`sparse_hes@work` is non-empty. subset ====== If the *i*-th row and *i*-th column do not appear in *subset* , the *i*-th row and column of *pattern* do not matter. In this case the *i-th*-th row and column may have no entries in *pattern* even though they are possibly non-zero in :math:`H(x)`. (This can be used to reduce the amount of computation required to find *pattern* .) coloring ******** The coloring algorithm determines which rows and columns can be computed during the same sweep. This field has prototype ``const std::string&`` *coloring* This value only matters when work is empty; i.e., after the *work* constructor or *work* . ``clear`` () . cppad.symmetric =============== This coloring takes advantage of the fact that the Hessian matrix is symmetric when find a coloring that requires fewer :ref:`sweeps` . cppad.general ============= This is the same as the sparse Jacobian :ref:`sparse_jac@coloring@cppad` method which does not take advantage of symmetry. colpack.symmetric ================= If :ref:`colpack_prefix-name` was specified on the :ref:`cmake@CMake Command` line, you can set *coloring* to ``colpack.symmetric`` . This also takes advantage of the fact that the Hessian matrix is symmetric. colpack.general =============== If :ref:`colpack_prefix-name` was specified on the :ref:`cmake@CMake Command` line, you can set *coloring* to ``colpack.general`` . This is the same as the sparse Jacobian :ref:`sparse_jac@coloring@colpack` method which does not take advantage of symmetry. colpack.star Deprecated 2017-06-01 ================================== The ``colpack.star`` method is deprecated. It is the same as the ``colpack.symmetric`` method which should be used instead. work **** This argument has prototype ``sparse_hes_work&`` *work* We refer to its initial value, and its value after *work* . ``clear`` () , as empty. If it is empty, information is stored in *work* . This can be used to reduce computation when a future call is for the same object *f* , and the same subset of the Hessian. In fact, it can be used with a different *f* and a different *subset* provided that Hessian sparsity pattern for *f* and the sparsity pattern in *subset* are the same. If either of these values change, use *work* . ``clear`` () to empty this structure. n_sweep ******* The return value *n_sweep* has prototype ``size_t`` *n_sweep* It is the number of first order forward sweeps used to compute the requested Hessian values. Each first forward sweep is followed by a second order reverse sweep so it is also the number of reverse sweeps. It is also the number of colors determined by the coloring method mentioned above. This is proportional to the total computational work, not counting the zero order forward sweep, or combining multiple columns and rows into a single sweep. Uses Forward ************ After each call to :ref:`Forward-name` , the object *f* contains the corresponding :ref:`Taylor coefficients` . After a call to ``sparse_hes`` the zero order coefficients correspond to *f* . ``Forward`` (0, *x* ) All the other forward mode coefficients are unspecified. Example ******* {xrst_toc_hidden example/sparse/sparse_hes.cpp } The files :ref:`sparse_hes.cpp-name` is an example and test of ``sparse_hes`` . It returns ``true`` , if it succeeds, and ``false`` otherwise. Subset Hessian ************** The routine :ref:`sparse_sub_hes.cpp-name` is an example and test that compute a subset of a sparse Hessian. It returns ``true`` , for success, and ``false`` otherwise. {xrst_end sparse_hes} */ # include # include # include # include /*! \file sparse_hes.hpp Sparse Hessian calculation routines. */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! Class used to hold information used by Sparse Hessian routine in this file, so it does not need to be recomputed every time. */ class sparse_hes_work { public: /// row and column indices for return values /// (some may be reflected by symmetric coloring algorithms) CppAD::vector row; CppAD::vector col; /// indices that sort the row and col arrays by color CppAD::vector order; /// results of the coloring algorithm CppAD::vector color; /// constructor sparse_hes_work(void) { } /// inform CppAD that this information needs to be recomputed void clear(void) { row.clear(); col.clear(); order.clear(); color.clear(); } }; // ---------------------------------------------------------------------------- /*! Calculate sparse Hessians using forward mode \tparam Base the base type for the recording that is stored in the ADFun object. \tparam SizeVector a simple vector class with elements of type size_t. \tparam BaseVector a simple vector class with elements of type Base. \param x a vector of length n, the number of independent variables in f (this ADFun object). \param w a vector of length m, the number of dependent variables in f (this ADFun object). \param subset spedifies the subset of the sparsity pattern where the Hessian is evaluated. subset.nr() == n, subset.nc() == n. \param pattern is a sparsity pattern for the Hessian of w^T * f; pattern.nr() == n, pattern.nc() == n, where m is number of dependent variables in f. \param coloring determines which coloring algorithm is used. This must be cppad.symmetric, cppad.general, colpack.symmetric, or colpack.star. \param work this structure must be empty, or contain the information stored by a previous call to sparse_hes. The previous call must be for the same ADFun object f and the same subset. \return This is the number of first order forward (and second order reverse) sweeps used to compute the Hessian. */ template template size_t ADFun::sparse_hes( const BaseVector& x , const BaseVector& w , sparse_rcv& subset , const sparse_rc& pattern , const std::string& coloring , sparse_hes_work& work ) { size_t n = Domain(); // CPPAD_ASSERT_KNOWN( subset.nr() == n, "sparse_hes: subset.nr() not equal domain dimension for f" ); CPPAD_ASSERT_KNOWN( subset.nc() == n, "sparse_hes: subset.nc() not equal domain dimension for f" ); CPPAD_ASSERT_KNOWN( size_t( x.size() ) == n, "sparse_hes: x.size() not equal domain dimension for f" ); CPPAD_ASSERT_KNOWN( size_t( w.size() ) == Range(), "sparse_hes: w.size() not equal range dimension for f" ); // // work information vector& row(work.row); vector& col(work.col); vector& color(work.color); vector& order(work.order); // // subset information const SizeVector& subset_row( subset.row() ); const SizeVector& subset_col( subset.col() ); // // point at which we are evaluating the Hessian Forward(0, x); // // number of elements in the subset size_t K = subset.nnz(); // // check for case were there is nothing to do // (except for call to Forward(0, x) if( K == 0 ) return 0; // # ifndef NDEBUG if( color.size() != 0 ) { CPPAD_ASSERT_KNOWN( color.size() == n, "sparse_hes: work is non-empty and conditions have changed" ); CPPAD_ASSERT_KNOWN( row.size() == K, "sparse_hes: work is non-empty and conditions have changed" ); CPPAD_ASSERT_KNOWN( col.size() == K, "sparse_hes: work is non-empty and conditions have changed" ); // for(size_t k = 0; k < K; k++) { bool ok = row[k] == subset_row[k] && col[k] == subset_col[k]; ok |= row[k] == subset_col[k] && col[k] == subset_row[k]; CPPAD_ASSERT_KNOWN( ok, "sparse_hes: work is non-empty and conditions have changed" ); } } # endif // // check for case where input work is empty if( color.size() == 0 ) { // compute work color and order vectors CPPAD_ASSERT_KNOWN( pattern.nr() == n, "sparse_hes: pattern.nr() not equal domain dimension for f" ); CPPAD_ASSERT_KNOWN( pattern.nc() == n, "sparse_hes: pattern.nc() not equal domain dimension for f" ); // // initialize work row, col to be same as subset row, col row.resize(K); col.resize(K); // cannot assign vectors because may be of different types // (SizeVector and CppAD::vector) for(size_t k = 0; k < K; k++) { row[k] = subset_row[k]; col[k] = subset_col[k]; } // // convert pattern to an internal version of its transpose local::pod_vector internal_index(n); for(size_t j = 0; j < n; j++) internal_index[j] = j; bool transpose = true; bool zero_empty = false; bool input_empty = true; local::sparse::list_setvec internal_pattern; internal_pattern.resize(n, n); local::sparse::set_internal_pattern(zero_empty, input_empty, transpose, internal_index, internal_pattern, pattern ); // // execute coloring algorithm // (we are using transpose because coloring groups rows, not columns) color.resize(n); if( coloring == "cppad.general" ) local::color_general_cppad(internal_pattern, col, row, color); else if( coloring == "cppad.symmetric" ) local::color_symmetric_cppad(internal_pattern, col, row, color); else if( coloring == "colpack.general" ) { # if CPPAD_HAS_COLPACK local::color_general_colpack(internal_pattern, col, row, color); # else CPPAD_ASSERT_KNOWN( false, "sparse_hes: coloring = colpack.star " "and colpack_prefix not in cmake command line." ); # endif } else if( coloring == "colpack.symmetric" || coloring == "colpack.star" ) { # if CPPAD_HAS_COLPACK local::color_symmetric_colpack(internal_pattern, col, row, color); # else CPPAD_ASSERT_KNOWN( false, "sparse_hes: coloring = colpack.symmetric or colpack.star " "and colpack_prefix not in cmake command line." ); # endif } else CPPAD_ASSERT_KNOWN( false, "sparse_hes: coloring is not valid." ); // // put sorting indices in color order SizeVector key(K); order.resize(K); for(size_t k = 0; k < K; k++) key[k] = color[ col[k] ]; index_sort(key, order); } // Base versions of zero and one Base one(1.0); Base zero(0.0); // size_t n_color = 1; for(size_t j = 0; j < n; j++) if( color[j] < n ) n_color = std::max(n_color, color[j] + 1); // // initialize the return Hessian values as zero for(size_t k = 0; k < K; k++) subset.set(k, zero); // // direction vector for calls to first order forward BaseVector dx(n); // // return values for calls to second order reverse BaseVector ddw(2 * n); // // loop over colors size_t k = 0; for(size_t ell = 0; ell < n_color; ell++) if( k == K ) { // kludge because colpack returns colors that are not used // (it does not know about the subset corresponding to row, col) CPPAD_ASSERT_UNKNOWN( coloring == "colpack.general" || coloring == "colpack.symmetric" || coloring == "colpack.star" ); } else if( color[ col[ order[k] ] ] != ell ) { // kludge because colpack returns colors that are not used // (it does not know about the subset corresponding to row, col) CPPAD_ASSERT_UNKNOWN( coloring == "colpack.general" || coloring == "colpack.symmetric" || coloring == "colpack.star" ); } else { CPPAD_ASSERT_UNKNOWN( color[ col[ order[k] ] ] == ell ); // // combine all columns with this color for(size_t j = 0; j < n; j++) { dx[j] = zero; if( color[j] == ell ) dx[j] = one; } // call forward mode for all these rows at once Forward(1, dx); // // evaluate derivative of w^T * F'(x) * dx ddw = Reverse(2, w); // // set the corresponding components of the result while( k < K && color[ col[order[k]] ] == ell ) { size_t index = row[ order[k] ] * 2 + 1; subset.set(order[k], ddw[index] ); k++; } } // check that all the required entries have been set CPPAD_ASSERT_UNKNOWN( k == K ); return n_color; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/sparse_hessian.hpp ================================================ # ifndef CPPAD_CORE_SPARSE_HESSIAN_HPP # define CPPAD_CORE_SPARSE_HESSIAN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sparse_hessian} {xrst_spell valarray } Sparse Hessian ############## Syntax ****** | *hes* = *f* . ``SparseHessian`` ( *x* , *w* ) | *hes* = *f* . ``SparseHessian`` ( *x* , *w* , *p* ) | *n_sweep* = *f* . ``SparseHessian`` ( *x* , *w* , *p* , *row* , *col* , *hes* , *work* ) Purpose ******* We use :math:`n` for the :ref:`fun_property@Domain` size, and :math:`m` for the :ref:`fun_property@Range` size of *f* . We use :math:`F : \B{R}^n \rightarrow \B{R}^m` do denote the :ref:`glossary@AD Function` corresponding to *f* . The syntax above sets *hes* to the Hessian .. math:: H(x) = \dpow{2}{x} \sum_{i=1}^m w_i F_i (x) This routine takes advantage of the sparsity of the Hessian in order to reduce the amount of computation necessary. If *row* and *col* are present, it also takes advantage of the reduced set of elements of the Hessian that need to be computed. One can use speed tests (e.g. :ref:`speed_test-name` ) to verify that results are computed faster than when using the routine :ref:`Hessian-name` . f * The object *f* has prototype ``ADFun`` < *Base* > *f* Note that the :ref:`ADFun-name` object *f* is not ``const`` (see :ref:`sparse_hessian@Uses Forward` below). x * The argument *x* has prototype ``const`` *BaseVector* & *x* (see :ref:`sparse_hessian@BaseVector` below) and its size must be equal to *n* , the dimension of the :ref:`fun_property@Domain` space for *f* . It specifies that point at which to evaluate the Hessian. w * The argument *w* has prototype ``const`` *BaseVector* & *w* and size :math:`m`. It specifies the value of :math:`w_i` in the expression for *hes* . The more components of :math:`w` that are identically zero, the more sparse the resulting Hessian may be (and hence the more efficient the calculation of *hes* may be). p * The argument *p* is optional and has prototype ``const`` *SetVector* & *p* (see :ref:`sparse_hessian@SetVector` below) If it has elements of type ``bool`` , its size is :math:`n * n`. If it has elements of type ``std::set`` , its size is :math:`n` and all its set elements are between zero and :math:`n - 1`. It specifies a :ref:`glossary@Sparsity Pattern` for the Hessian :math:`H(x)`. Purpose ======= If this sparsity pattern does not change between calls to ``SparseHessian`` , it should be faster to calculate *p* once and pass this argument to ``SparseHessian`` . If you specify *p* , CppAD will use the same type of sparsity representation (vectors of ``bool`` or vectors of ``std::set`` ) for its internal calculations. Otherwise, the representation for the internal calculations is unspecified. work ==== If you specify *work* in the calling sequence, it is not necessary to keep the sparsity pattern; see the heading :ref:`sparse_hessian@work@p` under the *work* description. Column Subset ============= If the arguments *row* and *col* are present, and :ref:`sparse_hessian@work@color_method` is ``cppad.general`` or ``cppad.symmetric`` , it is not necessary to compute the entire sparsity pattern. Only the following subset of column values will matter: { *col* [ *k* ] : *k* = 0 , ... , *K* ``-1`` } . row, col ******** The arguments *row* and *col* are optional and have prototype | |tab| ``const`` *SizeVector* & *row* | |tab| ``const`` *SizeVector* & *col* (see :ref:`sparse_hessian@SizeVector` below). They specify which rows and columns of :math:`H (x)` are returned and in what order. We use :math:`K` to denote the value *hes* . ``size`` () which must also equal the size of *row* and *col* . Furthermore, for :math:`k = 0 , \ldots , K-1`, it must hold that :math:`row[k] < n` and :math:`col[k] < n`. In addition, all of the :math:`(row[k], col[k])` pairs must correspond to a true value in the sparsity pattern *p* . hes *** The result *hes* has prototype *BaseVector* *hes* In the case where *row* and *col* are not present, the size of *hes* is :math:`n * n` and its size is :math:`n * n`. In this case, for :math:`i = 0 , \ldots , n - 1` and :math:`ell = 0 , \ldots , n - 1` .. math:: hes [ j * n + \ell ] = \DD{ w^{\rm T} F }{ x_j }{ x_\ell } ( x ) In the case where the arguments *row* and *col* are present, we use :math:`K` to denote the size of *hes* . The input value of its elements does not matter. Upon return, for :math:`k = 0 , \ldots , K - 1`, .. math:: hes [ k ] = \DD{ w^{\rm T} F }{ x_j }{ x_\ell } (x) \; , \; \; {\rm where} \; j = row[k] \; {\rm and } \; \ell = col[k] work **** If this argument is present, it has prototype ``sparse_hessian_work&`` *work* This object can only be used with the routines ``SparseHessian`` . During its the first use, information is stored in *work* . This is used to reduce the work done by future calls to ``SparseHessian`` with the same *f* , *p* , *row* , and *col* . If a future call is made where any of these values have changed, you must first call *work* . ``clear`` () to inform CppAD that this information needs to be recomputed. color_method ============ The coloring algorithm determines which rows and columns can be computed during the same sweep. This field has prototype ``std::string`` *work* . ``color_method`` This value only matters on the first call to ``sparse_hessian`` that follows the *work* constructor or a call to *work* . ``clear`` () . ``"cppad.symmetric"`` This is the default coloring method (after a constructor or ``clear()`` ). It takes advantage of the fact that the Hessian matrix is symmetric to find a coloring that requires fewer :ref:`sweeps` . ``"cppad.general"`` This is the same as the ``"cppad"`` method for the :ref:`sparse_jacobian` calculation. ``"colpack.symmetric"`` This method requires that :ref:`colpack_prefix-name` was specified on the :ref:`cmake@CMake Command` line. It also takes advantage of the fact that the Hessian matrix is symmetric. ``"colpack.general"`` This is the same as the ``"colpack"`` method for the :ref:`sparse_jacobian` calculation. colpack.star Deprecated 2017-06-01 ================================== The ``colpack.star`` method is deprecated. It is the same as the ``colpack.symmetric`` which should be used instead. p = If *work* is present, and it is not the first call after its construction or a clear, the sparsity pattern *p* is not used. This enables one to free the sparsity pattern and still compute corresponding sparse Hessians. n_sweep ******* The return value *n_sweep* has prototype ``size_t`` *n_sweep* It is the number of first order forward sweeps used to compute the requested Hessian values. Each first forward sweep is followed by a second order reverse sweep so it is also the number of reverse sweeps. This is proportional to the total work that ``SparseHessian`` does, not counting the zero order forward sweep, or the work to combine multiple columns into a single forward-reverse sweep pair. BaseVector ********** The type *BaseVector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` *Base* . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. SetVector ********* The type *SetVector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` ``bool`` or ``std::set`` ; see :ref:`glossary@Sparsity Pattern` for a discussion of the difference. The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Restrictions ============ If *SetVector* has elements of ``std::set`` , then *p* [ *i* ] must return a reference (not a copy) to the corresponding set. According to section 26.3.2.3 of the 1998 C++ standard, ``std::valarray< std::set >`` does not satisfy this condition. SizeVector ********** The type *SizeVector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` ``size_t`` . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Uses Forward ************ After each call to :ref:`Forward-name` , the object *f* contains the corresponding :ref:`Taylor coefficients` . After a call to any of the sparse Hessian routines, the zero order Taylor coefficients correspond to *f* . ``Forward`` (0, *x* ) and the other coefficients are unspecified. {xrst_toc_hidden example/sparse/sparse_hessian.cpp example/sparse/sub_sparse_hes.cpp example/sparse/sparse_sub_hes.cpp } Example ******* The routine :ref:`sparse_hessian.cpp-name` is examples and tests of ``sparse_hessian`` . It return ``true`` , if it succeeds and ``false`` otherwise. Subset Hessian ************** The routine :ref:`sub_sparse_hes.cpp-name` is an example and test that compute a sparse Hessian for a subset of the variables. It returns ``true`` , for success, and ``false`` otherwise. {xrst_end sparse_hessian} ----------------------------------------------------------------------------- */ # include # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file sparse_hessian.hpp Sparse Hessian driver routine and helper functions. */ // =========================================================================== /*! class used by SparseHessian to hold information so it does not need to be recomputed. */ class sparse_hessian_work { public: /// Coloring method: "cppad", or "colpack" /// (this field is set by user) std::string color_method; /// row and column indices for return values /// (some may be reflected by star coloring algorithm) CppAD::vector row; CppAD::vector col; /// indices that sort the user row and col arrays by color CppAD::vector order; /// results of the coloring algorithm CppAD::vector color; /// constructor sparse_hessian_work(void) : color_method("cppad.symmetric") { } /// inform CppAD that this information needs to be recomputed void clear(void) { color_method = "cppad.symmetric"; row.clear(); col.clear(); order.clear(); color.clear(); } }; // =========================================================================== /*! Private helper function that does computation for all Sparse Hessian cases. \tparam Base is the base type for the recording that is stored in this ADFun. \tparam SizeVector is sparse_pack or sparse_list. \param x [in] is a vector specifying the point at which to compute the Hessian. \param w [in] is the weighting vector that defines a scalar valued function by a weighted sum of the components of the vector valued function $latex F(x)$$. \param sparsity [in] is the sparsity pattern for the Hessian that we are calculating. \param user_row [in] is the vector of row indices for the returned Hessian values. \param user_col [in] is the vector of columns indices for the returned Hessian values. It must have the same size as user_row. \param hes [out] is the vector of Hessian values. It must have the same size as user_row. The return value hes[k] is the second partial of \f$ w^{\rm T} F(x)\f$ with respect to the row[k] and col[k] component of \f$ x\f$. \param work This structure contains information that is computed by SparseHessianCompute. If the sparsity pattern, row vector, or col vectors are not the same between calls to SparseHessianCompute, work.clear() must be called to reinitialize work. \return Is the number of first order forward sweeps used to compute the requested Hessian values. (This is also equal to the number of second order reverse sweeps.) The total work, not counting the zero order forward sweep, or the time to combine computations, is proportional to this return value. */ template template size_t ADFun::SparseHessianCompute( const BaseVector& x , const BaseVector& w , SetVector& sparsity , const SizeVector& user_row , const SizeVector& user_col , BaseVector& hes , sparse_hessian_work& work ) { using CppAD::vectorBool; size_t i, k, ell; CppAD::vector& row(work.row); CppAD::vector& col(work.col); CppAD::vector& color(work.color); CppAD::vector& order(work.order); size_t n = Domain(); // some values const Base zero(0); const Base one(1); // check BaseVector is Simple Vector class with Base type elements CheckSimpleVector(); // number of components of Hessian that are required size_t K = hes.size(); CPPAD_ASSERT_UNKNOWN( size_t( user_row.size() ) == K ); CPPAD_ASSERT_UNKNOWN( size_t( user_col.size() ) == K ); CPPAD_ASSERT_UNKNOWN( size_t(x.size()) == n ); CPPAD_ASSERT_UNKNOWN( color.size() == 0 || color.size() == n ); CPPAD_ASSERT_UNKNOWN( row.size() == 0 || row.size() == K ); CPPAD_ASSERT_UNKNOWN( col.size() == 0 || col.size() == K ); // Point at which we are evaluating the Hessian Forward(0, x); // check for case where nothing (except Forward above) to do if( K == 0 ) return 0; // Rows of the Hessian (i below) correspond to the forward mode index // and columns (j below) correspond to the reverse mode index. if( color.size() == 0 ) { CPPAD_ASSERT_UNKNOWN( sparsity.n_set() == n ); CPPAD_ASSERT_UNKNOWN( sparsity.end() == n ); // copy user rwo and col to work space row.resize(K); col.resize(K); for(k = 0; k < K; k++) { row[k] = user_row[k]; col[k] = user_col[k]; } // execute coloring algorithm color.resize(n); if( work.color_method == "cppad.general" ) local::color_general_cppad(sparsity, row, col, color); else if( work.color_method == "cppad.symmetric" ) local::color_symmetric_cppad(sparsity, row, col, color); else if( work.color_method == "colpack.general" ) { # if CPPAD_HAS_COLPACK local::color_general_colpack(sparsity, row, col, color); # else CPPAD_ASSERT_KNOWN( false, "SparseHessian: work.color_method = colpack.general " "and colpack_prefix missing from cmake command line." ); # endif } else if( work.color_method == "colpack.symmetric" || work.color_method == "colpack.star" ) { # if CPPAD_HAS_COLPACK local::color_symmetric_colpack(sparsity, row, col, color); # else CPPAD_ASSERT_KNOWN( false, "SparseHessian: work.color_method is " "colpack.symmetric or colpack.star\n" "and colpack_prefix missing from cmake command line." ); # endif } else { CPPAD_ASSERT_KNOWN( false, "SparseHessian: work.color_method is not valid." ); } // put sorting indices in color order SizeVector key(K); order.resize(K); for(k = 0; k < K; k++) key[k] = color[ row[k] ]; index_sort(key, order); } size_t n_color = 1; for(ell = 0; ell < n; ell++) if( color[ell] < n ) n_color = std::max(n_color, color[ell] + 1); // direction vector for calls to forward (rows of the Hessian) BaseVector u(n); // location for return values from reverse (columns of the Hessian) BaseVector ddw(2 * n); // initialize the return value for(k = 0; k < K; k++) hes[k] = zero; // loop over colors # ifndef NDEBUG const std::string& coloring = work.color_method; # endif k = 0; for(ell = 0; ell < n_color; ell++) if( k == K ) { // kludge because colpack returns colors that are not used // (it does not know about the subset corresponding to row, col) CPPAD_ASSERT_UNKNOWN( coloring == "colpack.general" || coloring == "colpack.symmetric" || coloring == "colpack.star" ); } else if( color[ row[ order[k] ] ] != ell ) { // kludge because colpack returns colors that are not used // (it does not know about the subset corresponding to row, col) CPPAD_ASSERT_UNKNOWN( coloring == "colpack.general" || coloring == "colpack.symmetric" || coloring == "colpack.star" ); } else { CPPAD_ASSERT_UNKNOWN( color[ row[ order[k] ] ] == ell ); // combine all rows with this color for(i = 0; i < n; i++) { u[i] = zero; if( color[i] == ell ) u[i] = one; } // call forward mode for all these rows at once Forward(1, u); // evaluate derivative of w^T * F'(x) * u ddw = Reverse(2, w); // set the corresponding components of the result while( k < K && color[ row[ order[k] ] ] == ell ) { hes[ order[k] ] = ddw[ col[ order[k] ] * 2 + 1 ]; k++; } } return n_color; } // =========================================================================== // Public Member Functions // =========================================================================== /*! Compute user specified subset of a sparse Hessian. The C++ source code corresponding to this operation is \verbatim SparseHessian(x, w, p, row, col, hes, work) \endverbatim \tparam Base is the base type for the recording that is stored in this ADFun. \tparam SizeVector is a simple vector class with elements of type size_t. \param x [in] is a vector specifying the point at which to compute the Hessian. \param w [in] is the weighting vector that defines a scalar valued function by a weighted sum of the components of the vector valued function $latex F(x)$$. \param p [in] is the sparsity pattern for the Hessian that we are calculating. \param row [in] is the vector of row indices for the returned Hessian values. \param col [in] is the vector of columns indices for the returned Hessian values. It must have the same size are r. \param hes [out] is the vector of Hessian values. It must have the same size are r. The return value hes[k] is the second partial of \f$ w^{\rm T} F(x)\f$ with respect to the row[k] and col[k] component of \f$ x\f$. \param work This structure contains information that is computed by SparseHessianCompute. If the sparsity pattern, row vector, or col vectors are not the same between calls to SparseHessian, work.clear() must be called to reinitialize work. \return Is the number of first order forward sweeps used to compute the requested Hessian values. (This is also equal to the number of second order reverse sweeps.) The total work, not counting the zero order forward sweep, or the time to combine computations, is proportional to this return value. */ template template size_t ADFun::SparseHessian( const BaseVector& x , const BaseVector& w , const SetVector& p , const SizeVector& row , const SizeVector& col , BaseVector& hes , sparse_hessian_work& work ) { size_t n = Domain(); size_t K = hes.size(); # ifndef NDEBUG size_t k; CPPAD_ASSERT_KNOWN( size_t(x.size()) == n , "SparseHessian: size of x not equal domain dimension for f." ); CPPAD_ASSERT_KNOWN( size_t(row.size()) == K && size_t(col.size()) == K , "SparseHessian: either r or c does not have the same size as ehs." ); CPPAD_ASSERT_KNOWN( work.color.size() == 0 || work.color.size() == n, "SparseHessian: invalid value in work." ); for(k = 0; k < K; k++) { CPPAD_ASSERT_KNOWN( row[k] < n, "SparseHessian: invalid value in r." ); CPPAD_ASSERT_KNOWN( col[k] < n, "SparseHessian: invalid value in c." ); } if( work.color.size() != 0 ) for(size_t j = 0; j < n; j++) CPPAD_ASSERT_KNOWN( work.color[j] <= n, "SparseHessian: invalid value in work." ); # endif // check for case where there is nothing to compute size_t n_sweep = 0; if( K == 0 ) return n_sweep; typedef typename SetVector::value_type Set_type; typedef typename local::sparse::internal_pattern::pattern_type Pattern_type; Pattern_type s; if( work.color.size() == 0 ) { bool transpose = false; const char* error_msg = "SparseHessian: sparsity pattern" " does not have proper row or column dimension"; sparsity_user2internal(s, p, n, n, transpose, error_msg); } n_sweep = SparseHessianCompute(x, w, s, row, col, hes, work); return n_sweep; } /*! Compute a sparse Hessian. The C++ source code corresponding to this operation is \verbatim hes = SparseHessian(x, w, p) \endverbatim \tparam Base is the base type for the recording that is stored in this ADFun. \param x [in] is a vector specifying the point at which to compute the Hessian. \param w [in] The Hessian is computed for a weighted sum of the components of the function corresponding to this ADFun object. The argument w specifies the weights for each component. It must have size equal to the range dimension for this ADFun object. \param p [in] is a sparsity pattern for the Hessian. \return Will be a vector of size n * n containing the Hessian of at the point specified by x (where n is the domain dimension for this ADFun object). */ template template BaseVector ADFun::SparseHessian( const BaseVector& x, const BaseVector& w, const SetVector& p ) { size_t i, j, k; size_t n = Domain(); BaseVector hes(n * n); CPPAD_ASSERT_KNOWN( size_t(x.size()) == n, "SparseHessian: size of x not equal domain size for f." ); typedef typename SetVector::value_type Set_type; typedef typename local::sparse::internal_pattern::pattern_type Pattern_type; // initialize the return value as zero Base zero(0); for(i = 0; i < n; i++) for(j = 0; j < n; j++) hes[i * n + j] = zero; // arguments to SparseHessianCompute Pattern_type s; CppAD::vector row; CppAD::vector col; sparse_hessian_work work; bool transpose = false; const char* error_msg = "SparseHessian: sparsity pattern" " does not have proper row or column dimension"; sparsity_user2internal(s, p, n, n, transpose, error_msg); k = 0; for(i = 0; i < n; i++) { typename Pattern_type::const_iterator itr(s, i); j = *itr; while( j != s.end() ) { row.push_back(i); col.push_back(j); k++; j = *(++itr); } } size_t K = k; BaseVector H(K); // now we have folded this into the following case SparseHessianCompute(x, w, s, row, col, H, work); // now set the non-zero return values for(k = 0; k < K; k++) hes[ row[k] * n + col[k] ] = H[k]; return hes; } /*! Compute a sparse Hessian The C++ source code corresponding to this operation is \verbatim hes = SparseHessian(x, w) \endverbatim \tparam Base is the base type for the recording that is stored in this ADFun object. The argument w specifies the weights for each component. It must have size equal to the range dimension for this ADFun object. \return Will be a vector of size n * n containing the Hessian of at the point specified by x (where n is the domain dimension for this ADFun object). */ template template BaseVector ADFun::SparseHessian(const BaseVector &x, const BaseVector &w) { size_t i, j, k; typedef CppAD::vectorBool BoolVector; size_t m = Range(); size_t n = Domain(); // determine the sparsity pattern p for Hessian of w^T F BoolVector r(n * n); for(j = 0; j < n; j++) { for(k = 0; k < n; k++) r[j * n + k] = false; r[j * n + j] = true; } ForSparseJac(n, r); // BoolVector s(m); for(i = 0; i < m; i++) s[i] = w[i] != 0; BoolVector p = RevSparseHes(n, s); // compute sparse Hessian return SparseHessian(x, w, p); } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/sparse_jac.hpp ================================================ # ifndef CPPAD_CORE_SPARSE_JAC_HPP # define CPPAD_CORE_SPARSE_JAC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sparse_jac} {xrst_spell nr rc rcv } Computing Sparse Jacobians ########################## Syntax ****** | *n_color* = *f* . ``sparse_jac_for`` ( | |tab| *group_max* , *x* , *subset* , *pattern* , *coloring* , *work* | ) | *n_color* = *f* . ``sparse_jac_rev`` ( | |tab| *x* , *subset* , *pattern* , *coloring* , *work* | ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the function corresponding to *f* . Here *n* is the :ref:`fun_property@Domain` size, and *m* is the :ref:`fun_property@Range` size, or *f* . The syntax above takes advantage of sparsity when computing the Jacobian .. math:: J(x) = F^{(1)} (x) In the sparse case, this should be faster and take less memory than :ref:`Jacobian-name` . We use the notation :math:`J_{i,j} (x)` to denote the partial of :math:`F_i (x)` with respect to :math:`x_j`. SizeVector ********** The type *SizeVector* is a :ref:`SimpleVector-name` class with :ref:`elements of type` ``size_t`` . BaseVector ********** The type *BaseVector* is a :ref:`SimpleVector-name` class with :ref:`elements of type` ``size_t`` . sparse_jac_for ************** This function uses first order forward mode sweeps :ref:`forward_one-name` to compute multiple columns of the Jacobian at the same time. sparse_jac_rev ************** This uses function first order reverse mode sweeps :ref:`reverse_one-name` to compute multiple rows of the Jacobian at the same time. f * This object has prototype ``ADFun`` < *Base* > *f* Note that the Taylor coefficients stored in *f* are affected by this operation; see :ref:`sparse_jac@Uses Forward` below. group_max ********* This argument has prototype ``size_t`` *group_max* and must be greater than zero. It specifies the maximum number of colors to group during a single forward sweep. If a single color is in a group, a single direction for of first order forward mode :ref:`forward_one-name` is used for each color. If multiple colors are in a group, the multiple direction for of first order forward mode :ref:`forward_dir-name` is used with one direction for each color. This uses separate memory for each direction (more memory), but my be significantly faster. x * This argument has prototype ``const`` *BaseVector* & *x* and its size is *n* . It specifies the point at which to evaluate the Jacobian :math:`J(x)`. subset ****** This argument has prototype ``sparse_rcv`` < *SizeVector* , *BaseVector* >& *subset* Its row size is *subset* . ``nr`` () == *m* , and its column size is *subset* . ``nc`` () == *n* . It specifies which elements of the Jacobian are computed. The input value of its value vector *subset* . ``val`` () does not matter. Upon return it contains the value of the corresponding elements of the Jacobian. All of the row, column pairs in *subset* must also appear in *pattern* ; i.e., they must be possibly non-zero. pattern ******* This argument has prototype ``const sparse_rc`` < *SizeVector* >& *pattern* Its row size is *pattern* . ``nr`` () == *m* , and its column size is *pattern* . ``nc`` () == *n* . It is a sparsity pattern for the Jacobian :math:`J(x)`. This argument is not used (and need not satisfy any conditions), when :ref:`sparse_jac@work` is non-empty. coloring ******** The coloring algorithm determines which rows (reverse) or columns (forward) can be computed during the same sweep. This field has prototype ``const std::string&`` *coloring* This value only matters when work is empty; i.e., after the *work* constructor or *work* . ``clear`` () . cppad ===== This uses a general purpose coloring algorithm written for Cppad. colpack ======= If :ref:`colpack_prefix-name` is specified on the :ref:`cmake@CMake Command` line, you can set *coloring* to ``colpack`` . This uses a general purpose coloring algorithm that is part of Colpack. work **** This argument has prototype ``sparse_jac_work&`` *work* We refer to its initial value, and its value after *work* . ``clear`` () , as empty. If it is empty, information is stored in *work* . This can be used to reduce computation when a future call is for the same object *f* , the same member function ``sparse_jac_for`` or ``sparse_jac_rev`` , and the same subset of the Jacobian. In fact, it can be used with a different *f* and a different *subset* provided that Jacobian sparsity pattern for *f* and the sparsity pattern in *subset* are the same. If any of these values change, use *work* . ``clear`` () to empty this structure. n_color ******* The return value *n_color* has prototype ``size_t`` *n_color* If ``sparse_jac_for`` (``sparse_jac_rev`` ) is used, *n_color* is the number of first order forward directions used to compute the requested Jacobian values. It is also the number of colors determined by the coloring method mentioned above. This is proportional to the total computational work, not counting the zero order forward sweep, or combining multiple columns (rows) into a single sweep. Note that if *group_max* == 1 , or if we are using ``sparse_jac_rev`` , *n_color* is equal to the number of sweeps. Uses Forward ************ After each call to :ref:`Forward-name` , the object *f* contains the corresponding :ref:`Taylor coefficients` . After a call to ``sparse_jac_forward`` or ``sparse_jac_rev`` , the zero order coefficients correspond to *f* . ``Forward`` (0, *x* ) All the other forward mode coefficients are unspecified. Example ******* {xrst_toc_hidden example/sparse/sparse_jac_for.cpp example/sparse/sparse_jac_rev.cpp } The files :ref:`sparse_jac_for.cpp-name` and :ref:`sparse_jac_rev.cpp-name` are examples and tests of ``sparse_jac_for`` and ``sparse_jac_rev`` . They return ``true`` , if they succeed, and ``false`` otherwise. {xrst_end sparse_jac} */ # include # include # include # include /*! \file sparse_jac.hpp Sparse Jacobian calculation routines. */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! Class used to hold information used by Sparse Jacobian routines in this file, so they do not need to be recomputed every time. */ class sparse_jac_work { public: /// indices that sort the user row and col arrays by color CppAD::vector order; /// results of the coloring algorithm CppAD::vector color; // /// constructor sparse_jac_work(void) { } /// reset work to empty. /// This informs CppAD that color and order need to be recomputed void clear(void) { order.clear(); color.clear(); } }; // ---------------------------------------------------------------------------- /*! Calculate sparse Jacobains using forward mode \tparam Base the base type for the recording that is stored in the ADFun object. \tparam SizeVector a simple vector class with elements of type size_t. \tparam BaseVector a simple vector class with elements of type Base. \param group_max specifies the maximum number of colors to group during a single forward sweep. This must be greater than zero and group_max = 1 minimizes memory usage. \param x a vector of length n, the number of independent variables in f (this ADFun object). \param subset spedifies the subset of the sparsity pattern where the Jacobian is evaluated. subset.nr() == m, subset.nc() == n. \param pattern is a sparsity pattern for the Jacobian of f; pattern.nr() == m, pattern.nc() == n, where m is number of dependent variables in f. \param coloring determines which coloring algorithm is used. This must be cppad or colpack. \param work this structure must be empty, or contain the information stored by a previous call to sparse_jac_for. The previous call must be for the same ADFun object f and the same subset. \return This is the number of first order forward sweeps used to compute the Jacobian. */ template template size_t ADFun::sparse_jac_for( size_t group_max , const BaseVector& x , sparse_rcv& subset , const sparse_rc& pattern , const std::string& coloring , sparse_jac_work& work ) { size_t m = Range(); size_t n = Domain(); // CPPAD_ASSERT_KNOWN( subset.nr() == m, "sparse_jac_for: subset.nr() not equal range dimension for f" ); CPPAD_ASSERT_KNOWN( subset.nc() == n, "sparse_jac_for: subset.nc() not equal domain dimension for f" ); // // row and column vectors in subset const SizeVector& row( subset.row() ); const SizeVector& col( subset.col() ); // vector& color(work.color); vector& order(work.order); CPPAD_ASSERT_KNOWN( color.size() == 0 || color.size() == n, "sparse_jac_for: work is non-empty and conditions have changed" ); // // point at which we are evaluating the Jacobian Forward(0, x); // // number of elements in the subset size_t K = subset.nnz(); // // check for case were there is nothing to do // (except for call to Forward(0, x) if( K == 0 ) return 0; // // check for case where input work is empty if( color.size() == 0 ) { // compute work color and order vectors CPPAD_ASSERT_KNOWN( pattern.nr() == m, "sparse_jac_for: pattern.nr() not equal range dimension for f" ); CPPAD_ASSERT_KNOWN( pattern.nc() == n, "sparse_jac_for: pattern.nc() not equal domain dimension for f" ); // // convert pattern to an internal version of its transpose local::pod_vector internal_index(n); for(size_t j = 0; j < n; j++) internal_index[j] = j; bool transpose = true; bool zero_empty = false; bool input_empty = true; local::sparse::list_setvec pattern_transpose; pattern_transpose.resize(n, m); local::sparse::set_internal_pattern(zero_empty, input_empty, transpose, internal_index, pattern_transpose, pattern ); // // execute coloring algorithm // (we are using transpose because coloring groups rows, not columns). color.resize(n); if( coloring == "cppad" ) local::color_general_cppad(pattern_transpose, col, row, color); else if( coloring == "colpack" ) { # if CPPAD_HAS_COLPACK local::color_general_colpack(pattern_transpose, col, row, color); # else CPPAD_ASSERT_KNOWN( false, "sparse_jac_for: coloring = colpack " "and colpack_prefix missing from cmake command line." ); # endif } else CPPAD_ASSERT_KNOWN( false, "sparse_jac_for: coloring is not valid." ); // // put sorting indices in color order SizeVector key(K); order.resize(K); for(size_t k = 0; k < K; k++) key[k] = color[ col[k] ]; index_sort(key, order); } // Base versions of zero and one Base one(1.0); Base zero(0.0); // size_t n_color = 1; for(size_t j = 0; j < n; j++) if( color[j] < n ) n_color = std::max(n_color, color[j] + 1); // // initialize the return Jacobian values as zero for(size_t k = 0; k < K; k++) subset.set(k, zero); // // index in subset size_t k = 0; // number of colors computed so far size_t color_count = 0; // while( color_count < n_color ) { // number of colors that will be in this group size_t group_size = std::min(group_max, n_color - color_count); // // forward mode values for independent and dependent variables BaseVector dx(n * group_size), dy(m * group_size); // // set dx for(size_t ell = 0; ell < group_size; ell++) { // combine all columns with this color for(size_t j = 0; j < n; j++) { dx[j * group_size + ell] = zero; if( color[j] == ell + color_count ) dx[j * group_size + ell] = one; } } if( group_size == 1 ) dy = Forward(1, dx); else dy = Forward(1, group_size, dx); // // store results in subset for(size_t ell = 0; ell < group_size; ell++) { // color with index ell + color_count is in this group while(k < K && color[ col[ order[k] ] ] == ell + color_count ) { // subset element with index order[k] is included in this color size_t r = row[ order[k] ]; subset.set( order[k], dy[ r * group_size + ell ] ); ++k; } } // advance color count color_count += group_size; } CPPAD_ASSERT_UNKNOWN( color_count == n_color ); // return n_color; } // ---------------------------------------------------------------------------- /*! Calculate sparse Jacobains using reverse mode \tparam Base the base type for the recording that is stored in the ADFun object. \tparam SizeVector a simple vector class with elements of type size_t. \tparam BaseVector a simple vector class with elements of type Base. \param x a vector of length n, the number of independent variables in f (this ADFun object). \param subset spedifies the subset of the sparsity pattern where the Jacobian is evaluated. subset.nr() == m, subset.nc() == n. \param pattern is a sparsity pattern for the Jacobian of f; pattern.nr() == m, pattern.nc() == n, where m is number of dependent variables in f. \param coloring determines which coloring algorithm is used. This must be cppad or colpack. \param work this structure must be empty, or contain the information stored by a previous call to sparse_jac_rev. The previous call must be for the same ADFun object f and the same subset. \return This is the number of first order reverse sweeps used to compute the Jacobian. */ template template size_t ADFun::sparse_jac_rev( const BaseVector& x , sparse_rcv& subset , const sparse_rc& pattern , const std::string& coloring , sparse_jac_work& work ) { size_t m = Range(); size_t n = Domain(); // CPPAD_ASSERT_KNOWN( subset.nr() == m, "sparse_jac_rev: subset.nr() not equal range dimension for f" ); CPPAD_ASSERT_KNOWN( subset.nc() == n, "sparse_jac_rev: subset.nc() not equal domain dimension for f" ); // // row and column vectors in subset const SizeVector& row( subset.row() ); const SizeVector& col( subset.col() ); // vector& color(work.color); vector& order(work.order); CPPAD_ASSERT_KNOWN( color.size() == 0 || color.size() == m, "sparse_jac_rev: work is non-empty and conditions have changed" ); // // point at which we are evaluating the Jacobian Forward(0, x); // // number of elements in the subset size_t K = subset.nnz(); // // check for case were there is nothing to do // (except for call to Forward(0, x) if( K == 0 ) return 0; // // check for case where input work is empty if( color.size() == 0 ) { // compute work color and order vectors CPPAD_ASSERT_KNOWN( pattern.nr() == m, "sparse_jac_rev: pattern.nr() not equal range dimension for f" ); CPPAD_ASSERT_KNOWN( pattern.nc() == n, "sparse_jac_rev: pattern.nc() not equal domain dimension for f" ); // // convert pattern to an internal version local::pod_vector internal_index(m); for(size_t i = 0; i < m; i++) internal_index[i] = i; bool transpose = false; bool zero_empty = false; bool input_empty = true; local::sparse::list_setvec internal_pattern; internal_pattern.resize(m, n); local::sparse::set_internal_pattern(zero_empty, input_empty, transpose, internal_index, internal_pattern, pattern ); // // execute coloring algorithm color.resize(m); if( coloring == "cppad" ) local::color_general_cppad(internal_pattern, row, col, color); else if( coloring == "colpack" ) { # if CPPAD_HAS_COLPACK local::color_general_colpack(internal_pattern, row, col, color); # else CPPAD_ASSERT_KNOWN( false, "sparse_jac_rev: coloring = colpack " "and colpack_prefix missing from cmake command line." ); # endif } else CPPAD_ASSERT_KNOWN( false, "sparse_jac_rev: coloring is not valid." ); // // put sorting indices in color order SizeVector key(K); order.resize(K); for(size_t k = 0; k < K; k++) key[k] = color[ row[k] ]; index_sort(key, order); } // Base versions of zero and one Base one(1.0); Base zero(0.0); // size_t n_color = 1; for(size_t i = 0; i < m; i++) if( color[i] < m ) n_color = std::max(n_color, color[i] + 1); // // initialize the return Jacobian values as zero for(size_t k = 0; k < K; k++) subset.set(k, zero); // // weighting vector and return values for calls to Reverse BaseVector w(m), dw(n); // // loop over colors size_t k = 0; for(size_t ell = 0; ell < n_color; ell++) if( k == K ) { // kludge because colpack returns colors that are not used // (it does not know about the subset corresponding to row, col) CPPAD_ASSERT_UNKNOWN( coloring == "colpack" ); } else if( color[ row[ order[k] ] ] != ell ) { // kludge because colpack returns colors that are not used // (it does not know about the subset corresponding to row, col) CPPAD_ASSERT_UNKNOWN( coloring == "colpack" ); } else { CPPAD_ASSERT_UNKNOWN( color[ row[ order[k] ] ] == ell ); // // combine all rows with this color for(size_t i = 0; i < m; i++) { w[i] = zero; if( color[i] == ell ) w[i] = one; } // call reverse mode for all these rows at once dw = Reverse(1, w); // // set the corresponding components of the result while( k < K && color[ row[order[k]] ] == ell ) { subset.set(order[k], dw[col[order[k]]] ); k++; } } return n_color; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/sparse_jacobian.hpp ================================================ # ifndef CPPAD_CORE_SPARSE_JACOBIAN_HPP # define CPPAD_CORE_SPARSE_JACOBIAN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- // maximum number of sparse directions to compute at the same time // # define CPPAD_SPARSE_JACOBIAN_MAX_MULTIPLE_DIRECTION 1 # define CPPAD_SPARSE_JACOBIAN_MAX_MULTIPLE_DIRECTION 64 /* {xrst_begin sparse_jacobian} {xrst_spell valarray } Sparse Jacobian ############### Syntax ****** | *jac* = *f* . ``SparseJacobian`` ( *x* ) | *jac* = *f* . ``SparseJacobian`` ( *x* , *p* ) | *n_sweep* = *f* . ``SparseJacobianForward`` ( *x* , *p* , *row* , *col* , *jac* , *work* ) | *n_sweep* = *f* . ``SparseJacobianReverse`` ( *x* , *p* , *row* , *col* , *jac* , *work* ) Purpose ******* We use :math:`n` for the :ref:`fun_property@Domain` size, and :math:`m` for the :ref:`fun_property@Range` size of *f* . We use :math:`F : \B{R}^n \rightarrow \B{R}^m` do denote the :ref:`glossary@AD Function` corresponding to *f* . The syntax above sets *jac* to the Jacobian .. math:: jac = F^{(1)} (x) This routine takes advantage of the sparsity of the Jacobian in order to reduce the amount of computation necessary. If *row* and *col* are present, it also takes advantage of the reduced set of elements of the Jacobian that need to be computed. One can use speed tests (e.g. :ref:`speed_test-name` ) to verify that results are computed faster than when using the routine :ref:`Jacobian-name` . f * The object *f* has prototype ``ADFun`` < *Base* > *f* Note that the :ref:`ADFun-name` object *f* is not ``const`` (see :ref:`sparse_jacobian@Uses Forward` below). x * The argument *x* has prototype ``const`` *BaseVector* & *x* (see :ref:`sparse_jacobian@BaseVector` below) and its size must be equal to *n* , the dimension of the :ref:`fun_property@Domain` space for *f* . It specifies that point at which to evaluate the Jacobian. p * The argument *p* is optional and has prototype ``const`` *SetVector* & *p* (see :ref:`sparse_jacobian@SetVector` below). If it has elements of type ``bool`` , its size is :math:`m * n`. If it has elements of type ``std::set`` , its size is :math:`m` and all its set elements are between zero and :math:`n - 1`. It specifies a :ref:`glossary@Sparsity Pattern` for the Jacobian :math:`F^{(1)} (x)`. If this sparsity pattern does not change between calls to ``SparseJacobian`` , it should be faster to calculate *p* once (using :ref:`ForSparseJac-name` or :ref:`RevSparseJac-name` ) and then pass *p* to ``SparseJacobian`` . Furthermore, if you specify *work* in the calling sequence, it is not necessary to keep the sparsity pattern; see the heading :ref:`sparse_jacobian@work@p` under the *work* description. In addition, if you specify *p* , CppAD will use the same type of sparsity representation (vectors of ``bool`` or vectors of ``std::set`` ) for its internal calculations. Otherwise, the representation for the internal calculations is unspecified. row, col ******** The arguments *row* and *col* are optional and have prototype | |tab| ``const`` *SizeVector* & *row* | |tab| ``const`` *SizeVector* & *col* (see :ref:`sparse_jacobian@SizeVector` below). They specify which rows and columns of :math:`F^{(1)} (x)` are computes and in what order. Not all the non-zero entries in :math:`F^{(1)} (x)` need be computed, but all the entries specified by *row* and *col* must be possibly non-zero in the sparsity pattern. We use :math:`K` to denote the value *jac* . ``size`` () which must also equal the size of *row* and *col* . Furthermore, for :math:`k = 0 , \ldots , K-1`, it must hold that :math:`row[k] < m` and :math:`col[k] < n`. jac *** The result *jac* has prototype *BaseVector* & *jac* In the case where the arguments *row* and *col* are not present, the size of *jac* is :math:`m * n` and for :math:`i = 0 , \ldots , m-1`, :math:`j = 0 , \ldots , n-1`, .. math:: jac [ i * n + j ] = \D{ F_i }{ x_j } (x) In the case where the arguments *row* and *col* are present, we use :math:`K` to denote the size of *jac* . The input value of its elements does not matter. Upon return, for :math:`k = 0 , \ldots , K - 1`, .. math:: jac [ k ] = \D{ F_i }{ x_j } (x) \; , \; \; {\rm where} \; i = row[k] \; {\rm and } \; j = col[k] work **** If this argument is present, it has prototype ``sparse_jacobian_work&`` *work* This object can only be used with the routines ``SparseJacobianForward`` and ``SparseJacobianReverse`` . During its the first use, information is stored in *work* . This is used to reduce the work done by future calls to the same mode (forward or reverse), the same *f* , *p* , *row* , and *col* . If a future call is for a different mode, or any of these values have changed, you must first call *work* . ``clear`` () to inform CppAD that this information needs to be recomputed. color_method ============ The coloring algorithm determines which columns (forward mode) or rows (reverse mode) can be computed during the same sweep. This field has prototype ``std::string`` *work* . ``color_method`` and its default value (after a constructor or ``clear()`` ) is ``"cppad"`` . If :ref:`colpack_prefix-name` is specified on the :ref:`cmake@CMake Command` line, you can set this method to ``"colpack"`` . This value only matters on the first call to ``sparse_jacobian`` that follows the *work* constructor or a call to *work* . ``clear`` () . p = If *work* is present, and it is not the first call after its construction or a clear, the sparsity pattern *p* is not used. This enables one to free the sparsity pattern and still compute corresponding sparse Jacobians. n_sweep ******* The return value *n_sweep* has prototype ``size_t`` *n_sweep* If ``SparseJacobianForward`` (``SparseJacobianReverse`` ) is used, *n_sweep* is the number of first order forward (reverse) sweeps used to compute the requested Jacobian values. (This is also the number of colors determined by the coloring method mentioned above). This is proportional to the total work that ``SparseJacobian`` does, not counting the zero order forward sweep, or the work to combine multiple columns (rows) into a single sweep. BaseVector ********** The type *BaseVector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` *Base* . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. SetVector ********* The type *SetVector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` ``bool`` or ``std::set`` ; see :ref:`glossary@Sparsity Pattern` for a discussion of the difference. The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Restrictions ============ If *SetVector* has elements of ``std::set`` , then *p* [ *i* ] must return a reference (not a copy) to the corresponding set. According to section 26.3.2.3 of the 1998 C++ standard, ``std::valarray< std::set >`` does not satisfy this condition. SizeVector ********** The type *SizeVector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` ``size_t`` . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Uses Forward ************ After each call to :ref:`Forward-name` , the object *f* contains the corresponding :ref:`Taylor coefficients` . After a call to any of the sparse Jacobian routines, the zero order Taylor coefficients correspond to *f* . ``Forward`` (0, *x* ) and the other coefficients are unspecified. After ``SparseJacobian`` , the previous calls to :ref:`Forward-name` are undefined. Example ******* {xrst_toc_hidden example/sparse/sparse_jacobian.cpp } The routine :ref:`sparse_jacobian.cpp-name` is examples and tests of ``sparse_jacobian`` . It return ``true`` , if it succeeds and ``false`` otherwise. {xrst_end sparse_jacobian} ============================================================================== */ # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file sparse_jacobian.hpp Sparse Jacobian driver routine and helper functions. */ // =========================================================================== /*! class used by SparseJacobian to hold information so it does not need to be recomputed. */ class sparse_jacobian_work { public: /// Coloring method: "cppad", or "colpack" /// (this field is set by user) std::string color_method; /// indices that sort the user row and col arrays by color CppAD::vector order; /// results of the coloring algorithm CppAD::vector color; /// constructor sparse_jacobian_work(void) : color_method("cppad") { } /// reset coloring method to its default and /// inform CppAD that color and order need to be recomputed void clear(void) { color_method = "cppad"; order.clear(); color.clear(); } }; // =========================================================================== /*! Private helper function forward mode cases \tparam Base is the base type for the recording that is stored in this ADFun object. \tparam BaseVector is a simple vector class with elements of type Base. \tparam SetVector is either sparse_pack or sparse_list. \tparam SizeVector is a simple vector class with elements of type size_t. \param x [in] is a vector specifying the point at which to compute the Jacobian. \param p_transpose [in] If work.color.size() != 0, then p_transpose is not used. Otherwise, it is a sparsity pattern for the transpose of the Jacobian of this ADFun object. Note that we do not change the values in p_transpose, but is not const because we use its iterator facility. \param row [in] is the vector of row indices for the returned Jacobian values. \param col [in] is the vector of columns indices for the returned Jacobian values. It must have the same size as row. \param jac [out] is the vector of Jacobian values. We use K to denote the size of jac. The return value jac[k] is the partial of the row[k] range component of the function with respect the the col[k] domain component of its argument. \param work work.color_method is an input. The rest of this structure contains information that is computed by SparseJacobainFor. If the sparsity pattern, row vector, or col vectors are not the same between calls to SparseJacobianFor, work.clear() must be called to reinitialize work. \return Is the number of first order forward sweeps used to compute the requested Jacobian values. The total work, not counting the zero order forward sweep, or the time to combine computations, is proportional to this return value. */ template template size_t ADFun::SparseJacobianFor( const BaseVector& x , SetVector& p_transpose , const SizeVector& row , const SizeVector& col , BaseVector& jac , sparse_jacobian_work& work ) { size_t j, k, ell; CppAD::vector& order(work.order); CppAD::vector& color(work.color); size_t m = Range(); size_t n = Domain(); // some values const Base zero(0); const Base one(1); // check BaseVector is Simple Vector class with Base type elements CheckSimpleVector(); CPPAD_ASSERT_UNKNOWN( size_t(x.size()) == n ); CPPAD_ASSERT_UNKNOWN( color.size() == 0 || color.size() == n ); // number of components of Jacobian that are required size_t K = size_t(jac.size()); CPPAD_ASSERT_UNKNOWN( size_t( row.size() ) == K ); CPPAD_ASSERT_UNKNOWN( size_t( col.size() ) == K ); // Point at which we are evaluating the Jacobian Forward(0, x); // check for case where nothing (except Forward above) to do if( K == 0 ) return 0; if( color.size() == 0 ) { CPPAD_ASSERT_UNKNOWN( p_transpose.n_set() == n ); CPPAD_ASSERT_UNKNOWN( p_transpose.end() == m ); // execute coloring algorithm color.resize(n); if( work.color_method == "cppad" ) local::color_general_cppad(p_transpose, col, row, color); else if( work.color_method == "colpack" ) { # if CPPAD_HAS_COLPACK local::color_general_colpack(p_transpose, col, row, color); # else CPPAD_ASSERT_KNOWN( false, "SparseJacobianForward: work.color_method = colpack " "and colpack_prefix missing from cmake command line." ); # endif } else CPPAD_ASSERT_KNOWN( false, "SparseJacobianForward: work.color_method is not valid." ); // put sorting indices in color order SizeVector key(K); order.resize(K); for(k = 0; k < K; k++) key[k] = color[ col[k] ]; index_sort(key, order); } size_t n_color = 1; for(j = 0; j < n; j++) if( color[j] < n ) n_color = std::max(n_color, color[j] + 1); // initialize the return value for(k = 0; k < K; k++) jac[k] = zero; # if CPPAD_SPARSE_JACOBIAN_MAX_MULTIPLE_DIRECTION == 1 // direction vector and return values for calls to forward BaseVector dx(n), dy(m); // loop over colors k = 0; for(ell = 0; ell < n_color; ell++) { CPPAD_ASSERT_UNKNOWN( color[ col[ order[k] ] ] == ell ); // combine all columns with this color for(j = 0; j < n; j++) { dx[j] = zero; if( color[j] == ell ) dx[j] = one; } // call forward mode for all these columns at once dy = Forward(1, dx); // set the corresponding components of the result while( k < K && color[ col[order[k]] ] == ell ) { jac[ order[k] ] = dy[row[order[k]]]; k++; } } # else // abbreviation for this value size_t max_r = CPPAD_SPARSE_JACOBIAN_MAX_MULTIPLE_DIRECTION; CPPAD_ASSERT_UNKNOWN( max_r > 1 ); // count the number of colors done so far size_t count_color = 0; // count the sparse matrix entries done so far k = 0; while( count_color < n_color ) { // number of colors we will do this time size_t r = std::min(max_r , n_color - count_color); BaseVector dx(n * r), dy(m * r); // loop over colors we will do this time for(ell = 0; ell < r; ell++) { // combine all columns with this color for(j = 0; j < n; j++) { dx[j * r + ell] = zero; if( color[j] == ell + count_color ) dx[j * r + ell] = one; } } size_t q = 1; dy = Forward(q, r, dx); // store results for(ell = 0; ell < r; ell++) { // set the components of the result for this color while( k < K && color[ col[order[k]] ] == ell + count_color ) { jac[ order[k] ] = dy[ row[order[k]] * r + ell ]; k++; } } count_color += r; } # endif return n_color; } /*! Private helper function for reverse mode cases. \tparam Base is the base type for the recording that is stored in this ADFun object. \tparam BaseVector is a simple vector class with elements of type Base. \tparam SetVector is either sparse_pack or sparse_list. \tparam SizeVector is a simple vector class with elements of type size_t. \param x [in] is a vector specifying the point at which to compute the Jacobian. \param p [in] If work.color.size() != 0, then p is not used. Otherwise, it is a sparsity pattern for the Jacobian of this ADFun object. Note that we do not change the values in p, but is not const because we use its iterator facility. \param row [in] is the vector of row indices for the returned Jacobian values. \param col [in] is the vector of columns indices for the returned Jacobian values. It must have the same size as row. \param jac [out] is the vector of Jacobian values. It must have the same size as row. The return value jac[k] is the partial of the row[k] range component of the function with respect the the col[k] domain component of its argument. \param work work.color_method is an input. The rest of This structure contains information that is computed by SparseJacobainRev. If the sparsity pattern, row vector, or col vectors are not the same between calls to SparseJacobianRev, work.clear() must be called to reinitialize work. \return Is the number of first order reverse sweeps used to compute the reverse Jacobian values. The total work, not counting the zero order forward sweep, or the time to combine computations, is proportional to this return value. */ template template size_t ADFun::SparseJacobianRev( const BaseVector& x , SetVector& p , const SizeVector& row , const SizeVector& col , BaseVector& jac , sparse_jacobian_work& work ) { size_t i, k, ell; CppAD::vector& order(work.order); CppAD::vector& color(work.color); size_t m = Range(); size_t n = Domain(); // some values const Base zero(0); const Base one(1); // check BaseVector is Simple Vector class with Base type elements CheckSimpleVector(); CPPAD_ASSERT_UNKNOWN( size_t(x.size()) == n ); CPPAD_ASSERT_UNKNOWN (color.size() == m || color.size() == 0 ); // number of components of Jacobian that are required size_t K = size_t(jac.size()); CPPAD_ASSERT_UNKNOWN( size_t( size_t( row.size() ) ) == K ); CPPAD_ASSERT_UNKNOWN( size_t( size_t( col.size() ) ) == K ); // Point at which we are evaluating the Jacobian Forward(0, x); // check for case where nothing (except Forward above) to do if( K == 0 ) return 0; if( color.size() == 0 ) { CPPAD_ASSERT_UNKNOWN( p.n_set() == m ); CPPAD_ASSERT_UNKNOWN( p.end() == n ); // execute the coloring algorithm color.resize(m); if( work.color_method == "cppad" ) local::color_general_cppad(p, row, col, color); else if( work.color_method == "colpack" ) { # if CPPAD_HAS_COLPACK local::color_general_colpack(p, row, col, color); # else CPPAD_ASSERT_KNOWN( false, "SparseJacobianReverse: work.color_method = colpack " "and colpack_prefix missing from cmake command line." ); # endif } else CPPAD_ASSERT_KNOWN( false, "SparseJacobianReverse: work.color_method is not valid." ); // put sorting indices in color order SizeVector key(K); order.resize(K); for(k = 0; k < K; k++) key[k] = color[ row[k] ]; index_sort(key, order); } size_t n_color = 1; for(i = 0; i < m; i++) if( color[i] < m ) n_color = std::max(n_color, color[i] + 1); // weighting vector for calls to reverse BaseVector w(m); // location for return values from Reverse BaseVector dw(n); // initialize the return value for(k = 0; k < K; k++) jac[k] = zero; // loop over colors k = 0; for(ell = 0; ell < n_color; ell++) { CPPAD_ASSERT_UNKNOWN( color[ row[ order[k] ] ] == ell ); // combine all the rows with this color for(i = 0; i < m; i++) { w[i] = zero; if( color[i] == ell ) w[i] = one; } // call reverse mode for all these rows at once dw = Reverse(1, w); // set the corresponding components of the result while( k < K && color[ row[order[k]] ] == ell ) { jac[ order[k] ] = dw[col[order[k]]]; k++; } } return n_color; } // ========================================================================== // Public Member functions // ========================================================================== /*! Compute user specified subset of a sparse Jacobian using forward mode. The C++ source code corresponding to this operation is \verbatim SparseJacobinanForward(x, p, row, col, jac, work) \endverbatim \tparam Base is the base type for the recording that is stored in this ADFun object. \tparam BaseVector is a simple vector class with elements of type Base. \tparam SetVector is a simple vector class with elements of type bool or std::set. \tparam SizeVector is a simple vector class with elements of type size_t. \param x [in] is a vector specifying the point at which to compute the Jacobian. \param p [in] is the sparsity pattern for the Jacobian that we are calculating. \param row [in] is the vector of row indices for the returned Jacobian values. \param col [in] is the vector of columns indices for the returned Jacobian values. It must have the same size as row. \param jac [out] is the vector of Jacobian values. It must have the same size as row. The return value jac[k] is the partial of the row[k] range component of the function with respect the the col[k] domain component of its argument. \param work [in,out] this structure contains information that depends on the function object, sparsity pattern, row vector, and col vector. If they are not the same between calls to SparseJacobianForward, work.clear() must be called to reinitialize them. \return Is the number of first order forward sweeps used to compute the requested Jacobian values. The total work, not counting the zero order forward sweep, or the time to combine computations, is proportional to this return value. */ template template size_t ADFun::SparseJacobianForward( const BaseVector& x , const SetVector& p , const SizeVector& row , const SizeVector& col , BaseVector& jac , sparse_jacobian_work& work ) { size_t n = Domain(); size_t m = Range(); size_t K = jac.size(); # ifndef NDEBUG size_t k; CPPAD_ASSERT_KNOWN( size_t(x.size()) == n , "SparseJacobianForward: size of x not equal domain dimension for f." ); CPPAD_ASSERT_KNOWN( size_t(row.size()) == K && size_t(col.size()) == K , "SparseJacobianForward: either r or c does not have " "the same size as jac." ); CPPAD_ASSERT_KNOWN( work.color.size() == 0 || work.color.size() == n, "SparseJacobianForward: invalid value in work." ); for(k = 0; k < K; k++) { CPPAD_ASSERT_KNOWN( row[k] < m, "SparseJacobianForward: invalid value in r." ); CPPAD_ASSERT_KNOWN( col[k] < n, "SparseJacobianForward: invalid value in c." ); } if( work.color.size() != 0 ) for(size_t j = 0; j < n; j++) CPPAD_ASSERT_KNOWN( work.color[j] <= n, "SparseJacobianForward: invalid value in work." ); # endif // check for case where there is nothing to compute size_t n_sweep = 0; if( K == 0 ) return n_sweep; typedef typename SetVector::value_type Set_type; typedef typename local::sparse::internal_pattern::pattern_type Pattern_type; Pattern_type s_transpose; if( work.color.size() == 0 ) { bool transpose = true; const char* error_msg = "SparseJacobianForward: transposed sparsity" " pattern does not have proper row or column dimension"; sparsity_user2internal(s_transpose, p, n, m, transpose, error_msg); } n_sweep = SparseJacobianFor(x, s_transpose, row, col, jac, work); return n_sweep; } /*! Compute user specified subset of a sparse Jacobian using forward mode. The C++ source code corresponding to this operation is \verbatim SparseJacobinanReverse(x, p, row, col, jac, work) \endverbatim \tparam Base is the base type for the recording that is stored in this ADFun object. \tparam BaseVector is a simple vector class with elements of type Base. \tparam SetVector is a simple vector class with elements of type bool or std::set. \tparam SizeVector is a simple vector class with elements of type size_t. \param x [in] is a vector specifying the point at which to compute the Jacobian. \param p [in] is the sparsity pattern for the Jacobian that we are calculating. \param row [in] is the vector of row indices for the returned Jacobian values. \param col [in] is the vector of columns indices for the returned Jacobian values. It must have the same size as row. \param jac [out] is the vector of Jacobian values. It must have the same size as row. The return value jac[k] is the partial of the row[k] range component of the function with respect the the col[k] domain component of its argument. \param work [in,out] this structure contains information that depends on the function object, sparsity pattern, row vector, and col vector. If they are not the same between calls to SparseJacobianReverse, work.clear() must be called to reinitialize them. \return Is the number of first order reverse sweeps used to compute the reverse Jacobian values. The total work, not counting the zero order forward sweep, or the time to combine computations, is proportional to this return value. */ template template size_t ADFun::SparseJacobianReverse( const BaseVector& x , const SetVector& p , const SizeVector& row , const SizeVector& col , BaseVector& jac , sparse_jacobian_work& work ) { size_t m = Range(); size_t n = Domain(); size_t K = jac.size(); # ifndef NDEBUG size_t k; CPPAD_ASSERT_KNOWN( size_t(x.size()) == n , "SparseJacobianReverse: size of x not equal domain dimension for f." ); CPPAD_ASSERT_KNOWN( size_t(row.size()) == K && size_t(col.size()) == K , "SparseJacobianReverse: either r or c does not have " "the same size as jac." ); CPPAD_ASSERT_KNOWN( work.color.size() == 0 || work.color.size() == m, "SparseJacobianReverse: invalid value in work." ); for(k = 0; k < K; k++) { CPPAD_ASSERT_KNOWN( row[k] < m, "SparseJacobianReverse: invalid value in r." ); CPPAD_ASSERT_KNOWN( col[k] < n, "SparseJacobianReverse: invalid value in c." ); } if( work.color.size() != 0 ) for(size_t i = 0; i < m; i++) CPPAD_ASSERT_KNOWN( work.color[i] <= m, "SparseJacobianReverse: invalid value in work." ); # endif // check for case where there is nothing to compute size_t n_sweep = 0; if( K == 0 ) return n_sweep; typedef typename SetVector::value_type Set_type; typedef typename local::sparse::internal_pattern::pattern_type Pattern_type; Pattern_type s; if( work.color.size() == 0 ) { bool transpose = false; const char* error_msg = "SparseJacobianReverse: sparsity" " pattern does not have proper row or column dimension"; sparsity_user2internal(s, p, m, n, transpose, error_msg); } n_sweep = SparseJacobianRev(x, s, row, col, jac, work); return n_sweep; } /*! Compute a sparse Jacobian. The C++ source code corresponding to this operation is \verbatim jac = SparseJacobian(x, p) \endverbatim \tparam Base is the base type for the recording that is stored in this ADFun object. \tparam BaseVector is a simple vector class with elements of type Base. \tparam SetVector is a simple vector class with elements of type bool or std::set. \param x [in] is a vector specifying the point at which to compute the Jacobian. \param p [in] is the sparsity pattern for the Jacobian that we are calculating. \return Will be a vector if size m * n containing the Jacobian at the specified point (in row major order). */ template template BaseVector ADFun::SparseJacobian( const BaseVector& x, const SetVector& p ) { size_t i, j, k; size_t m = Range(); size_t n = Domain(); BaseVector jac(m * n); CPPAD_ASSERT_KNOWN( size_t(x.size()) == n, "SparseJacobian: size of x not equal domain size for f." ); CheckSimpleVector(); typedef typename SetVector::value_type Set_type; typedef typename local::sparse::internal_pattern::pattern_type Pattern_type; // initialize the return value as zero Base zero(0); for(i = 0; i < m; i++) for(j = 0; j < n; j++) jac[i * n + j] = zero; sparse_jacobian_work work; CppAD::vector row; CppAD::vector col; if( n <= m ) { // need an internal copy of sparsity pattern Pattern_type s_transpose; bool transpose = true; const char* error_msg = "SparseJacobian: transposed sparsity" " pattern does not have proper row or column dimension"; sparsity_user2internal(s_transpose, p, n, m, transpose, error_msg); k = 0; for(j = 0; j < n; j++) { typename Pattern_type::const_iterator itr(s_transpose, j); i = *itr; while( i != s_transpose.end() ) { row.push_back(i); col.push_back(j); k++; i = *(++itr); } } size_t K = k; BaseVector J(K); // now we have folded this into the following case SparseJacobianFor(x, s_transpose, row, col, J, work); // now set the non-zero return values for(k = 0; k < K; k++) jac[ row[k] * n + col[k] ] = J[k]; } else { // need an internal copy of sparsity pattern Pattern_type s; bool transpose = false; const char* error_msg = "SparseJacobian: sparsity" " pattern does not have proper row or column dimension"; sparsity_user2internal(s, p, m, n, transpose, error_msg); k = 0; for(i = 0; i < m; i++) { typename Pattern_type::const_iterator itr(s, i); j = *itr; while( j != s.end() ) { row.push_back(i); col.push_back(j); k++; j = *(++itr); } } size_t K = k; BaseVector J(K); // now we have folded this into the following case SparseJacobianRev(x, s, row, col, J, work); // now set the non-zero return values for(k = 0; k < K; k++) jac[ row[k] * n + col[k] ] = J[k]; } return jac; } /*! Compute a sparse Jacobian. The C++ source code corresponding to this operation is \verbatim jac = SparseJacobian(x) \endverbatim \tparam Base is the base type for the recording that is stored in this ADFun object. \tparam BaseVector is a simple vector class with elements of the Base. \param x [in] is a vector specifying the point at which to compute the Jacobian. \return Will be a vector of size m * n containing the Jacobian at the specified point (in row major order). */ template template BaseVector ADFun::SparseJacobian( const BaseVector& x ) { typedef CppAD::vectorBool BoolVector; size_t m = Range(); size_t n = Domain(); // sparsity pattern for Jacobian BoolVector p(m * n); if( n <= m ) { size_t j, k; // use forward mode BoolVector r(n * n); for(j = 0; j < n; j++) { for(k = 0; k < n; k++) r[j * n + k] = false; r[j * n + j] = true; } p = ForSparseJac(n, r); } else { size_t i, k; // use reverse mode BoolVector s(m * m); for(i = 0; i < m; i++) { for(k = 0; k < m; k++) s[i * m + k] = false; s[i * m + i] = true; } p = RevSparseJac(m, s); } return SparseJacobian(x, p); } } // END_CPPAD_NAMESPACE # undef CPPAD_SPARSE_JACOBIAN_MAX_MULTIPLE_DIRECTION # endif ================================================ FILE: include/cppad/core/standard_math.hpp ================================================ # ifndef CPPAD_CORE_STANDARD_MATH_HPP # define CPPAD_CORE_STANDARD_MATH_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin unary_standard_math} {xrst_spell acosh asinh expm } The Unary Standard Math Functions ################################# Syntax ****** | *y* = *fun* ( *x* ) Purpose ******* Evaluates the standard math function *fun* . Possible Types ************** Base ==== If *Base* satisfies the :ref:`base type requirements` and argument *x* has prototype ``const`` *Base* & *x* then the result *y* has prototype *Base* *y* AD ======== If the argument *x* has prototype ``const AD`` < *Base* >& *x* then the result *y* has prototype ``AD`` < *Base* > *y* VecAD =========== If the argument *x* has prototype ``const VecAD`` < *Base* >:: ``reference&`` *x* then the result *y* has prototype ``AD`` < *Base* > *y* {xrst_toc_hidden include/cppad/core/std_math_11.hpp include/cppad/core/abs.hpp include/cppad/core/sign.hpp } fun *** The possible values for *fun* are .. csv-table:: :widths: auto *fun*,Description abs,:ref:`abs-title` acos,:ref:`acos-title` acosh,:ref:`acosh-title` asin,:ref:`asin-title` asinh,:ref:`asinh-title` atan,:ref:`atan-title` atanh,:ref:`atanh-title` cos,:ref:`cos-title` cosh,:ref:`cosh-title` erf,:ref:`erf-title` exp,:ref:`exp-title` expm1,:ref:`expm1-title` :ref:`fabs` :ref:`abs-title` log10,:ref:`log10-title` log1p,:ref:`log1p-title` log,:ref:`log-title` sign,:ref:`sign-title` sin,:ref:`sin-title` sinh,:ref:`sinh-title` sqrt,:ref:`sqrt-title` tan,:ref:`tan-title` tanh,:ref:`tanh-title` {xrst_end unary_standard_math} */ # include # include /* {xrst_begin binary_math} The Binary Math Functions ######################### Contents ******** {xrst_toc_table include/cppad/core/atan2.hpp include/cppad/core/pow.hpp include/cppad/core/azmul.hpp } {xrst_end binary_math} */ # include # include # endif ================================================ FILE: include/cppad/core/std_math_11.hpp ================================================ # ifndef CPPAD_CORE_STD_MATH_11_HPP # define CPPAD_CORE_STD_MATH_11_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* ------------------------------------------------------------------------------- {xrst_begin acos} Inverse Cosine Function: acos ############################# Syntax ****** | *y* = ``acos`` ( *x* ) x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Atomic ****** This is an :ref:`atomic operation` . Derivative ********** .. math:: \R{acos}^{(1)} (x) = - (1 - x * x)^{-1/2} Example ******* {xrst_toc_hidden example/general/acos.cpp } The file :ref:`acos.cpp-name` contains an example and test of this function. {xrst_end acos} ------------------------------------------------------------------------------- {xrst_begin acosh} The Inverse Hyperbolic Cosine Function: acosh ############################################# Syntax ****** *y* = ``acosh`` ( *x* ) Description *********** The inverse hyperbolic cosine function is defined by *x* == ``cosh`` ( *y* ) . x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Atomic ****** This is an :ref:`atomic operation` . Example ******* {xrst_toc_hidden example/general/acosh.cpp } The file :ref:`acosh.cpp-name` contains an example and test of this function. {xrst_end acosh} ------------------------------------------------------------------------------- {xrst_begin asin} Inverse Sine Function: asin ########################### Syntax ****** *y* = ``asin`` ( *x* ) x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Atomic ****** This is an :ref:`atomic operation` . Derivative ********** .. math:: \R{asin}^{(1)} (x) = (1 - x * x)^{-1/2} Example ******* {xrst_toc_hidden example/general/asin.cpp } The file :ref:`asin.cpp-name` contains an example and test of this function. {xrst_end asin} ------------------------------------------------------------------------------- {xrst_begin asinh} The Inverse Hyperbolic Sine Function: asinh ########################################### Syntax ****** *y* = ``asinh`` ( *x* ) Description *********** The inverse hyperbolic sine function is defined by *x* == ``sinh`` ( *y* ) . x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Atomic ****** This is an :ref:`atomic operation` . Example ******* {xrst_toc_hidden example/general/asinh.cpp } The file :ref:`asinh.cpp-name` contains an example and test of this function. {xrst_end asinh} ------------------------------------------------------------------------------- {xrst_begin atan} Inverse Tangent Function: atan ############################## Syntax ****** *y* = ``atan`` ( *x* ) x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Atomic ****** This is an :ref:`atomic operation` . Derivative ********** .. math:: \R{atan}^{(1)} (x) = \frac{1}{1 + x^2} Example ******* {xrst_toc_hidden example/general/atan.cpp } The file :ref:`atan.cpp-name` contains an example and test of this function. {xrst_end atan} ------------------------------------------------------------------------------- {xrst_begin atanh} The Inverse Hyperbolic Tangent Function: atanh ############################################## Syntax ****** *y* = ``atanh`` ( *x* ) Description *********** The inverse hyperbolic tangent function is defined by *x* == ``tanh`` ( *y* ) . x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Atomic ****** This is an :ref:`atomic operation` . Example ******* {xrst_toc_hidden example/general/atanh.cpp } The file :ref:`atanh.cpp-name` contains an example and test of this function. {xrst_end atanh} ------------------------------------------------------------------------------- {xrst_begin cos} The Cosine Function: cos ######################## Syntax ****** *y* = ``cos`` ( *x* ) x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Atomic ****** This is an :ref:`atomic operation` . Derivative ********** .. math:: \R{cos}^{(1)} (x) = - \sin(x) Example ******* {xrst_toc_hidden example/general/cos.cpp } The file :ref:`cos.cpp-name` contains an example and test of this function. {xrst_end cos} ------------------------------------------------------------------------------- {xrst_begin cosh} The Hyperbolic Cosine Function: cosh #################################### Syntax ****** *y* = ``cosh`` ( *x* ) x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Atomic ****** This is an :ref:`atomic operation` . Derivative ********** .. math:: \R{cosh}^{(1)} (x) = \sinh(x) Example ******* {xrst_toc_hidden example/general/cosh.cpp } The file :ref:`cosh.cpp-name` contains an example and test of this function. {xrst_end cosh} ------------------------------------------------------------------------------- {xrst_begin erf} The Error Function ################## Syntax ****** *y* = ``erf`` ( *x* ) Description *********** Returns the value of the error function which is defined by .. math:: {\rm erf} (x) = \frac{2}{ \sqrt{\pi} } \int_0^x \exp( - t * t ) \; {\bf d} t x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Atomic ****** This is an :ref:`atomic operation` . Example ******* {xrst_toc_hidden example/general/erf.cpp } The file :ref:`erf.cpp-name` contains an example and test of this function. {xrst_end erf} ------------------------------------------------------------------------------- {xrst_begin erfc} The Complementary Error Function: erfc ###################################### Syntax ****** *y* = ``erfc`` ( *x* ) Description *********** Returns the value of the complementary error function which is defined by *y* == 1 ``- erf`` ( *x* ) . x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Atomic ****** This is an :ref:`atomic operation` . Example ******* {xrst_toc_hidden example/general/erfc.cpp } The file :ref:`erfc.cpp-name` contains an example and test of this function. {xrst_end erfc} ------------------------------------------------------------------------------- {xrst_begin exp} The Exponential Function: exp ############################# Syntax ****** *y* = ``exp`` ( *x* ) x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Atomic ****** This is an :ref:`atomic operation` . Derivative ********** .. math:: \R{exp}^{(1)} (x) = \exp(x) Example ******* {xrst_toc_hidden example/general/exp.cpp } The file :ref:`exp.cpp-name` contains an example and test of this function. {xrst_end exp} ------------------------------------------------------------------------------- {xrst_begin expm1} The Exponential Function Minus One: expm1 ######################################### Syntax ****** *y* = ``expm1`` ( *x* ) Description *********** Returns the value of the exponential function minus one which is defined by *y* == ``exp`` ( *x* ) ``- 1`` . x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Atomic ****** This is an :ref:`atomic operation` . Example ******* {xrst_toc_hidden example/general/expm1.cpp } The file :ref:`expm1.cpp-name` contains an example and test of this function. {xrst_end expm1} ------------------------------------------------------------------------------- {xrst_begin log} The Exponential Function: log ############################# Syntax ****** *y* = ``log`` ( *x* ) x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Atomic ****** This is an :ref:`atomic operation` . Derivative ********** .. math:: \R{log}^{(1)} (x) = \frac{1}{x} Example ******* {xrst_toc_hidden example/general/log.cpp } The file :ref:`log.cpp-name` contains an example and test of this function. {xrst_end log} ------------------------------------------------------------------------------- {xrst_begin log1p} The Logarithm of One Plus Argument: log1p ######################################### Syntax ****** *y* = ``log1p`` ( *x* ) Description *********** Returns the value of the logarithm of one plus argument which is defined by *y* == ``log`` (1 + *x* ) . x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Atomic ****** This is an :ref:`atomic operation` . Example ******* {xrst_toc_hidden example/general/log1p.cpp } The file :ref:`log1p.cpp-name` contains an example and test of this function. {xrst_end log1p} ------------------------------------------------------------------------------- {xrst_begin log10} The Base 10 Logarithm Function: log10 ##################################### Syntax ****** *y* = ``log10`` ( *x* ) x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Method ****** CppAD uses the representation .. math:: {\rm log10} (x) = \log(x) / \log(10) Example ******* {xrst_toc_hidden example/general/log10.cpp } The file :ref:`log10.cpp-name` contains an example and test of this function. {xrst_end log10} ------------------------------------------------------------------------------- {xrst_begin sin} The Sine Function: sin ###################### Syntax ****** *y* = ``sin`` ( *x* ) x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Atomic ****** This is an :ref:`atomic operation` . Derivative ********** .. math:: \R{sin}^{(1)} (x) = \cos(x) Example ******* {xrst_toc_hidden example/general/sin.cpp } The file :ref:`sin.cpp-name` contains an example and test of this function. {xrst_end sin} ------------------------------------------------------------------------------- {xrst_begin sinh} The Hyperbolic Sine Function: sinh ################################## Syntax ****** *y* = ``sinh`` ( *x* ) x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Atomic ****** This is an :ref:`atomic operation` . Derivative ********** .. math:: \R{sinh}^{(1)} (x) = \cosh(x) Example ******* {xrst_toc_hidden example/general/sinh.cpp } The file :ref:`sinh.cpp-name` contains an example and test of this function. {xrst_end sinh} ------------------------------------------------------------------------------- {xrst_begin sqrt} The Square Root Function: sqrt ############################## Syntax ****** *y* = ``sqrt`` ( *x* ) x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Atomic ****** This is an :ref:`atomic operation` . Derivative ********** .. math:: \R{sqrt}^{(1)} (x) = \frac{1}{2 \R{sqrt} (x) } Example ******* {xrst_toc_hidden example/general/sqrt.cpp } The file :ref:`sqrt.cpp-name` contains an example and test of this function. {xrst_end sqrt} ------------------------------------------------------------------------------- {xrst_begin tan} The Tangent Function: tan ######################### Syntax ****** *y* = ``tan`` ( *x* ) x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Atomic ****** This is an :ref:`atomic operation` . Derivative ********** .. math:: \R{tan}^{(1)} (x) = 1 + \tan (x)^2 Example ******* {xrst_toc_hidden example/general/tan.cpp } The file :ref:`tan.cpp-name` contains an example and test of this function. {xrst_end tan} ------------------------------------------------------------------------------- {xrst_begin tanh} The Hyperbolic Tangent Function: tanh ##################################### Syntax ****** *y* = ``tanh`` ( *x* ) x, y **** See the :ref:`unary_standard_math@Possible Types` for a unary standard math function. Atomic ****** This is an :ref:`atomic operation` . Derivative ********** .. math:: \R{tanh}^{(1)} (x) = 1 - \tanh (x)^2 Example ******* {xrst_toc_hidden example/general/tanh.cpp } The file :ref:`tanh.cpp-name` contains an example and test of this function. {xrst_end tanh} ------------------------------------------------------------------------------- */ /*! \file std_math_11.hpp Define AD standard math functions (using their Base versions) */ /*! \def CPPAD_STANDARD_MATH_UNARY_AD(Name, Op) Defines function Name with argument type AD and tape operation Op The macro defines the function x.Name() where x has type AD. It then uses this function to define Name(x) where x has type AD or VecAD_reference. If x is a variable, the tape unary operator Op is used to record the operation and the result is identified as corresponding to this operation; i.e., Name(x).taddr_ identifies the operation and Name(x).tape_id_ identifies the tape. This macro is used to define AD versions of acos, asin, atan, cos, cosh, exp, fabs, log, sin, sinh, sqrt, tan, tanh. */ # define CPPAD_STANDARD_MATH_UNARY_AD(Name, Op) \ template \ inline AD Name(const AD &x) \ { return x.Name##_me(); \ } \ template \ inline AD AD::Name##_me (void) const \ { \ AD result; \ result.value_ = CppAD::Name(value_); \ CPPAD_ASSERT_UNKNOWN( Parameter(result) ); \ \ local::ADTape* tape = AD::tape_ptr(); \ if( tape == nullptr ) \ return result; \ \ if( tape_id_ != tape->id_ ) \ return result; \ \ if(ad_type_ == dynamic_enum) \ { result.taddr_ = tape->Rec_.put_dyn_par( \ result.value_, local::Name##_dyn, taddr_ \ ); \ result.tape_id_ = tape_id_; \ result.ad_type_ = dynamic_enum; \ } \ else \ { CPPAD_ASSERT_UNKNOWN( NumArg(Op) == 1 ); \ tape->Rec_.PutArg(taddr_); \ result.taddr_ = tape->Rec_.PutOp(Op); \ result.tape_id_ = tape->id_; \ result.ad_type_ = variable_enum; \ } \ return result; \ } \ template \ inline AD Name(const VecAD_reference &x) \ { return x.ADBase().Name##_me(); } // BEGIN CppAD namespace namespace CppAD { CPPAD_STANDARD_MATH_UNARY_AD(acos, local::AcosOp) CPPAD_STANDARD_MATH_UNARY_AD(acosh, local::AcoshOp) CPPAD_STANDARD_MATH_UNARY_AD(asin, local::AsinOp) CPPAD_STANDARD_MATH_UNARY_AD(asinh, local::AsinhOp) CPPAD_STANDARD_MATH_UNARY_AD(atan, local::AtanOp) CPPAD_STANDARD_MATH_UNARY_AD(atanh, local::AtanhOp) CPPAD_STANDARD_MATH_UNARY_AD(cos, local::CosOp) CPPAD_STANDARD_MATH_UNARY_AD(cosh, local::CoshOp) CPPAD_STANDARD_MATH_UNARY_AD(exp, local::ExpOp) CPPAD_STANDARD_MATH_UNARY_AD(expm1, local::Expm1Op) CPPAD_STANDARD_MATH_UNARY_AD(fabs, local::AbsOp) CPPAD_STANDARD_MATH_UNARY_AD(log, local::LogOp) CPPAD_STANDARD_MATH_UNARY_AD(log1p, local::Log1pOp) CPPAD_STANDARD_MATH_UNARY_AD(sin, local::SinOp) CPPAD_STANDARD_MATH_UNARY_AD(sinh, local::SinhOp) CPPAD_STANDARD_MATH_UNARY_AD(sqrt, local::SqrtOp) CPPAD_STANDARD_MATH_UNARY_AD(tan, local::TanOp) CPPAD_STANDARD_MATH_UNARY_AD(tanh, local::TanhOp) // Error function is a special case template inline AD erf(const AD &x) { bool complement = false; return x.erf_me(complement); } template inline AD erfc(const AD &x) { bool complement = true; return x.erf_me(complement); } template inline AD AD::erf_me (bool complement) const { AD result; if( complement ) result.value_ = CppAD::erfc(value_); else result.value_ = CppAD::erf(value_); CPPAD_ASSERT_UNKNOWN( Parameter(result) ); // check if there is a recording in progress local::ADTape* tape = AD::tape_ptr(); if( tape == nullptr ) return result; // check if operand is a constant parameter if( tape_id_ != tape->id_ ) return result; if(ad_type_ == dynamic_enum) { local::op_code_dyn op = local::erf_dyn; if( complement ) op = local::erfc_dyn; // dynamic parameter argument result.taddr_ = tape->Rec_.put_dyn_par( result.value_, op, taddr_ ); result.tape_id_ = tape_id_; result.ad_type_ = dynamic_enum; } else { local::op_code_var op = local::ErfOp; if( complement ) op = local::ErfcOp; // variable argument CPPAD_ASSERT_UNKNOWN( local::NumArg(op) == 3 ); // arg[0] = argument to erf function tape->Rec_.PutArg(taddr_); // arg[1] = zero addr_t p = tape->Rec_.put_con_par( Base(0.0) ); tape->Rec_.PutArg(p); // arg[2] = 2 / sqrt(pi) p = tape->Rec_.put_con_par(Base( 1.0 / std::sqrt( std::atan(1.0) ) )); tape->Rec_.PutArg(p); // result.taddr_ = tape->Rec_.PutOp(op); result.tape_id_ = tape->id_; result.ad_type_ = variable_enum; } return result; } template inline AD erf(const VecAD_reference &x) { bool complement = false; return x.ADBase().erf_me(complement); } template inline AD erfc(const VecAD_reference &x) { bool complement = true; return x.ADBase().erf_me(complement); } /*! Compute the log of base 10 of x where has type AD \tparam Base is the base type (different from base for log) for this AD type, see base_require. \param x is the argument for the log10 function. \result if the result is y, then \f$ x = 10^y \f$. */ template inline AD log10(const AD &x) { return CppAD::log(x) / CppAD::log( Base(10) ); } template inline AD log10(const VecAD_reference &x) { return CppAD::log(x.ADBase()) / CppAD::log( Base(10) ); } } # undef CPPAD_STANDARD_MATH_UNARY_AD # endif ================================================ FILE: include/cppad/core/sub.hpp ================================================ # ifndef CPPAD_CORE_SUB_HPP # define CPPAD_CORE_SUB_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN CppAD namespace namespace CppAD { template AD operator - (const AD &left , const AD &right) { // compute the Base part AD result; result.value_ = left.value_ - right.value_; CPPAD_ASSERT_UNKNOWN( Parameter(result) ); // check if there is a recording in progress local::ADTape* tape = AD::tape_ptr(); if( tape == nullptr ) return result; tape_id_t tape_id = tape->id_; // tape_id cannot match the default value for tape_id_; i.e., 0 CPPAD_ASSERT_UNKNOWN( tape_id > 0 ); // check if left and right tapes match bool match_left = left.tape_id_ == tape_id; bool match_right = right.tape_id_ == tape_id; // check if left and right are dynamic parameters bool dyn_left = match_left & (left.ad_type_ == dynamic_enum); bool dyn_right = match_right & (right.ad_type_ == dynamic_enum); // check if left and right are variables bool var_left = match_left & (left.ad_type_ != dynamic_enum); bool var_right = match_right & (right.ad_type_ != dynamic_enum); CPPAD_ASSERT_KNOWN( left.tape_id_ == right.tape_id_ || ! match_left || ! match_right , "Subtract: AD variables or dynamic parameters on different threads." ); if( var_left ) { if( var_right ) { // result = variable - variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::SubvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::SubvvOp) == 2 ); // put operand addresses in tape tape->Rec_.PutArg(left.taddr_, right.taddr_); // put operator in the tape result.taddr_ = tape->Rec_.PutOp(local::SubvvOp); // make result a variable result.tape_id_ = tape_id; result.ad_type_ = variable_enum; } else if( (! dyn_right) && IdenticalZero(right.value_) ) { // result = variable - 0 result.make_variable(left.tape_id_, left.taddr_); } else { // result = variable - parameter CPPAD_ASSERT_UNKNOWN( local::NumRes(local::SubvpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::SubvpOp) == 2 ); // put operand addresses in tape addr_t p = right.taddr_; if( ! dyn_right ) p = tape->Rec_.put_con_par(right.value_); tape->Rec_.PutArg(left.taddr_, p); // put operator in the tape result.taddr_ = tape->Rec_.PutOp(local::SubvpOp); // make result a variable result.tape_id_ = tape_id; result.ad_type_ = variable_enum; } } else if( var_right ) { // result = parameter - variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::SubpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::SubpvOp) == 2 ); // put operand addresses in tape addr_t p = left.taddr_; if( ! dyn_left ) p = tape->Rec_.put_con_par(left.value_); tape->Rec_.PutArg(p, right.taddr_); // put operator in the tape result.taddr_ = tape->Rec_.PutOp(local::SubpvOp); // make result a variable result.tape_id_ = tape_id; result.ad_type_ = variable_enum; } else if( dyn_left | dyn_right ) { if( (! dyn_right) && IdenticalZero(right.value_) ) { // this is dynamic - 0 result.make_dynamic(left.tape_id_, left.taddr_); } else { addr_t arg0 = left.taddr_; addr_t arg1 = right.taddr_; if( ! dyn_left ) arg0 = tape->Rec_.put_con_par(left.value_); if( ! dyn_right ) arg1 = tape->Rec_.put_con_par(right.value_); // // parameters with a dynamic parameter result result.taddr_ = tape->Rec_.put_dyn_par( result.value_, local::sub_dyn, arg0, arg1 ); result.tape_id_ = tape_id; result.ad_type_ = dynamic_enum; } } return result; } // convert other cases into the case above CPPAD_FOLD_AD_VALUED_BINARY_OPERATOR(-) } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/sub_eq.hpp ================================================ # ifndef CPPAD_CORE_SUB_EQ_HPP # define CPPAD_CORE_SUB_EQ_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN CppAD namespace namespace CppAD { template AD& AD::operator -= (const AD &right) { // compute the Base part Base left; left = value_; value_ -= right.value_; // check if there is a recording in progress local::ADTape* tape = AD::tape_ptr(); if( tape == nullptr ) return *this; tape_id_t tape_id = tape->id_; // tape_id cannot match the default value for tape_id_; i.e., 0 CPPAD_ASSERT_UNKNOWN( tape_id > 0 ); // check if left and right tapes match bool match_left = tape_id_ == tape_id; bool match_right = right.tape_id_ == tape_id; // check if left and right are dynamic parameters bool dyn_left = match_left & (ad_type_ == dynamic_enum); bool dyn_right = match_right & (right.ad_type_ == dynamic_enum); // check if left and right are variables bool var_left = match_left & (ad_type_ != dynamic_enum); bool var_right = match_right & (right.ad_type_ != dynamic_enum); CPPAD_ASSERT_KNOWN( tape_id_ == right.tape_id_ || ! match_left || ! match_right , "-= : AD variables or dynamic parameters on different threads." ); if( var_left ) { if( var_right ) { // this = variable - variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::SubvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::SubvvOp) == 2 ); // put operand addresses in tape tape->Rec_.PutArg(taddr_, right.taddr_); // put operator in the tape taddr_ = tape->Rec_.PutOp(local::SubvvOp); // check that this is a variable CPPAD_ASSERT_UNKNOWN( tape_id_ == tape_id ); CPPAD_ASSERT_UNKNOWN( ad_type_ == variable_enum); } else if( (! dyn_right) && IdenticalZero(right.value_) ) { // this = variable - 0 } else { // this = variable - parameter CPPAD_ASSERT_UNKNOWN( local::NumRes(local::SubvpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::SubvpOp) == 2 ); // put operand addresses in tape addr_t p = right.taddr_; if( ! dyn_right ) p = tape->Rec_.put_con_par(right.value_); tape->Rec_.PutArg(taddr_, p); // put operator in the tape taddr_ = tape->Rec_.PutOp(local::SubvpOp); // check that this is a variable CPPAD_ASSERT_UNKNOWN( tape_id_ == tape_id ); CPPAD_ASSERT_UNKNOWN( ad_type_ == variable_enum); } } else if( var_right ) { // this = parameter - variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::SubpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::SubpvOp) == 2 ); // put operand addresses in tape addr_t p = taddr_; if( ! dyn_left ) p = tape->Rec_.put_con_par(left); tape->Rec_.PutArg(p, right.taddr_); // put operator in the tape taddr_ = tape->Rec_.PutOp(local::SubpvOp); // make this a variable tape_id_ = tape_id; ad_type_ = variable_enum; } else if( dyn_left | dyn_right ) { if( (! dyn_right) && IdenticalZero(right.value_) ) { // this is left -= 0, so do nothing } else { addr_t arg0 = taddr_; addr_t arg1 = right.taddr_; if( ! dyn_left ) arg0 = tape->Rec_.put_con_par(left); if( ! dyn_right ) arg1 = tape->Rec_.put_con_par(right.value_); // // parameters with a dynamic parameter results taddr_ = tape->Rec_.put_dyn_par( value_, local::sub_dyn, arg0, arg1 ); tape_id_ = tape_id; ad_type_ = dynamic_enum; } } return *this; } CPPAD_FOLD_ASSIGNMENT_OPERATOR(-=) } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/subgraph_jac_rev.hpp ================================================ # ifndef CPPAD_CORE_SUBGRAPH_JAC_REV_HPP # define CPPAD_CORE_SUBGRAPH_JAC_REV_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin subgraph_jac_rev} {xrst_spell nnz nr subgraphs } Compute Sparse Jacobians Using Subgraphs ######################################## Syntax ****** | *f* . ``subgraph_jac_rev`` ( *x* , *subset* ) | *f* . ``subgraph_jac_rev`` ( | |tab| *select_domain* , *select_range* , *x* , *matrix_out* | ) See Also ******** :ref:`subgraph_reverse@clear_subgraph` . Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the function corresponding to *f* . Here *n* is the :ref:`fun_property@Domain` size, and *m* is the :ref:`fun_property@Range` size, or *f* . The syntax above takes advantage of sparsity when computing the Jacobian .. math:: J(x) = F^{(1)} (x) The first syntax requires one to know what which elements of the Jacobian they want to compute. The second syntax computes the sparsity pattern and the value of the Jacobian at the same time. If one only wants the sparsity pattern, it should be faster to use :ref:`subgraph_sparsity-name` . Method ****** This routine uses a subgraph technique. To be specific, for each dependent variable, it creates a subgraph of the operation sequence containing the variables that affect the dependent variable. This avoids the overhead of performing set operations that is inherent in other methods for computing sparsity patterns. BaseVector ********** The type *BaseVector* is a :ref:`SimpleVector-name` class with :ref:`elements of type` *Base* . SizeVector ********** The type *SizeVector* is a :ref:`SimpleVector-name` class with :ref:`elements of type` ``size_t`` . BoolVector ********** The type *BoolVector* is a :ref:`SimpleVector-name` class with :ref:`elements of type` ``bool`` . f * This object has prototype ``ADFun`` < *Base* > *f* Note that the Taylor coefficients stored in *f* are affected by this operation; see :ref:`sparse_jac@Uses Forward` below. x * This argument has prototype ``const`` *BaseVector* & *x* It is the value of *x* at which we are computing the Jacobian. Uses Forward ************ After each call to :ref:`Forward-name` , the object *f* contains the corresponding :ref:`Taylor coefficients` . After a call to ``sparse_jac_forward`` or ``sparse_jac_rev`` , the zero order coefficients correspond to *f* . ``Forward`` (0, *x* ) All the other forward mode coefficients are unspecified. subset ****** This argument has prototype ``sparse_rcv`` < *SizeVector* , *BaseVector* >& *subset* Its row size is *subset* . ``nr`` () == *m* , and its column size is *subset* . ``nc`` () == *n* . It specifies which elements of the Jacobian are computed. The input elements in its value vector *subset* . ``val`` () do not matter. Upon return it contains the value of the corresponding elements of the Jacobian. select_domain ************* The argument *select_domain* has prototype ``const`` *BoolVector* & *select_domain* It has size :math:`n` and specifies which independent variables to include. select_range ************ The argument *select_range* has prototype ``const`` *BoolVector* & *select_range* It has size :math:`m` and specifies which components of the range to include in the calculation. A subgraph is built for each dependent variable and the selected set of independent variables. matrix_out ********** This argument has prototype ``sparse_rcv`` < *SizeVector* , *BaseVector* >& *matrix_out* This input value of *matrix_out* does not matter. Upon return *matrix_out* is :ref:`sparse matrix` representation of :math:`F^{(1)} (x)`. The matrix has :math:`m` rows, :math:`n` columns. If *select_domain* [ *j* ] is true, *select_range* [ *i* ] is true, and :math:`F_i (x)` depends on :math:`x_j`, then the pair :math:`(i, j)` is in *matrix_out* . For each *k* = 0 , ..., *matrix_out* . ``nnz`` () , let | |tab| *i* = *matrix_out* . ``row`` ()[ *k* ] | |tab| *j* = *matrix_out* . ``col`` ()[ *k* ] | |tab| *v* = *matrix_out* . ``val`` ()[ *k* ] It follows that the partial of :math:`F_i (x)` with respect to :math:`x_j` is equal to :math:`v`. Example ******* {xrst_toc_hidden example/sparse/subgraph_jac_rev.cpp example/sparse/subgraph_hes2jac.cpp } The files :ref:`subgraph_jac_rev.cpp-name` and :ref:`subgraph_hes2jac.cpp-name` are examples and tests using ``subgraph_jac_rev`` . They returns ``true`` for success and ``false`` for failure. {xrst_end subgraph_jac_rev} ----------------------------------------------------------------------------- */ # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! Subgraph sparsity patterns. \tparam Base is the base type for this recording. \tparam SizeVector is the simple vector with elements of type size_t that is used for row, column index sparsity patterns. \tparam BaseVector a simple vector class with elements of type Base. \param x a vector of length n, the number of independent variables in f (this ADFun object). \param subset spedifies the subset of the sparsity pattern where the Jacobian is evaluated. subset.nr() == m, subset.nc() == n. */ template template void ADFun::subgraph_jac_rev( const BaseVector& x , sparse_rcv& subset ) { size_t m = Range(); size_t n = Domain(); // CPPAD_ASSERT_KNOWN( subset.nr() == m, "subgraph_jac_rev: subset.nr() not equal range dimension for f" ); CPPAD_ASSERT_KNOWN( subset.nc() == n, "subgraph_jac_rev: subset.nc() not equal domain dimension for f" ); // // point at which we are evaluating Jacobian Forward(0, x); // // nnz and row, column, and row_major vectors for subset size_t nnz = subset.nnz(); const SizeVector& row( subset.row() ); const SizeVector& col( subset.col() ); SizeVector row_major = subset.row_major(); // // determine set of independent variables local::pod_vector select_domain(n); for(size_t j = 0; j < n; j++) select_domain[j] = false; for(size_t k = 0; k < nnz; k++) select_domain[ col[k] ] = true; // // initialize reverse mode computation on subgraphs subgraph_reverse(select_domain); // // memory used to hold subgraph_reverse results BaseVector dw; SizeVector dw_col; // // initialize index in row_major size_t k = 0; Base zero(0); while(k < nnz ) { size_t q = 1; size_t i_dep = row[ row_major[k] ]; size_t i_ind = col[ row_major[k] ]; size_t ell = i_dep; subgraph_reverse(q, ell, dw_col, dw); // size_t c = 0; while( i_dep == ell ) { // row numbers match // // advance c to possible match with column i_ind while( c < size_t( dw_col.size() ) && dw_col[c] < i_ind ) ++c; // // check for match with i_ind if( i_ind == dw_col[c] ) subset.set( row_major[k], dw[i_ind] ); else subset.set( row_major[k], zero); // // advance to next (i_dep, i_ind) ++k; if( k == nnz ) { i_dep = m; i_ind = n; } else { i_dep = row[ row_major[k] ]; i_ind = col[ row_major[k] ]; } } } return; } template template void ADFun::subgraph_jac_rev( const BoolVector& select_domain , const BoolVector& select_range , const BaseVector& x , sparse_rcv& matrix_out ) { size_t m = Range(); size_t n = Domain(); // // point at which we are evaluating Jacobian Forward(0, x); // // nnz and row, column, and row_major vectors for subset local::pod_vector row_out; local::pod_vector col_out; local::pod_vector_maybe val_out; // // initialize reverse mode computation on subgraphs subgraph_reverse(select_domain); // // memory used to hold subgraph_reverse results BaseVector dw; SizeVector col; // // loop through selected independent variables for(size_t i = 0; i < m; ++i) if( select_range[i] ) { // compute Jacobian and sparsity for this dependent variable size_t q = 1; subgraph_reverse(q, i, col, dw); CPPAD_ASSERT_UNKNOWN( size_t( dw.size() ) == n ); // // offset for this dependent variable size_t index = row_out.size(); CPPAD_ASSERT_UNKNOWN( col_out.size() == index ); CPPAD_ASSERT_UNKNOWN( val_out.size() == index ); // // extend vectors to hold results for this dependent variable size_t col_size = size_t( col.size() ); row_out.extend( col_size ); col_out.extend( col_size ); val_out.extend( col_size ); // // store results for this dependent variable for(size_t c = 0; c < col_size; ++c) { row_out[index + c] = i; col_out[index + c] = col[c]; val_out[index + c] = dw[ col[c] ]; } } // // create sparsity pattern corresponding to row_out, col_out size_t nr = m; size_t nc = n; size_t nnz = row_out.size(); sparse_rc pattern(nr, nc, nnz); for(size_t k = 0; k < nnz; ++k) pattern.set(k, row_out[k], col_out[k]); // // create sparse matrix sparse_rcv matrix(pattern); for(size_t k = 0; k < nnz; ++k) matrix.set(k, val_out[k]); // // return matrix matrix_out = matrix; // return; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/subgraph_reverse.hpp ================================================ # ifndef CPPAD_CORE_SUBGRAPH_REVERSE_HPP # define CPPAD_CORE_SUBGRAPH_REVERSE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin subgraph_reverse} {xrst_spell dw subgraphs } Reverse Mode Using Subgraphs ############################ Syntax ****** | *f* . ``subgraph_reverse`` ( *select_domain* ) | *f* . ``subgraph_reverse`` ( *q* , *ell* , *col* , *dw* ) | *f* . ``clear_subgraph`` () Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* . Reverse mode computes the derivative of the :ref:`Forward-name` mode :ref:`Taylor coefficients` with respect to the domain variable :math:`x`. Notation ******** We use the reverse mode :ref:`reverse_any@Notation` with the following change: the vector :ref:`reverse_any@Notation@w^(k)` is defined .. math:: w_i^{(k)} = \left\{ \begin{array}{ll} 1 & {\rm if} \; k = q-1 \; \R{and} \; i = \ell \\ 0 & {\rm otherwise} \end{array} \right. BaseVector ********** The type *BaseVector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` *Base* . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. BoolVector ********** The type *BoolVector* is a :ref:`SimpleVector-name` class with :ref:`elements of type` ``bool`` . SizeVector ********** The type *SizeVector* is a :ref:`SimpleVector-name` class with :ref:`elements of type` ``size_t`` . select_domain ************* The argument *select_domain* has prototype ``const`` *BoolVector* & *select_domain* It has size :math:`n` and specifies which independent variables to include in future ``subgraph_reverse`` calculations. If *select_domain* [ *j* ] is false, it is assumed that :math:`u^{(k)}_j = 0` for :math:`k > 0`; i.e., the *j*-th component of the Taylor coefficient for :math:`x`, with order greater that zero, are zero; see :ref:`reverse_any@Notation@u^(k)` . q * The argument *q* has prototype ``size_t`` *q* and specifies the number of Taylor coefficient orders to be differentiated. ell *** The argument *ell* has prototype ``size_t`` *ell* and specifies the dependent variable index that we are computing the derivatives for; i.e. :math:`\ell`. This index can only be used once per, and after, a call that selects the independent variables using *select_domain* . col *** This argument *col* has prototype *SizeVector* *col* The input size and value of its elements do not matter. The *col* . ``resize`` member function is used to change its size to the number the number of possible non-zero derivative components. For each *c* , | |tab| *select_domain* [ *col* [ *c* ] ] == ``true`` | |tab| *col* [ *c* +1] >= *col* [ *c* ] and the derivative with respect to the *j*-th independent variable is possibly non-zero where *j* = *col* [ *c* ] . dw ** The argument *dw* has prototype *Vector* *dw* Its input size and value does not matter. Upon return, it is a vector with size :math:`n \times q`. For :math:`c = 0 , \ldots , %col%.size()-1`, and :math:`k = 0, \ldots , q-1`, .. math:: dw[ j * q + k ] = W^{(1)} ( x )_{j,k} is the derivative of the specified Taylor coefficients w.r.t the *j*-th independent variable where *j* = *col* [ *c* ] . Note that this corresponds to the :ref:`reverse_any-name` convention when :ref:`reverse_any@w` has size *m* * *q* . clear_subgraph ************** Calling this routine will free memory that holds information between calls to subgraph calculations so that it does not need to be recalculated. (This memory is automatically freed when *f* is deleted.) You cannot free this memory between calls that select the domain and corresponding calls that compute reverse mode derivatives. Some of this information is also used by :ref:`subgraph_sparsity-name` . Example ******* {xrst_toc_hidden example/sparse/subgraph_reverse.cpp } The file :ref:`subgraph_reverse.cpp-name` contains an example and test of this operation. {xrst_end subgraph_reverse} */ namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file subgraph_reverse.hpp Compute derivatives using reverse mode and subgraphs. */ /// clear all subgraph information template void ADFun::clear_subgraph(void) { play_.clear_random(); subgraph_info_.clear(); subgraph_partial_.clear(); } /*! Initialize reverse mode derivative computation on subgraphs. \param select_domain is a vector with size equal to the dimension of the domain for this function. Only derivatives w.r.t. the components that are true will be computed. \par subgraph_info_.map_user_op() If the input size of this vector is zero, its value for this player (play_) is computed. \par subgraph_info.in_subgraph_ This vector is initialized for a reverse mode computation on subgraphs. \par subgraph_info.select_domain() This vector is set equal to the select_domain argument. \par subgraph_info.process_range() This vector is initialized to have size Range() and its elements are false. */ template template void ADFun::subgraph_reverse( const BoolVector& select_domain ) { using local::pod_vector; // CPPAD_ASSERT_UNKNOWN( dep_taddr_.size() == subgraph_info_.n_dep() ); CPPAD_ASSERT_UNKNOWN( size_t( select_domain.size() ) == subgraph_info_.n_ind() ); // map_user_op if( subgraph_info_.map_user_op().size() == 0 ) subgraph_info_.set_map_user_op(&play_); else { CPPAD_ASSERT_UNKNOWN( subgraph_info_.check_map_user_op(&play_) ); } CPPAD_ASSERT_UNKNOWN( subgraph_info_.map_user_op().size() == play_.num_var_op() ); // initialize for reverse mode subgraph computations switch( play_.address_type() ) { case local::play::unsigned_short_enum: subgraph_info_.init_rev(&play_, select_domain); break; case local::play::addr_t_enum: subgraph_info_.init_rev(&play_, select_domain); break; case local::play::size_t_enum: subgraph_info_.init_rev(&play_, select_domain); break; default: CPPAD_ASSERT_UNKNOWN(false); } CPPAD_ASSERT_UNKNOWN( subgraph_info_.in_subgraph().size() == play_.num_var_op() ); return; } /*! Use reverse mode to compute derivative of Taylor coefficients on a subgraph. The function \f$ X : {\bf R} \times {\bf R}^{n \times q} \rightarrow {\bf R} \f$ is defined by \f[ X(t , u) = \sum_{k=0}^{q-1} u^{(k)} t^k \f] The function \f$ Y : {\bf R} \times {\bf R}^{n \times q} \rightarrow {\bf R} \f$ is defined by \f[ Y(t , u) = F[ X(t, u) ] \f] The function \f$ W : {\bf R}^{n \times q} \rightarrow {\bf R} \f$ is defined by \f[ W(u) = \sum_{k=0}^{q-1} ( w^{(k)} )^{\rm T} \frac{1}{k !} \frac{ \partial^k } { t^k } Y(0, u) \f] \param q is the number of Taylor coefficient we are differentiating. \param ell is the component of the range that is selected for differentiation. \param col is the set of indices j = col[c] where the return value is defined. If an index j is not in col, then either its derivative is zero, or it is not in select_domain. \param dw Is a vector \f$ dw \f$ such that for j = col[c], \f$ k = 0 , \ldots , q-1 \f$ \f[ dw[ j * q + k ] = W^{(1)} ( x )_{j,k} \f] where the matrix \f$ x \f$ is the value for \f$ u \f$ that corresponding to the forward mode Taylor coefficients for the independent variables as specified by previous calls to Forward. \par subgraph_info.process_range() The element process_range[ell] is set to true by this operation. \par subgraph_info.in_subgraph_ some of the elements of this vector are set to have value ell (so it can not longer be used to determine the subgraph corresponding to the ell-th dependent variable). */ template template void ADFun::subgraph_reverse_helper( size_t q , size_t ell , SizeVector& col , BaseVector& dw ) { using local::pod_vector; // used to identify the RecBase type in calls to sweeps RecBase not_used_rec_base(0.0); // // get a random iterator for this player Addr not_used; play_.setup_random(not_used); typename local::play::const_random_iterator random_itr = play_.get_random( not_used ); // check BaseVector is Simple Vector class with Base type elements CheckSimpleVector(); CPPAD_ASSERT_KNOWN( q > 0, "The second argument to Reverse must be greater than zero." ); CPPAD_ASSERT_KNOWN( num_order_taylor_ >= q, "Less than q Taylor coefficients are currently stored" " in this ADFun object." ); CPPAD_ASSERT_KNOWN( num_direction_taylor_ == 1, "reverse mode for Forward(q, r, xq) with more than one direction" "\n(r > 1) is not yet supported." ); CPPAD_ASSERT_KNOWN( ell < dep_taddr_.size(), "dependent variable index in to large for this function" ); CPPAD_ASSERT_KNOWN( subgraph_info_.process_range()[ell] == false, "This dependent variable index has already been processed\n" "after the previous subgraph_reverse(select_domain)." ); // subgraph of operators connected to dependent variable ell pod_vector subgraph; subgraph_info_.get_rev( random_itr, dep_taddr_, addr_t(ell), subgraph ); // Add all the atomic function call operators // for calls that have first operator in the subgraph local::subgraph::entire_call(random_itr, subgraph); // First add the BeginOp and EndOp to the subgraph and then sort it // sort the subgraph addr_t i_op_begin_op = 0; addr_t i_op_end_op = addr_t( play_.num_var_op() - 1); subgraph.push_back(i_op_begin_op); subgraph.push_back(i_op_end_op); std::sort( subgraph.data(), subgraph.data() + subgraph.size() ); CPPAD_ASSERT_UNKNOWN( subgraph[0] == i_op_begin_op ); CPPAD_ASSERT_UNKNOWN( subgraph[subgraph.size()-1] == i_op_end_op ); /* // Use this printout for debugging std::cout << "{ "; for(size_t k = 0; k < subgraph.size(); k++) { if( k > 0 ) std::cout << ", "; std::cout << subgraph[k]; } std::cout << "}\n"; */ // initialize subgraph_partial_ matrix to zero on subgraph Base zero(0); subgraph_partial_.resize(num_var_tape_ * q); for(size_t k = 0; k < subgraph.size(); ++k) { size_t i_op = size_t( subgraph[k] ); local::op_code_var op; const addr_t* arg; size_t i_var; random_itr.op_info(i_op, op, arg, i_var); if( NumRes(op) == 0 ) { CPPAD_ASSERT_UNKNOWN( op == local::AFunOp || op == local::FunapOp || op == local::FunavOp || op == local::FunrpOp || op == local::EndOp ); } else if( op != local::BeginOp ) { CPPAD_ASSERT_UNKNOWN( i_var >= NumRes(op) ); size_t j_var = i_var + 1 - NumRes(op); for(size_t i = j_var; i <= i_var; ++i) { for(size_t j = 0; j < q; ++j) subgraph_partial_[i * q + j] = zero; } } } // set partial to one for component we are differentiating subgraph_partial_[ dep_taddr_[ell] * q + q - 1] = Base(1); // evaluate the derivatives CPPAD_ASSERT_UNKNOWN( cskip_op_.size() == play_.num_var_op() ); CPPAD_ASSERT_UNKNOWN( load_op2var_.size() == play_.num_var_load() ); size_t n = Domain(); // local::play::const_subgraph_iterator subgraph_itr = play_.end_subgraph(random_itr, &subgraph); // local::sweep::reverse( num_var_tape_, &play_, cap_order_taylor_, taylor_.data(), q, subgraph_partial_.data(), cskip_op_.data(), load_op2var_, subgraph_itr, not_used_rec_base ); // number of non-zero in return value size_t col_size = 0; size_t subgraph_index = 0; CPPAD_ASSERT_UNKNOWN( subgraph[subgraph_index] == 0 ); // Skip BeginOp ++subgraph_index; while( subgraph_index < subgraph.size() ) { // check for InvOp if( subgraph[subgraph_index] > addr_t(n) ) subgraph_index = subgraph.size(); else { ++col_size; ++subgraph_index; } } col.resize(col_size); // return the derivative values dw.resize(n * q); for(size_t c = 0; c < col_size; ++c) { size_t i_op = size_t( subgraph[c + 1] ); CPPAD_ASSERT_UNKNOWN( play_.GetOp(i_op) == local::InvOp ); // size_t j = i_op - 1; CPPAD_ASSERT_UNKNOWN( i_op == random_itr.var2op( ind_taddr_[j] ) ); // // return partial for this independent variable col[c] = j; for(size_t k = 0; k < q; k++) dw[j * q + k ] = subgraph_partial_[ind_taddr_[j] * q + k]; } // CPPAD_ASSERT_KNOWN( ! ( hasnan(dw) && check_for_nan_ ) , "f.subgraph_reverse(dw, q, ell): dw has a nan,\n" "but none of f's Taylor coefficients are nan." ); // return; } /*! \copydoc subgraph_reverse_helper */ template template void ADFun::subgraph_reverse( size_t q , size_t ell , SizeVector& col , BaseVector& dw ) { using local::pod_vector; // // call proper version of helper function switch( play_.address_type() ) { case local::play::unsigned_short_enum: subgraph_reverse_helper(q, ell, col, dw); break; case local::play::addr_t_enum: subgraph_reverse_helper(q, ell, col, dw); break; case local::play::size_t_enum: subgraph_reverse_helper(q, ell, col, dw); break; default: CPPAD_ASSERT_UNKNOWN(false); } // return; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/subgraph_sparsity.hpp ================================================ # ifndef CPPAD_CORE_SUBGRAPH_SPARSITY_HPP # define CPPAD_CORE_SUBGRAPH_SPARSITY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin subgraph_sparsity} {xrst_spell rc subgraphs } Subgraph Dependency Sparsity Patterns ##################################### Syntax ****** | *f* . ``subgraph_sparsity`` ( | |tab| *select_domain* , *select_range* , *transpose* , *pattern_out* | ) See Also ******** :ref:`subgraph_reverse@clear_subgraph` . Notation ******** We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to the operation sequence stored in *f* . Method ****** This routine uses a subgraph technique. To be specific, for each dependent variable, it creates a subgraph of the operation sequence containing the variables that affect the dependent variable. This avoids the overhead of performing set operations that is inherent in other methods for computing sparsity patterns. Atomic Function *************** The sparsity calculation for :ref:`atomic functions` in the *f* operation sequence are not efficient. To be specific, each atomic function is treated as if all of its outputs depend on all of its inputs. This may be improved upon in the future; see the :ref:`subgraph sparsity` wish list item. BoolVector ********** The type *BoolVector* is a :ref:`SimpleVector-name` class with :ref:`elements of type` ``bool`` . SizeVector ********** The type *SizeVector* is a :ref:`SimpleVector-name` class with :ref:`elements of type` ``size_t`` . f * The object *f* has prototype ``ADFun`` < *Base* > *f* select_domain ************* The argument *select_domain* has prototype ``const`` *BoolVector* & *select_domain* It has size :math:`n` and specifies which independent variables to include in the calculation. If not all the independent variables are included in the calculation, a forward pass on the operation sequence is used to determine which nodes may be in the subgraphs. select_range ************ The argument *select_range* has prototype ``const`` *BoolVector* & *select_range* It has size :math:`m` and specifies which components of the range to include in the calculation. A subgraph is built for each dependent variable and the selected set of independent variables. transpose ********* This argument has prototype ``bool`` *transpose* If *transpose* it is false (true), upon return *pattern_out* is a sparsity pattern for :math:`J(x)` (:math:`J(x)^\R{T}`) defined below. pattern_out *********** This argument has prototype ``sparse_rc`` < *SizeVector* >& *pattern_out* This input value of *pattern_out* does not matter. Upon return *pattern_out* is a :ref:`dependency.cpp@Dependency Pattern` for :math:`F(x)`. The pattern has :math:`m` rows, :math:`n` columns. If *select_domain* [ *j* ] is true, *select_range* [ *i* ] is true, and :math:`F_i (x)` depends on :math:`x_j`, then the pair :math:`(i, j)` is in *pattern_out* . Not that this is also a sparsity pattern for the Jacobian .. math:: J(x) = R F^{(1)} (x) D where :math:`D` (:math:`R`) is the diagonal matrix corresponding to *select_domain* ( *select_range* ). Example ******* {xrst_toc_hidden example/sparse/subgraph_sparsity.cpp } The file :ref:`subgraph_sparsity.cpp-name` contains an example and test of this operation. {xrst_end subgraph_sparsity} ----------------------------------------------------------------------------- */ # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! Subgraph sparsity patterns. \tparam Base is the base type for this recording. \tparam SizeVector is the simple vector with elements of type size_t that is used for row, column index sparsity patterns. \param select_domain sparsity pattern for the diagonal of the square matrix D. \param select_range sparsity pattern for the diagonal of the square matrix R \param transpose If true, the return is a dependency sparsity pattern for \f$ D F^{(1)} (x)^T R \f$ \param pattern_out The input value does not matter. The return value is a dependency sparsity pattern for \f$ R F^{(1)} (x) D \f$ where F is the function corresponding to the operation sequence and x is any argument value. is the sparsity pattern transposed. */ template template void ADFun::subgraph_sparsity( const BoolVector& select_domain , const BoolVector& select_range , bool transpose , sparse_rc& pattern_out ) { // compute the sparsity pattern in row, col local::pod_vector row; local::pod_vector col; // create the optimized recording switch( play_.address_type() ) { case local::play::unsigned_short_enum: local::subgraph::subgraph_sparsity( &play_, subgraph_info_, dep_taddr_, select_domain, select_range, row, col ); break; case local::play::addr_t_enum: local::subgraph::subgraph_sparsity( &play_, subgraph_info_, dep_taddr_, select_domain, select_range, row, col ); break; case local::play::size_t_enum: local::subgraph::subgraph_sparsity( &play_, subgraph_info_, dep_taddr_, select_domain, select_range, row, col ); break; default: CPPAD_ASSERT_UNKNOWN(false); } CPPAD_ASSERT_UNKNOWN( row.size() == col.size() ); // return the sparsity pattern size_t nr = dep_taddr_.size(); size_t nc = ind_taddr_.size(); size_t nnz = row.size(); if( transpose ) { pattern_out.resize(nc, nr, nnz); for(size_t k = 0; k < nnz; k++) pattern_out.set(k, col[k], row[k]); } else { pattern_out.resize(nr, nc, nnz); for(size_t k = 0; k < nnz; k++) pattern_out.set(k, row[k], col[k]); } return; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/tape_link.hpp ================================================ # ifndef CPPAD_CORE_TAPE_LINK_HPP # define CPPAD_CORE_TAPE_LINK_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include // needed before one can use CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file tape_link.hpp Routines that Link AD and local::ADTape Objects. The routines that connect the AD class to the corresponding tapes (one for each thread). */ /*! Pointer to the tape identifier for this AD class and the specific thread. \tparam Base is the base type for this AD class. \param thread is the thread number. The following condition must hold \code (! thread_alloc::in_parallel()) || thread == thread_alloc::thread_num() \endcode \return is a pointer to the tape identifier for this thread and AD class. */ template tape_id_t* AD::tape_id_ptr(size_t thread) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; static tape_id_t tape_id_table[CPPAD_MAX_NUM_THREADS]; CPPAD_ASSERT_UNKNOWN( (! thread_alloc::in_parallel()) || thread == thread_alloc::thread_num() ); return tape_id_table + thread; } /*! Handle for the tape for this AD class and the specific thread. \tparam Base is the base type for this AD class. \param thread is the thread number; i.e., \code (! thread_alloc::in_parallel()) || thread == thread_alloc::thread_num() \endcode \return is a handle for the tape for this AD class and the specified thread. */ template local::ADTape** AD::tape_handle(size_t thread) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; static local::ADTape* tape_table[CPPAD_MAX_NUM_THREADS]; CPPAD_ASSERT_UNKNOWN( (! thread_alloc::in_parallel()) || thread == thread_alloc::thread_num() ); return tape_table + thread; } /*! Pointer for the tape for this AD class and the current thread. \code thread == thread_alloc::thread_num() \endcode \tparam Base is the base type corresponding to AD operations. \return is a pointer to the tape that is currently recording AD operations for the current thread. If this value is nullptr, there is no tape currently recording AD operations for this thread. */ template local::ADTape* AD::tape_ptr(void) { size_t thread = thread_alloc::thread_num(); return *tape_handle(thread); } /*! Pointer for the tape for this AD class and the specified tape identifier. \tparam Base is the base type corresponding to AD operations. \param tape_id is the identifier for the tape that is currently recording AD operations for the current thread. It must hold that the current thread is \code thread = size_t( tape_id % CPPAD_MAX_NUM_THREADS ) \endcode and that there is a tape recording AD operations for this thread. If this is not the currently executing thread, a variable from a different thread is being recorded on the tape for this thread which is a user error. \return is a pointer to the tape that is currently recording AD operations for the current thread (and it is not nullptr). \par Restrictions This routine should only be called if there is a tape recording operations for the specified thread. */ template local::ADTape* AD::tape_ptr(tape_id_t tape_id) { size_t thread = size_t( tape_id % CPPAD_MAX_NUM_THREADS ); CPPAD_ASSERT_KNOWN( thread == thread_alloc::thread_num(), "Attempt to use an AD variable with two different threads." ); CPPAD_ASSERT_UNKNOWN( tape_id == *tape_id_ptr(thread) ); CPPAD_ASSERT_UNKNOWN( *tape_handle(thread) != nullptr ); return *tape_handle(thread); } /*! Create and delete tapes that record AD operations for current thread. \par thread the current thread is given by \code thread = thread_alloc::thread_num() \endcode \tparam Base is the base type corresponding to AD operations. \param job This argument determines if we are creating a new tape, or deleting an old one. - new_tape_manage : Creates and a new tape. It is assumed that there is no tape recording AD operations for this thread when tape_manage is called. - delete_tape_manage : It is assumed that there is a tape recording AD operations for this thread when tape_manage is called. The value of *tape_id_ptr(thread) will be advanced by CPPAD_MAX_NUM_THREADS. \return - job == new_tape_manage: a pointer to the new tape is returned. - job == delete_tape_manage: the value nullptr is returned. */ template local::ADTape* AD::tape_manage(tape_manage_enum job) { CPPAD_ASSERT_UNKNOWN( job == new_tape_manage || job == delete_tape_manage ); // thread, tape_id, and tape for this call size_t thread = thread_alloc::thread_num(); tape_id_t* tape_id_p = tape_id_ptr(thread); local::ADTape** tape_h = tape_handle(thread); // ----------------------------------------------------------------------- // new_tape_manage if( job == new_tape_manage ) { // tape for this thread must be null at the start CPPAD_ASSERT_UNKNOWN( *tape_h == nullptr ); // allocate separate memory to avoid false sharing *tape_h = new local::ADTape(); // if tape id is zero, initialize it so that // thread == tape id % CPPAD_MAX_NUM_THREADS if( *tape_id_p == 0 ) { size_t new_tape_id = thread + CPPAD_MAX_NUM_THREADS; CPPAD_ASSERT_KNOWN( size_t( std::numeric_limits::max() ) >= new_tape_id, "cppad_tape_id_type maximum value has been exceeded" ); *tape_id_p = static_cast( new_tape_id ); } // make sure tape_id value is valid for this thread CPPAD_ASSERT_UNKNOWN( size_t( *tape_id_p % CPPAD_MAX_NUM_THREADS ) == thread ); // set the tape_id for this tape (*tape_h)->id_ = *tape_id_p; } // ----------------------------------------------------------------------- // delete_tape_manage if( job == delete_tape_manage ) { // delete this tape CPPAD_ASSERT_UNKNOWN( *tape_h != nullptr ); delete *tape_h; *tape_h = nullptr; // // advance tape_id so that all AD variables become parameters CPPAD_ASSERT_KNOWN( std::numeric_limits::max() - CPPAD_MAX_NUM_THREADS > *tape_id_p, "To many different tapes given the type used for " "CPPAD_TAPE_ID_TYPE" ); *tape_id_p += CPPAD_MAX_NUM_THREADS; } // ----------------------------------------------------------------------- return *tape_h; } /*! Get a pointer to tape that records AD operations for the current thread. \tparam Base is the base type corresponding to AD operations. \par thread The current thread must be given by \code thread = this->tape_id_ % CPPAD_MAX_NUM_THREADS \endcode \return is a pointer to the tape that is currently recording AD operations for the current thread. This value must not be nullptr; i.e., there must be a tape currently recording AD operations for this thread. */ template local::ADTape *AD::tape_this(void) const { size_t thread = size_t( tape_id_ % CPPAD_MAX_NUM_THREADS ); CPPAD_ASSERT_UNKNOWN( tape_id_ == *tape_id_ptr(thread) ); CPPAD_ASSERT_UNKNOWN( *tape_handle(thread) != nullptr ); return *tape_handle(thread); } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/core/testvector.hpp ================================================ # ifndef CPPAD_CORE_TESTVECTOR_HPP # define CPPAD_CORE_TESTVECTOR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin testvector} {xrst_spell ublas } Using The CppAD Test Vector Template Class ########################################## Syntax ****** | ``CPPAD_TESTVECTOR`` ( *Scalar* ) Choice ****** The user can choose, during the install procedure, which template class to use in the examples and tests; see below. This shows that any :ref:`simple vector` class can be used in place of ``CPPAD_TESTVECTOR`` ( *Type* ) When writing their own code, users can choose a specific simple vector they prefer; for example, ``CppAD::vector<`` *Type* > CppAD::vector ************* If in the :ref:`cmake@CMake Command` you specify :ref:`cppad_testvector-name` to be ``cppad`` , # ``define CPPAD_TESTVECTOR`` ( *Scalar* ) ``CppAD::vector<`` *Scalar* > CPPAD_CPPADVECTOR, Deprecated 2022-06-22 ======================================== This symbol is 1 (0) if the definition above is used (is not used) for ``CPPAD_TESTVECTOR`` . std::vector *********** If in the cmake command you specify *cppad_testvector* to be ``std`` , # ``define CPPAD_TESTVECTOR`` ( *Scalar* ) ``std::vector<`` *Scalar* > CPPAD_STDVECTOR, Deprecated 2022-06-22 ====================================== This symbol is 1 (0) if the definition above is used (is not used) for ``CPPAD_TESTVECTOR`` . boost::numeric::ublas::vector ***************************** If in the cmake command you specify *cppad_testvector* to be ``boost`` , # ``define CPPAD_TESTVECTOR`` ( *Scalar* ) ``boost::numeric::ublas::vector<`` *Scalar* > CPPAD_BOOSTVECTOR, Deprecated 2022-06-22 ======================================== This symbol is 1 (0) if the definition above is used (is not used) for ``CPPAD_TESTVECTOR`` . CppAD::eigen_vector ******************* If in the cmake command you specify *cppad_testvector* to be ``eigen`` , # ``define CPPAD_TESTVECTOR`` ( *Scalar* ) ``CppAD::eigen_vector<`` *Scalar* > see :ref:`cppad_eigen.hpp@eigen_vector` . In this case CppAD will use the Eigen vector for many of its examples and tests. CPPAD_EIGENVECTOR, Deprecated 2022-06-22 ======================================== This symbol is 1 (0) if the definition above is used (is not used) for ``CPPAD_TESTVECTOR`` . {xrst_end testvector} ------------------------------------------------------------------------ */ # include # # if CPPAD_CPPADVECTOR # define CPPAD_TESTVECTOR(Scalar) CppAD::vector< Scalar > # endif // # if CPPAD_STDVECTOR # include # define CPPAD_TESTVECTOR(Scalar) std::vector< Scalar > # endif // # if CPPAD_BOOSTVECTOR # include # define CPPAD_TESTVECTOR(Scalar) boost::numeric::ublas::vector< Scalar > # endif // # if CPPAD_EIGENVECTOR # include # define CPPAD_TESTVECTOR(Scalar) CppAD::eigen_vector< Scalar > # endif // # endif ================================================ FILE: include/cppad/core/to_csrc.hpp ================================================ # ifndef CPPAD_CORE_TO_CSRC_HPP # define CPPAD_CORE_TO_CSRC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include /* ------------------------------------------------------------------------------ {xrst_begin to_csrc} {xrst_spell cdecl declspec dllimport ny typedef underbar } C Source Code Corresponding to an ADFun Object ############################################## Syntax ****** | *fun* . ``to_csrc`` ( *os* , *c_type* ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } fun *** is the :ref:`adfun-name` object. Base **** is the type corresponding to this :ref:`adfun-name` object; i.e., its calculations are done using the type *Base* . RecBase ******* in the prototype above, *RecBase* is the same type as *Base* . os ** The C source code representation of the function *fun* is written to *os* . c_type ****** The possible values for this argument are: ``float`` , ``double`` , or ``long_double`` . JIT Functions ************* Function Type ============= The function type ``jit_``\ *c_type* is defined in the CppAD namespace as: | ``typedef int`` (* ``jit_``\ *c_type* )( | |tab| ``size_t`` , ``const`` *type* * , ``size_t`` , *type* * , ``size_t`` * | ) Here *type* is the same as *c_type* except that the underbar in ``long_double`` is replaced by a space. In the case of the Visual C++ compiler (``_MSC_VER`` is defined), ``__cdecl`` and ``__declspec(dllimport)`` are added to the function type definition. Syntax ====== | *flag* = ``cppad_jit_``\ *function_name* ( | |tab| *nu* , *u* , *ny* , *y* , *compare_change* | ) A corresponding function call evaluations zero order forward mode for the function *fun* and | *function_name* = *fun*\ ``.function_name_get`` () see :ref:`function_name-name` . Atomic Callbacks **************** Function Type ============= The function type ``atomic_``\ *c_type* is defined in the CppAD namespace. | ``typedef int`` (* ``atomic_`` *c_type* )( | |tab| ``size_t`` , ``size_t`` , ``const`` *type* * , ``size_t`` , *type* * , ``size_t`` * | ) Syntax ====== | *flag* = ``cppad_atomic_``\ *atomic_name* ( | |tab| *call_id* , *nu* , *u* , *ny* , *y* , *compare_change* | ) A corresponding function call evaluates zero order forward mode for the atomic function with the specified *atomic_name* ; see atomic_four :ref:`atomic_four_ctor@atomic_four@name` . call_id ******* This argument is only used during atomic four function callbacks, in which case it is the corresponding :ref:`atomic_four_call@call_id` . nu ** is the number of independent dynamic parameters plus number of independent variables for the function *fun* . u * is a C vector of size *nu* containing the independent dynamic parameters and independent variables. The independent dynamic parameter come first as in the same order as :ref:`Independent@dynamic` in the call to ``Independent`` for this function. The independent variables are in the same order as :ref:`Independent@x` in the call to ``Independent`` for this function. ny ** is the number of dependent values for this function (a dependent value can be a variable, dynamic parameter, or constant parameter). y * is a C vector of size *ny* . This input values of its elements do not matter. Upon return, it contains the function value correspond to *u* . compare_change ************** This argument is both an input and an output. The number of comparison operators that change their bool result value is added to *compare_change* . This way, *compare_change* can be used to accumulate the number of changes between multiplier calls. flag **** If this is zero, no error was detected. If it is one (two), *nu* ( *ny* ) does not have its expected value. Restrictions ************ The ``to_csrc`` routine is not implemented for :ref:`vecad-name` operations. {xrst_toc_hidden example/jit/jit.xrst } Example ******* The section :ref:`example_jit-name` contains examples and tests that use ``to_csrc`` . {xrst_end to_csrc} */ # include # if CPPAD_C_COMPILER_MSVC_FLAGS # define CPPAD_FUN_TYPE __cdecl # define CPPAD_IMPORT __declspec(dllimport) # else # define CPPAD_FUN_TYPE # define CPPAD_IMPORT # endif namespace CppAD { extern "C" { // // jit_c_type CPPAD_IMPORT typedef int (CPPAD_FUN_TYPE *jit_float)( size_t, const float*, size_t, float*, size_t* ); CPPAD_IMPORT typedef int (CPPAD_FUN_TYPE *jit_double)( size_t, const double*, size_t, double*, size_t* ); CPPAD_IMPORT typedef int (CPPAD_FUN_TYPE *jit_long_double)( size_t, const long double*, size_t, long double*, size_t* ); // // atomic_c_type CPPAD_IMPORT typedef int (CPPAD_FUN_TYPE *atomic_float)( size_t, size_t, const float*, size_t, float*, size_t* ); CPPAD_IMPORT typedef int (CPPAD_FUN_TYPE *atomic_double)( size_t, size_t, const double*, size_t, double*, size_t* ); CPPAD_IMPORT typedef int (CPPAD_FUN_TYPE *atomic_long_double)( size_t, size_t, const long double*, size_t, long double*, size_t* ); } } # undef CPPAD_FUN_TYPE # undef CPPAD_IMPORT // BEGIN_PROTOTYPE template void CppAD::ADFun::to_csrc( std::ostream& os , const std::string& c_type ) // END_PROTOTYPE { // // type # ifndef NDEBUG bool ok = false; ok |= c_type == "float"; ok |= c_type == "double"; ok |= c_type == "long_double"; CPPAD_ASSERT_KNOWN(ok, "f.to_csrc: c_type is not one of the following: " "float, double, long_double" ); # endif // to_graph return values cpp_graph graph_obj; // // graph corresponding to this function to_graph(graph_obj); // // os local::graph::csrc_writer(os, graph_obj, c_type); // return; } # endif ================================================ FILE: include/cppad/core/unary_minus.hpp ================================================ # ifndef CPPAD_CORE_UNARY_MINUS_HPP # define CPPAD_CORE_UNARY_MINUS_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin unary_minus} AD Unary Minus Operator ####################### Syntax ****** | *y* = ``-`` *x* Purpose ******* Computes the negative of *x* . Base **** The operation in the syntax above must be supported for the case where the operand is a ``const`` *Base* object. x * The operand *x* has one of the following prototypes | |tab| ``const AD`` < *Base* > & *x* | |tab| ``const VecAD`` < *Base* >:: ``reference &`` *x* y * The result *y* has type ``AD`` < *Base* > *y* It is equal to the negative of the operand *x* . Operation Sequence ****************** This is an AD of *Base* :ref:`atomic operation` and hence is part of the current AD of *Base* :ref:`operation sequence` . Derivative ********** If :math:`f` is a :ref:`glossary@Base Function` , .. math:: \D{[ - f(x) ]}{x} = - \D{f(x)}{x} Example ******* {xrst_toc_hidden example/general/unary_minus.cpp } The file :ref:`unary_minus.cpp-name` contains an example and test of this operation. {xrst_end unary_minus} ------------------------------------------------------------------------------- */ // BEGIN CppAD namespace namespace CppAD { // template AD AD::operator - (void) const { // compute the Base part of this AD object AD result; result.value_ = - value_; CPPAD_ASSERT_UNKNOWN( Parameter(result) ); // check if there is a recording in progress local::ADTape* tape = AD::tape_ptr(); if( tape == nullptr ) return result; // tape_id cannot match the default value for tape_id; i.e., 0 CPPAD_ASSERT_UNKNOWN( tape->id_ > 0 ); // if( tape->id_ != tape_id_ ) return result; // if( ad_type_ == variable_enum ) { // result is a variable CPPAD_ASSERT_UNKNOWN( local::NumRes(local::NegOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumRes(local::NegOp) == 1 ); // // put operand address in the tape tape->Rec_.PutArg(taddr_); // put operator in the tape result.taddr_ = tape->Rec_.PutOp(local::NegOp); // make result a variable result.tape_id_ = tape_id_; result.ad_type_ = variable_enum; } else { CPPAD_ASSERT_UNKNOWN( ad_type_ == dynamic_enum ); addr_t arg0 = taddr_; result.taddr_ = tape->Rec_.put_dyn_par( result.value_, local::neg_dyn, arg0 ); result.tape_id_ = tape_id_; result.ad_type_ = dynamic_enum; } return result; } // template AD operator - (const VecAD_reference &right) { return - right.ADBase(); } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/unary_plus.hpp ================================================ # ifndef CPPAD_CORE_UNARY_PLUS_HPP # define CPPAD_CORE_UNARY_PLUS_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin unary_plus} AD Unary Plus Operator ###################### Syntax ****** | *y* = + *x* Purpose ******* Performs the unary plus operation (the result *y* is equal to the operand *x* ). x * The operand *x* has one of the following prototypes | |tab| ``const AD`` < *Base* > & *x* | |tab| ``const VecAD`` < *Base* >:: ``reference &`` *x* y * The result *y* has type ``AD`` < *Base* > *y* It is equal to the operand *x* . Operation Sequence ****************** This is an AD of *Base* :ref:`atomic operation` and hence is part of the current AD of *Base* :ref:`operation sequence` . Derivative ********** If :math:`f` is a :ref:`glossary@Base Function` , .. math:: \D{[ + f(x) ]}{x} = \D{f(x)}{x} Example ******* {xrst_toc_hidden example/general/unary_plus.cpp } The file :ref:`unary_plus.cpp-name` contains an example and test of this operation. {xrst_end unary_plus} ------------------------------------------------------------------------------- */ // BEGIN CppAD namespace namespace CppAD { template AD AD::operator + (void) const { AD result(*this); return result; } template AD operator + (const VecAD_reference &right) { return right.ADBase(); } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/undef.hpp ================================================ # ifndef CPPAD_CORE_UNDEF_HPP # define CPPAD_CORE_UNDEF_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- /* ---------------------------------------------------------------------------- Preprecessor definitions that persist after cppad/cppad.hpp is included. These are part of the user API (see omh/preprocessor.omh) with some exceptions that are used by the CppAD examples and tests. // BEGIN_SORT_THIS_LINE_PLUS_1 # undef CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL used by CPPAD_USER_ATOMIC # undef CPPAD_ASSERT_KNOWN used by cppad_ipopt # undef CPPAD_ASSERT_UNKNOWN used by cppad_ipopt # undef CPPAD_C_COMPILER_CMD used by dll examples. # undef CPPAD_C_COMPILER_GNU_FLAGS used by dll examples. # undef CPPAD_C_COMPILER_MSVC_FLAGS used by dll examples. # undef CPPAD_HASH_TABLE_SIZE used by test_more/optimize.cpp # undef CPPAD_HAS_COLPACK used by speed/cppad/sparse_*.cpp # undef CPPAD_LINK_FLAGS_HAS_M32 used to exclude certain tests # undef EIGEN_MATRIXBASE_PLUGIN example use of Eigen with CppAD // END_SORT_THIS_LINE_MINUS_1 // for conditional testing when implicit conversion is not present # undef CPPAD_DEPRECATED ----------------------------------------------------------------------------- */ // Preprecessor definitions that do not persist. None of these are in the // user API. // BEGIN_SORT_THIS_LINE_PLUS_1 # undef CPPAD_ASSERT_AD_TYPE # undef CPPAD_ASSERT_NARG_NRES # undef CPPAD_AZMUL # undef CPPAD_BOOSTVECTOR # undef CPPAD_COMPILER_HAS_CONVERSION_WARN # undef CPPAD_COND_EXP # undef CPPAD_COND_EXP_BASE_REL # undef CPPAD_COND_EXP_REL # undef CPPAD_CPPADVECTOR # undef CPPAD_DEBUG_AND_RELEASE # undef CPPAD_EIGENVECTOR # undef CPPAD_FOLD_AD_VALUED_BINARY_OPERATOR # undef CPPAD_FOLD_ASSIGNMENT_OPERATOR # undef CPPAD_FOLD_BOOL_VALUED_BINARY_OPERATOR # undef CPPAD_HAS_ADOLC # undef CPPAD_HAS_EIGEN # undef CPPAD_HAS_GETTIMEOFDAY # undef CPPAD_HAS_IPOPT # undef CPPAD_HAS_MKSTEMP # undef CPPAD_HAS_TMPNAM_S # undef CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION # undef CPPAD_IS_SAME_TAPE_ADDR_TYPE_SIZE_T # undef CPPAD_IS_SAME_UNSIGNED_INT_SIZE_T # undef CPPAD_LIB_EXPORT # undef CPPAD_MAX_NUM_CAPACITY # undef CPPAD_MIN_DOUBLE_CAPACITY # undef CPPAD_NDEBUG_NOEXCEPT # undef CPPAD_NOEXCEPT # undef CPPAD_PADDING_BLOCK_T # undef CPPAD_STANDARD_MATH_UNARY_AD # undef CPPAD_STDVECTOR # undef CPPAD_TRACE_CAPACITY # undef CPPAD_TRACE_THREAD # undef CPPAD_TRACK_DEBUG # undef CPPAD_USER_MACRO # undef CPPAD_USER_MACRO_ONE # undef CPPAD_USER_MACRO_TWO # undef CPPAD_VEC_AD_COMP_ASSIGN # undef CPPAD_VEC_ENUM_TYPE // END_SORT_THIS_LINE_MINUS_1 # endif ================================================ FILE: include/cppad/core/user_ad.hpp ================================================ # ifndef CPPAD_CORE_USER_AD_HPP # define CPPAD_CORE_USER_AD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* --------------------------------------------------------------------------- {xrst_begin AD} AD Objects ########## Purpose ******* The sections listed below describe the operations that are available to :ref:`glossary@AD of Base` objects. These objects are used to :ref:`glossary@Tape` an AD of *Base* :ref:`operation sequence` . This operation sequence can be transferred to an :ref:`ADFun-name` object where it can be used to evaluate the corresponding function and derivative values. Base Type Requirements ********************** The *Base* requirements are provided by the CppAD package for the following base types: ``float`` , ``double`` , ``std::complex`` , ``std::complex`` . Otherwise, see :ref:`base_require-name` . Contents ******** {xrst_toc_table include/cppad/core/ad_ctor.hpp include/cppad/core/ad_assign.hpp include/cppad/core/convert.hpp include/cppad/core/ad_valued.hpp include/cppad/core/bool_valued.hpp include/cppad/core/vec_ad/user.xrst include/cppad/base_require.hpp } {xrst_end AD} --------------------------------------------------------------------------- */ # include # include # include # include # include # include # include # endif ================================================ FILE: include/cppad/core/value.hpp ================================================ # ifndef CPPAD_CORE_VALUE_HPP # define CPPAD_CORE_VALUE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin Value} Convert From an AD Type to its Base Type ######################################## Syntax ****** | *b* = ``Value`` ( *x* ) See Also ******** :ref:`var2par-name` Purpose ******* Converts from an AD type to the corresponding :ref:`glossary@Base Type` . x * The argument *x* has prototype ``const AD`` < *Base* > & *x* b * The return value *b* has prototype *Base* *b* Operation Sequence ****************** The result of this operation is not an :ref:`glossary@AD of Base` object. Thus it will not be recorded as part of an AD of *Base* :ref:`operation sequence` . Restriction *********** The argument *x* must not be a :ref:`glossary@Variable` or :ref:`glossary@Parameter@Dynamic` parameter because its dependency information would not be included in the ``Value`` result *b* . Example ******* {xrst_toc_hidden example/general/value.cpp } The file :ref:`value.cpp-name` contains an example and test of this operation. {xrst_end Value} ------------------------------------------------------------------------------- */ // BEGIN CppAD namespace namespace CppAD { template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION Base Value(const AD &x) { Base result; // CPPAD_ASSERT_KNOWN( ! ( Variable(x) || Dynamic(x) ) , "Value: argument is a variable or dynamic parameter" ); // result = x.value_; return result; } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/var2par.hpp ================================================ # ifndef CPPAD_CORE_VAR2PAR_HPP # define CPPAD_CORE_VAR2PAR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* ------------------------------------------------------------------------------ {xrst_begin Var2Par} Convert an AD Variable or Dynamic Parameter to a Constant ######################################################### Syntax ****** | *y* = ``Var2Par`` ( *x* ) See Also ******** :ref:`value-name` Purpose ******* Returns a :ref:`constant parameter` *y* with the same value as *x* . x * The argument *x* has prototype ``const AD`` < *Base* > & ``x`` The argument *x* may be a variable, dynamic parameter, or constant parameter. y * The result *y* has prototype ``AD`` < *Base* > & ``y`` and is a constant parameter. Example ******* {xrst_toc_hidden example/general/var2par.cpp } The file :ref:`var2par.cpp-name` contains an example and test of this operation. {xrst_end Var2Par} ------------------------------------------------------------------------------ */ // BEGIN CppAD namespace namespace CppAD { template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION AD Var2Par(const AD &x) { AD y(x.value_); return y; } template CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION AD Var2Par(const VecAD_reference &x) { AD y(x.ADBase()); y.id_ = 0; } } // END CppAD namespace # endif ================================================ FILE: include/cppad/core/vec_ad/user.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- /* {xrst_begin VecAD} {xrst_spell grep ldp ldv vp wc } AD Vectors that Record Index Operations ####################################### Syntax ****** | ``VecAD`` < *Base* > *vec* ( *n* ) | *vec* . ``size`` () | *base* = *vec* [ *i* ] | *abase* = *vec* [ *ind* ] | *vec* [ *ind* ] = *right* | *left* = *vec* [ *ind* ] Purpose ******* If either *vec* or *ind* is a :ref:`glossary@Variable` or :ref:`dynamic parameter` , the indexing operation *vec* [ *ind* ] is recorded in the corresponding ``AD`` < *Base* > :ref:`operation sequence` and included in the corresponding :ref:`ADFun-name` object *f* . Such an index can change each time zero order :ref:`f.Forward` is used; i.e., each time *f* is evaluated with new value for the :ref:`independent variables` . Note that the value of *vec* [ *ind* ] depends on the value of *ind* in a discrete fashion and CppAD computes its partial derivative with respect to *ind* as zero. Alternatives ************ If only the values in *vec* , and not the indices *ind* , depend on the independent variables, a :ref:`SimpleVector-name` with elements of type ``AD`` < *Base* > would be more efficient than using ``VecAD`` < *Base* > . If only the indices, and not the values in the vector, depend on the independent variables, a :ref:`Discrete-name` functions would be a much more efficient. Efficiency ********** If one uses ``VecAD`` vector where one could use a simple vector, the :ref:`sparsity_pattern-name` will be less efficient because the dependence on different elements cannot be separated. In addition, ``VecAD`` objects that only depend on dynamic parameters are treated as if they were variables making sparsity patterns even less efficient (have more possibly non-zero values than necessary); see :ref:`wish_list@Dynamic Parameters@VecAD Vectors` under dynamic parameters in the wish list. VecAD::reference ********************** The expression *vec* [ *ind* ] has prototype ``VecAD`` < *Base* >:: ``reference`` *vec* [ *ind* ] which is like the ``AD`` < *Base* > type with some notable exceptions: Exceptions ========== #. This object cannot be used with the :ref:`Value-name` function to compute the corresponding *Base* value. In some cases, the syntax *vec* [ *i* ] can be used to obtain the corresponding *Base* value; see below. #. This object cannot be used as the left hand side in a with a :ref:`compound assignment` ; i.e., ``+=`` , ``-=`` , ``*=`` , or ``/=`` . For example, the following syntax is not valid: *vec* [ *ind* ] += *z* ; no matter what the types of *z* . #. Assignment to ``vec`` [ ``ind`` ] returns a ``void`` . For example, the following syntax is not valid: *z* = *vec* [ *ind* ] = *u* ; no matter what the types of *z* , and *u* . #. A *vec* [ *ind* ] object cannot appear in a :ref:`CondExp-name` ; For example, the following syntax is not valid: ``CondExpGt`` ( *vec* [ *ind* ], *u* , *v* , *w* ) no matter what the types of *u* , *v* , and *w* . #. A *vec* [ *ind* ] object should not be used with the ``Constant`` , ``Dynamic`` , ``Parameter`` , and ``Variable`` functions (see :ref:`con_dyn_var-name` ). The entire vector *vec* should be used instead. #. A ``VecAD`` vector cannot be passed to ``Independent`` function. Constructor *********** vec === The syntax ``VecAD`` < *Base* > *vec* ( *n* ) creates an ``VecAD`` object *vec* with *n* elements. The initial value of the elements of *vec* is unspecified. n = The argument *n* has prototype ``size_t`` *n* size **** The syntax *vec* . ``size`` () returns the number of elements in the vector *vec* ; i.e., the value of *n* when it was constructed. Base Indexing ************* We refer to the syntax *base* = *vec* [ *i* ] as base indexing of a ``VecAD`` object. This indexing is only valid if the vector *vec* is a :ref:`con_dyn_var@Constant` ; i.e., it does not depend on the independent variables. i = The operand *i* has prototype ``size_t`` *i* and must be less than *n* ; i.e., less than the number of elements in *vec* . base ==== The result *base* has prototype *Base* & *base* i.e., it is a reference to the *i*-th element in the vector *vec* . It can be used to change the element value; for example, *vec* [ *i* ] = *b* is valid where *b* is a *Base* object. The reference *base* is no longer valid once the *vec* changes in any way; i.e., has another assignment. AD Indexing *********** We refer to the syntax *vec* [ *ind* ] as AD indexing of a ``VecAD`` object. ind === The argument *ind* has prototype ``const AD`` < *Base* >& *ind* The value of *ind* must be greater than or equal zero and less than *n* ; i.e., less than the number of elements in *vec* . result ====== The resulting expression has prototype ``VecAD`` < *Base* >:: ``reference`` *vec* [ *ind* ] This objects operations are recorded as part of the ``AD`` < *Base* > :ref:`operation sequence` . It acts like a reference to the element with index ``floor`` ( *ind* ) in the vector *vec* ; ( ``floor`` ( *ind* ) is the greatest integer less than or equal *ind* ). right ===== Is the right hand side of the assignment statement and specifies the new value for the corresponding element of *vec* . It has one of the following prototypes: | |tab| ``int`` *right* | |tab| ``const`` *Base* & *right* | |tab| ``const AD`` < *Base* >& *right* | |tab| ``const VecAD_reverence`` < *Base* >& *right* left ==== Is the left hand side of the assignment statement is the current value for the corresponding element of *vec* . It has the following prototype: ``const AD`` < *Base* >& *left* Example ******* {xrst_toc_hidden example/general/vec_ad.cpp } The file :ref:`vec_ad.cpp-name` contains an example and test using ``VecAD`` vectors. base2ad ******* Forward mode on a ``base2ad`` function does not preserve :ref:`VecAD-name` operations (which might be expected); see the :ref:`base2vec_ad.cpp-name` example. Speed and Memory **************** The :ref:`VecAD-name` vector type is inefficient because every time an element of a vector is accessed, a new CppAD :ref:`glossary@Variable` is created on the tape using either the ``Ldp`` or ``Ldv`` operation (unless all of the elements of the vector are :ref:`parameters` ). The effect of this can be seen by executing the following steps: #. In the file ``cppad/local/forward1sweep.h`` , change the definition of ``CPPAD_FORWARD1SWEEP_TRACE`` to :: # define CPPAD_FORWARD1SWEEP_TRACE 1 #. In the ``Example`` directory, execute the command :: ./test_one.sh lu_vec_ad_ok.cpp lu_vec_ad.cpp -DNDEBUG > lu_vec_ad_ok.log This will write a trace of all the forward tape operations, for the test case :ref:`lu_vec_ad_ok.cpp-name` , to the file ``lu_vec_ad_ok.log`` . #. In the ``Example`` directory execute the commands :: grep "op=" lu_vec_ad_ok.log | wc -l grep "op=Ld[vp]" lu_vec_ad_ok.log | wc -l grep "op=St[vp][vp]" lu_vec_ad_ok.log | wc -l The first command counts the number of operators in the tracing, the second counts the number of VecAD load operations, and the third counts the number of VecAD store operations. (For CppAD version 05-11-20 these counts were 956, 348, and 118 respectively.) {xrst_end VecAD} ------------------------------------------------------------------------ */ ================================================ FILE: include/cppad/core/vec_ad/vec_ad.hpp ================================================ # ifndef CPPAD_CORE_VEC_AD_VEC_AD_HPP # define CPPAD_CORE_VEC_AD_VEC_AD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /* {xrst_begin_parent dev_vec_ad dev} Developer Documentation for VecAD Operations ############################################ Contents ******** {xrst_toc_table } {xrst_end dev_vec_ad} ------------------------------------------------------------------------------ {xrst_begin vec_ad_comp_assign dev} VecAD: Prints Error Message If A Compound Assignment Is Used ############################################################ Syntax ****** | ``CPPAD_VEC_AD_COMP_ASSIGN`` ( *cop* ) | ``ref cop right`` CPPAD_VEC_AD_COMP_ASSIGN ************************ This macro defines the compound assignment operator *cop* for a VecAD reference element to be an error with an error message. cop *** Is one of the following computed assignment operators: += , -= , \*= , /=. ref *** is the VecAD reference. right ***** is the right hand side for the compound assignment. Source ****** {xrst_spell_off} {xrst_code hpp} */ # define CPPAD_VEC_AD_COMP_ASSIGN(cop) \ VecAD_reference& operator cop (const VecAD_reference &right) \ { CPPAD_ASSERT_KNOWN(false, \ "Can't use VecAD::reference on left side of " #cop \ ); \ return *this; \ } \ VecAD_reference& operator cop (const AD &right) \ { CPPAD_ASSERT_KNOWN(false, \ "Can't use VecAD::reference on left side of " #cop \ ); \ return *this; \ } \ VecAD_reference& operator cop (const Base &right) \ { CPPAD_ASSERT_KNOWN(false, \ "Can't use VecAD::reference on left side of " #cop \ ); \ return *this; \ } \ VecAD_reference& operator cop (int right) \ { CPPAD_ASSERT_KNOWN(false, \ "Can't use VecAD::reference on left side of " #cop \ ); \ return *this; \ } /* {xrst_code} {xrst_spell_on} {xrst_end vec_ad_comp_assign} ------------------------------------------------------------------------------ {xrst_begin vec_ad_reference dev} VecAD Element Reference Class ############################# Syntax ****** | ``VecAD_reverence`` *ref* ( *vec* , *ind* ) | *ref* = *right* | ``ref cop right`` | ``element`` = ``ref`` . *ADBase* () Member Variables **************** vec\_ ===== This private data is a reference to *vec* in the constructor. ind\_ ===== This private data is a copy of *ind* in the constructor. Base **** Elements of this reference class act like an ``AD`` < *Base* > object (in a restricted sense), in addition they track (on the tape) the index *ind* they correspond to. vec *** is the vector containing the element being referenced and has prototype ``VecAD`` < *Base* > *vec* ind *** is the index of the element being referenced and has prototype ``const AD`` < *Base* >& ``ind`` If *ind* . ``tape_id_`` matches a current recording, so does *vec* . ``tape_id_`` and the :ref:`AD type` corresponding to *vec* includes this indexing operation; i.e., it is greater than or equal the AD type corresponding to *ind* . right ***** Is the right hand side of the assignment statement and has one of the following prototypes: | |tab| ``int`` *right* | |tab| ``const`` *Base* & *right* | |tab| ``const AD`` < *Base* >& *right* | |tab| ``const VecAD_reverence`` < *Base* >& *right* cop *** Is one of the following computed assignment operators: += , -= , \*= , /=. All of these operations report an error. element ******* Is a copy of the element corresponding to the reference *ref* and has prototype: ``AD`` < *Base* > *element* {xrst_end vec_ad_reference} */ template class VecAD_reference { friend bool Constant (const VecAD &vec); friend bool Parameter (const VecAD &vec); friend bool Dynamic (const VecAD &vec); friend bool Variable (const VecAD &vec); friend class VecAD; friend class local::ADTape; private: VecAD& vec_; // reverence to vector AD ind_; // index for this element public: VecAD_reference(VecAD& vec, const AD& ind) : vec_( vec ) , ind_(ind) { } // assignment operators void operator = (const VecAD_reference &right); void operator = (const AD &right); void operator = (const Base &right); void operator = (int right); // compound assignments CPPAD_VEC_AD_COMP_ASSIGN( += ) CPPAD_VEC_AD_COMP_ASSIGN( -= ) CPPAD_VEC_AD_COMP_ASSIGN( *= ) CPPAD_VEC_AD_COMP_ASSIGN( /= ) /// Conversion from VecAD_reference to AD. /// puts the correspond vecad load instruction in the tape. AD ADBase(void) const { // start with default constructor (hence dynamic_ is false). AD result; size_t i = static_cast( Integer(ind_) ); CPPAD_ASSERT_UNKNOWN( i < vec_.length_ ); // AD value corresponding to this element result.value_ = vec_.data_[i]; CPPAD_ASSERT_UNKNOWN( Constant(result) ); // check if there is a recording in progress local::ADTape* tape = AD::tape_ptr(); if( tape == nullptr ) return result; // tape_id cannot match the default value zero CPPAD_ASSERT_UNKNOWN( tape->id_ > 0 ); // check if vector, index match tape_id bool match_vec = vec_.tape_id_ == tape->id_; bool match_ind = ind_.tape_id_ == tape->id_; // check if vector, index are dynamic parmaerters CPPAD_ASSERT_UNKNOWN( vec_.ad_type_ != dynamic_enum); bool dyn_ind = match_ind & (ind_.ad_type_ == dynamic_enum); // check if vector, index are variables bool var_vec = match_vec & (vec_.ad_type_ == variable_enum); bool var_ind = match_ind & (ind_.ad_type_ == variable_enum); // check if vector, index are constants bool con_vec = ! var_vec; bool con_ind = ! ( dyn_ind | var_ind); if( con_vec & con_ind ) return result; # ifndef NDEBUG if( match_vec & match_ind ) CPPAD_ASSERT_KNOWN( vec_.tape_id_ == ind_.tape_id_ , "VecAD: vector and index are dynamic parameters or variables " "on different treads." ); # endif // parameter or variable index corresponding to ind_ addr_t ind_taddr = ind_.taddr_; if( con_ind ) ind_taddr = tape->Rec_.put_con_par(ind_.value_); // index corresponding to this element CPPAD_ASSERT_UNKNOWN( var_vec ); { CPPAD_ASSERT_UNKNOWN( vec_.offset_ > 0 ); size_t load_op_index = tape->Rec_.num_var_load(); // if( var_ind ) { CPPAD_ASSERT_UNKNOWN( local::NumRes(local::LdvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::LdvOp) == 3 ); // put operand addresses in tape, ind_ is a variable result.taddr_ = tape->Rec_.PutLoadOp(local::LdvOp); tape->Rec_.PutArg( (addr_t) vec_.offset_, ind_taddr, (addr_t) load_op_index ); // change result to variable for this load result.tape_id_ = tape->id_; result.ad_type_ = variable_enum; } else { CPPAD_ASSERT_UNKNOWN( local::NumRes(local::LdpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::LdpOp) == 3 ); CPPAD_ASSERT_UNKNOWN( con_ind | dyn_ind ); // put operand addresses in tape tape->Rec_.PutArg( (addr_t) vec_.offset_, ind_taddr, (addr_t) load_op_index ); // put operator in the tape, ind_ is a parameter result.taddr_ = tape->Rec_.PutLoadOp(local::LdpOp); // change result to variable for this load result.tape_id_ = tape->id_; result.ad_type_ = variable_enum; } } return result; } }; // --------------------------------------------------------------------------- /*! {xrst_begin vec_ad_class dev} {xrst_spell taddr } VecAD Class Objects ################### Syntax ****** | ``VecAD`` *empty* , *vec* ( *length* ) | *length* = *vec* . ``size`` () | *b* = *vec* [ *i* ] | *ref* = *vec* [ *ind* ] length ****** is the size of the vector and has prototype ``size_t`` *length* Private Members *************** {xrst_literal // BEGIN_VECAD_PRIVATE_DATA // END_VECAD_PRIVATE_DATA } length\_ ======== is a copy of *length* . data\_ ====== This vector has size *length* and contains the value of the elements of the vector. taddr\_ ======= This vector has size *length* . If ``tape_id_`` matches the current recording and ``ad_type_`` is ``dynamic_enum`` , ``taddr`` [ *i* ] is the parameter index for the corresponding element. offset\_ ======== If *tape_id_* is the current tape, *offset_* is the index of the first element of this vector in the combined vector that contains all the VecAD elements for this recording. *offset_* ``-1`` is the index of the size of this vector in the combined vector. tape_id\_ ========= is the tape currently associated with this vector. ad_type\_ ========= is the :ref:`atomic_three_define@ad_type` corresponding to this vector. i * is a ``size_t`` value less than *length* . This form of indexing can only be used when *vec* is a constant parameter; i.e., its operations are not being recorded. b * is a reference to the *Base* value for the *i*-th element of the vector. ind *** is a ``AD`` < *Base* > value less than *length* . This form of indexing gets recorded and the value of the index can change. ref *** is a reference to the ``AD`` < *Base* > value for the *x*-th element of the vector. If the vector is a parameter and the index is a variable, the vector is changed to be a variable. {xrst_end vec_ad_class} */ template class VecAD { friend bool Constant (const VecAD &vec); friend bool Parameter (const VecAD &vec); friend bool Dynamic (const VecAD &vec); friend bool Variable (const VecAD &vec); friend class local::ADTape; friend class VecAD_reference; friend std::ostream& operator << (std::ostream &os, const VecAD &vec_); private: // BEGIN_VECAD_PRIVATE_DATA const size_t length_; local::pod_vector_maybe data_; local::pod_vector taddr_; tape_id_t tape_id_; addr_t offset_; ad_type_enum ad_type_; // END_VECAD_PRIVATE_DATA public: // declare the user's view of this type here typedef VecAD_reference reference; // default constructor // initialize tape_id_ same as for default constructor; see default.hpp VecAD(void) : length_(0), tape_id_(0), offset_(0), ad_type_(constant_enum) { CPPAD_ASSERT_UNKNOWN( Constant(*this) ); } // sizing constructor // initialize tape_id_ same as for constants; see ad_copy.hpp VecAD(size_t length) : length_(length), tape_id_(0), offset_(0), ad_type_(constant_enum) { if( length_ > 0 ) { size_t i; Base zero(0); data_.extend(length_); taddr_.extend(length_); // Initialize data to zero so all have same value. // This uses less memory and avoids a valgrind error // during TapeRec::PutPar for(i = 0; i < length_; i++) { data_[i] = zero; taddr_[i] = 0; } } CPPAD_ASSERT_UNKNOWN( Constant(*this) ); } // destructor ~VecAD(void) { } // number of elements in the vector size_t size(void) { return length_; } // element access (not taped) Base& operator[](size_t i) { CPPAD_ASSERT_KNOWN( Constant(*this), "VecAD: cannot use size_t indexing because this" " VecAD vector is not a constant parameter." ); CPPAD_ASSERT_KNOWN( i < length_, "VecAD: element index is >= vector length" ); return data_[i]; } // element access (taped) VecAD_reference operator[](const AD &ind) { CPPAD_ASSERT_KNOWN( 0 <= Integer(ind), "VecAD: element index is less than zero" ); CPPAD_ASSERT_KNOWN( static_cast( Integer(ind) ) < length_, "VecAD: element index is >= vector length" ); // check if there is a recording in progress local::ADTape* tape = AD::tape_ptr(); if( tape == nullptr ) return VecAD_reference(*this, ind); // tape_id cannot match the default value zero CPPAD_ASSERT_UNKNOWN( tape->id_ > 0 ); // check if vector, index match tape_id bool match_vec = tape_id_ == tape->id_; bool match_ind = ind.tape_id_ == tape->id_; // check if vector, index are dynamic parmaerters CPPAD_ASSERT_UNKNOWN( ad_type_ != dynamic_enum ); bool dyn_ind = match_ind & (ind.ad_type_ == dynamic_enum); // check if vector, index are variables bool var_vec = match_vec & (ad_type_ == variable_enum); bool var_ind = match_ind & (ind.ad_type_ == variable_enum); // check if vector, index are constants bool con_vec = ! var_vec; bool con_ind = ! ( dyn_ind | var_ind); if( con_vec & con_ind ) return VecAD_reference(*this, ind); # ifndef NDEBUG if( match_vec & match_ind ) CPPAD_ASSERT_KNOWN( tape_id_ == ind.tape_id_ , "VecAD: vector and index are dynamic parameters or variables " "on different treads." ); # endif if( con_vec ) { // place a copy of vector in tape for(size_t i = 0; i < length_; ++i) taddr_[i] = tape->Rec_.put_con_par( data_[i] ); offset_ = tape->Rec_.put_var_vecad(length_, taddr_); // Advance pointer by one so starts at first component of this // vector; i.e., skip length at beginning (so is always > 0) offset_++; // tape_id corresponding to this vector tape_id_ = ind.tape_id_; // VecAD objects go straight from constants to variables; i.e., // they never are dynamic parameters. ad_type_ = variable_enum; } CPPAD_ASSERT_UNKNOWN( Variable(*this) ); return VecAD_reference(*this, ind); } }; // --------------------------------------------------------------------------- // ref = right template void VecAD_reference::operator=(const AD &right) { // index in vector for this element size_t index = static_cast( Integer(ind_) ); CPPAD_ASSERT_UNKNOWN( index < vec_.length_ ); // Base part of assignment for this element vec_.data_[index] = right.value_; // check if there is a recording in progress local::ADTape* tape = AD::tape_ptr(); if( tape == nullptr ) return; // tape_id cannot match the default value zero tape_id_t tape_id = tape->id_; CPPAD_ASSERT_UNKNOWN( tape_id > 0 ); // check if vector, index, right match tape_id bool match_vec = vec_.tape_id_ == tape_id; bool match_ind = ind_.tape_id_ == tape_id; bool match_right = right.tape_id_ == tape_id; CPPAD_ASSERT_UNKNOWN( match_vec || ! match_ind ); // check if vector, index, right are dynamic parmaerters CPPAD_ASSERT_UNKNOWN(vec_.ad_type_ != dynamic_enum); bool dyn_ind = match_ind & (ind_.ad_type_ == dynamic_enum); bool dyn_right = match_right & (right.ad_type_ == dynamic_enum); // check if vector, index, right are variables bool var_vec = match_vec & (vec_.ad_type_ == variable_enum); bool var_ind = match_ind & (ind_.ad_type_ == variable_enum); bool var_right = match_right & (right.ad_type_ == variable_enum); // check if vector, index, right are constants bool con_vec = ! var_vec; bool con_ind = ! ( dyn_ind | var_ind); bool con_right = ! ( dyn_right | var_right); if( con_vec & con_right ) return; # ifndef NDEBUG if( match_ind ) { CPPAD_ASSERT_UNKNOWN( ind_.tape_id_ == vec_.tape_id_ ); CPPAD_ASSERT_UNKNOWN( ind_.ad_type_ <= vec_.ad_type_ ); } if( match_vec & match_right ) CPPAD_ASSERT_KNOWN( vec_.tape_id_ == right.tape_id_ , "VecAD: vector and element are dynamic parameters or variables " "on different treads." ); # endif if( con_vec ) { CPPAD_ASSERT_UNKNOWN( con_ind ); // place a copy of vector in tape for(size_t i = 0; i < vec_.length_; ++i) vec_.taddr_[i] = tape->Rec_.put_con_par( vec_.data_[i] ); vec_.offset_ = tape->Rec_.put_var_vecad(vec_.length_, vec_.taddr_); // advance offset from size of vector to first element in vector (vec_.offset_)++; // tape_id corresponding to this vector vec_.tape_id_ = right.tape_id_; // VecAD objects go straight from constants to variables; i.e., // they never are dynamic parameters. vec_.ad_type_ = variable_enum; } CPPAD_ASSERT_UNKNOWN( Variable(vec_) ); CPPAD_ASSERT_UNKNOWN( vec_.offset_ > 0 ); // parameter or variable index for ind_ addr_t ind_taddr = ind_.taddr_; if( con_ind ) ind_taddr = tape->Rec_.put_con_par(ind_.value_); CPPAD_ASSERT_UNKNOWN( ind_taddr > 0 ); // parameter or variable index for right addr_t right_taddr = right.taddr_; if( con_right ) right_taddr = tape->Rec_.put_con_par(right.value_); CPPAD_ASSERT_UNKNOWN( right_taddr > 0 ); // record the setting of this array element if( var_right ) { // resulting vector is a variable vec_.ad_type_ = variable_enum; // put operator arguments in tape tape->Rec_.PutArg(vec_.offset_, ind_taddr, right_taddr); if( con_ind | dyn_ind) { CPPAD_ASSERT_UNKNOWN( local::NumArg(local::StpvOp) == 3 ); CPPAD_ASSERT_UNKNOWN( local::NumRes(local::StpvOp) == 0 ); // put operator in the tape, ind_ is parameter, right is variable tape->Rec_.PutOp(local::StpvOp); } else { CPPAD_ASSERT_UNKNOWN( var_ind ); CPPAD_ASSERT_UNKNOWN( local::NumArg(local::StvvOp) == 3 ); CPPAD_ASSERT_UNKNOWN( local::NumRes(local::StvvOp) == 0 ); // put operator in the tape, ind_ is variable, right is variable tape->Rec_.PutOp(local::StvvOp); } } else { // put operator arguments in tape tape->Rec_.PutArg(vec_.offset_, ind_taddr, right_taddr); // record the setting of this array element if( con_ind | dyn_ind ) { CPPAD_ASSERT_UNKNOWN( local::NumArg(local::StppOp) == 3 ); CPPAD_ASSERT_UNKNOWN( local::NumRes(local::StppOp) == 0 ); // put operator in the tape, ind_ is parameter, right is parameter tape->Rec_.PutOp(local::StppOp); } else { CPPAD_ASSERT_UNKNOWN( local::NumArg(local::StvpOp) == 3 ); CPPAD_ASSERT_UNKNOWN( local::NumRes(local::StvpOp) == 0 ); // put operator in the tape, ind_ is variable, right is parameter tape->Rec_.PutOp(local::StvpOp); } } } template void VecAD_reference::operator=(const Base &right) { *this = AD(right); } // template void VecAD_reference::operator= (const VecAD_reference &right) { *this = right.ADBase(); } // template void VecAD_reference::operator=(int right) { *this = Base(right); } // --------------------------------------------------------------------------- } // END_CPPAD_NAMESPACE // preprocessor symbols that are local to this file # undef CPPAD_VEC_AD_COMP_ASSIGN # endif ================================================ FILE: include/cppad/core/zdouble.hpp ================================================ # ifndef CPPAD_CORE_ZDOUBLE_HPP # define CPPAD_CORE_ZDOUBLE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin zdouble app} zdouble: An AD Base Type With Absolute Zero ########################################### Deprecated 2015-09-26 ********************* Use the function :ref:`azmul-name` instead. Absolute Zero ************* The ``zdouble`` class acts like the ``double`` type with the added property that zero times any value is zero. This includes zero time :ref:`nan-name` and zero times infinity. In addition, zero divided by any value and any value times zero are also zero. Syntax ****** Constructor and Assignment ========================== | ``zdouble z`` | ``zdouble z`` ( ``x`` ) | ``zdouble z`` ( ``i`` ) | *z1* *op* *x* were *i* is a ``size_t`` or *long* or ``int`` , *x* is a ``double`` or ``zdouble`` , and *op* is ``=`` or ``+=`` or ``-=`` or ``*=`` or ``/=-`` . Comparison Operators ==================== | *b* = *z* *op* *x* | *b* = *x* *op* *z* where *b* is a ``bool`` object, *z* is a ``zdouble`` object, *x* is a ``double`` or ``zdouble`` object, and *op* is ``==`` , ``!=`` , ``<=`` , ``>=`` , ``<`` or ``>`` . Arithmetic Operators ==================== | *z2* = *z1* *op* *x* | *z2* = *x* *op* *z1* where *z1* , *z2* are ``zdouble`` objects, *x* is a ``double`` or ``zdouble`` object, and *op* is ``+`` , ``-`` , ``*`` or ``/`` . Standard Math ============= | *z2* = *fun* ( *z1* ) | *z3* = ``pow`` ( *z1* , *z2* ) where *z1* , *z2* , *z3* are ``zdouble`` objects and *fun* is a :ref:`unary_standard_math-name` function. Nan === There is a specialization of :ref:`nan-name` so that ``z2`` = *nan* ( ``z1`` ) returns 'not a number' when *z1* has type ``zdouble`` . Note that this template function needs to be specialized because ``zdouble`` (0.0) == ``zdouble`` (0.0) / ``zdouble`` (0.0) Motivation ********** General ======= Often during computing (and more so in parallel computing) alternative values for an expression are computed and one of the alternatives is chosen using some boolean variable. This is often represented by *result* = *flag* * *value_if_true* + (1 ``-`` *flag* ) * *value_if_false* where *flag* is one for true and zero for false. This representation does not work for ``double`` when the value being multiplied by zero is ``+inf`` , ``-inf`` , or ``nan`` . CppAD ===== In CppAD one can use :ref:`conditional expressions` to achieve the representation *result* = *flag* * *value_if_true* + (1 ``-`` *flag* ) * *value_if_false* This works fine except when there are :ref:`multiple levels of AD` ; e.g., when using ``AD< AD >`` . In this case the corresponding AD function objects have type :ref:`ADFun\< AD\ >` . When these AD function objects compute derivatives using :ref:`reverse-name` mode, the conditional expressions are represented use zeros to multiply the expression that is not used. Using ``AD< AD >`` instead of ``AD< AD >`` makes this representation work and fixes the problem. Base Type Requirements ********************** The type ``zdouble`` satisfies all of the CppAD :ref:`base type requirements` . {xrst_end zdouble} */ # include # include /*! \file zdouble.hpp Define a class like double but with an absolute zero. */ /*! \def CPPAD_ZDOUBLE_NORMAL_ASSIGN_OPERATOR(op) Define a compound assignment member operator that functions the same as corresponding double operator. */ # define CPPAD_ZDOUBLE_NORMAL_ASSIGN_OPERATOR(op) \ zdouble& operator op (const zdouble& z) \ { dbl_ op z.dbl_; \ return *this; \ } \ zdouble& operator op (const double& x) \ { dbl_ op x; \ return *this; \ } /*! \def CPPAD_ZDOUBLE_UNARY_OPERATOR(op) Define a unary compound assignment member operator. */ # define CPPAD_ZDOUBLE_UNARY_OPERATOR(op) \ zdouble operator op (void) const \ { return zdouble( op dbl_ ); } /*! # define CPPAD_ZDOUBLE_NORMAL_BINARY_OPERATOR(op) Define a binary arithmetic member operator that functions the same as corresponding double operator. */ # define CPPAD_ZDOUBLE_NORMAL_BINARY_OPERATOR(op) \ zdouble operator op (const zdouble& z) const \ { return zdouble( dbl_ op z.dbl_ ); } \ zdouble operator op (const double& x) const \ { return zdouble( dbl_ op x ); } /*! \def CPPAD_ZDOUBLE_COMPARE_OPERATOR(op) Define a comparison member operator. */ # define CPPAD_ZDOUBLE_COMPARE_OPERATOR(op) \ bool operator op (const zdouble& z) const \ { return dbl_ op z.dbl_; } \ bool operator op (const double& x) const \ { return dbl_ op x; } /*! \def CPPAD_ZDOUBLE_OTHER_BINARY_OPERATOR(op) Define a binary arithmetic operator that is not a member because the double operand is on the left. */ # define CPPAD_ZDOUBLE_OTHER_BINARY_OPERATOR(op) \ inline zdouble operator op(const double& x, const zdouble& z) \ { return zdouble(x) op z; } /*! \def CPPAD_ZDOUBLE_OTHER_COMPARE_OPERATOR(op, op_switch) Define a comparison operator that is not a member because the double operand is on the left. Convert it to the case where the double operand is on the right by by using op_switch instead of op. */ # define CPPAD_ZDOUBLE_OTHER_COMPARE_OPERATOR(op, op_switch) \ inline bool operator op(const double& x, const zdouble& z) \ { return z op_switch x; } /*! \def CPPAD_ZDOUBLE_STD_MATH_FRIEND(fun) Declare that a standard math function is a friend. */ # define CPPAD_ZDOUBLE_STD_MATH_FRIEND(fun) \ friend zdouble fun(const zdouble& z); /*! \def CPPAD_ZDOUBLE_STD_MATH(fun) Define a standard math function. */ # define CPPAD_ZDOUBLE_STD_MATH(fun) \ inline zdouble fun(const zdouble& z ) \ { return zdouble( std::fun(z.dbl_) ); } namespace CppAD { // CPPAD_BEGIN_NAMESPACDE /*! Class that is like double, except that it has an absolute zero. */ class zdouble { /*! For zdouble objects z1, z2, and std::ostream os, declare the following friends: \code os << z1 Integer(z1) fabs(z1) pow(z1, z2) fabs_geq(z1, z2) fun(z1) \endcode where fun is any of the standard math unary functions. */ friend std::ostream& operator << (std::ostream &os, const zdouble& z); friend int Integer(const zdouble& z); friend zdouble pow(const zdouble& x, const zdouble& y); friend bool abs_geq(const zdouble& x, const zdouble& y); // CPPAD_ZDOUBLE_STD_MATH_FRIEND(acos) CPPAD_ZDOUBLE_STD_MATH_FRIEND(asin) CPPAD_ZDOUBLE_STD_MATH_FRIEND(atan) CPPAD_ZDOUBLE_STD_MATH_FRIEND(cos) CPPAD_ZDOUBLE_STD_MATH_FRIEND(cosh) CPPAD_ZDOUBLE_STD_MATH_FRIEND(exp) CPPAD_ZDOUBLE_STD_MATH_FRIEND(fabs) CPPAD_ZDOUBLE_STD_MATH_FRIEND(log) CPPAD_ZDOUBLE_STD_MATH_FRIEND(log10) CPPAD_ZDOUBLE_STD_MATH_FRIEND(sin) CPPAD_ZDOUBLE_STD_MATH_FRIEND(sinh) CPPAD_ZDOUBLE_STD_MATH_FRIEND(sqrt) CPPAD_ZDOUBLE_STD_MATH_FRIEND(tan) CPPAD_ZDOUBLE_STD_MATH_FRIEND(tanh) // CPPAD_ZDOUBLE_STD_MATH_FRIEND(asinh) CPPAD_ZDOUBLE_STD_MATH_FRIEND(acosh) CPPAD_ZDOUBLE_STD_MATH_FRIEND(atanh) CPPAD_ZDOUBLE_STD_MATH_FRIEND(erf) CPPAD_ZDOUBLE_STD_MATH_FRIEND(erfc) CPPAD_ZDOUBLE_STD_MATH_FRIEND(expm1) CPPAD_ZDOUBLE_STD_MATH_FRIEND(log1p) // private: /// The value for this object double dbl_; public: /// Default constructor zdouble(void) : dbl_() { } /// Copy constructor zdouble(const zdouble& z) : dbl_(z.dbl_) { } /// Constructor from double zdouble(const double& dbl) : dbl_(dbl) { } /// Constructor from size_t zdouble(const size_t& i) : dbl_( double(i) ) { } /// Constructor from long zdouble(const long& i) : dbl_( double(i) ) { } /// Constructor from int zdouble(const int& i) : dbl_( double(i) ) { } // /// Destructor ~zdouble(void) { } // /// Assignment from zdouble zdouble& operator=(const zdouble& z) { dbl_ = z.dbl_; return *this; } /// Assignment from double zdouble& operator=(const double& dbl) { dbl_ = dbl; return *this; } // /// Normal compound assignment CPPAD_ZDOUBLE_NORMAL_ASSIGN_OPERATOR(+=) /// Normal compound assignment CPPAD_ZDOUBLE_NORMAL_ASSIGN_OPERATOR(-=) /// Normal unary operator CPPAD_ZDOUBLE_UNARY_OPERATOR(+) /// Normal unary operator CPPAD_ZDOUBLE_UNARY_OPERATOR(-) /// Normal compare operator CPPAD_ZDOUBLE_COMPARE_OPERATOR(==) /// Normal compare operator CPPAD_ZDOUBLE_COMPARE_OPERATOR(!=) /// Normal compare operator CPPAD_ZDOUBLE_COMPARE_OPERATOR(<=) /// Normal compare operator CPPAD_ZDOUBLE_COMPARE_OPERATOR(>=) /// Normal compare operator CPPAD_ZDOUBLE_COMPARE_OPERATOR(<) /// Normal compare operator CPPAD_ZDOUBLE_COMPARE_OPERATOR(>) // /// Normal binary arithmetic operator CPPAD_ZDOUBLE_NORMAL_BINARY_OPERATOR(+) /// Normal binary arithmetic operator CPPAD_ZDOUBLE_NORMAL_BINARY_OPERATOR(-) // /// Binary arithmetic * with absolute zero zdouble operator * (const zdouble& z) const { bool zero = (dbl_ == 0.0) || (z.dbl_ == 0.0); return zdouble( zero ? 0.0 : (dbl_ * z.dbl_) ); } /// Binary arithmetic * with absolute zero zdouble operator * (const double& x) const { bool zero = (dbl_ == 0.0) || (x == 0.0); return zdouble( zero ? 0.0 : (dbl_ * x) ); } /// Binary arithmetic / with absolute zero zdouble operator / (const zdouble& z) const { bool zero = (dbl_ == 0.0); return zdouble( zero ? 0.0 : (dbl_ / z.dbl_) ); } /// Binary arithmetic / with absolute zero zdouble operator / (const double& x) const { bool zero = (dbl_ == 0.0); return zdouble( zero ? 0.0 : (dbl_ / x) ); } // /// Compute assignment *= with absolute zero zdouble& operator *= (const zdouble& z) { bool zero = (dbl_ == 0.0) || (z.dbl_ == 0.0); zero ? (dbl_ = 0.0) : (dbl_ *= z.dbl_); return *this; } /// Compute assignment *= with absolute zero zdouble& operator *= (const double& x) { bool zero = (dbl_ == 0.0) || (x == 0.0); zero ? (dbl_ = 0.0) : (dbl_ *= x); return *this; } // /// Compute assignment /= with absolute zero zdouble& operator /= (const zdouble& z) { bool zero = (dbl_ == 0.0); zero ? (dbl_ = 0.0) : (dbl_ /= z.dbl_); return *this; } /// Compute assignment /= with absolute zero zdouble& operator /= (const double& x) { bool zero = (dbl_ == 0.0); zero ? (dbl_ = 0.0) : (dbl_ /= x); return *this; } }; // BEGIN nan /// Must specialize CppAD::nan because zdouble 0/0 is not nan. template <> inline zdouble nan(const zdouble& zero) { return zdouble( std::numeric_limits::quiet_NaN() ); } // END nan // /// Normal non-member compare operator CPPAD_ZDOUBLE_OTHER_COMPARE_OPERATOR(==, ==) /// Normal non-member compare operator CPPAD_ZDOUBLE_OTHER_COMPARE_OPERATOR(!=, !=) /// Normal non-member compare operator CPPAD_ZDOUBLE_OTHER_COMPARE_OPERATOR(<=, >=) /// Normal non-member compare operator CPPAD_ZDOUBLE_OTHER_COMPARE_OPERATOR(>=, <=) /// Normal non-member compare operator CPPAD_ZDOUBLE_OTHER_COMPARE_OPERATOR(<, >) /// Normal non-member compare operator CPPAD_ZDOUBLE_OTHER_COMPARE_OPERATOR(>, <) // /// Normal binary arithmetic operator CPPAD_ZDOUBLE_OTHER_BINARY_OPERATOR(+) /// Normal binary arithmetic operator CPPAD_ZDOUBLE_OTHER_BINARY_OPERATOR(-) /// Binary arithmetic operator with absolute zero CPPAD_ZDOUBLE_OTHER_BINARY_OPERATOR(*) /// Binary arithmetic operator with absolute zero CPPAD_ZDOUBLE_OTHER_BINARY_OPERATOR(/) // ------------------------------------------------------------------------- // Base type requirements // ------------------------------------------------------------------------- /// Base type requirement: CondExpOp inline zdouble CondExpOp( enum CompareOp cop , const zdouble& left , const zdouble& right , const zdouble& exp_if_true , const zdouble& exp_if_false ) { return CondExpTemplate(cop, left, right, exp_if_true, exp_if_false); } /// Base type requirement: CondExpRel CPPAD_COND_EXP_REL(zdouble) /// Base type requirement: EqualOpSeq inline bool EqualOpSeq(const zdouble& x, const zdouble& y) { return x == y; } /// Base type requirement: Identical inline bool IdenticalCon(const zdouble& x) { return true; } inline bool IdenticalZero(const zdouble& x) { return (x == 0.0); } inline bool IdenticalOne(const zdouble& x) { return (x == 1.); } inline bool IdenticalEqualCon(const zdouble& x, const zdouble& y) { return (x == y); } /// Base type requirement: output operator inline std::ostream& operator << (std::ostream &os, const zdouble& z) { os << z.dbl_; return os; } /// Base type requirement: Integer inline int Integer(const zdouble& x) { return static_cast(x.dbl_); } /// Base type requirement: azmul inline zdouble azmul(const zdouble& x, const zdouble& y) { return x * y; } /// Base type requirement: Ordered inline bool GreaterThanZero(const zdouble& x) { return x > 0.0; } inline bool GreaterThanOrZero(const zdouble& x) { return x >= 0.0; } inline bool LessThanZero(const zdouble& x) { return x < 0.0; } inline bool LessThanOrZero(const zdouble& x) { return x <= 0.0; } inline bool abs_geq(const zdouble& x, const zdouble& y) { return std::fabs(x.dbl_) >= std::fabs(y.dbl_); } /// Normal standard math function CPPAD_ZDOUBLE_STD_MATH(acos) /// Normal standard math function CPPAD_ZDOUBLE_STD_MATH(asin) /// Normal standard math function CPPAD_ZDOUBLE_STD_MATH(atan) /// Normal standard math function CPPAD_ZDOUBLE_STD_MATH(cos) /// Normal standard math function CPPAD_ZDOUBLE_STD_MATH(cosh) /// Normal standard math function CPPAD_ZDOUBLE_STD_MATH(exp) /// Normal standard math function CPPAD_ZDOUBLE_STD_MATH(fabs) /// Normal standard math function CPPAD_ZDOUBLE_STD_MATH(log) /// Normal standard math function CPPAD_ZDOUBLE_STD_MATH(log10) /// Normal standard math function CPPAD_ZDOUBLE_STD_MATH(sin) /// Normal standard math function CPPAD_ZDOUBLE_STD_MATH(sinh) /// Normal standard math function CPPAD_ZDOUBLE_STD_MATH(sqrt) /// Normal standard math function CPPAD_ZDOUBLE_STD_MATH(tan) /// Normal standard math function CPPAD_ZDOUBLE_STD_MATH(tanh) // /// C++2011 standard math function CPPAD_ZDOUBLE_STD_MATH(asinh) /// C++2011 standard math function CPPAD_ZDOUBLE_STD_MATH(acosh) /// C++2011 standard math function CPPAD_ZDOUBLE_STD_MATH(atanh) /// C++2011 standard math function CPPAD_ZDOUBLE_STD_MATH(erf) /// C++2011 standard math function CPPAD_ZDOUBLE_STD_MATH(erfc) /// C++2011 standard math function CPPAD_ZDOUBLE_STD_MATH(expm1) /// C++2011 standard math function CPPAD_ZDOUBLE_STD_MATH(log1p) /// Base type requirement: abs inline zdouble abs(const zdouble& x) { return fabs(x); } /// Base type requirement: sign inline zdouble sign(const zdouble& x) { if( x > 0.0 ) return zdouble(1.); if( x == 0.0 ) return zdouble(0.0); return zdouble(-1.); } /// Base type requirement: pow inline zdouble pow(const zdouble& x, const zdouble& y) { return std::pow(x.dbl_, y.dbl_); } /// Base type requirement: limits CPPAD_NUMERIC_LIMITS(double, zdouble) } // CPPAD_END_NAMESPACE /// undef all macros defined in this file # undef CPPAD_ZDOUBLE_NORMAL_ASSIGN_OPERATOR # undef CPPAD_ZDOUBLE_UNARY_OPERATOR # undef CPPAD_ZDOUBLE_NORMAL_BINARY_OPERATOR # undef CPPAD_ZDOUBLE_COMPARE_OPERATOR # undef CPPAD_ZDOUBLE_OTHER_BINARY_OPERATOR # undef CPPAD_ZDOUBLE_OTHER_COMPARE_OPERATOR # undef CPPAD_ZDOUBLE_STD_MATH_FRIEND # undef CPPAD_ZDOUBLE_STD_MATH # endif ================================================ FILE: include/cppad/cppad.hpp ================================================ # ifndef CPPAD_CPPAD_HPP # define CPPAD_CPPAD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /*! \file cppad.hpp \brief includes the entire CppAD package in the necessary order. \namespace CppAD \brief contains all the variables and functions defined by the CppAD package. */ # include // all base type requirements // --------------------------------------------------------------------------- // CppAD general purpose library routines (can be included separately) # include // -------------------------------------------------------------------------- // System routines that can be used by rest of CppAD with out including # include # include # include # include // --------------------------------------------------------------------------- // definitions needed by rest of includes // definitions that come from the installation # include // definitions that are local to the CppAD include files # include // vectors used with CppAD # include // Declare classes and functions that are used before defined # include // --------------------------------------------------------------------------- // declare the AD template class # include // --------------------------------------------------------------------------- # include // AD class methods available to the user // tape that tape for AD acts as a user of Base operations // so user_ad.hpp must come before op.hpp # include // executes taped operations # include // ADFun objects // // Putting these includes in ad_fun.hpp leads to circular including; i.e, // a file needs to include itself and the include guard stops it. # include # include // --------------------------------------------------------------------------- // library routines that require the rest of CppAD # include # include # include # include # include # if CPPAD_HAS_IPOPT # include # endif // undo definitions in Define.h # include # endif ================================================ FILE: include/cppad/example/atomic_four/lin_ode/base_solver.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_LIN_ODE_BASE_SOLVER_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_LIN_ODE_BASE_SOLVER_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_lin_ode_base_solver.hpp} Atomic Linear ODE: Example Implementation ######################################### Syntax ****** | *lin_ode* . ``base_solver`` ( | |tab| *r* , *step* , *pattern* , *transpose* , *x* , *y* | ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } Notation ******** We use the notation: :ref:`atomic_four_lin_ode@call_id` :ref:`atomic_four_lin_ode@r` :ref:`atomic_four_lin_ode@pattern` :ref:`atomic_four_lin_ode@transpose` :ref:`atomic_four_lin_ode@pattern@nnz` , :ref:`atomic_four_lin_ode@pattern@row` , :ref:`atomic_four_lin_ode@pattern@col` , :ref:`atomic_four_lin_ode@x` , :ref:`atomic_four_lin_ode@x@n` , :ref:`atomic_four_lin_ode@x@A(x)` , :ref:`atomic_four_lin_ode@x@b(x)` , :ref:`atomic_four_lin_ode@y(x)` , :ref:`atomic_four_lin_ode@y(x)@m` , :ref:`atomic_four_lin_ode@vk(x)` Rosen34 ******* This example uses one step of :ref:`rosen34-name` ODE solver. Any initial value ODE solver, with any number of steps, could be used. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_lin_ode_base_solver.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // // base_solver // BEGIN_PROTOTYPE template void atomic_lin_ode::base_solver( const Base& r , const Base& step , const sparse_rc& pattern , const bool& transpose , const CppAD::vector& x , CppAD::vector& y ) // END_PROTOTYPE { class Fun { private: const sparse_rc& pattern_; const bool& transpose_; const CppAD::vector& x_; public: Fun( const sparse_rc& pattern , const bool& transpose , const CppAD::vector& x ) : pattern_(pattern), transpose_(transpose), x_(x) { } void Ode( const Base& t , const CppAD::vector& z , CppAD::vector& f ) { size_t m = z.size(); size_t nnz = pattern_.nnz(); CPPAD_ASSERT_UNKNOWN( f.size() == m ); CPPAD_ASSERT_UNKNOWN( x_.size() == nnz + m ); CPPAD_ASSERT_UNKNOWN( pattern_.nr() == m ); CPPAD_ASSERT_UNKNOWN( pattern_.nc() == m ); // for(size_t i = 0; i < m; ++i) f[i] = Base(0); for(size_t k = 0; k < nnz; ++k) { size_t i = pattern_.row()[k]; size_t j = pattern_.col()[k]; if( transpose_ ) std::swap(i, j); f[i] += x_[k] * z[j]; } } void Ode_ind( const Base& t , const CppAD::vector& z , CppAD::vector& f_t ) { size_t m = z.size(); # ifndef NDEBUG size_t nnz = pattern_.nnz(); CPPAD_ASSERT_UNKNOWN( f_t.size() == m ); CPPAD_ASSERT_UNKNOWN( x_.size() == nnz + m ); CPPAD_ASSERT_UNKNOWN( pattern_.nr() == m ); CPPAD_ASSERT_UNKNOWN( pattern_.nc() == m ); # endif // for(size_t i = 0; i < m; ++i) f_t[i] = Base(0); } void Ode_dep( const Base& t , const CppAD::vector& z , CppAD::vector& f_x ) { size_t m = z.size(); size_t nnz = pattern_.nnz(); CPPAD_ASSERT_UNKNOWN( f_x.size() == m * m ); CPPAD_ASSERT_UNKNOWN( x_.size() == nnz + m ); CPPAD_ASSERT_UNKNOWN( pattern_.nr() == m ); CPPAD_ASSERT_UNKNOWN( pattern_.nc() == m ); // for(size_t i = 0; i < m * m; ++i) f_x[i] = Base(0); for(size_t k = 0; k < nnz; ++k) { size_t i = pattern_.row()[k]; size_t j = pattern_.col()[k]; if( transpose_ ) std::swap(i, j); f_x[i * m + j] = x_[k]; } } }; // // nnz size_t nnz = pattern.nnz(); // m size_t m = y.size(); CPPAD_ASSERT_UNKNOWN( x.size() == nnz + m ); // // fun Fun fun(pattern, transpose, x); // // y Base ti = Base(0.0); Base tf = r; size_t n_step = 1; if( step < abs(r) ) n_step = size_t( Integer( abs(r) / step ) ) + 1; CppAD::vector zi(m), e(m); for(size_t j = 0; j < m; ++j) zi[j] = x[nnz + j]; y = CppAD::Rosen34(fun, n_step, ti, tf, zi, e); return; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/lin_ode/for_type.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_LIN_ODE_FOR_TYPE_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_LIN_ODE_FOR_TYPE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_lin_ode_for_type.hpp} Atomic Linear ODE Forward Type Calculation: Example Implementation ################################################################## Purpose ******* The ``for_type`` routine overrides the virtual functions used by the atomic_four base; see :ref:`for_type` . Notation ******** We use the notation: :ref:`atomic_four_lin_ode@call_id` :ref:`atomic_four_lin_ode@r` :ref:`atomic_four_lin_ode@pattern` :ref:`atomic_four_lin_ode@transpose` :ref:`atomic_four_lin_ode@pattern@nnz` , :ref:`atomic_four_lin_ode@pattern@row` , :ref:`atomic_four_lin_ode@pattern@col` , :ref:`atomic_four_lin_ode@x` , :ref:`atomic_four_lin_ode@x@n` , :ref:`atomic_four_lin_ode@x@A(x)` , :ref:`atomic_four_lin_ode@x@b(x)` , :ref:`atomic_four_lin_ode@y(x)` , :ref:`atomic_four_lin_ode@y(x)@m` , :ref:`atomic_four_lin_ode@vk(x)` , and the following additional notation: T(s) ==== We use :math:`\R{T} ( s )` to denote the ad_type of a scalar value :math:`s`. There are four possible :ref:`ad_types` : identical_zero, constant, dynamic, and variable in that order. Theory ****** This routine must calculate the following value for :math:`i = 0, \ldots, m-1`; see :ref:`atomic_four_lin_ode@y(x)@m` : .. math:: \R{T} [ y_i (x) ] = \max_k \R{T} [ v_i^k (x) ] The type :math:`\R{T} [ v_i^0 (x) ] = \R{T}[ b_i (x) ]`. This is easy to calculate given the type of the components of *x* ; see :ref:`atomic_four_lin_ode@x@b(x)` . Furthermore, for :math:`k > 0` .. math:: v_i^k (x) = \frac{r}{k} \sum_{j=0}^{m-1} A_{i,j} (x) v_j^{k-1} (x) .. math:: \R{T} [ v_i^k (x) ] = \max_j \R{T} [ A_{i,j} (x) v_j^{k-1} (x) ] .. math:: \R{T} [ A_{i,j} (x) v_j^k (x) ] = \left\{ \begin{array}{ll} \R{identical\_zero} & \R{if} A_{i,j} (x) \W{\R{or}} v_j^{k-1} (x) \W{\R{is}} \R{identical\_zero} \\ \max\{ \R{T} [ A_{i,j} (x) ] \W{,} \R{T} [ v_j^{k-1} (x) ] \} & \R{otherwise} \end{array} \right. If :math:`A_{i,j} (x)` is not in the sparsity :ref:`atomic_four_lin_ode@pattern` , it is identically zero. Furthermore we are allowing for the case where :math:`A_{i,j} (x)` is in the pattern and it is identically zero; i.e., the sparsity pattern is not efficient as it could be. The type :math:`\R{T} [ A_{i,j} (x) ]` for components in the sparsity pattern is easy to calculate given the type of the components of *x* ; see :ref:`atomic_four_lin_ode@x@A(x)` . Suppose :math:`\ell` is such that for all :math:`i` .. math:: \R{T} [ v_i^\ell (x) ] \leq \max_{k < \ell} \R{T} [ v_i^k (x) ] It follows that .. math:: \R{T} [ v_j^{\ell+1} (x) ] = \max_j \R{T} [ A_{i,j} (x) v_j^\ell (x) ] .. math:: \R{T} [ v_j^{\ell+1} (x) ] \leq \max_{k < \ell} \max_j \R{T} [ A_{i,j} (x) v_j^k (x) ] .. math:: \R{T} [ v_j^{\ell+1} (x) ] \leq \max_{k < \ell} \R{T} [ v_i^k (x) ] From this it is clear that .. math:: \R{T} [ y_i (x) ] = \max_{k < \ell} \R{T} [ v_i^k (x) ] Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_lin_ode_for_type.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // // for_type override template bool atomic_lin_ode::for_type( size_t call_id , const CppAD::vector& type_x , CppAD::vector& type_y ) { // pattern, transpose, nnz Base r; Base step; sparse_rc pattern; bool transpose; get(call_id, step, r, pattern, transpose); size_t nnz = pattern.nnz(); // // m size_t m = type_y.size(); CPPAD_ASSERT_UNKNOWN( pattern.nr() == m ); CPPAD_ASSERT_UNKNOWN( pattern.nc() == m ); // // type_x CPPAD_ASSERT_UNKNOWN( type_x.size() == nnz + m ); // // type_y[i] = type_b[i] // type_y[i] = max T[ v_i^k (x) ] for k = 0 for(size_t i = 0; i < m; ++i) type_y[i] = type_x[nnz + i]; // // change // Did type_y change during the previous iteration of the while loop bool change = true; while(change) { change = false; // we use k = 1, 2, ... to denote the pass through this loop // type_y[i] = max q < k T[ v_i^q (x) ] // for(size_t p = 0; p < nnz; ++p) { size_t i = pattern.row()[p]; size_t j = pattern.col()[p]; if( transpose ) std::swap(i, j); // // type_y[i], change if( type_x[p] != identical_zero_enum ) { // A_ij (x) is not identically zero if( type_y[j] > type_y[i] ) { change = true; type_y[i] = type_y[j]; } } // // type_y[i], change if( type_y[j] != identical_zero_enum ) { // There is a q < k such that v_j^q (x) not identically zero if( type_x[p] > type_y[i] ) { change = true; type_y[i] = type_x[p]; } } } } return true; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/lin_ode/forward.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_LIN_ODE_FORWARD_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_LIN_ODE_FORWARD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_lin_ode_forward.hpp} Atomic Linear ODE Forward Mode: Example Implementation ###################################################### Purpose ******* The ``forward`` routine overrides the virtual functions used by the atomic_four base; see :ref:`forward` . Theory ****** Suppose we are given Taylor coefficients :math:`x^0`, :math:`x^1`, for :ref:`atomic_four_lin_ode@x` . The zero order Taylor coefficient for :ref:`atomic_four_lin_ode@z(t, x)` solves the following initial value ODE: .. math:: z_t^0 (t) = A^0 z(t) \W{,} z^0 (0) = b^0 Note that :math:`A^0` and :math:`b^0` are just certain components of :math:`x^0`; see :ref:`atomic_four_lin_ode@x@A(x)` and :ref:`atomic_four_lin_ode@x@b(x)` . The first order Taylor coefficient for :math:`z(t, x)` solves the following initial value ODE: .. math:: z_t^1 (t) = A^0 z^1 (t) + A^1 z^0 (t) \W{,} z^1 (0) = b^1 Note that :math:`A^1` and :math:`c^1` are just certain components of :math:`x^1`. We can solve for :math:`z^1 (t)` using the following extended initial value ODE: .. math:: \left[ \begin{array}{c} z^0_t (t, x) \\ z^1_t (t, x) \end{array} \right] = \left[ \begin{array}{cc} A^0 & 0 \\ A^1 & A^0 \end{array} \right] \left[ \begin{array}{c} z^0 (t, x) \\ z^1 (t, x) \end{array} \right] \; , \; \left[ \begin{array}{c} z^0 (0, x) \\ z^1 (0, x) \end{array} \right] = \left[ \begin{array}{c} b^0 \\ b^1 \end{array} \right] extend_ode ********** The extended system above is created form the original system by the ``extend_ode`` function defined below: Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_lin_ode_forward.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // ---------------------------------------------------------------------------- // forward override for Base atomic linear ODE template bool atomic_lin_ode::forward( size_t call_id , const CppAD::vector& select_y , size_t order_low , size_t order_up , const CppAD::vector& taylor_x , CppAD::vector& taylor_y ) { // order_up if( order_up > 1 ) return false; // // r, pattern, transpose, nnz Base r; Base step; sparse_rc pattern; bool transpose; get(call_id, r, step, pattern, transpose); # ifndef NDEBUG size_t nnz = pattern.nnz(); # endif // // q size_t q = order_up + 1; // // m CPPAD_ASSERT_UNKNOWN( taylor_y.size() % q == 0 ); size_t m = taylor_y.size() / q; CPPAD_ASSERT_UNKNOWN( pattern.nr() == m ); CPPAD_ASSERT_UNKNOWN( pattern.nc() == m ); // // taylor_x CPPAD_ASSERT_UNKNOWN( taylor_x.size() == (nnz + m) * q ); // // taylor_y if( order_up == 0 ) base_solver(r, step, pattern, transpose, taylor_x, taylor_y); else { CPPAD_ASSERT_UNKNOWN( order_up == 1 ); // // pattern_extend, x_extend sparse_rc pattern_extend; CppAD::vector x_extend; extend_ode(pattern, transpose, taylor_x, pattern_extend, x_extend); // // y_extend size_t m_extend = pattern_extend.nr(); CppAD::vector y_extend(m_extend); bool transpose_extend = false; base_solver( r, step, pattern_extend, transpose_extend, x_extend, y_extend ); // // taylor_y if( order_low == 0 ) { for(size_t i = 0; i < m; ++i) taylor_y[i * q + 0] = y_extend[i]; } for(size_t i = 0; i < m; ++i) taylor_y[i * q + 1] = y_extend[m + i]; } // return true; } // ---------------------------------------------------------------------------- // forward override for AD atomic linear ODE template bool atomic_lin_ode::forward( size_t call_id , const CppAD::vector& select_y , size_t order_low , size_t order_up , const CppAD::vector< CppAD::AD >& ataylor_x , CppAD::vector< CppAD::AD >& ataylor_y ) { // // ad_base typedef CppAD::AD ad_base; // // order_up if( order_up > 1 ) return false; // // r, pattern, transpose, nnz Base r; Base step; sparse_rc pattern; bool transpose; get(call_id, r, step, pattern, transpose); # ifndef NDEBUG size_t nnz = pattern.nnz(); # endif // // q size_t q = order_up + 1; // // m CPPAD_ASSERT_UNKNOWN( ataylor_y.size() % q == 0 ); size_t m = ataylor_y.size() / q; // // ataylor_x CPPAD_ASSERT_UNKNOWN( ataylor_x.size() == (nnz + m) * q ); // // ataylor_y if( order_up == 0 ) (*this)(call_id, ataylor_x, ataylor_y); else { CPPAD_ASSERT_UNKNOWN( order_up == 1 ); // // pattern_extend, x_extend sparse_rc pattern_extend; CppAD::vector ax_extend; extend_ode(pattern, transpose, ataylor_x, pattern_extend, ax_extend); // // call_id_2 bool transpose_extend = false; size_t call_id_2 = set(r, step, pattern_extend, transpose_extend); // // ay_extend size_t m_extend = pattern_extend.nr(); CppAD::vector ay_extend(m_extend); (*this)(call_id_2, ax_extend, ay_extend); // // ataylor_y if( order_low == 0 ) { for(size_t i = 0; i < m; ++i) ataylor_y[i * q + 0] = ay_extend[i]; } for(size_t i = 0; i < m; ++i) ataylor_y[i * q + 1] = ay_extend[m + i]; } // return true; } // --------------------------------------------------------------------------- // extend_ode /* [ z_t^0 (t, x^0 ) ] = [ A^0 0 ] * [ z^0 (t, x^0 ) ] [ z_t^1 (t, x^0, x^1 ) ] [ A^1 A^0 ] [ z^1 (t, x^0, x^1 ) ] [ z^0 (0, x^0 ) ] = [ b^0 ] [ z^1 (0, x^0, x^1 ) ] [ b^1 ] */ template template void atomic_lin_ode::extend_ode( const CppAD::sparse_rc< CppAD::vector >& pattern , const bool& transpose , const CppAD::vector& taylor_x , CppAD::sparse_rc< CppAD::vector >& pattern_extend , CppAD::vector& x_extend ) { // // m size_t m = pattern.nr(); size_t nnz = pattern.nnz(); CPPAD_ASSERT_UNKNOWN( pattern.nc() == m ); // // q size_t q = 2; CPPAD_ASSERT_UNKNOWN( taylor_x.size() == q * (nnz + m) ); // // m_extend size_t m_extend = 2 * m; size_t nnz_extend = 3 * nnz; // // pattern_extend, x_extend pattern_extend.resize(m_extend, m_extend, nnz_extend); x_extend.resize(nnz_extend + m_extend); for(size_t k = 0; k < nnz; ++k) { size_t i = pattern.row()[k]; size_t j = pattern.col()[k]; if( transpose ) std::swap(i, j); // // A^0_ij Float A0ij = taylor_x[k * q + 0]; // // A^1_ij Float A1ij = taylor_x[k * q + 1]; // // upper diagonal pattern_extend.set(3 * k + 0, i, j); x_extend[3 * k + 0] = A0ij; // // lower left pattern_extend.set(3 * k + 1, m + i, j); x_extend[3 * k + 1] = A1ij; // // lower diagonal pattern_extend.set(3 * k + 2, m + i, m + j); x_extend[3 * k + 2] = A0ij; } for(size_t i = 0; i < m; ++i) { // b^0_i x_extend[nnz_extend + i] = taylor_x[ (nnz + i) * q + 0 ]; // b^1_i x_extend[nnz_extend + m + i] = taylor_x[ (nnz + i) * q + 1 ]; } return; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/lin_ode/get.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_LIN_ODE_GET_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_LIN_ODE_GET_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_lin_ode_get.hpp} atomic_lin_ode Get Routine: Example Implementation ################################################## Syntax ****** | *lin_ode* . ``get`` ( *call_id* , *r* , *step* , *pattern* , *transpose* ) Prototype ********* {xrst_literal include/cppad/example/atomic_four/lin_ode/lin_ode.hpp // BEGIN sparse_rc_type // END sparse_rc_type } {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Purpose ******* Retrieves the auxiliary information for a an atomic operation that computes the solution of a linear ODE. call_id ******* This input argument identifies the auxiliary information for this ODE. r * This output argument is the final value for the variable that the ODE is with respect to. step **** This is a positive maximum step size to use when solving the ODE. pattern ******* This output argument is a sparsity pattern. transpose ********* If this output argument is true (false) the sparsity pattern is for :math:`A(x)^\R{T}` (:math:`A(x)`). Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_lin_ode_get.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN PROTOTYPE template void atomic_lin_ode::get( size_t call_id, Base& r, Base& step, sparse_rc& pattern, bool& transpose ) // END PROTOTYPE { // thread size_t thread = thread_alloc::thread_num(); CPPAD_ASSERT_UNKNOWN( work_[thread] != nullptr ); // // pattern_vec CppAD::vector& pattern_vec( work_[thread]->pattern_vec ); // // call_vec CppAD::vector& call_vec( work_[thread]->call_vec ); // CPPAD_ASSERT_UNKNOWN( thread == call_vec[call_id].thread ); // // r call_struct& call = call_vec[call_id]; r = call.r; step = call.step; pattern = pattern_vec[call.pattern_index]; transpose = call.transpose; // return; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/lin_ode/hes_sparsity.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_LIN_ODE_HES_SPARSITY_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_LIN_ODE_HES_SPARSITY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_lin_ode_hes_sparsity.hpp} {xrst_spell wk } Atomic Linear ODE Hessian Sparsity Pattern: Example Implementation ################################################################## Purpose ******* The ``hes_sparsity`` routine overrides the virtual functions used by the atomic_four base class for Hessian sparsity calculations; see :ref:`hes_sparsity` . Notation ******** We use the notation: :ref:`atomic_four_lin_ode@call_id` :ref:`atomic_four_lin_ode@r` :ref:`atomic_four_lin_ode@pattern` :ref:`atomic_four_lin_ode@transpose` :ref:`atomic_four_lin_ode@pattern@nnz` , :ref:`atomic_four_lin_ode@pattern@row` , :ref:`atomic_four_lin_ode@pattern@col` , :ref:`atomic_four_lin_ode@x` , :ref:`atomic_four_lin_ode@x@n` , :ref:`atomic_four_lin_ode@x@A(x)` , :ref:`atomic_four_lin_ode@x@b(x)` , :ref:`atomic_four_lin_ode@y(x)` , :ref:`atomic_four_lin_ode@y(x)@m` , :ref:`atomic_four_lin_ode@vk(x)` , and the following additional notation: wk(x) ===== Because we are using the :ref:`Rosen34-name` solver, our actual sequence of operations is only fourth order accurate. So it suffices to compute the sparsity pattern for .. math:: \tilde{y} (x) = \sum_{k=0}^4 v^k (x) Note that the factor :math:`r / k`, in the definition of :math:`v^k (x)`, is constant (with respect to the variables). Hence it suffices to compute the sparsity pattern for .. math:: h (x) = \sum_{k=0}^4 w^k (x) where :math:`w^0 (x) = b(x)` and for :math:`k = 1, 2, \ldots`, :math:`w^k (x) = A(x) w^{k-1} (x)`. Example ******* The file :ref:`atomic_four_lin_ode_sparsity.cpp-name` contains an example and test using this operator. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_lin_ode_hes_sparsity.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // // hes_sparsity override template bool atomic_lin_ode::hes_sparsity( size_t call_id , const CppAD::vector& ident_zero_x , const CppAD::vector& select_x , const CppAD::vector& select_y , CppAD::sparse_rc< CppAD::vector >& pattern_out ) { // // adouble typedef AD adouble; // // pattern_A, transpose, nnz Base r; Base step; sparse_rc pattern_A; bool transpose; get(call_id, r, step, pattern_A, transpose); size_t nnz = pattern_A.nnz(); // // m, n size_t m = select_y.size(); size_t n = select_x.size(); // // au vector au(n); for(size_t j = 0; j < n; ++j) au[j] = 1.0; Independent( au ); // // ax vector ax(n); for(size_t j = 0; j < n; ++j) if( ident_zero_x[j] ) ax[j] = 0.0; else ax[j] = au[j]; // // aw // aw = w^0 (x) vector aw(m); for(size_t i = 0; i < m; ++i) aw[i] = ax[nnz + i]; // // ah = w^0 (x) vector ah = aw; // // ah = sum_k=0^4 w^k (x) vector awk(m); for(size_t k = 1; k < 5; ++k) { // aw = w^{k-1} (x) // // awk = w^k (x) for(size_t i = 0; i < m; ++i) awk[i] = 0.0; for(size_t p = 0; p < nnz; ++p) { // // i, j size_t i = pattern_A.row()[p]; size_t j = pattern_A.col()[p]; if( transpose ) std::swap(i, j); // // awk awk[i] += ax[p] * aw[j]; } // // ah = ah + w^k(x) for(size_t i = 0; i < m; ++i) ah[i] += awk[i]; // // aw = w^k (x) aw = awk; } // // h ADFun h; h.Dependent(au, ah); // // pattern_out // can use h.for_hes_sparsity or h.rev_hes_sparsity bool internal_bool = false; h.for_hes_sparsity(select_x, select_y, internal_bool, pattern_out); // return true; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/lin_ode/implement.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin atomic_four_lin_ode_implement} Implementing Atomic Linear ODE ############################## Contents ******** {xrst_toc_table include/cppad/example/atomic_four/lin_ode/lin_ode.hpp include/cppad/example/atomic_four/lin_ode/set.hpp include/cppad/example/atomic_four/lin_ode/get.hpp include/cppad/example/atomic_four/lin_ode/base_solver.hpp include/cppad/example/atomic_four/lin_ode/for_type.hpp include/cppad/example/atomic_four/lin_ode/forward.hpp include/cppad/example/atomic_four/lin_ode/reverse.hpp include/cppad/example/atomic_four/lin_ode/jac_sparsity.hpp include/cppad/example/atomic_four/lin_ode/hes_sparsity.hpp include/cppad/example/atomic_four/lin_ode/rev_depend.hpp } {xrst_end atomic_four_lin_ode_implement} ================================================ FILE: include/cppad/example/atomic_four/lin_ode/jac_sparsity.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_LIN_ODE_JAC_SPARSITY_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_LIN_ODE_JAC_SPARSITY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_lin_ode_jac_sparsity.hpp} {xrst_spell nnz } Atomic Linear ODE Jacobian Sparsity Pattern: Example Implementation ################################################################### Purpose ******* The ``jac_sparsity`` routine overrides the virtual functions used by the atomic_four base class for Jacobian sparsity calculations; see :ref:`jac_sparsity` . Notation ******** We use the notation: :ref:`atomic_four_lin_ode@call_id` :ref:`atomic_four_lin_ode@r` :ref:`atomic_four_lin_ode@pattern` :ref:`atomic_four_lin_ode@transpose` :ref:`atomic_four_lin_ode@pattern@nnz` , :ref:`atomic_four_lin_ode@pattern@row` , :ref:`atomic_four_lin_ode@pattern@col` , :ref:`atomic_four_lin_ode@x` , :ref:`atomic_four_lin_ode@x@n` , :ref:`atomic_four_lin_ode@x@A(x)` , :ref:`atomic_four_lin_ode@x@b(x)` , :ref:`atomic_four_lin_ode@y(x)` , :ref:`atomic_four_lin_ode@y(x)@m` , :ref:`atomic_four_lin_ode@vk(x)` , and the following additional notation: S[ g(x) ] ========= We use :math:`S [ g(x) ]` to denote the sparsity pattern for a function :math:`g : \B{R}^n \rightarrow \B{R}^m` as a vector of sets. To be specific, for :math:`i = 0, \ldots , m-1`, :math:`S_i [ g(x) ]` is the set of indices between zero and :math:`n - 1` such that :math:`\partial g_i (x) / \partial x_j` is possibly non-zero. N [ g(x) ] ========== We use :math:`N[ g(x) ]` to denote the set of :math:`i` such that :math:`g_i (x)` is not identically zero. J_i [ A(x) ] ============ We use :math:`J_i [ A(x) ]` to denote the set of :math:`j` between zero and :math:`m-1` such that :math:`A_{i,j}` is not known to be identically zero. P_i [ g(x) ] ============ We use :math:`P_i [ g(x) ]` to denote the set if sparsity pattern indices .. math:: P_i [ g(x) ] = \left\{ p \W{:} 0 \leq p < \R{nnz} \W{,} i = \R{row} [p] \W{,} \R{col}[p] \in N [ g(x) ] \right\} Theory ****** This routine must calculate the following value for :math:`i = 0, \ldots, m-1`; see :ref:`atomic_four_lin_ode@y(x)@m` : .. math:: S_i [ y (x) ] = \bigcup_k S_i [ v^k (x) ] The set :math:`S_i [ v^0 (x) ]` has just one element corresponding to :math:`b_i (x)`; i.e, .. math:: S_i [ v^0 (x) ] = \{ \R{nnz} + i \} see :ref:`atomic_four_lin_ode@x@b(x)` . Furthermore, for :math:`k > 0`, .. math:: v^k (x) = \frac{r}{k} A(x) v^{k-1} (x) .. math:: S_i [ v^k (x) ] = S_i [ A(x) v^{k-1} (x) ] .. math:: S_i [ v^k (x) ] = P_i [ v^{k-1} (x) ] \cup \left\{ S_j [ v^{k-1} (x) ] \W{:} j \in J_i [ A(x) ] \right\} Suppose that :math:`\ell` is such that for all :math:`i` the following two conditions hold .. math:: N [ v^\ell (x) ] \subset \bigcup_{k < \ell} N [ v^k (x) ] .. math:: S_i [ v^\ell (x) ] \subset \bigcup_{k < \ell} S_i [ v^k (x) ] From the first condition above it follows that .. math:: P_i [ v^\ell (x) ] \subset \bigcup_{k < \ell} P_i [ v^k (x) ] Using the second condition we have .. math:: S_i [ v^{\ell+1} (x) ] = P_i [ v^\ell (x) ] \cup \left\{ S_j [ v^\ell (x) ] \W{:} j \in J_i [ A(x) ] \right\} .. math:: S_i [ v^{\ell+1} (x) ] \subset \left\{ S_j [ v^\ell (x) ] \W{:} j \in J_i [ A(x) ] \right\} \bigcup_{k \leq \ell } S_i [ v^k (x) ] .. math:: S_i [ v^{\ell+1} (x) ] \subset \bigcup_{k \leq \ell} S_i [ v^k (x) ] \cup \left\{ S_j [ v^k (x) ] \W{:} j \in J_i [ A(x) ] \right\} .. math:: S_i [ v^{\ell+1} (x) ] \subset \bigcup_{k < \ell} S_i [ v^k (x) ] \cup \left\{ S_j [ v^k (x) ] \W{:} j \in J_i [ A(x) ] \right\} .. math:: S_i [ v^{\ell+1} (x) ] \subset \bigcup_{k \leq \ell} S_i [ v^k (x) ] .. math:: S_i [ v^{\ell+1} (x) ] \subset \bigcup_{k < \ell} S_i [ v^k (x) ] It follows that .. math:: S_i [ y(x) ] = \bigcup_{k < \ell} S_i [ v^k (x) ] Example ******* The file :ref:`atomic_four_lin_ode_sparsity.cpp-name` contains an example and test using this operator. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_lin_ode_jac_sparsity.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // // jac_sparsity override template bool atomic_lin_ode::jac_sparsity( size_t call_id , bool dependency , const CppAD::vector& ident_zero_x , const CppAD::vector& select_x , const CppAD::vector& select_y , CppAD::sparse_rc< CppAD::vector >& pattern_out ) { // // pattern_A, transpose, nnz Base r; Base step; sparse_rc pattern_A; bool transpose; get(call_id, r, step, pattern_A, transpose); size_t nnz = pattern_A.nnz(); // // m, n size_t m = select_y.size(); size_t n = select_x.size(); // CPPAD_ASSERT_UNKNOWN( n == nnz + m ); CPPAD_ASSERT_UNKNOWN( pattern_A.nr() == m ); CPPAD_ASSERT_UNKNOWN( pattern_A.nc() == m ); // // pattern_out // Accumulates elements of the sparsity pattern for y(x) that satisfy // select_x and select_y pattern_out.resize(m, n, 0); // // list_setvec // This vector of sets interface is not in the CppAD user API typedef CppAD::local::sparse::list_setvec list_setvec; // // setvec // Accumulates the sparsity pattern for y(x) that satisfy select_x. // There are m sets and the set elements are between zero and n-1. list_setvec setvec; size_t n_set = m; size_t end = n; setvec.resize(n_set, end); // // setvec, pattern_out // iniialize as equal to S[ v^0 (x) ] for(size_t i = 0; i < m; ++i) { size_t element = nnz + i; if( select_x[element] ) { setvec.add_element(i, element); if( select_y[i] ) pattern_out.push_back(i, element); } } // // non_zero // Accumulates union_k for k < ell, N[ v^k (x) ] // initialize as N[ v^0 (x) ] CppAD::vector non_zero(m); for(size_t i = 0; i < m; ++i) non_zero[i] = ! ident_zero_x[nnz + i]; // // change // Did setvec or non_zero change in previous iteration of while loop bool change = true; while(change) { change = false; // we use k = 1, 2, ... to denote the pass through this loop // setvec[i] contains union q < k S_i [ v^q (x) ] // non_zero contains union q < k N [ v^q (x) ] // // For each element of the sparsity pattern for A subject to select_x for(size_t p = 0; p < nnz; ++p) if( select_x[p] ) { CPPAD_ASSERT_UNKNOWN( ! ident_zero_x[p] ); size_t i = pattern_A.row()[p]; size_t j = pattern_A.col()[p]; if( transpose ) std::swap(i, j); // if( non_zero[j] ) { // p, corresponding to A_{i,j}, is in P_i [ v^k (x) ] if( ! setvec.is_element(i, p) ) { change = true; setvec.add_element(i, p); if( select_y[i] ) pattern_out.push_back(i, p); } } // j is in J_i [ A(x) ] list_setvec::const_iterator itr(setvec, j); size_t element = *itr; while(element != end ) { if( ! setvec.is_element(i, element) ) { change = true; setvec.add_element(i, element); if( select_y[i] ) pattern_out.push_back(i, element); } ++itr; element = *itr; } // A_{i,j} update to non_zero if( non_zero[i] == false ) { if( non_zero[j] == true ) { change = true; non_zero[i] = true; } } } } // return true; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/lin_ode/lin_ode.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_LIN_ODE_LIN_ODE_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_LIN_ODE_LIN_ODE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_lin_ode.hpp} Atomic Linear ODE Class: Example Implementation ############################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_lin_ode.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // template class atomic_lin_ode : public CppAD::atomic_four { // public: // BEGIN sparse_rc_typedef typedef CppAD::sparse_rc< CppAD::vector > sparse_rc; // END sparse_rc_typedef // // ctor atomic_lin_ode(const std::string& name) : CppAD::atomic_four(name) { for(size_t thread = 0; thread < CPPAD_MAX_NUM_THREADS; ++thread) work_[thread] = nullptr; } // destructor ~atomic_lin_ode(void) { for(size_t thread = 0; thread < CPPAD_MAX_NUM_THREADS; ++thread) { if( work_[thread] != nullptr ) { // allocated in set member function delete work_[thread]; } } } // set size_t set( const Base& r, const Base& step, sparse_rc& pattern, const bool& transpose ); // // get void get( size_t call_id, Base& r, Base& step, sparse_rc& pattern, bool& transpose ); // // base_solver static void base_solver( const Base& r , const Base& step , const sparse_rc& pattern , const bool& transpose , const CppAD::vector& x , CppAD::vector& y ); // // test_rev_depend // public version of this function that is used for example / testing bool test_rev_depend( size_t call_id, CppAD::vector& ident_zero_x, CppAD::vector& depend_x, const CppAD::vector& depend_y) { bool ok = rev_depend(call_id, ident_zero_x, depend_x, depend_y); return ok; } private: // // information connected to one call of this atomic function // pattern points to pattern_vec for this thread struct call_struct { size_t thread; Base r; Base step; size_t pattern_index; bool transpose; }; // // information connected to each thread // patterns are in a separate vector so do not need one for every call struct thread_struct { CppAD::vector pattern_vec; CppAD::vector call_vec; }; // // Use pointers, to avoid false sharing between threads. thread_struct* work_[CPPAD_MAX_NUM_THREADS]; // // extend_ode template static void extend_ode( const sparse_rc& pattern , const bool& transpose , const CppAD::vector& taylor_x , sparse_rc& pattern_extend , CppAD::vector& x_extend ); // // reverse_one static void reverse_one( const Base& r , const Base& step , const sparse_rc& pattern , const bool& transpose , const CppAD::vector& select_x , const CppAD::vector& x , const CppAD::vector& w , CppAD::vector& partial_x ); void reverse_one( const size_t& call_id , const CppAD::vector& select_x , const CppAD::vector< CppAD::AD >& x , const CppAD::vector< CppAD::AD >& w , CppAD::vector< CppAD::AD >& partial_x ); // ----------------------------------------------------------------------- // overrides // ----------------------------------------------------------------------- // for_type bool for_type( size_t call_id, const CppAD::vector& type_x, CppAD::vector& type_y ) override; // // Base forward bool forward( size_t call_id, const CppAD::vector& select_y, size_t order_low, size_t order_up, const CppAD::vector& taylor_x, CppAD::vector& taylor_y ) override; // Cases not yet implemented // // AD forward bool forward( size_t call_id, const CppAD::vector& select_y, size_t order_low, size_t order_up, const CppAD::vector< CppAD::AD >& ataylor_x, CppAD::vector< CppAD::AD >& ataylor_y ) override; // // Base reverse bool reverse( size_t call_id, const CppAD::vector& select_x, size_t order_up, const CppAD::vector& taylor_x, const CppAD::vector& taylor_y, CppAD::vector& partial_x, const CppAD::vector& partial_y ) override; // // AD reverse bool reverse( size_t call_id, const CppAD::vector& select_x, size_t order_up, const CppAD::vector< CppAD::AD >& ataylor_x, const CppAD::vector< CppAD::AD >& ataylor_y, CppAD::vector< CppAD::AD >& apartial_x, const CppAD::vector< CppAD::AD >& apartial_y ) override; // // jac_sparsity bool jac_sparsity( size_t call_id, bool dependency, const CppAD::vector& ident_zero_x, const CppAD::vector& select_x, const CppAD::vector& select_y, CppAD::sparse_rc< CppAD::vector >& pattern_out ) override; // // hes_sparsity bool hes_sparsity( size_t call_id, const CppAD::vector& ident_zero_x, const CppAD::vector& select_x, const CppAD::vector& select_y, CppAD::sparse_rc< CppAD::vector >& pattern_out ) override; // // rev_depend bool rev_depend( size_t call_id, const CppAD::vector& ident_zero_x, CppAD::vector& depend_x, const CppAD::vector& depend_y ) override; }; } // END_CPPAD_NAMESPACE # include # include # include # include # include # include # include # include # include // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/lin_ode/lin_ode.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin atomic_four_lin_ode} {xrst_spell nnz rosen vk } Atomic First Order Linear ODE Method: Example Implementation ############################################################ Syntax ****** | ``atomic_lin_ode`` *ode* ( *name* ) | *call_id* = *lin_ode* . ``set`` ( *r* , *step* , *pattern* , *transpose* ) | *lin_ode* . ``get`` ( *call_id* , *r* , *step* , *pattern* , *transpose* ) | *lin_ode* . ``base_solver`` ( *r* , *step* , *pattern* , *transpose* , *x* , *y* ) | *lin_ode* ( *call_id* , *ax* , *ay* ) z(t, x) ******* Construct an atomic operation that computes an approximate solution of the first order linear initial value ODE .. math:: z_t (t, x) = A(x) z(t, x) \W{,} z(0, x) = b(x) where :math:`z : \B{R} \times \B{R}^n \rightarrow \B{R}^m`, :math:`A : \B{R}^n \rightarrow \B{R}^{m \times m}`, :math:`b : \B{R}^n \rightarrow \B{R}^m`, and the subscript :math:`t` denotes partial differentiation w.r.t :math:`t`. call_id ******* This is a return value (argument) for the ``set`` (``get`` ) routine. r * This is the value of *t* at which we are approximating :math:`z(t, x)`. This is a argument (return value) for the ``set`` (``get`` ) routine. step **** This is a positive maximum step size to use when solving the ODE. pattern ******* This is a sparsity pattern. This is a argument (return value) for the ``set`` (``get`` ) routine. nnz === We use *nnz* to denote *pattern* . ``nnz`` () . row === We use *row* to denote *pattern* . ``row`` () . col === We use *col* to denote *pattern* . ``col`` () . transpose ********* If *transpose* is true (false) the sparsity pattern is for :math:`A(x)^\R{T}` (:math:`A(x)`). This is a argument (return value) for the ``set`` (``get`` ) routine. x * We use *x* to denote the argument to the atomic function. In the call to ``base_solver`` it is a CppAD vector with elements of type *Base* . n = The size of the vector *x* is :math:`n = nnz + m`. A(x) ==== This matrix stored in the same order as *pattern* at the beginning of the vector *x* . To be specific, if *transpose* is true (false), for *k* = 0, ..., *nnz* ``-1`` , :math:`A_{j,i} (x)` ( :math:`A_{i,j} (x)` ) is equal to :math:`x[k]` where *i* = *row* [ *k* ] and *j* = *col* [ *k* ] . b(x) ==== This vector is stored at the end of *x* ; i.e. its *j*-th element is :math:`b_j (x) = x[ nnz + j ]` y(x) **** We use :math:`y(x)` to denote the final value of the ODE; i.e., :math:`y(x) = z(r, x)`. m = We use *m* to denote the size of the vector *y* ( *x* ) . This is the number of equations in the ODE. y = In the call to ``base_solver`` , *y* is a CppAD vector with elements of type *Base* . The input value of its elements does not matter. Upon return it contains the value :math:`y(x)`. ax ** In the call to *lin_ode* , *ax* is a simple vector with elements of type ``AD`` < *Base* > . The elements of *ax* have the same meaning as *x* . ay ** In the call to *lin_ode* , *ay* is a simple vector with elements of type ``AD`` < *Base* > . The input value of its elements does not matter. Upon return it represents the solution *y* ( *ax* ) . vk(x) ***** We sometimes use the following representation for :math:`y(x)`: .. math:: y(x) = \exp [ r A(x) ] b(x) = \sum_{k=0}^\infty \frac{r^k}{k!} A(x)^k b(x) Define :math:`v^0 (x) = b(x)` and for :math:`k = 1, 2, \ldots`, :math:`v^k (x) = (r / k) A(x) v^{k-1} (x)`. Using this notation, .. math:: y(x) = \sum_{k=0}^\infty v^k (x) Approximations ************** Rosen34 ======= The :ref:`atomic_four_lin_ode_base_solver.hpp@Rosen34` routine is used to approximate the solution of the ODE. Any initial value ODE solver can be used for this purpose. Simpson's Rule ============== :ref:`atomic_four_lin_ode_reverse.hpp@Simpson's Rule` is used to approximate the integral .. math:: \int_0^r \lambda_i (t, x) z_j (r, x) \R{d} t Any other approximation for this integral can be used. Contents ******** {xrst_toc_table include/cppad/example/atomic_four/lin_ode/implement.xrst example/atomic_four/lin_ode/forward.cpp example/atomic_four/lin_ode/reverse.cpp example/atomic_four/lin_ode/sparsity.cpp example/atomic_four/lin_ode/rev_depend.cpp } {xrst_end atomic_four_lin_ode} ================================================ FILE: include/cppad/example/atomic_four/lin_ode/rev_depend.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_LIN_ODE_REV_DEPEND_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_LIN_ODE_REV_DEPEND_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_lin_ode_rev_depend.hpp} {xrst_spell wk } Atomic Linear ODE Forward Type Calculation: Example Implementation ################################################################## Purpose ******* The ``rev_depend`` routine overrides the virtual functions used by the atomic_four base; see :ref:`rev_depend` . Notation ******** We use the notation: :ref:`atomic_four_lin_ode@call_id` :ref:`atomic_four_lin_ode@r` :ref:`atomic_four_lin_ode@pattern` :ref:`atomic_four_lin_ode@transpose` :ref:`atomic_four_lin_ode@pattern@nnz` , :ref:`atomic_four_lin_ode@pattern@row` , :ref:`atomic_four_lin_ode@pattern@col` , :ref:`atomic_four_lin_ode@x` , :ref:`atomic_four_lin_ode@x@n` , :ref:`atomic_four_lin_ode@x@A(x)` , :ref:`atomic_four_lin_ode@x@b(x)` , :ref:`atomic_four_lin_ode@y(x)` , :ref:`atomic_four_lin_ode@y(x)@m` , :ref:`atomic_four_lin_ode@vk(x)` , and the following additional notation: wk(x) ===== Note that the factor :math:`r / k`, in the definition of :math:`v^k (x)`, is constant (with respect to the variables). Hence it suffices to compute the dependency for .. math:: h (x) = \sum_{k=0}^4 w^k (x) where :math:`w^0 (x) = b(x)` and for :math:`k = 1, 2, \ldots`, :math:`w^k (x) = A(x) w^{k-1} (x)`. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_lin_ode_rev_depend.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // // rev_depend override template bool atomic_lin_ode::rev_depend( size_t call_id, const CppAD::vector& ident_zero_x, CppAD::vector& depend_x, const CppAD::vector& depend_y ) { // nnz Base r; Base step; sparse_rc pattern; bool transpose; get(call_id, r, step, pattern, transpose); size_t nnz = pattern.nnz(); // // m size_t m = depend_y.size(); CPPAD_ASSERT_UNKNOWN( ident_zero_x.size() == depend_x.size() ); CPPAD_ASSERT_UNKNOWN( pattern.nr() == m ); CPPAD_ASSERT_UNKNOWN( pattern.nc() == m ); // // depend_w CppAD::vector depend_w = depend_y; // // depend_x for(size_t p = 0; p < nnz; ++p) depend_x[p] = false; for(size_t i = 0; i < m; ++i) depend_x[nnz + i] = depend_y[i]; // // change // Did depend_w change during the previous iteration of the while loop bool change = true; while(change) { change = false; // we use k = 1, 2, ... to denote the pass through this loop // // depend_w, depend_x // include dependency for w^k (x) for(size_t p = 0; p < nnz; ++p) if( ! ident_zero_x[p] ) { size_t i = pattern.row()[p]; size_t j = pattern.col()[p]; if( transpose ) std::swap(i, j); // // back propagate dependency on y if( depend_w[i] && ! depend_w[j] ) { change = true; depend_w[j] = true; } // // depend_x // for propagate dependency on A_{i,j} if( depend_w[i] && ! depend_x[p] ) { change = true; depend_x[p] = true; } } } // // depend_x // terms corresponding to b(x) for(size_t i = 0; i < m; ++i) depend_x[nnz + i] = depend_w[i]; // return true; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/lin_ode/reverse.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_LIN_ODE_REVERSE_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_LIN_ODE_REVERSE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_lin_ode_reverse.hpp} {xrst_spell lll nnz } Atomic Linear ODE Reverse Mode: Example Implementation ###################################################### Purpose ******* The ``reverse`` routine overrides the virtual functions used by the atomic_four base; see :ref:`reverse` . First Order Theory ****************** We are given a vector :math:`w \in \B{R}^m` and need to compute .. math:: \partial_x w^\R{T} z(r, x) see the definition of :ref:`atomic_four_lin_ode@z(t, x)` . Consider the Lagrangian corresponding to :math:`w^\R{T} z(r, x)` as the objective and the ODE as the constraint: .. math:: L(x, \lambda) = w^\R{T} z(r, x) + \int_0^r \lambda(t, x)^\R{T} [ A(x) z(t, x) - z_t (t, x) ] \R{d} t where :math:`\lambda : \R{R} \times \B{R}^n \rightarrow \B{R}^m` is a smooth function. If :math:`z(t, x)` satisfies its ODE, then .. math:: \partial_x w^\R{T} z(r, x) = \partial_x L(x, \lambda) We use the following integration by parts to replace the :math:`z_t (t, x)` term in the integral defining :math:`L(x, \lambda)`: .. math:: - \int_0^r \lambda(t, x)^\R{T} z_t (t, x) \R{d} t = - \left. \lambda(t, x)^\R{T} z(t, x) \right|_0^r + \int_0^r \lambda_t (t, x)^\R{T} z(t, x) \R{d} t Adding the condition :math:`\lambda(r, x) = w`, and noting that :math:`z(0, x) = b(x)`, we have .. math:: L(x, \lambda) = \lambda(0, x)^\R{T} z(0, x) + \int_0^r \lambda_t (t, x)^\R{T} z(t, x) \R{d} t + \int_0^r \lambda(t, x)^\R{T} A(x) z(t, x) \R{d} t .. math:: L(x, \lambda) = \lambda(0, x)^\R{T} b (x) + \int_0^r [ \lambda_t (t, x)^\R{T} + \lambda(t, x)^\R{T} A(x) ] z(t, x) \R{d} t .. math:: L(x, \lambda) = \lambda(0, x)^\R{T} b (x) + \int_0^r z(t, x)^\R{T} [ \lambda_t (t, x) + A(x)^\R{T} \lambda(t, x) ] \R{d} t The partial derivative of :math:`L(x, \lambda)` with respect to :math:`b_j`, (not including the dependence of :math:`\lambda(t, x)` on :math:`x`) is : .. math:: \partial_{b(j)} L(x, \lambda) = \lambda_j (0, x) The partial derivative of :math:`L(x, \lambda)` with respect to :math:`A_{i,j}` (not including The dependence of :math:`\lambda(t, x)` on :math:`x`) is : .. math:: \partial_{A(i,j)} L(x, \lambda) = \int_0^r \partial_{A(i,j)} z(t, x)^\R{T} [ \lambda_t (t, x) + A(x)^\R{T} \lambda(t, x) ] \R{d} t + \int_0^r z_j (t, x) \lambda_i (t, x) \R{d} t If :math:`\lambda(t, x)` satisfies the ODE .. math:: 0 = \lambda_t (t, x) + A(x)^\R{T} \lambda(t, x) The partial derivative with respect to :math:`A_{i,j}` is .. math:: \partial_{A(i,j)} L(x, \lambda) = \int_0^r z_j (t, x) \lambda_i (t, x) \R{d} t In summary, we can compute an approximate solution for the initial value ODE: .. math:: z_t (t, x) = A(x) z(t, x) \W{,} z(0, x) = b(x) and approximate solution for the final value ODE: .. math:: \lambda_t (t, x) = - A(x)^\R{T} \lambda(t, x) \W{,} \lambda(r, x) = w Using the notation :ref:`atomic_four_lin_ode@pattern@nnz` , :ref:`atomic_four_lin_ode@pattern@row` , and :ref:`atomic_four_lin_ode@pattern@col` , We can compute an approximation for .. math:: \partial_{x(k)} w^\R{T} z(r, x) = \left\{ \begin{array}{lll} \int_0^r \lambda_i (t, x) z_j (r, x) \R{d} t & \R{where} \; i = \R{row} [k] \W{,} j = \R{col}[k] & \R{if} \; k < nnz \\ \lambda_i (0, x) & \R{where} \; i = k - nnz & \R{otherwise} % \end{array} \right. {xrst_toc_hidden include/cppad/example/atomic_four/lin_ode/reverse_2.xrst } Second Order Theory ******************* :ref:`atomic_four_lin_ode_reverse_2-name` . Simpson's Rule ************** This example uses Simpson's rule to approximate the integral .. math:: \int_0^r \lambda_i (t, x) z_j (t, x) \R{d} t Any other approximation for this integral can be used. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_lin_ode_reverse.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // ---------------------------------------------------------------------------- // reverse override for Base template bool atomic_lin_ode::reverse( size_t call_id , const CppAD::vector& select_x , size_t order_up , const CppAD::vector& taylor_x , const CppAD::vector& taylor_y , CppAD::vector& partial_x , const CppAD::vector& partial_y ) { // order_up if( order_up > 0 ) return false; // // r, step, pattern, transpose Base r; Base step; sparse_rc pattern; bool transpose; get(call_id, r, step, pattern, transpose); // reverse_one( r, step, pattern, transpose, select_x, taylor_x, partial_y, partial_x ); // return true; } // --------------------------------------------------------------------------- // reverse override for AD template bool atomic_lin_ode::reverse( size_t call_id , const CppAD::vector& select_x , size_t order_up , const CppAD::vector< CppAD::AD >& ataylor_x , const CppAD::vector< CppAD::AD >& ataylor_y , CppAD::vector< CppAD::AD >& apartial_x , const CppAD::vector< CppAD::AD >& apartial_y ) { // order_up if( order_up > 0 ) return false; // reverse_one(call_id, select_x, ataylor_x, apartial_y, apartial_x); // return true; } // --------------------------------------------------------------------------- // reverse_one // Base version of first order reverse mode calculation as in Theory above template void atomic_lin_ode::reverse_one( const Base& r , const Base& step , const sparse_rc& pattern , const bool& transpose , const CppAD::vector& select_x , const CppAD::vector& x , const CppAD::vector& w , CppAD::vector& partial_x ) { // // nnz size_t nnz = pattern.nnz(); // // m size_t m = w.size(); CPPAD_ASSERT_UNKNOWN( w.size() == m ); CPPAD_ASSERT_UNKNOWN( pattern.nr() == m ); CPPAD_ASSERT_UNKNOWN( pattern.nc() == m ); // // n # ifndef NDEBUG size_t n = nnz + m; # endif // // x, partial_x CPPAD_ASSERT_UNKNOWN( x.size() == n ); CPPAD_ASSERT_UNKNOWN( partial_x.size() == n ); // // n_step size_t n_step = 2; if( step < abs(r) / Base(n_step) ) n_step = size_t( abs(r) / step ); while( step < abs(r) / Base(n_step) ) ++n_step; if( n_step % 2 == 1 ) ++n_step; CPPAD_ASSERT_UNKNOWN( n_step % 2 == 0 ); // // h Base h = r / Base(n_step); // // x_tmp = [A, b] CppAD::vector x_tmp = x; // // z_all CppAD::vector< CppAD::vector > z_all(n_step + 1); // // z_all[0] = z(0, x) z_all[0].resize(m); for(size_t i = 0; i < m; ++i) z_all[0][i] = x[nnz + i]; // // p for(size_t p = 0; p < n_step; ++p) { // x_tmp = [A, z(h*p, x) ] for(size_t i = 0; i < nnz; ++i) x_tmp[nnz + i] = z_all[p][i]; // // z_all[p+1] = z( h*(p+1), x) z_all[p+1].resize(m); base_solver(h, step, pattern, transpose, x_tmp, z_all[p+1]); } // lambda_previous = lambda(r, x) = w CppAD::vector lambda_previous = w; // // lambda_middle, lambda_next CppAD::vector lambda_middle(m), lambda_next(m); // // partial_x // partial_A L(x, lambda) for(size_t k = 0; k < nnz; ++k) partial_x[k] = Base(0.0); // // p size_t p = n_step - 1; while(p) { CPPAD_ASSERT_UNKNOWN( p % 2 == 1 ); // // x_tmp = [ A, lambda( (p+1)*h, x ) ] for(size_t i = 0; i < m; ++i) x_tmp[nnz + i] = lambda_previous[i]; // // lambda_middle = lambda(p*h, x) // We convert the final value ODE to an initial value ODE by changing // the sign of A^T and changing limits from [(p+1)*h, p*h] -> [0, h]. base_solver(h, step, pattern, ! transpose, x_tmp, lambda_middle); // // x_tmp = [ A, lambda(p*h, x)] for(size_t i = 0; i < m; ++i) x_tmp[nnz + i] = lambda_middle[i]; // // lambda_next = lambda((p-1), x) base_solver(h, step, pattern, ! transpose, x_tmp, lambda_next); // // partial_x // partail_A L(x, lambda) for(size_t k = 0; k < nnz; ++k) if( select_x[k] ) { size_t i = pattern.row()[k]; size_t j = pattern.col()[k]; if( transpose ) std::swap(i, j); // // sum = lambda_i ((p+1)*h, x) * z_j ((p+1)*h, x) Base sum = lambda_previous[i] * z_all[p+1][j]; // // sum += 4 * lambad_i(p*h, x) * z_j(p*h, x) sum += Base(4.0) * lambda_middle[i] * z_all[p][j]; // // sum += lambda_i ((p-1)*h, x) * z_j ((p-1)*h, x) sum += lambda_next[i] * z_all[p-1][j]; // // Simpson's rule for int_0^2*h lambda_i (t, x) z_j (t, x) dt Base integral = h * sum / Base(3.0); // // partial_{A(i,j)} partial_x[k] += integral; } // // lambda_previous lambda_previous = lambda_next; // // p if( p == 1 ) p = 0; else p -= 2; } // // partial_x // partial_b L(x, lambda) = lambda(0, x) for(size_t i = 0; i < m; ++i) { // partial_{b(i)} partial_x[nnz + i] = lambda_next[i]; } // return; } // --------------------------------------------------------------------------- // reverse_one // AD version of first order reverse mode calculation as in Theory above template void atomic_lin_ode::reverse_one( const size_t& call_id , const CppAD::vector& select_x , const CppAD::vector< CppAD::AD >& ax , const CppAD::vector< CppAD::AD >& aw , CppAD::vector< CppAD::AD >& apartial_x ) { typedef CppAD::AD ad_base; // // // r, step, pattern, transpose Base r; Base step; sparse_rc pattern; bool transpose; get(call_id, step, r, pattern, transpose); // // nnz size_t nnz = pattern.nnz(); // // m size_t m = aw.size(); CPPAD_ASSERT_UNKNOWN( aw.size() == m ); CPPAD_ASSERT_UNKNOWN( pattern.nr() == m ); CPPAD_ASSERT_UNKNOWN( pattern.nc() == m ); // // n # ifndef NDEBUG size_t n = nnz + m; # endif // // ax, apartial_x CPPAD_ASSERT_UNKNOWN( ax.size() == n ); CPPAD_ASSERT_UNKNOWN( apartial_x.size() == n ); // // n_step size_t n_step = 2; if( step < abs(r) / Base(n_step) ) n_step = size_t( abs(r) / step ); while( step < abs(r) / Base(n_step) ) ++n_step; if( n_step % 2 == 1 ) ++n_step; CPPAD_ASSERT_UNKNOWN( n_step % 2 == 0 ); // // h Base h = r / Base(n_step); // // ax_tmp = [A, b] CppAD::vector ax_tmp = ax; // // az_all CppAD::vector< CppAD::vector > az_all(n_step + 1); // // az_all[0] = z(0, x) az_all[0].resize(m); for(size_t i = 0; i < m; ++i) az_all[0][i] = ax[nnz + i]; // // call_id_1 size_t call_id_1 = (*this).set(h, step, pattern, transpose); // // p for(size_t p = 0; p < n_step; ++p) { // ax_tmp = [A, z(h*p, x) ] for(size_t i = 0; i < nnz; ++i) ax_tmp[nnz + i] = az_all[p][i]; // // az_all[p+1] = az( h*(p+1), x) // This interface requires a separate atomic function call for each // step in the Simpson's rule integration. Perhaps it would be more // efficient (but more complicated) to have an option whereby one call // that returns all the values in az_all expect for az_all[0]. az_all[p+1].resize(m); (*this)(call_id_1, ax_tmp, az_all[p+1]); } // alambda_previous = lambda(r, x) = w CppAD::vector alambda_previous = aw; // // alambda_middle, alambda_next CppAD::vector alambda_middle(m), alambda_next(m); // // apartial_x // apartial_A L(x, lambda) for(size_t k = 0; k < nnz; ++k) apartial_x[k] = ad_base(0.0); // // call_id_2 size_t call_id_2 = (*this).set(h, step, pattern, ! transpose); // // // p size_t p = n_step - 1; while(p) { CPPAD_ASSERT_UNKNOWN( p % 2 == 1 ); // // ax_tmp = [ A, lambda( (p+1)*h, x ) ] for(size_t i = 0; i < m; ++i) ax_tmp[nnz + i] = alambda_previous[i]; // // alambda_middle = lambda(p*h, x) // We convert the final value ODE to an initial value ODE by changing // the sign of A^T and changing limits from [(p+1)*h, p*h] -> [0, h]. (*this)(call_id_2, ax_tmp, alambda_middle); // // ax_tmp = [ A, lambda(p*h, x)] for(size_t i = 0; i < m; ++i) ax_tmp[nnz + i] = alambda_middle[i]; // // alambda_next = lambda((p-1), x) (*this)(call_id_2, ax_tmp, alambda_next); // // apartial_x // partail_A L(x, lambda) for(size_t k = 0; k < nnz; ++k) if( select_x[k] ) { size_t i = pattern.row()[k]; size_t j = pattern.col()[k]; if( transpose ) std::swap(i, j); // // asum = lambda_i ((p+1)*h, x) * z_j ((p+1)*h, x) ad_base asum = alambda_previous[i] * az_all[p+1][j]; // // asum += 4 * lambad_i(p*h, x) * z_j(p*h, x) asum += ad_base(4.0) * alambda_middle[i] * az_all[p][j]; // // asum += lambda_i ((p-1)*h, x) * z_j ((p-1)*h, x) asum += alambda_next[i] * az_all[p-1][j]; // // Simpson's rule for int_0^2*h lambda_i (t, x) z_j (t, x) dt ad_base aintegral = h * asum / ad_base(3.0); // // apartial_x // partial_{A(i,j)} apartial_x[k] += aintegral; } // // alambda_previous alambda_previous = alambda_next; // // p if( p == 1 ) p = 0; else p -= 2; } // // apartial_x // partial_b L(x, lambda) = lambda(0, x) for(size_t i = 0; i < m; ++i) { // partial_{b(i)} apartial_x[nnz + i] = alambda_next[i]; } // return; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/lin_ode/reverse_2.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin atomic_four_lin_ode_reverse_2} Atomic Linear ODE Second Order Reverse ###################################### x^1 Partial *********** We need to compute .. math:: \R{partial\_x} [ j * q + 1 ] = \sum_{i=0}^{m-1} \R{partial\_y} [ i * q + 1] ( \partial y_i^1 ( x^0 , x^1 ) / \partial x_j^1 ) where :math:`q = 2` and :math:`j = 0 , \ldots , n-1`. Using the :ref:`reverse_identity-name` we have .. math:: \partial y_i^1 ( x^0 , x^1 ) / \partial x_j^1 = \partial y_i^0 ( x^0 ) / \partial x_j^0 .. math:: \R{partial\_x} [ j * q + 1 ] = \sum_{i=0}^{m-1} \R{partial\_y} [ i * q + 1] ( \partial y_i^0 ( x^0 ) / \partial x_j^0 ) which is the same as the first order :ref:`theory` with .. math:: w_i = \R{partial\_y} [ i * q + 1] x^0 Partial *********** We also need to compute .. math:: \R{partial\_x} [ j * q + 0 ] = \sum_{i=0}^{m-1} \R{partial\_y} [ i * q + 0] ( \partial y_i^0 ( x^0 ) / \partial x_j^0 ) + \R{partial\_y} [ i * q + 1] ( \partial y_i^1 ( x^0 , x^1 ) / \partial x_j^0 ) Note that we can solve for .. math:: y^1 ( x^0 , x^1 ) = z^1 ( r , x^0 , x^1 ) using the following extended ODE; see :ref:`forward theory` . .. math:: \left[ \begin{array}{c} z^0_t (t, x^0 ) \\ z^1_t (t, x^0 , x^1 ) \end{array} \right] = \left[ \begin{array}{cc} A^0 & 0 \\ A^1 & A^0 \end{array} \right] \left[ \begin{array}{c} z^0 (t, x^0 ) \\ z^1 (t, x^0 , x^1 ) \end{array} \right] \; , \; \left[ \begin{array}{c} z^0 (0, x^0 ) \\ z^1 (0, x^0 , x^1 ) \end{array} \right] = \left[ \begin{array}{c} b^0 \\ b^1 \end{array} \right] Note that :math:`A^0`, :math:`b^0` are components of :math:`x^0` and :math:`A^1`, :math:`b^1` are components of :math:`x^1`. We use the following notation .. math:: \bar{x} = \left[ \begin{array}{c} x^0 \\ x^1 \end{array} \right] \W{,} \bar{z}(t , \bar{x} ) = \left[ \begin{array}{c} z^0 (t, x^0) \\ z^1 ( t, x^0 , x^1 ) \end{array} \right] \W{,} \bar{A} = \left[ \begin{array}{cc} A^0 & 0 \\ A^1 & A^0 \end{array} \right] \W{,} \bar{b} = \left[ \begin{array}{c} b^0 \\ b^1 \end{array} \right] Using this notation we have .. math:: \bar{z}_t ( t , \bar{x} ) = \bar{A} \bar{z} (t, \bar{x} ) \W{,} \bar{z} (0, \bar{x} ) = \bar{b} Define :math:`\bar{w} \in \B{R}^{m + m}` by .. math:: \bar{w}_i = \R{partial\_y}[ i * q + 0 ] \W{,} \bar{w}_{m + i} = \R{partial\_y}[ i * q + 1 ] For this case, we can compute .. math:: \partial_\bar{x} \bar{w}^\R{T} \bar{z}(r, \bar{x} ) which is same as the first order case but with the extended variables and extended ODE. We will only use the components of :math:`\partial_\bar{x}` that correspond to partials w.r.t. :math:`x^0`. {xrst_end atomic_four_lin_ode_reverse_2} ================================================ FILE: include/cppad/example/atomic_four/lin_ode/set.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_LIN_ODE_SET_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_LIN_ODE_SET_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_lin_ode_set.hpp} atomic_lin_ode Set Routine: Example Implementation ################################################## Syntax ****** | *call_id* = *lin_ode* . ``set`` ( *r* , *step* , *pattern* , *transpose* ) Prototype ********* {xrst_literal include/cppad/example/atomic_four/lin_ode/lin_ode.hpp // BEGIN sparse_rc_type // END sparse_rc_type } {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Purpose ******* Stores the auxiliary information for a an atomic operation that computes the solution of a linear ODE. r * This argument is the final value for the variable that the ODE is with respect to. step **** This is a positive maximum step size to use when solving the ODE. pattern ******* This argument is a sparsity pattern. It would be ``const`` except for the fact that *pattern.set_row_major* () is called so that checking for equality is faster; see :ref:`sparse_rc@set_row_major` . transpose ********* If this argument is true (false) the sparsity pattern is for :math:`A(x)\R{T}` (:math:`A(x)`). Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_lin_ode_set.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN PROTOTYPE template size_t atomic_lin_ode::set( const Base& r, const Base& step, sparse_rc& pattern, const bool& transpose ) // END PROTOTYPE { // pattern // set_row_major so that checking for pattern equality is faster pattern.set_row_major(); // // thread size_t thread = thread_alloc::thread_num(); // // work_[thread] if( work_[thread] == nullptr ) work_[thread] = new thread_struct; // // pattern_vec CppAD::vector& pattern_vec( work_[thread]->pattern_vec ); // // pattern_index size_t n_pattern = pattern_vec.size(); size_t pattern_index = n_pattern; for(size_t i = 0; i < n_pattern; ++i) if( pattern == pattern_vec[i] ) pattern_index = i; if( pattern_index == n_pattern ) { pattern_vec.push_back( pattern ); CPPAD_ASSERT_UNKNOWN( pattern_vec[pattern_index] == pattern ); } // // call_vec CppAD::vector& call_vec( work_[thread]->call_vec ); // // call_id size_t call_id = call_vec.size(); // // call call_struct call; call.thread = thread; call.r = r; call.step = step; call.pattern_index = pattern_index; call.transpose = transpose; // // work_[thread] call_vec.push_back( call ); // return call_id; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/mat_mul/base_mat_mul.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_MAT_MUL_BASE_MAT_MUL_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_MAT_MUL_BASE_MAT_MUL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_mat_mul_base_mat_mul.hpp} Atomic Multiply Base Matrices: Example Implementation ##################################################### Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_mat_mul_base_mat_mul.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // // base_mat_mul template void atomic_mat_mul::base_mat_mul( size_t n_left , size_t n_middle , size_t n_right , const CppAD::vector& x , CppAD::vector& y ) { # ifndef NDEBUG // n, m size_t n = x.size(); size_t m = y.size(); // // check sizes assert( n == n_middle * (n_left + n_right ) ); assert( m == n_left * n_right ); # endif // // offset size_t offset = n_left * n_middle; // // y // y[ i * n_right + j] = sum_k // x[i * n_middle + k] * x[ offset + k * n_right + j] // type_y for(size_t i = 0; i < n_left; ++i) { for(size_t j = 0; j < n_right; ++j) { Base sum_ij = Base(0); for(size_t k = 0; k < n_middle; ++k) { Base left_ik = x[i * n_middle + k]; Base right_kj = x[offset + k * n_right + j]; sum_ij += left_ik * right_kj; } y[i * n_right + j] = sum_ij; } } return; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/mat_mul/for_type.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_MAT_MUL_FOR_TYPE_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_MAT_MUL_FOR_TYPE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_mat_mul_for_type.hpp} Atomic Matrix Multiply Forward Type Calculation: Example Implementation ####################################################################### Purpose ******* The ``for_type`` routine overrides the virtual functions used by the atomic_four base; see :ref:`for_type` . Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_mat_mul_for_type.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // // for_type override template bool atomic_mat_mul::for_type( size_t call_id , const CppAD::vector& type_x , CppAD::vector& type_y ) { // // n_left, n_middle, n_right size_t n_left, n_middle, n_right; get(call_id, n_left, n_middle, n_right); # ifndef NDEBUG // n, m size_t n = type_x.size(); size_t m = type_y.size(); // // check sizes assert( n == n_left * n_middle + n_middle * n_right ); assert( m == n_left * n_right ); # endif // // offset size_t offset = n_left * n_middle; // // type_y // y[ i * n_right + j] = sum_k // x[i * n_middle + k] * x[ offset + k * n_right + j] // treat multiplication by zero like absolute zero for(size_t i = 0; i < n_left; ++i) { for(size_t j = 0; j < n_right; ++j) { CppAD::ad_type_enum type_ij = CppAD::identical_zero_enum; for(size_t k = 0; k < n_middle; ++k) { CppAD::ad_type_enum type_ik = type_x[i * n_middle + k]; CppAD::ad_type_enum type_kj = type_x[offset + k * n_right + j]; if( type_ik != identical_zero_enum ) { if( type_kj != identical_zero_enum ) { type_ij = std::max(type_ij, type_ik); type_ij = std::max(type_ij, type_kj); } } } type_y[ i * n_right + j] = type_ij; } } return true; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/mat_mul/forward.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_MAT_MUL_FORWARD_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_MAT_MUL_FORWARD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_mat_mul_forward.hpp} Atomic Matrix Multiply Forward Mode: Example Implementation ########################################################### Purpose ******* The ``forward`` routine overrides the virtual functions used by the atomic_four base; see :ref:`forward` . Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_mat_mul_forward.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // // forward override for Base matrix multiply template bool atomic_mat_mul::forward( size_t call_id , const CppAD::vector& select_y , size_t order_low , size_t order_up , const CppAD::vector& taylor_x , CppAD::vector& taylor_y ) { // q size_t q = order_up + 1; // // n_left, n_middle, n_right size_t n_left, n_middle, n_right; get(call_id, n_left, n_middle, n_right); # ifndef NDEBUG // n, m size_t n = taylor_x.size(); size_t m = taylor_y.size(); // // check sizes assert( n == n_middle * ( n_left + n_right ) * q ); assert( m == n_left * n_right * q ); # endif // // offset size_t offset = n_left * n_middle; // // for k = order_low, ..., order_up : // C^k = sum_ell A^ell * B^{k-ell} CppAD::vector x( n_middle * ( n_left + n_right) ); CppAD::vector y(n_left * n_right); CppAD::vector sum(n_left * n_right); for(size_t k = order_low; k < q; ++k) { // sum = 0 for(size_t i = 0; i < n_left * n_right; ++i) sum[i] = Base(0); for(size_t ell = 0; ell <= k; ++ell) { // x = [ A^ell, B^{k-ell} ] for(size_t i = 0; i < n_left * n_middle; ++i) x[i] = taylor_x[ i * q + ell ]; for(size_t i = 0; i < n_middle * n_right; ++i) x[offset + i] = taylor_x[ (offset + i) * q + (k - ell) ]; // // y = A^ell * B^{k-ell} base_mat_mul(n_left, n_middle, n_right, x, y); // // sum += y for(size_t i = 0; i < n_left * n_right; ++i) sum[i] += y[i]; } // C^k = sum for(size_t i = 0; i < n_left * n_right; ++i) taylor_y[i * q + k] = sum[i]; } return true; } // // forward override for AD matrix multiply template bool atomic_mat_mul::forward( size_t call_id , const CppAD::vector& select_y , size_t order_low , size_t order_up , const CppAD::vector< CppAD::AD >& ataylor_x , CppAD::vector< CppAD::AD >& ataylor_y ) { // // vector, AD using CppAD::vector; using CppAD::AD; // q size_t q = order_up + 1; // // n_left, n_middle, n_right size_t n_left, n_middle, n_right; get(call_id, n_left, n_middle, n_right); # ifndef NDEBUG // n, m size_t n = ataylor_x.size(); size_t m = ataylor_y.size(); // // check sizes assert( n == n_middle * ( n_left + n_right ) * q ); assert( m == n_left * n_right * q ); # endif // // offset size_t offset = n_left * n_middle; // // for k = order_low, ..., order_up : // C^k = sum_ell A^ell * B^{k-ell} vector< AD > ax( n_middle *( n_left + n_right) ); vector< AD > ay(n_left * n_right); vector< AD > asum(n_left * n_right); for(size_t k = order_low; k < q; ++k) { // sum = 0 for(size_t i = 0; i < n_left * n_right; ++i) asum[i] = AD(0); for(size_t ell = 0; ell <= k; ++ell) { // ax = [ A^ell, B^{k-ell} ] for(size_t i = 0; i < n_left * n_middle; ++i) ax[i] = ataylor_x[ i * q + ell ]; for(size_t i = 0; i < n_middle * n_right; ++i) ax[offset + i] = ataylor_x[ (offset + i) * q + (k - ell) ]; // // ay = A^ell * B^{k-ell} (*this)(call_id, ax, ay); // // asum += ay for(size_t i = 0; i < n_left * n_right; ++i) asum[i] += ay[i]; } // C^k = asum for(size_t i = 0; i < n_left * n_right; ++i) ataylor_y[i * q + k] = asum[i]; } return true; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/mat_mul/get.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_MAT_MUL_GET_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_MAT_MUL_GET_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_mat_mul_get.hpp} atomic_mat_mul Get Routine: Example Implementation ################################################## Syntax ****** | ``mat_mul`` . *get* ( ``call_id`` , ``n_left`` , ``n_middle`` , ``n_right`` ) Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Purpose ******* Retrieves the dimension information for a an atomic operation that computes the matrix product *R* = *A* * *B* . call_id ******* This argument identifies the dimension information for this matrix product. n_left ****** This result is the row dimension of the matrices *A* and *R* . n_middle ******** This result is the column dimension of the matrix *A* and row dimension of the matrix *B* . n_right ******* This result is the column dimension of the matrices *B* and *R* . Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_mat_mul_get.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN PROTOTYPE template void atomic_mat_mul::get( size_t call_id, size_t& n_left, size_t& n_middle, size_t& n_right ) // END PROTOTYPE { // thread size_t thread = thread_alloc::thread_num(); assert( work_[thread] != nullptr ); assert( thread == (*work_[thread])[call_id].thread ); // // n_left, n_middle, n_right call_struct& call = (*work_[thread])[call_id]; n_left = call.n_left; n_middle = call.n_middle; n_right = call.n_right; // return; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/mat_mul/hes_sparsity.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_MAT_MUL_HES_SPARSITY_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_MAT_MUL_HES_SPARSITY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_mat_mul_hes_sparsity.hpp} Atomic Matrix Multiply Jacobian Sparsity Pattern: Example Implementation ######################################################################## Purpose ******* The ``hes_sparsity`` routine overrides the virtual functions used by the atomic_four base class for Jacobian sparsity calculations; see :ref:`hes_sparsity` . Example ******* The file :ref:`atomic_four_mat_mul_sparsity.cpp-name` contains an example and test using this operator. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_mat_mul_hes_sparsity.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // // hes_sparsity override template bool atomic_mat_mul::hes_sparsity( size_t call_id , const CppAD::vector& ident_zero_x , const CppAD::vector& select_x , const CppAD::vector& select_y , CppAD::sparse_rc< CppAD::vector >& pattern_out ) { // n_left, n_middle, n_right size_t n_left, n_middle, n_right; get(call_id, n_left, n_middle, n_right); // // n size_t n = select_x.size(); // // check sizes # ifndef NDEBUG size_t m = select_y.size(); assert( n == n_middle * ( n_left + n_right ) ); assert( m == n_left * n_right ); # endif // // offset size_t offset = n_left * n_middle; // // pattern_out pattern_out.resize(n, n, 0); for(size_t i = 0; i < n_left; ++i) { for(size_t j = 0; j < n_right; ++j) { size_t ij = i * n_right + j; // C_{i,j} = y[ij] if( select_y[ij] ) for(size_t k = 0; k < n_middle; ++k) { size_t ik = i * n_middle + k; // A_{i,k} = x[ik] size_t kj = offset + k * n_right + j; // B_{k,j} = x[kj] if( select_x[ik] && select_x[kj] ) { // an (ik, kj) pair can only occur once in this loop pattern_out.push_back(ik, kj); pattern_out.push_back(kj, ik); } } } } # ifndef NDEBUG // sorting checks hat there are no duplicate entries pattern_out.row_major(); # endif // return true; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/mat_mul/implement.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin atomic_four_mat_mul_implement} Implementing Atomic Matrix Multiply ################################### Contents ******** {xrst_toc_table include/cppad/example/atomic_four/mat_mul/mat_mul.hpp include/cppad/example/atomic_four/mat_mul/set.hpp include/cppad/example/atomic_four/mat_mul/get.hpp include/cppad/example/atomic_four/mat_mul/base_mat_mul.hpp include/cppad/example/atomic_four/mat_mul/for_type.hpp include/cppad/example/atomic_four/mat_mul/forward.hpp include/cppad/example/atomic_four/mat_mul/reverse.hpp include/cppad/example/atomic_four/mat_mul/jac_sparsity.hpp include/cppad/example/atomic_four/mat_mul/hes_sparsity.hpp include/cppad/example/atomic_four/mat_mul/rev_depend.hpp } {xrst_end atomic_four_mat_mul_implement} ================================================ FILE: include/cppad/example/atomic_four/mat_mul/jac_sparsity.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_MAT_MUL_JAC_SPARSITY_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_MAT_MUL_JAC_SPARSITY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_mat_mul_jac_sparsity.hpp} Atomic Matrix Multiply Jacobian Sparsity Pattern: Example Implementation ######################################################################## Purpose ******* The ``jac_sparsity`` routine overrides the virtual functions used by the atomic_four base class for Jacobian sparsity calculations; see :ref:`jac_sparsity` . Example ******* The file :ref:`atomic_four_mat_mul_sparsity.cpp-name` contains an example and test using this operator. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_mat_mul_jac_sparsity.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // // jac_sparsity override template bool atomic_mat_mul::jac_sparsity( size_t call_id , bool dependency , const CppAD::vector& ident_zero_x , const CppAD::vector& select_x , const CppAD::vector& select_y , CppAD::sparse_rc< CppAD::vector >& pattern_out ) { // n_left, n_middle, n_right size_t n_left, n_middle, n_right; get(call_id, n_left, n_middle, n_right); // // n, m size_t n = select_x.size(); size_t m = select_y.size(); // // check sizes assert( n == n_middle * ( n_left + n_right ) ); assert( m == n_left * n_right ); // // offset size_t offset = n_left * n_middle; // // pattern_out pattern_out.resize(m, n, 0); for(size_t i = 0; i < n_left; ++i) { for(size_t j = 0; j < n_right; ++j) { size_t ij = i * n_right + j; // C_{i,j} = y[ij] if( select_y[ij] ) for(size_t k = 0; k < n_middle; ++k) { size_t ik = i * n_middle + k; // A_{i,k} = x[ik] size_t kj = offset + k * n_right + j; // B_{k,j} = x[kj] if( select_x[ik] && ! ident_zero_x[kj] ) pattern_out.push_back(ij, ik); if( select_x[kj] && ! ident_zero_x[ij] ) pattern_out.push_back(ij, kj); } } } // return true; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/mat_mul/mat_mul.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_MAT_MUL_MAT_MUL_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_MAT_MUL_MAT_MUL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_mat_mul.hpp} Atomic Matrix Multiply Class: Example Implementation #################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_mat_mul.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // template class atomic_mat_mul : public CppAD::atomic_four { // public: // ctor atomic_mat_mul(const std::string& name) : CppAD::atomic_four(name) { for(size_t thread = 0; thread < CPPAD_MAX_NUM_THREADS; ++thread) work_[thread] = nullptr; } // destructor ~atomic_mat_mul(void) { for(size_t thread = 0; thread < CPPAD_MAX_NUM_THREADS; ++thread) { if( work_[thread] != nullptr ) { // allocated in set member function delete work_[thread]; } } } // set size_t set( size_t n_left, size_t n_right, size_t n_middle ); // get void get( size_t call_id, size_t& n_left, size_t& n_right, size_t& n_middle ); private: // // matrix dimensions corresponding to a call_id struct call_struct { size_t n_left; size_t n_middle; size_t n_right; size_t thread; }; // map from call_id to matrix dimensions typedef CppAD::vector call_vector; // // Use pointers, to avoid false sharing between threads. call_vector* work_[CPPAD_MAX_NUM_THREADS]; // // base_mat_mul static void base_mat_mul( size_t n_left , size_t n_middle , size_t n_right , const CppAD::vector& x , CppAD::vector& y ); // // ----------------------------------------------------------------------- // overrides // ----------------------------------------------------------------------- // // for_type bool for_type( size_t call_id, const CppAD::vector& type_x, CppAD::vector& type_y ) override; // // Base forward bool forward( size_t call_id, const CppAD::vector& select_y, size_t order_low, size_t order_up, const CppAD::vector& taylor_x, CppAD::vector& taylor_y ) override; // // AD forward bool forward( size_t call_id, const CppAD::vector& select_y, size_t order_low, size_t order_up, const CppAD::vector< CppAD::AD >& ataylor_x, CppAD::vector< CppAD::AD >& ataylor_y ) override; // // Base reverse bool reverse( size_t call_id, const CppAD::vector& select_x, size_t order_up, const CppAD::vector& taylor_x, const CppAD::vector& taylor_y, CppAD::vector& partial_x, const CppAD::vector& partial_y ) override; // // AD reverse bool reverse( size_t call_id, const CppAD::vector& select_x, size_t order_up, const CppAD::vector< CppAD::AD >& ataylor_x, const CppAD::vector< CppAD::AD >& ataylor_y, CppAD::vector< CppAD::AD >& apartial_x, const CppAD::vector< CppAD::AD >& apartial_y ) override; // // jac_sparsity bool jac_sparsity( size_t call_id, bool dependency, const CppAD::vector& ident_zero_x, const CppAD::vector& select_x, const CppAD::vector& select_y, CppAD::sparse_rc< CppAD::vector >& pattern_out ) override; // // hes_sparsity bool hes_sparsity( size_t call_id, const CppAD::vector& ident_zero_x, const CppAD::vector& select_x, const CppAD::vector& select_y, CppAD::sparse_rc< CppAD::vector >& pattern_out ) override; // // rev_depend bool rev_depend( size_t call_id, const CppAD::vector& ident_zero_x, CppAD::vector& depend_x, const CppAD::vector& depend_y ) override; }; } // END_CPPAD_NAMESPACE # include # include # include # include # include # include # include # include # include // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/mat_mul/mat_mul.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin atomic_four_mat_mul} {xrst_spell tr } Atomic Matrix Multiply Class: Example Implementation #################################################### Syntax ****** | ``atomic_mat_mul`` *mat_mul* ( *name* ) | *call_id* = *mat_mul* . ``set`` ( *n_left* , *n_middle* , *n_right* ) | *mat_mul* . ``get`` ( *call_id* , *n_left* , *n_middle* , *n_right* ) | *mat_mul* ( *call_id* , *x* , *y* ) Purpose ******* Construct an atomic operation that computes the matrix product *C* = *A* * *B* . n_left ****** This is the row dimension of the matrices *A* and *C* . This is an argument (return value) for the ``set`` (``get`` ) routine. n_middle ******** This is the column dimension of the matrix *A* and row dimension of the matrix *B* This is an argument (return value) for the ``set`` (``get`` ) routine. n_right ******* This is the column dimension of the matrices *B* and *C* . This is an argument (return value) for the ``set`` (``get`` ) routine. call_id ******* This is a return value (argument) for the ``set`` (``get`` ) routine. x * We use *x* to denote the argument to the atomic function. The size of this vector must be *n* = *n_left* * *n_middle* + *n_middle* * *n_right* The matrix *A* is stored in row major order at the beginning of *x* ; i.e. its ( *i* , *k* ) element is *A* ( *i* , *k* ) = *x* [ *i* * *n_middle* + *k* ] The matrix *B* is stored in row major order at the end of *x* ; i.e. its ( *k* , *j* ) element is *B* ( *k* , *j* ) = *x* [ *n_left* * *n_middle* + *k* * *n_right* + *j* ] y * We use *y* to denote the result of the atomic function. The size of this vector must be *m* = *n_middle* * *n_right* . The matrix *C* is stored in row major order in *y* ; i.e. its ( *i* , *k* ) element is *C* ( *i* , *j* ) = *y* [ *i* * *n_right* + *j* ] Theory ****** Forward ======= For :math:`k = 0 , \ldots`, the *k*-th order Taylor coefficient :math:`C^{(k)}` is given by .. math:: C^{(k)} = \sum_{\ell = 0}^{k} A^{(\ell)} B^{(k-\ell)} Matrix Argument Scalar Valued Function ====================================== Suppose :math:`\bar{F}` is the derivative of the scalar value function :math:`s(F)` with respect to the matrix :math:`F`; i.e., .. math:: \bar{F}_{i,j} = \frac{ \partial s } { \partial F_{i,j} } Also suppose that :math:`t` is a scalar valued argument and .. math:: F(t) = D(t) E(t) It follows that .. math:: F'(t) = D'(t) E(t) + D(t) E'(t) .. math:: (s \circ F)'(t) = \R{tr} [ \bar{F}^\R{T} F'(t) ] .. math:: = \R{tr} [ \bar{F}^\R{T} D'(t) E(t) ] + \R{tr} [ \bar{F}^\R{T} D(t) E'(t) ] .. math:: = \R{tr} [ E(t) \bar{F}^\R{T} D'(t) ] + \R{tr} [ \bar{F}^\R{T} D(t) E'(t) ] Letting :math:`E(t) = 0` and :math:`D(t) = \Delta^{i,j} (t)` (where :math:`\Delta^{i,j} (t)` is the matrix that is zero, except for :math:`i = j` where it is :math:`t`) we have .. math:: \bar{D}_{i,j} = \frac{ \partial s } { \partial D_{i,j} } = (s \circ F)'(t) = \R{tr} [ E(t) \bar{F}^\R{T} \Delta^{i,j}(1) ] .. math:: \bar{D}_{i,j} = \sum_k D_{j,k} \bar{F}^\R{T}_{k,i} = \sum_k \bar{F}_{i,k} E^\R{T}_{k,j} .. math:: \bar{D} = \bar{F} E^\R{T} Letting :math:`D(t) = 0` and :math:`E(t) = \Delta^{i,j} (t)` we have .. math:: \bar{E}_{i,j} = \frac{ \partial s } { \partial E_{i,j} } = (s \circ F)'(t) = \R{tr} [ \bar{F}^\R{T} D(t) \Delta^{i,j} ] .. math:: \bar{E}_{i,j} = \sum_k \bar{F}^\R{T}_{j,k} C_{k,i} = \sum_k D^\R{T}_{i,k} \bar{F}_{k,j} .. math:: \bar{E} = D^\R{T} \bar{F} Reverse ======= Reverse mode eliminates :math:`C^{(k)}` as follows: for :math:`\ell = 0, \ldots , k`, .. math:: \bar{A}^{(\ell)} = \bar{A}^{(\ell)} + \bar{C}^{(k)} [ B^{(k-\ell)} ] ^\R{T} .. math:: \bar{B}^{(k-\ell)} = \bar{B}^{(k-\ell)} + [ A^{(\ell)} ]^\R{T} \bar{C}^{(k)} Contents ******** {xrst_toc_table include/cppad/example/atomic_four/mat_mul/implement.xrst example/atomic_four/mat_mul/forward.cpp example/atomic_four/mat_mul/reverse.cpp example/atomic_four/mat_mul/sparsity.cpp example/atomic_four/mat_mul/rev_depend.cpp example/atomic_four/mat_mul/identical_zero.cpp } {xrst_end atomic_four_mat_mul} ================================================ FILE: include/cppad/example/atomic_four/mat_mul/rev_depend.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_MAT_MUL_REV_DEPEND_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_MAT_MUL_REV_DEPEND_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_mat_mul_rev_depend.hpp} Atomic Matrix Multiply Reverse Dependency Analysis: Example Implementation ########################################################################## Purpose ******* The ``rev_depend`` routine is used by :ref:`optimize-name` to reduce the number of variables in the recording of a function. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_mat_mul_rev_depend.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // // rev_depend override template bool atomic_mat_mul::rev_depend( size_t call_id , const CppAD::vector& ident_zero_x, CppAD::vector& depend_x , const CppAD::vector& depend_y ) { // // n_left, n_middle, n_right size_t n_left, n_middle, n_right; get(call_id, n_left, n_middle, n_right); # ifndef NDEBUG // n, m size_t n = depend_x.size(); size_t m = depend_y.size(); // // check sizes assert( n == n_left * n_middle + n_middle * n_right ); assert( m == n_left * n_right ); # endif // // offset size_t offset = n_left * n_middle; // // type_y // y[ i * n_right + j] = sum_k // x[i * n_middle + k] * x[ offset + k * n_right + j] // type_y for(size_t i = 0; i < n_left; ++i) { for(size_t j = 0; j < n_right; ++j) { size_t ij = i * n_right + j; if( depend_y[ij] ) { for(size_t k = 0; k < n_middle; ++k) { size_t ik = i * n_middle + k; size_t kj = offset + k * n_right + j; depend_x[ik] = true; depend_x[kj] = true; } } } } return true; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/mat_mul/reverse.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_MAT_MUL_REVERSE_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_MAT_MUL_REVERSE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_mat_mul_reverse.hpp} Atomic Matrix Multiply Reverse Mode: Example Implementation ########################################################### Purpose ******* The ``reverse`` routine overrides the virtual functions used by the atomic_four base; see :ref:`reverse` . Theory ****** See mat_mul :ref:`atomic_four_mat_mul@Theory@Reverse` theory. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_mat_mul_reverse.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // // reverse override for Base matrix multiply template bool atomic_mat_mul::reverse( size_t call_id , const CppAD::vector& select_y , size_t order_up , const CppAD::vector& taylor_x , const CppAD::vector& taylor_y , CppAD::vector& partial_x , const CppAD::vector& partial_y ) { // q size_t q = order_up + 1; // // n_left, n_middle, n_right size_t n_left, n_middle, n_right; get(call_id, n_left, n_middle, n_right); # ifndef NDEBUG // n, m size_t n = taylor_x.size(); size_t m = taylor_y.size(); // // check sizes assert( n == n_middle * ( n_left + n_right ) * q ); assert( m == n_left * n_right * q ); assert( n == partial_x.size() ); assert( m == partial_y.size() ); # endif // // offset size_t x_offset = n_left * n_middle; // // u, v, u_offset // note that resize only re-allocates when capacity is not large enough CppAD::vector u; CppAD::vector v; size_t u_offset; // // partial_x for(size_t i = 0; i < partial_x.size(); ++i) partial_x[i] = Base(0); // // k size_t k = q; while(k > 0) { --k; // // for ell = 0, ..., k : // bar{A}^ell += bar{C}^k [ B^{k-ell} ]^T // bar{B}^{k-ell} += [ A^ell ]^T \bar{C}^k for(size_t ell = 0; ell < q; ++ell) { // // u = [ \bar{C}^k, B^{k-ell}^T ] u.resize(0); u.resize( n_left * n_right + n_right * n_middle ); u_offset = n_left * n_right; for(size_t i = 0; i < n_left * n_right; ++i) u[i] = partial_y[ i * q + k ]; for(size_t i = 0; i < n_middle; ++i) { for(size_t j = 0; j < n_right; ++j) { size_t ij = i * n_right + j; size_t ji = j * n_middle + i; u[u_offset + ji] = taylor_x[(x_offset + ij) * q + (k - ell) ]; } } // // v = \bar{C} * [ B^{k-ell} ]^T v.resize(0); v.resize( n_left * n_middle ); base_mat_mul(n_left, n_right, n_middle, u, v); // // \bar{A}^ell += v for(size_t i = 0; i < n_left * n_middle; ++i) partial_x[i * q + ell] += v[i]; // // u = [ A^ell^T , \bar{C}^k ] u.resize(0); u.resize( n_middle * n_left + n_left * n_right ); u_offset = n_middle * n_left; for(size_t i = 0; i < n_left; ++i) { for(size_t j = 0; j < n_middle; ++j) { size_t ij = i * n_middle + j; size_t ji = j * n_left + i; u[ji] = taylor_x[ij * q + ell]; } } for(size_t i = 0; i < n_left * n_right; ++i) u[u_offset + i] = partial_y[ i * q + k ]; // // v = [ A^ell ]^T * \bar{C}^k v.resize(0); v.resize( n_middle * n_right ); base_mat_mul(n_middle, n_left, n_right, u, v); // // \bar{B}^{k-\ell} += v for(size_t i = 0; i < n_middle * n_right; ++i) partial_x[ (x_offset + i) * q + (k - ell) ] += v[i]; } } return true; } // // reverse override for AD matrix multiply template bool atomic_mat_mul::reverse( size_t call_id , const CppAD::vector& select_y , size_t order_up , const CppAD::vector< AD >& ataylor_x , const CppAD::vector< AD >& ataylor_y , CppAD::vector< AD >& apartial_x , const CppAD::vector< AD >& apartial_y ) { // q size_t q = order_up + 1; // // n_left, n_middle, n_right size_t n_left, n_middle, n_right; get(call_id, n_left, n_middle, n_right); # ifndef NDEBUG // n, m size_t n = ataylor_x.size(); size_t m = ataylor_y.size(); // // check sizes assert( n == n_middle * ( n_left + n_right ) * q ); assert( m == n_left * n_right * q ); assert( n == apartial_x.size() ); assert( m == apartial_y.size() ); # endif // // offset size_t x_offset = n_left * n_middle; // // u, v, u_offset // note that resize only re-allocates when capacity is not large enough CppAD::vector< AD > u; CppAD::vector< AD > v; size_t u_offset; size_t i_call; // // apartial_x for(size_t i = 0; i < apartial_x.size(); ++i) apartial_x[i] = AD (0); // // k size_t k = q; while(k > 0) { --k; // // for ell = 0, ..., k : // bar{A}^ell += bar{C}^k [ B^{k-ell} ]^T // bar{B}^{k-ell} += [ A^ell ]^T \bar{C}^k for(size_t ell = 0; ell < q; ++ell) { // // u = [ \bar{C}^k, B^{k-ell}^T ] u.resize(0); u.resize( n_left * n_right + n_right * n_middle ); u_offset = n_left * n_right; for(size_t i = 0; i < n_left * n_right; ++i) u[i] = apartial_y[ i * q + k ]; for(size_t i = 0; i < n_middle; ++i) { for(size_t j = 0; j < n_right; ++j) { size_t ij = i * n_right + j; size_t ji = j * n_middle + i; u[u_offset + ji] = ataylor_x[(x_offset + ij) * q + (k - ell) ]; } } // // v = \bar{C} * [ B^{k-ell} ]^T v.resize(0); v.resize( n_left * n_middle ); i_call = set(n_left, n_right, n_middle); (*this)(i_call, u, v); // // \bar{A}^ell += v for(size_t i = 0; i < n_left * n_middle; ++i) apartial_x[i * q + ell] += v[i]; // // u = [ A^ell^T , \bar{C}^k ] u.resize(0); u.resize( n_middle * n_left + n_left * n_right ); u_offset = n_middle * n_left; for(size_t i = 0; i < n_left; ++i) { for(size_t j = 0; j < n_middle; ++j) { size_t ij = i * n_middle + j; size_t ji = j * n_left + i; u[ji] = ataylor_x[ij * q + ell]; } } for(size_t i = 0; i < n_left * n_right; ++i) u[u_offset + i] = apartial_y[ i * q + k ]; // // v = [ A^ell ]^T * \bar{C}^k v.resize(0); v.resize( n_middle * n_right ); i_call = set(n_middle, n_left, n_right); (*this)(i_call, u, v); // // \bar{B}^{k-\ell} += v for(size_t i = 0; i < n_middle * n_right; ++i) apartial_x[ (x_offset + i) * q + (k - ell) ] += v[i]; } } return true; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/mat_mul/set.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_MAT_MUL_SET_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_MAT_MUL_SET_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_mat_mul_set.hpp} atomic_mat_mul Set Routine: Example Implementation ################################################## Syntax ****** | *call_id* = *mat_mul* . ``set`` ( *n_left* , *n_middle* , *n_right* ) Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Purpose ******* Stores the dimension information for a an atomic operation that computes the matrix product *R* = *A* * *B* . n_left ****** This argument is the row dimension of the matrices *A* and *R* . n_middle ******** This argument is the column dimension of the matrix *A* and row dimension of the matrix *B* . n_right ******* This argument is the column dimension of the matrices *B* and *R* . call_id ******* This return value identifies the dimension information above. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_mat_mul_set.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN PROTOTYPE template size_t atomic_mat_mul::set( size_t n_left, size_t n_middle, size_t n_right ) // END PROTOTYPE { // thread size_t thread = thread_alloc::thread_num(); // // work_[thread] if( work_[thread] == nullptr ) work_[thread] = new call_vector; // // call_id size_t call_id = work_[thread]->size(); // // call call_struct call; call.n_left = n_left; call.n_middle = n_middle; call.n_right = n_right; call.thread = thread; // // work_[thread] work_[thread]->push_back( call ); // return call_id; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/vector/add_op.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_ADD_OP_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_ADD_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_vector_add_op.hpp} Atomic Vector Add Operator: Example Implementation ################################################## Forward Mode ************ see theory for forward mode :ref:`forward_theory@Binary Operators@Addition` . Reverse Mode ************ see theory for reverse mode :ref:`reverse_theory@Binary Operators@Addition` . Example ******* The file :ref:`atomic_four_vector_add.cpp-name` contains an example and test for this operator. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_vector_add_op.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // --------------------------------------------------------------------------- // comment below is used by atomic_vector.omh // BEGIN forward_add template void atomic_vector::forward_add( size_t m, size_t p, size_t q, const CppAD::vector& tx, CppAD::vector& ty) { for(size_t k = p; k < q; ++k) { for(size_t i = 0; i < m; ++i) { size_t u_index = i * q + k; size_t v_index = (m + i) * q + k; size_t y_index = i * q + k; // y_i^k = u_i^k + v_i^k ty[y_index] = tx[u_index] + tx[v_index]; } } } template void atomic_vector::forward_add( size_t m, size_t p, size_t q, const CppAD::vector< CppAD::AD >& atx, CppAD::vector< CppAD::AD >& aty) { size_t n = 2 * m; assert( atx.size() == n * q ); assert( aty.size() == m * q ); // // atu, atv ad_const_iterator atu = atx.begin(); ad_const_iterator atv = atu + ad_difference_type(m * q); // // ax ad_vector ax(n); ad_iterator au = ax.begin(); ad_iterator av = au + ad_difference_type(m); // // ay ad_vector ay(m); // for(size_t k = p; k < q; ++k) { // au = u^k copy_mat_to_vec(m, q, k, atu, au); // av = v^k copy_mat_to_vec(m, q, k, atv, av); // ay = au + av (*this)(add_enum, ax, ay); // atomic vector add // y^k = ay copy_vec_to_mat(m, q, k, ay.begin(), aty.begin() ); } } // END forward_add // comment above is used by atomic_vector.omh // --------------------------------------------------------------------------- // reverse_add template void atomic_vector::reverse_add( size_t m, size_t q, const CppAD::vector& tx, const CppAD::vector& ty, CppAD::vector& px, const CppAD::vector& py) { for(size_t k = 0; k < q; ++k) { for(size_t i = 0; i < m; ++i) { size_t u_index = i * q + k; size_t v_index = (m + i) * q + k; size_t y_index = i * q + k; // y_i^k = u_i^k + v_i^k px[u_index] = py[y_index]; px[v_index] = py[y_index]; } } } template void atomic_vector::reverse_add( size_t m, size_t q, const CppAD::vector< CppAD::AD >& atx, const CppAD::vector< CppAD::AD >& aty, CppAD::vector< CppAD::AD >& apx, const CppAD::vector< CppAD::AD >& apy) { // // just copying values does not add any operators to the tape. for(size_t k = 0; k < q; ++k) { for(size_t i = 0; i < m; ++i) { size_t u_index = i * q + k; size_t v_index = (m + i) * q + k; size_t y_index = i * q + k; // y_i^k = u_i^k + v_i^k apx[u_index] = apy[y_index]; apx[v_index] = apy[y_index]; } } } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/vector/div_op.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_DIV_OP_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_DIV_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_vector_div_op.hpp} Atomic Vector Divide Operator: Example Implementation ##################################################### Forward Mode ************ see theory for forward mode :ref:`forward_theory@Binary Operators@Division` . Reverse Mode ************ see theory for reverse mode :ref:`reverse_theory@Binary Operators@Division` . Example ******* The file :ref:`atomic_four_vector_div.cpp-name` contains an example and test for this operator. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_vector_div_op.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // --------------------------------------------------------------------------- // forward_div template void atomic_vector::forward_div( size_t m, size_t p, size_t q, const CppAD::vector& tx, CppAD::vector& ty) { for(size_t i = 0; i < m; ++i) { for(size_t k = p; k < q; ++k) { size_t y_index = i * q + k; size_t u_index = i * q + k; // y^k = u^k ty[y_index] = tx[u_index]; for(size_t d = 1; d <= k; d++) { size_t y_other = i * q + (k-d); size_t v_index = (i + m) * q + d; // y^k -= y^{k-d} * v^d ty[y_index] -= ty[y_other] * tx[v_index]; } size_t v0_index = (i + m) * q + 0; // y^k /= v^0 ty[y_index] /= tx[v0_index]; } } } template void atomic_vector::forward_div( size_t m, size_t p, size_t q, const CppAD::vector< CppAD::AD >& atx, CppAD::vector< CppAD::AD >& aty) { size_t n = 2 * m; assert( atx.size() == n * q ); assert( aty.size() == m * q ); // // atu, atv ad_const_iterator atu = atx.begin(); ad_const_iterator atv = atu + ad_difference_type(m * q); // // ax_div ad_vector ax_div(n); ad_iterator au_div = ax_div.begin(); ad_iterator av_div = ax_div.begin() + ad_difference_type(m); // // ax_mul ad_vector ax_mul(n); ad_iterator au_mul = ax_mul.begin(); ad_iterator av_mul = ax_mul.begin() + ad_difference_type(m); // // ax_sub ad_vector ax_sub(n); ad_iterator au_sub = ax_sub.begin(); ad_iterator av_sub = ax_sub.begin() + ad_difference_type(m); // // ay ad_vector ay(m); // for(size_t k = p; k < q; ++k) { // u_sub = u^k copy_mat_to_vec(m, q, k, atu, au_sub); for(size_t d = 1; d <= k; d++) { // u_mul = y^{k-d} copy_mat_to_vec(m, q, k-d, aty.begin(), au_mul); // v_mul = v^d copy_mat_to_vec(m, q, d, atv, av_mul); // ay = u_mul * v_mul (*this)(mul_enum, ax_mul, ay); // atomic vector multiply // v_sub = ay for(size_t i = 0; i < m; ++i) av_sub[i] = ay[i]; // ay = u_sub - v_sub (*this)(sub_enum, ax_sub, ay); // atomic vector subtract // u_sub = ay for(size_t i = 0; i < m; ++i) au_sub[i] = ay[i]; } // u_div = u_sub for(size_t i = 0; i < m; ++i) au_div[i] = *(au_sub + ad_difference_type(i)); // v_div = v^0 copy_mat_to_vec(m, q, 0, atv, av_div); // ay = u_div / v_div (*this)(div_enum, ax_div, ay); // atomic vector divide // y^k = ay copy_vec_to_mat(m, q, k, ay.begin(), aty.begin()); } } // --------------------------------------------------------------------------- // reverse_div template void atomic_vector::reverse_div( size_t m, size_t q, const CppAD::vector& tx, const CppAD::vector& ty, CppAD::vector& px, const CppAD::vector& py) { # ifndef NDEBUG size_t n = 2 * m; assert( tx.size() == n * q ); assert( ty.size() == m * q ); assert( px.size() == n * q ); assert( py.size() == m * q ); # endif // // py_copy CppAD::vector py_copy( py ); // // pv for(size_t i = 0; i < m; ++i) { for(size_t k = 0; k < q; ++k) { size_t v_index = (i + m) * q + k; px[v_index] = 0.0; } } // px for(size_t i = 0; i < m; ++i) { size_t v0_index = (i + m) * q + 0; // // k size_t k = q; while(k) { --k; // // y_index size_t y_index = i * q + k; // // py_scaled double py_scaled = py_copy[y_index] / tx[v0_index]; // for(size_t d = 1; d <= k; d++) { size_t y_other = i * q + (k-d); size_t v_index = (i + m) * q + d; // py_copy[y_other] -= py_scaled * tx[v_index]; px[v_index] -= py_scaled * ty[y_other]; } size_t u_index = i * q + k; px[u_index] = py_scaled; px[v0_index] -= py_scaled * ty[y_index]; } } } template void atomic_vector::reverse_div( size_t m, size_t q, const CppAD::vector< CppAD::AD >& atx, const CppAD::vector< CppAD::AD >& aty, CppAD::vector< CppAD::AD >& apx, const CppAD::vector< CppAD::AD >& apy) { size_t n = 2 * m; assert( atx.size() == n * q ); assert( aty.size() == m * q ); assert( apx.size() == n * q ); assert( apy.size() == m * q ); // // atu, atv, apu, apv ad_const_iterator atu = atx.begin(); ad_const_iterator atv = atu + ad_difference_type(m * q); ad_iterator apu = apx.begin(); ad_iterator apv = apu + ad_difference_type(m * q); // // ax_sub ad_vector ax_sub(n); ad_iterator au_sub = ax_sub.begin(); ad_iterator av_sub = ax_sub.begin() + ad_difference_type(m); // // ax_mul ad_vector ax_mul(n); ad_iterator au_mul = ax_mul.begin(); ad_iterator av_mul = ax_mul.begin() + ad_difference_type(m); // // ax_div ad_vector ax_div(n); ad_iterator au_div = ax_div.begin(); ad_iterator av_div = ax_div.begin() + ad_difference_type(m); // // ay, apy_scaled ad_vector ay(m), apy_scaled(m); // // apy_copy ad_vector apy_copy( apy ); // // apv for(size_t i = 0; i < m; ++i) { for(size_t k = 0; k < q; ++k) { size_t v_index = (i + m) * q + k; apx[v_index] = 0.0; } } // // av_div = atv^0 copy_mat_to_vec(m, q, 0, atv, av_div); // // k size_t k = q; while(k) { --k; // // au_div = apy^k copy_mat_to_vec(m, q, k, apy_copy.begin(), au_div); // // apy_scaled = au_div / av_dir (*this)(div_enum, ax_div, apy_scaled); // // au_mul = apy_scaled for(size_t i = 0; i < m; ++i) au_mul[i] = apy_scaled[i]; // for(size_t d = 1; d <= k; ++d) { // // av_mul = atv^d copy_mat_to_vec(m, q, d, atv, av_mul); // // ay = au_mul * av_mul (*this)(mul_enum, ax_mul, ay); // // au_sub = apy^{k-d} copy_mat_to_vec(m, q, k-d, apy_copy.begin(), au_sub); // // av_sub = ay for(size_t i = 0; i < m; ++i) av_sub[i] = ay[i]; // // ay = au_sub - av_sub (*this)(sub_enum, ax_sub, ay); // // apy^{k-d} = ay copy_vec_to_mat(m, q, k-d, ay.begin(), apy_copy.begin()); // // av_mul = aty^{k-d} copy_mat_to_vec(m, q, k-d, aty.begin(), av_mul); // // ay = au_mul * av_mul (*this)(mul_enum, ax_mul, ay); // // au_sub = apv^d copy_mat_to_vec(m, q, d, apv, au_sub); // // av_sub = ay for(size_t i = 0; i < m; ++i) av_sub[i] = ay[i]; // // ay = au_sub - av_sub (*this)(sub_enum, ax_sub, ay); // // apv^d = ay copy_vec_to_mat(m, q, d, ay.begin(), apv); } // // apu^k = apy_scaled copy_vec_to_mat(m, q, k, apy_scaled.begin(), apu); // // av_mul = aty^k copy_mat_to_vec(m, q, k, aty.begin(), av_mul); // // ay = au_mul * av_mul (*this)(mul_enum, ax_mul, ay); // // au_sub = apv^0 copy_mat_to_vec(m, q, 0, apv, au_sub); // // av_sub = ay for(size_t i = 0; i < m; ++i) av_sub[i] = ay[i]; // // ay = au_sub - av_sub (*this)(sub_enum, ax_sub, ay); // // apv^0 = ay copy_vec_to_mat(m, q, 0, ay.begin(), apv); } } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/vector/for_type.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_FOR_TYPE_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_FOR_TYPE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_vector_for_type.hpp} Atomic Vector Forward Type Calculation: Example Implementation ############################################################## Purpose ******* The ``for_type`` routine overrides the virtual functions used by the atomic_four base; see :ref:`for_type` . Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_vector_for_type.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // // for_type override template bool atomic_vector::for_type( size_t call_id , const CppAD::vector& type_x , CppAD::vector& type_y ) { // n, m, op size_t n = type_x.size(); size_t m = type_y.size(); op_enum_t op = op_enum_t( call_id ); // // type_y if( n == m ) { // unary operator for(size_t i = 0; i < m; ++i) type_y[i] = type_x[i]; } else { // binary operator for(size_t i = 0; i < m; ++i) type_y[i] = std::max( type_x[i] , type_x[m + i] ); } switch(op) { // addition, subtraction // not sure result is identically 0 unless both are identically 0 case add_enum: case sub_enum: for(size_t i = 0; i < m; ++i) type_y[i] = std::max( type_x[i] , type_x[m + i] ); break; // multiplication // treat multiplication by zero like absolute zero case mul_enum: for(size_t i = 0; i < m; ++i) { if( type_x[i] == identical_zero_enum ) type_y[i] = identical_zero_enum; else if( type_x[m + i] == identical_zero_enum ) type_y[i] = identical_zero_enum; else type_y[i] = std::max( type_x[i] , type_x[m + i] ); } break; // division // treat divition of zero like absolute zero case div_enum: for(size_t i = 0; i < m; ++i) { if( type_x[i] == identical_zero_enum ) type_y[i] = identical_zero_enum; else type_y[i] = std::max( type_x[i] , type_x[m + i] ); } break; // unary minus case neg_enum: for(size_t i = 0; i < m; ++i) type_y[i] = type_x[i]; break; // error case number_op_enum: assert(false); break; } return true; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/vector/forward_op.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_FORWARD_OP_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_FORWARD_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_vector_forward_op.hpp} Atomic Vector Forward Mode: Example Implementation ################################################## Purpose ******* The ``forward`` routine overrides the virtual functions used by the atomic_four base class for forward mode calculations; see :ref:`forward` . It determines which operator is specified for this call and transfers the call to the operator's implementation. There are two versions of the ``forward`` routine, one for *Base* and one for ``AD`` . Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_vector_forward_op.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // // forward override // this routine called by ADFun objects template bool atomic_vector::forward( size_t call_id, const CppAD::vector& select_y, size_t order_low, size_t order_up, const CppAD::vector& tx, CppAD::vector& ty) { // p, q size_t p = order_low; size_t q = order_up + 1; CPPAD_ASSERT_UNKNOWN( tx.size() % q == 0 ); // // op, m op_enum_t op = op_enum_t( call_id ); size_t n = tx.size() / q; size_t m = n / 2; if( is_unary(op) ) m = n; CPPAD_ASSERT_UNKNOWN( ty.size() == m * q ); // bool ok = false; switch(op) { // addition case add_enum: forward_add(m, p, q, tx, ty); ok = true; break; // subtraction case sub_enum: forward_sub(m, p, q, tx, ty); ok = true; break; // multiplication case mul_enum: forward_mul(m, p, q, tx, ty); ok = true; break; // division case div_enum: forward_div(m, p, q, tx, ty); ok = true; break; // unary minus case neg_enum: forward_neg(m, p, q, tx, ty); ok = true; break; // error case number_op_enum: assert(false); break; } return ok; } // forward override // this routine called by ADFun< CppAD::AD , Base> objects template bool atomic_vector::forward( size_t call_id, const CppAD::vector& select_y, size_t order_low, size_t order_up, const CppAD::vector< CppAD::AD >& atx, CppAD::vector< CppAD::AD >& aty ) { // p, q size_t p = order_low; size_t q = order_up + 1; CPPAD_ASSERT_UNKNOWN( atx.size() % q == 0 ); // // op, m op_enum_t op = op_enum_t( call_id ); size_t n = atx.size() / q; size_t m = n / 2; if( is_unary(op) ) m = n; CPPAD_ASSERT_UNKNOWN( aty.size() == q * m ); // bool ok = false; switch(op) { // addition case add_enum: forward_add(m, p, q, atx, aty); ok = true; break; // subtraction case sub_enum: forward_sub(m, p, q, atx, aty); ok = true; break; // multiplication case mul_enum: forward_mul(m, p, q, atx, aty); ok = true; break; // division case div_enum: forward_div(m, p, q, atx, aty); ok = true; break; // unary minus case neg_enum: forward_neg(m, p, q, atx, aty); ok = true; break; // error case number_op_enum: assert(false); break; } return ok; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/vector/hes_sparsity.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_HES_SPARSITY_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_HES_SPARSITY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_vector_hes_sparsity.hpp} Atomic Vector Hessian Sparsity Pattern: Example Implementation ############################################################## Purpose ******* The ``hes_sparsity`` routine overrides the virtual functions used by the atomic_four base class for Jacobian sparsity calculations; see :ref:`hes_sparsity` . Example ******* The file :ref:`atomic_four_vector_hes_sparsity.cpp-name` contains an example and test using this operator. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_vector_hes_sparsity.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // // hes_sparsity override template bool atomic_vector::hes_sparsity( size_t call_id , const CppAD::vector& ident_zero_x , const CppAD::vector& select_x , const CppAD::vector& select_y , CppAD::sparse_rc< CppAD::vector >& pattern_out ) { size_t n = select_x.size(); size_t m = select_y.size(); assert( n == m || n == 2 * m ); // // op op_enum_t op = op_enum_t( call_id ); // switch(op) { // linear operator cases case add_enum: case sub_enum: case neg_enum: // // pattern_out is empty pattern_out.resize(n, n, 0); return true; default: break; } // // nnz // number of non-zeros in sparsity pattern size_t nnz = 0; for(size_t i = 0; i < m; ++i) if( select_y[i] ) { size_t j = i; if( select_x[j] && op != mul_enum ) ++nnz; if( n != m ) { // binary operator j = m + i; if( select_x[j] ) nnz += 2; } } // // pattern_out pattern_out.resize(n, n, nnz); size_t k = 0; for(size_t i = 0; i < m; ++i) if( select_y[i] ) { size_t j = i; if( select_x[j] && op != mul_enum ) pattern_out.set(k++, i, j); if( n != m ) { // binary operator j = m + i; if( select_x[j] ) { pattern_out.set(k++, i, j); pattern_out.set(k++, j, i); } } } assert( k == nnz); // return true; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/vector/implement.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin atomic_four_vector_implement} Implementing Atomic Vector Operations ##################################### Contents ******** {xrst_toc_table include/cppad/example/atomic_four/vector/vector.hpp include/cppad/example/atomic_four/vector/forward_op.hpp include/cppad/example/atomic_four/vector/reverse_op.hpp include/cppad/example/atomic_four/vector/jac_sparsity.hpp include/cppad/example/atomic_four/vector/hes_sparsity.hpp include/cppad/example/atomic_four/vector/for_type.hpp include/cppad/example/atomic_four/vector/rev_depend.hpp include/cppad/example/atomic_four/vector/add_op.hpp include/cppad/example/atomic_four/vector/sub_op.hpp include/cppad/example/atomic_four/vector/mul_op.hpp include/cppad/example/atomic_four/vector/div_op.hpp include/cppad/example/atomic_four/vector/neg_op.hpp } {xrst_end atomic_four_vector_implement} ================================================ FILE: include/cppad/example/atomic_four/vector/jac_sparsity.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_JAC_SPARSITY_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_JAC_SPARSITY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_vector_jac_sparsity.hpp} Atomic Vector Jacobian Sparsity Pattern: Example Implementation ############################################################### Purpose ******* The ``jac_sparsity`` routine overrides the virtual functions used by the atomic_four base class for Jacobian sparsity calculations; see :ref:`jac_sparsity` . Example ******* The file :ref:`atomic_four_vector_jac_sparsity.cpp-name` contains an example and test using this operator. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_vector_jac_sparsity.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // // jac_sparsity override template bool atomic_vector::jac_sparsity( size_t call_id , bool dependency , const CppAD::vector& ident_zero_x , const CppAD::vector& select_x , const CppAD::vector& select_y , CppAD::sparse_rc< CppAD::vector >& pattern_out ) { size_t n = select_x.size(); size_t m = select_y.size(); assert( n == m || n == 2 * m ); // // nnz // number of non-zeros in sparsity pattern size_t nnz = 0; for(size_t i = 0; i < m; ++i) if( select_y[i] ) { size_t j = i; if( select_x[j] ) ++nnz; if( n != m ) { // binary operator j = m + i; if( select_x[j] ) ++nnz; } } // // pattern_out pattern_out.resize(m, n, nnz); size_t k = 0; for(size_t i = 0; i < m; ++i) if( select_y[i] ) { size_t j = i; if( select_x[j] ) pattern_out.set(k++, i, j); if( n != m ) { // binary operator j = m + i; if( select_x[j] ) pattern_out.set(k++, i, j); } } assert( k == nnz); // return true; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/vector/mul_op.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_MUL_OP_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_MUL_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_vector_mul_op.hpp} Atomic Vector Multiply Operator: Example Implementation ####################################################### Forward Mode ************ see theory for forward mode :ref:`forward_theory@Binary Operators@Multiplication` . Reverse Mode ************ see theory for reverse mode :ref:`reverse_theory@Binary Operators@Multiplication` . Example ******* The file :ref:`atomic_four_vector_mul.cpp-name` contains an example and test for this operator. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_vector_mul_op.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // -------------------------------------------------------------------------- // forward_mul template void atomic_vector::forward_mul( size_t m, size_t p, size_t q, const CppAD::vector& tx, CppAD::vector& ty) { for(size_t i = 0; i < m; ++i) { for(size_t k = p; k < q; ++k) { size_t y_index = i * q + k; // y^k = 0 ty[y_index] = 0.0; for(size_t d = 0; d <= k; d++) { size_t u_index = i * q + (k-d); size_t v_index = (m + i) * q + d; // y^k += u^{k-d} * v^d ty[y_index] += tx[u_index] * tx[v_index]; } } } } template void atomic_vector::forward_mul( size_t m, size_t p, size_t q, const CppAD::vector< CppAD::AD >& atx, CppAD::vector< CppAD::AD >& aty) { size_t n = 2 * m; assert( atx.size() == n * q ); assert( aty.size() == m * q ); // // atu, atv ad_const_iterator atu = atx.begin(); ad_const_iterator atv = atu + ad_difference_type(m * q); // // ax_mul ad_vector ax_mul(n); ad_iterator au_mul = ax_mul.begin(); ad_iterator av_mul = ax_mul.begin() + ad_difference_type(m); // // ax_add ad_vector ax_add(n); ad_iterator au_add = ax_add.begin(); ad_iterator av_add = ax_add.begin() + ad_difference_type(m); // // ay ad_vector ay(m); // for(size_t k = p; k < q; ++k) { // ay = 0 for(size_t i = 0; i < m; ++i) ay[i] = 0.0; for(size_t d = 0; d <= k; d++) { // u_add = ay for(size_t i = 0; i < m; ++i) au_add[i] = ay[i]; // // au_mul = u^{k-d} copy_mat_to_vec(m, q, k-d, atu, au_mul); // // av_mul = v^d copy_mat_to_vec(m, q, d, atv, av_mul); // // ay = au_mul * av_mul (*this)(mul_enum, ax_mul, ay); // atomic vector multiply // // v_add = ay for(size_t i = 0; i < m; ++i) av_add[i] = ay[i]; // // ay = u_add + v_add (*this)(add_enum, ax_add, ay); // atomic vector add } // y^k = ay copy_vec_to_mat(m, q, k, ay.begin(), aty.begin()); } } // -------------------------------------------------------------------------- // reverse_mul template void atomic_vector::reverse_mul( size_t m, size_t q, const CppAD::vector& tx, const CppAD::vector& ty, CppAD::vector& px, const CppAD::vector& py) { size_t n = 2 * m; assert( tx.size() == n * q ); assert( ty.size() == m * q ); assert( px.size() == n * q ); assert( py.size() == m * q ); // // px for(size_t j = 0; j < n; ++j) { for(size_t k = 0; k < q; ++k) px[j * q + k] = 0.0; } // // px for(size_t i = 0; i < m; ++i) { // k size_t k = q; while(k) { --k; // // y_index size_t y_index = i * q + k; // // px for(size_t d = 0; d <= k; ++d) { size_t u_index = i * q + (k-d); size_t v_index = (m + i) * q + d; // // must use azmul because py[y_index] = 0 may mean that this // component of the function was not selected. px[u_index] += CppAD::azmul( py[y_index] , tx[v_index] ); px[v_index] += CppAD::azmul( py[y_index] , tx[u_index] ); } } } } template void atomic_vector::reverse_mul( size_t m, size_t q, const CppAD::vector< CppAD::AD >& atx, const CppAD::vector< CppAD::AD >& aty, CppAD::vector< CppAD::AD >& apx, const CppAD::vector< CppAD::AD >& apy) { size_t n = 2 * m; assert( atx.size() == n * q ); assert( aty.size() == m * q ); assert( apx.size() == n * q ); assert( apy.size() == m * q ); // // atu, atv, apu, apv ad_const_iterator atu = atx.begin(); ad_const_iterator atv = atu + ad_difference_type(m * q); ad_iterator apu = apx.begin(); ad_iterator apv = apu + ad_difference_type(m * q); // // ax_mul // need azmul_op but it is not yet available ad_vector ax_mul(n); ad_iterator au_mul = ax_mul.begin(); ad_iterator av_mul = ax_mul.begin() + ad_difference_type(m); // // ax_add ad_vector ax_add(n); ad_iterator au_add = ax_add.begin(); ad_iterator av_add = ax_add.begin() + ad_difference_type(m); // // ay ad_vector ay(m); // // px // assigning to the value zero does not create operators on the tape for(size_t j = 0; j < n; ++j) { for(size_t k = 0; k < q; ++k) apx[j * q + k] = 0.0; } // // k size_t k = q; while(k) { --k; // // au_mul = apy^k copy_mat_to_vec(m, q, k, apy.begin(), au_mul); // // d for(size_t d = 0; d <=k; ++d) { // ------------------------------------------------------------- // reverse: // px[v_index] += CppAD::azmul( py[y_index] , tx[u_index] ); // ------------------------------------------------------------- // av_mul = atu^{k-d} copy_mat_to_vec(m, q, k-d, atu, av_mul); // // ay = au_mul * av_mul (*this)(mul_enum, ax_mul, ay); // // au_add = ay for(size_t i = 0; i < m; ++i) au_add[i] = ay[i]; // // av_add = apv^d copy_mat_to_vec(m, q, d, apv, av_add); // // ay = au_add + av_add (*this)(add_enum, ax_add, ay); // // apv^d = ay copy_vec_to_mat(m, q, d, ay.begin(), apv); // ------------------------------------------------------------- // reverse: // px[u_index] += CppAD::azmul( py[y_index] , tx[v_index] ); // ------------------------------------------------------------- // av_mul = atv^{k-d} copy_mat_to_vec(m, q, k-d, atv, av_mul); // // ay = au_mul * av_mul (*this)(mul_enum, ax_mul, ay); // // au_add = ay for(size_t i = 0; i < m; ++i) au_add[i] = ay[i]; // // av_add = apu^d copy_mat_to_vec(m, q, d, apu, av_add); // // ay = au_add + av_add (*this)(add_enum, ax_add, ay); // // apu^d = ay copy_vec_to_mat(m, q, d, ay.begin(), apu); } } } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/vector/neg_op.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_NEG_OP_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_NEG_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_vector_neg_op.hpp} Atomic Vector Negative Operator: Example Implementation ####################################################### Example ******* The file :ref:`atomic_four_vector_neg.cpp-name` contains an example and test for this operator. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_vector_neg_op.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // --------------------------------------------------------------------------- template void atomic_vector::forward_neg( size_t m, size_t p, size_t q, const CppAD::vector& tx, CppAD::vector& ty) { for(size_t k = p; k < q; ++k) { for(size_t i = 0; i < m; ++i) { size_t u_index = i * q + k; size_t y_index = i * q + k; // y_i^k = - u_i^k ty[y_index] = - tx[u_index]; } } } template void atomic_vector::forward_neg( size_t m, size_t p, size_t q, const CppAD::vector< CppAD::AD >& atx, CppAD::vector< CppAD::AD >& aty) { size_t n = m; assert( atx.size() == n * q ); assert( aty.size() == m * q ); // // atu ad_const_iterator atu = atx.begin(); // // ax ad_vector ax(n); ad_iterator au = ax.begin(); // // ay ad_vector ay(m); // for(size_t k = p; k < q; ++k) { // au = u^k copy_mat_to_vec(m, q, k, atu, au); // ay = - au (*this)(neg_enum, ax, ay); // atomic vector neg // y^k = ay copy_vec_to_mat(m, q, k, ay.begin(), aty.begin() ); } } // --------------------------------------------------------------------------- // reverse_neg template void atomic_vector::reverse_neg( size_t m, size_t q, const CppAD::vector& tx, const CppAD::vector& ty, CppAD::vector& px, const CppAD::vector& py) { for(size_t k = 0; k < q; ++k) { for(size_t i = 0; i < m; ++i) { size_t u_index = i * q + k; size_t y_index = i * q + k; // y_i^k = - u_i^k px[u_index] = - py[y_index]; } } } template void atomic_vector::reverse_neg( size_t m, size_t q, const CppAD::vector< CppAD::AD >& atx, const CppAD::vector< CppAD::AD >& aty, CppAD::vector< CppAD::AD >& apx, const CppAD::vector< CppAD::AD >& apy) { size_t n = m; assert( atx.size() == n * q ); assert( aty.size() == m * q ); // // apu ad_iterator apu = apx.begin(); // // ax ad_vector ax(n); ad_iterator au = ax.begin(); // // ay ad_vector ay(m); // for(size_t k = 0; k < q; ++k) { // au = py^k copy_mat_to_vec(m, q, k, apy.begin(), au); // ay = - au (*this)(neg_enum, ax, ay); // atomic vector neg // pu^k = ay copy_vec_to_mat(m, q, k, ay.begin(), apu); } } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/vector/rev_depend.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_REV_DEPEND_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_REV_DEPEND_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_vector_rev_depend.hpp} Atomic Vector Forward Type Calculation: Example Implementation ############################################################## Purpose ******* The ``rev_depend`` routine overrides the virtual functions used by the atomic_four base class for Jacobian sparsity calculations; see :ref:`rev_depend` . Example ******* The file :ref:`atomic_four_vector_rev_depend.cpp-name` contains an example and test that uses this member function. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_vector_rev_depend.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // // rev_depend override template bool atomic_vector::rev_depend( size_t call_id , const CppAD::vector& ident_zero_x, CppAD::vector& depend_x , const CppAD::vector& depend_y ) { // n, m size_t n = depend_x.size(); size_t m = depend_y.size(); // // type_y if( n == m ) { // unary operator for(size_t i = 0; i < m; ++i) depend_x[i] = depend_y[i]; } else { // binary operator for(size_t i = 0; i < m; ++i) { depend_x[i] = depend_y[i]; depend_x[m + i] = depend_y[i]; } } return true; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/vector/reverse_op.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_REVERSE_OP_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_REVERSE_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_vector_reverse_op.hpp} Atomic Vector Forward Mode: Example Implementation ################################################## Purpose ******* The ``reverse`` routine overrides the virtual functions used by the atomic_four base class for reverse mode calculations; see :ref:`reverse` . It determines which operator is specified for this call and transfers the call to the operator's implementation. There are two versions of the ``reverse`` routine, one for *Base* and one for ``AD`` . Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_vector_reverse_op.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // // reverse override // this routine used by ADFun objects template bool atomic_vector::reverse( size_t call_id, const CppAD::vector& select_x, size_t order_up, const CppAD::vector& tx, const CppAD::vector& ty, CppAD::vector& px, const CppAD::vector& py) { // q size_t q = order_up + 1; // // op, n, m op_enum_t op = op_enum_t( call_id ); size_t n = select_x.size(); size_t m = n / 2; if( is_unary(op) ) m = n; assert( tx.size() == q * n ); assert( ty.size() == q * m ); // bool ok = false; switch(op) { // addition case add_enum: reverse_add(m, q, tx, ty, px, py); ok = true; break; // subtraction case sub_enum: reverse_sub(m, q, tx, ty, px, py); ok = true; break; // multiplication case mul_enum: reverse_mul(m, q, tx, ty, px, py); ok = true; break; // division case div_enum: reverse_div(m, q, tx, ty, px, py); ok = true; break; // unary minus case neg_enum: reverse_neg(m, q, tx, ty, px, py); ok = true; break; // error case number_op_enum: assert(false); break; } return ok; } // reverse override // this routine used by ADFun< CppAD::AD , Base> objects template bool atomic_vector::reverse( size_t call_id, const CppAD::vector& select_x, size_t order_up, const CppAD::vector< CppAD::AD >& atx, const CppAD::vector< CppAD::AD >& aty, CppAD::vector< CppAD::AD >& apx, const CppAD::vector< CppAD::AD >& apy) { // q size_t q = order_up + 1; // // op, m op_enum_t op = op_enum_t( call_id ); size_t n = select_x.size(); size_t m = n / 2; if( is_unary(op) ) m = n; assert( atx.size() == q * n ); assert( aty.size() == q * m ); bool ok = false; switch(op) { // addition case add_enum: reverse_add(m, q, atx, aty, apx, apy); ok = true; break; // subtraction case sub_enum: reverse_sub(m, q, atx, aty, apx, apy); ok = true; break; // multiplication case mul_enum: reverse_mul(m, q, atx, aty, apx, apy); ok = true; break; // division case div_enum: reverse_div(m, q, atx, aty, apx, apy); ok = true; break; // unary minus case neg_enum: reverse_neg(m, q, atx, aty, apx, apy); ok = true; break; // error case number_op_enum: assert(false); break; } return ok; } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/vector/sub_op.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_SUB_OP_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_SUB_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_vector_sub_op.hpp} Atomic Vector Subtract Operator: Example Implementation ####################################################### Forward Mode ************ see theory for forward mode :ref:`forward_theory@Binary Operators@Subtraction` . Reverse Mode ************ see theory for reverse mode :ref:`reverse_theory@Binary Operators@Subtraction` . Example ******* The file :ref:`atomic_four_vector_sub.cpp-name` contains an example and test for this operator. Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_vector_sub_op.hpp} */ // BEGIN C++ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // -------------------------------------------------------------------------- // forward_sub template void atomic_vector::forward_sub( size_t m, size_t p, size_t q, const CppAD::vector& tx, CppAD::vector& ty) { for(size_t k = p; k < q; ++k) { for(size_t i = 0; i < m; ++i) { size_t u_index = i * q + k; size_t v_index = (m + i) * q + k; size_t y_index = i * q + k; // y_i^k = u_i^k - v_i^k ty[y_index] = tx[u_index] - tx[v_index]; } } } template void atomic_vector::forward_sub( size_t m, size_t p, size_t q, const CppAD::vector< CppAD::AD >& atx, CppAD::vector< CppAD::AD >& aty) { size_t n = 2 * m; assert( atx.size() == n * q ); assert( aty.size() == m * q ); // // atu, atv ad_const_iterator atu = atx.begin(); ad_const_iterator atv = atu + ad_difference_type(m * q); // // ax ad_vector ax(n); ad_iterator au = ax.begin(); ad_iterator av = au + ad_difference_type(m); // // ay ad_vector ay(m); // for(size_t k = p; k < q; ++k) { // au = u^k copy_mat_to_vec(m, q, k, atu, au); // av = v^k copy_mat_to_vec(m, q, k, atv, av); // ay = au - av (*this)(sub_enum, ax, ay); // atomic vector sub // y^k = ay copy_vec_to_mat(m, q, k, ay.begin(), aty.begin()); } } // -------------------------------------------------------------------------- // reverse_sub template void atomic_vector::reverse_sub( size_t m, size_t q, const CppAD::vector& tx, const CppAD::vector& ty, CppAD::vector& px, const CppAD::vector& py) { for(size_t k = 0; k < q; ++k) { for(size_t i = 0; i < m; ++i) { size_t u_index = i * q + k; size_t v_index = (m + i) * q + k; size_t y_index = i * q + k; // y_i^k = u_i^k + v_i^k px[u_index] = py[y_index]; px[v_index] = - py[y_index]; } } } template void atomic_vector::reverse_sub( size_t m, size_t q, const CppAD::vector< CppAD::AD >& atx, const CppAD::vector< CppAD::AD >& aty, CppAD::vector< CppAD::AD >& apx, const CppAD::vector< CppAD::AD >& apy) { # ifndef NDEBUG size_t n = 2 * m; assert( atx.size() == n * q ); assert( aty.size() == m * q ); assert( apx.size() == n * q ); assert( apy.size() == m * q ); # endif // // apu, apv ad_iterator apu = apx.begin(); ad_iterator apv = apu + ad_difference_type(m * q); // // ax ad_vector ax(m); ad_iterator au = ax.begin(); // // ay ad_vector ay(m); // for(size_t k = 0; k < q; ++k) { // au = apy^k copy_mat_to_vec(m, q, k, apy.begin(), au); // apu^k = au copy_vec_to_mat(m, q, k, au, apu); // ay = - au (*this)(neg_enum, ax, ay); // atomic vector neg // apv^k = ay copy_vec_to_mat(m, q, k, ay.begin(), apv); } } } // END_CPPAD_NAMESPACE // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/vector/vector.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_VECTOR_HPP # define CPPAD_EXAMPLE_ATOMIC_FOUR_VECTOR_VECTOR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_four_vector.hpp} Atomic Vector Class: Example Implementation ########################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end atomic_four_vector.hpp} */ // BEGIN C++ # include // declare forward_op and reverse_op for Base and AD cases // where op is an operator name; e.g., forward_add and reverse_add. # define CPPAD_ATOMIC_FOUR_FORWARD_AND_REVERSE(op) \ static void forward_ ## op( \ size_t m, \ size_t p, \ size_t q, \ const CppAD::vector& tx, \ CppAD::vector& ty \ ); \ void forward_ ## op( \ size_t m, \ size_t p, \ size_t q, \ const CppAD::vector< CppAD::AD >& atx, \ CppAD::vector< CppAD::AD >& aty \ ); \ static void reverse_ ## op( \ size_t m, \ size_t q, \ const CppAD::vector& tx, \ const CppAD::vector& ty, \ CppAD::vector& px, \ const CppAD::vector& py \ ); \ void reverse_ ## op( \ size_t m, \ size_t q, \ const CppAD::vector< CppAD::AD >& atx, \ const CppAD::vector< CppAD::AD >& aty, \ CppAD::vector< CppAD::AD >& apx, \ const CppAD::vector< CppAD::AD >& apy \ ); namespace CppAD { // BEGIN_CPPAD_NAMESPACE // template class atomic_vector : public CppAD::atomic_four { // public: // BEGIN_SORT_THIS_LINE_PLUS_4 // BEGIN op_enum_t // atomic_vector::op_enum_t typedef enum { add_enum, div_enum, mul_enum, neg_enum, sub_enum, number_op_enum } op_enum_t; // END op_enum_t // END_SORT_THIS_LINE_MINUS_4 // // ctor atomic_vector(const std::string& name) : CppAD::atomic_four(name) { } private: typedef CppAD::vector< CppAD::AD > ad_vector; typedef typename ad_vector::iterator ad_iterator; typedef typename ad_vector::const_iterator ad_const_iterator; typedef typename ad_iterator::difference_type ad_difference_type; // static bool is_unary(op_enum_t op) { bool result = true; switch(op) { case add_enum: case sub_enum: case mul_enum: case div_enum: result = false; break; default: break; } return result; } // ------------------------------------------------------------------------ // copy routines // ------------------------------------------------------------------------ static void copy_vec_to_mat( size_t m, size_t q, size_t k , ad_const_iterator vec, ad_iterator mat) { for(size_t i = 0; i < m; ++i) { size_t index = i * q + k; *(mat + ad_difference_type(index) ) = *(vec + ad_difference_type(i) ); } } // copy_mat_to_vec static void copy_mat_to_vec( size_t m, size_t q, size_t k, ad_const_iterator mat, ad_iterator vec) { for(size_t i = 0; i < m; ++i) { size_t index = i * q + k; *(vec + ad_difference_type(i) ) = *(mat + ad_difference_type(index) ); } } // ----------------------------------------------------------------------- // overrides // ----------------------------------------------------------------------- // // for_type bool for_type( size_t call_id , const CppAD::vector& type_x , CppAD::vector& type_y ) override; // // rev_depend bool rev_depend( size_t call_id , const CppAD::vector& ident_zero_x , CppAD::vector& depend_x , const CppAD::vector& depend_y ) override; // // jac_sparsity bool jac_sparsity( size_t call_id , bool dependency , const CppAD::vector& ident_zero_x , const CppAD::vector& select_x , const CppAD::vector& select_y , CppAD::sparse_rc< CppAD::vector >& pattern_out ) override; // // hes_sparsity bool hes_sparsity( size_t call_id , const CppAD::vector& ident_zero_x , const CppAD::vector& select_x , const CppAD::vector& select_y , CppAD::sparse_rc< CppAD::vector >& pattern_out ) override; // // Base forward bool forward( size_t call_id, const CppAD::vector& select_y, size_t order_low, size_t order_up, const CppAD::vector& tx, CppAD::vector& ty ) override; // // AD forward bool forward( size_t call_id, const CppAD::vector& select_y, size_t order_low, size_t order_up, const CppAD::vector< CppAD::AD >& atx, CppAD::vector< CppAD::AD >& aty ) override; // // Base reverse bool reverse( size_t call_id, const CppAD::vector& select_x, size_t order_up, const CppAD::vector& tx, const CppAD::vector& ty, CppAD::vector& px, const CppAD::vector& py ) override; // // AD reverse bool reverse( size_t call_id, const CppAD::vector& select_x, size_t order_up, const CppAD::vector< CppAD::AD >& atx, const CppAD::vector< CppAD::AD >& aty, CppAD::vector< CppAD::AD >& apx, const CppAD::vector< CppAD::AD >& apy ) override; // ---------------------------------------------------------------------- // Forward and Reverse Implementation Routines // ---------------------------------------------------------------------- CPPAD_ATOMIC_FOUR_FORWARD_AND_REVERSE(add) CPPAD_ATOMIC_FOUR_FORWARD_AND_REVERSE(sub) CPPAD_ATOMIC_FOUR_FORWARD_AND_REVERSE(mul) CPPAD_ATOMIC_FOUR_FORWARD_AND_REVERSE(div) CPPAD_ATOMIC_FOUR_FORWARD_AND_REVERSE(neg) }; } // END_CPPAD_NAMESPACE # undef CPPAD_ATOMIC_FOUR_FORWARD_AND_REVERSE # include # include # include # include # include # include # include # include # include # include # include // END C++ # endif ================================================ FILE: include/cppad/example/atomic_four/vector/vector.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin atomic_four_vector} Atomic Vector Element-wise Operators: Example and Test ###################################################### Syntax ****** | ``atomic_vector_op`` *vec_op* ( *name* ) | *vec_op* ( *op* , *x* , *y* ) op ** The value *op* has the following possible values: {xrst_literal include/cppad/example/atomic_four/vector/vector.hpp // BEGIN op_enum_t // END op_enum_t } Purpose ******* This atomic function class can be used as a general purpose utility. It is unclear how much benefit there is do doing so. This is because the number of operations internal to an element-wise atomic function is not much more than the work required to pass the arguments to the atomic function. Vector Operations ================= This atomic function unary operations *y* = *op* ( ``u`` ) and binary operations *y* = *u* *op* *v* where *op* , *u* and *v* are defined below. atomic_four =========== This example demonstrates all the callbacks for an :ref:`atomic_four-name` function. base2ad ======= It include examples for how one can define ``AD`` < *Base* > atomic operations using atomic operators. This avoids expanding the atomic operator to an operator for each element when recording derivative calculations. For example, notice the difference between ``forward_add`` for the ``double`` and the ``AD`` cases (note that copying an AD variable does not create a new variable): {xrst_literal include/cppad/example/atomic_four/vector/add_op.hpp // BEGIN forward_add // END forward_add } x * We use *x* to denote the argument to the atomic function. The length of *x* is denoted by *n* . m * This is the length of the vectors in the operations. In the case of unary (binary) operators *m* = *n* ( *m* = *n* / 2 ). u * We use *u* to denote the following sub-vector of *x* : *u* = ( *x* [1] , ... , *x* [ *m* ] ) v * For binary operators, we use *v* to denote the following sub-vector of *x* : *v* = ( *x* [ *m* + 1] , ... , *x* [2 * *m* ] ) y * We use *y* to denote the atomic function return value. The length of *y* is equal to *m* . AD ********** During ``AD`` operations, copying variables from one vector to another does not add any operations to the resulting tape. Contents ******** {xrst_comment BEGIN_SORT_THIS_LINE_PLUS_2} {xrst_toc_table example/atomic_four/vector/add.cpp example/atomic_four/vector/div.cpp example/atomic_four/vector/hes_sparsity.cpp example/atomic_four/vector/jac_sparsity.cpp example/atomic_four/vector/mul.cpp example/atomic_four/vector/neg.cpp example/atomic_four/vector/rev_depend.cpp example/atomic_four/vector/sub.cpp include/cppad/example/atomic_four/vector/implement.xrst } {xrst_comment END_SORT_THIS_LINE_MINUS_2} {xrst_end atomic_four_vector} ================================================ FILE: include/cppad/example/atomic_three/mat_mul.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_THREE_MAT_MUL_HPP # define CPPAD_EXAMPLE_ATOMIC_THREE_MAT_MUL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_three_mat_mul.hpp} {xrst_spell nr px tx } Matrix Multiply as an Atomic Operation ###################################### See Also ******** :ref:`atomic_two_eigen_mat_mul.hpp-name` Purpose ******* Use scalar ``double`` operations in an :ref:`atomic_three-name` operation that computes the matrix product for ``AD namespace { // Begin empty namespace using CppAD::vector; // // matrix result = left * right class atomic_mat_mul : public CppAD::atomic_three { /* {xrst_code} {xrst_spell_on} Constructor *********** {xrst_spell_off} {xrst_code cpp} */ public: // --------------------------------------------------------------------- // constructor atomic_mat_mul(void) : CppAD::atomic_three("mat_mul") { } private: /* {xrst_code} {xrst_spell_on} Left Operand Element Index ************************** Index in the Taylor coefficient matrix *tx* of a left matrix element. {xrst_spell_off} {xrst_code cpp} */ size_t left( size_t i , // left matrix row index size_t j , // left matrix column index size_t k , // Taylor coeffocient order size_t nk , // number of Taylor coefficients in tx size_t nr_left , // rows in left matrix size_t n_middle , // rows in left and columns in right size_t nc_right ) // columns in right matrix { assert( i < nr_left ); assert( j < n_middle ); return (3 + i * n_middle + j) * nk + k; } /* {xrst_code} {xrst_spell_on} Right Operand Element Index *************************** Index in the Taylor coefficient matrix *tx* of a right matrix element. {xrst_spell_off} {xrst_code cpp} */ size_t right( size_t i , // right matrix row index size_t j , // right matrix column index size_t k , // Taylor coeffocient order size_t nk , // number of Taylor coefficients in tx size_t nr_left , // rows in left matrix size_t n_middle , // rows in left and columns in right size_t nc_right ) // columns in right matrix { assert( i < n_middle ); assert( j < nc_right ); size_t offset = 3 + nr_left * n_middle; return (offset + i * nc_right + j) * nk + k; } /* {xrst_code} {xrst_spell_on} Result Element Index ******************** Index in the Taylor coefficient matrix *ty* of a result matrix element. {xrst_spell_off} {xrst_code cpp} */ size_t result( size_t i , // result matrix row index size_t j , // result matrix column index size_t k , // Taylor coeffocient order size_t nk , // number of Taylor coefficients in ty size_t nr_left , // rows in left matrix size_t n_middle , // rows in left and columns in right size_t nc_right ) // columns in right matrix { assert( i < nr_left ); assert( j < nc_right ); return (i * nc_right + j) * nk + k; } /* {xrst_code} {xrst_spell_on} Forward Matrix Multiply *********************** Forward mode multiply Taylor coefficients in *tx* and sum into *ty* (for one pair of left and right orders) {xrst_spell_off} {xrst_code cpp} */ void forward_multiply( size_t k_left , // order for left coefficients size_t k_right , // order for right coefficients const vector& tx , // domain space Taylor coefficients vector& ty , // range space Taylor coefficients size_t nr_left , // rows in left matrix size_t n_middle , // rows in left and columns in right size_t nc_right ) // columns in right matrix { size_t nx = 3 + (nr_left + nc_right) * n_middle; size_t nk = tx.size() / nx; # ifndef NDEBUG size_t ny = nr_left * nc_right; assert( nk == ty.size() / ny ); # endif // size_t k_result = k_left + k_right; assert( k_result < nk ); // for(size_t i = 0; i < nr_left; i++) { for(size_t j = 0; j < nc_right; j++) { double sum = 0.0; for(size_t ell = 0; ell < n_middle; ell++) { size_t i_left = left( i, ell, k_left, nk, nr_left, n_middle, nc_right ); size_t i_right = right( ell, j, k_right, nk, nr_left, n_middle, nc_right ); sum += tx[i_left] * tx[i_right]; } size_t i_result = result( i, j, k_result, nk, nr_left, n_middle, nc_right ); ty[i_result] += sum; } } } /* {xrst_code} {xrst_spell_on} Reverse Matrix Multiply *********************** Reverse mode partials of Taylor coefficients and sum into *px* (for one pair of left and right orders) {xrst_spell_off} {xrst_code cpp} */ void reverse_multiply( size_t k_left , // order for left coefficients size_t k_right , // order for right coefficients const vector& tx , // domain space Taylor coefficients const vector& ty , // range space Taylor coefficients vector& px , // partials w.r.t. tx const vector& py , // partials w.r.t. ty size_t nr_left , // rows in left matrix size_t n_middle , // rows in left and columns in right size_t nc_right ) // columns in right matrix { size_t nx = 3 + (nr_left + nc_right) * n_middle; size_t nk = tx.size() / nx; # ifndef NDEBUG size_t ny = nr_left * nc_right; assert( nk == ty.size() / ny ); # endif assert( tx.size() == px.size() ); assert( ty.size() == py.size() ); // size_t k_result = k_left + k_right; assert( k_result < nk ); // for(size_t i = 0; i < nr_left; i++) { for(size_t j = 0; j < nc_right; j++) { size_t i_result = result( i, j, k_result, nk, nr_left, n_middle, nc_right ); for(size_t ell = 0; ell < n_middle; ell++) { size_t i_left = left( i, ell, k_left, nk, nr_left, n_middle, nc_right ); size_t i_right = right( ell, j, k_right, nk, nr_left, n_middle, nc_right ); // sum += tx[i_left] * tx[i_right]; px[i_left] += tx[i_right] * py[i_result]; px[i_right] += tx[i_left] * py[i_result]; } } } return; } /* {xrst_code} {xrst_spell_on} for_type ******** Routine called by CppAD during :ref:`afun(ax, ay)` . {xrst_spell_off} {xrst_code cpp} */ // calculate type_y virtual bool for_type( const vector& parameter_x , const vector& type_x , vector& type_y ) { assert( parameter_x.size() == type_x.size() ); bool ok = true; ok &= type_x[0] == CppAD::constant_enum; ok &= type_x[1] == CppAD::constant_enum; ok &= type_x[2] == CppAD::constant_enum; if( ! ok ) return false; // size_t nr_left = size_t( parameter_x[0] ); size_t n_middle = size_t( parameter_x[1] ); size_t nc_right = size_t( parameter_x[2] ); // ok &= type_x.size() == 3 + (nr_left + nc_right) * n_middle; ok &= type_y.size() == n_middle * nc_right; if( ! ok ) return false; // // compute type_y size_t nk = 1; // number of orders size_t k = 0; // order for(size_t i = 0; i < nr_left; ++i) { for(size_t j = 0; j < nc_right; ++j) { // compute type for result[i, j] CppAD::ad_type_enum type_yij = CppAD::constant_enum; for(size_t ell = 0; ell < n_middle; ++ell) { // index for left(i, ell) size_t i_left = left( i, ell, k, nk, nr_left, n_middle, nc_right ); // index for right(ell, j) size_t i_right = right( ell, j, k, nk, nr_left, n_middle, nc_right ); // multiplication on left or right by the constant zero // always results in a constant bool zero_left = type_x[i_left] == CppAD::constant_enum; zero_left &= parameter_x[i_left] == 0.0; bool zero_right = type_x[i_right] == CppAD::constant_enum; zero_right &= parameter_x[i_right] == 0.0; if( ! (zero_left | zero_right) ) { type_yij = std::max(type_yij, type_x[i_left] ); type_yij = std::max(type_yij, type_x[i_right] ); } } size_t i_result = result( i, j, k, nk, nr_left, n_middle, nc_right ); type_y[i_result] = type_yij; } } return true; } /* {xrst_code} {xrst_spell_on} forward ******* Routine called by CppAD during :ref:`Forward-name` mode. {xrst_spell_off} {xrst_code cpp} */ virtual bool forward( const vector& parameter_x , const vector& type_x , size_t need_y , size_t q , size_t p , const vector& tx , vector& ty ) { size_t n_order = p + 1; size_t nr_left = size_t( tx[ 0 * n_order + 0 ] ); size_t n_middle = size_t( tx[ 1 * n_order + 0 ] ); size_t nc_right = size_t( tx[ 2 * n_order + 0 ] ); # ifndef NDEBUG size_t nx = 3 + (nr_left + nc_right) * n_middle; size_t ny = nr_left * nc_right; # endif assert( nx * n_order == tx.size() ); assert( ny * n_order == ty.size() ); size_t i, j, ell; // initialize result as zero size_t k; for(i = 0; i < nr_left; i++) { for(j = 0; j < nc_right; j++) { for(k = q; k <= p; k++) { size_t i_result = result( i, j, k, n_order, nr_left, n_middle, nc_right ); ty[i_result] = 0.0; } } } for(k = q; k <= p; k++) { // sum the produces that result in order k for(ell = 0; ell <= k; ell++) forward_multiply( ell, k - ell, tx, ty, nr_left, n_middle, nc_right ); } // all orders are implemented, so always return true return true; } /* {xrst_code} {xrst_spell_on} reverse ******* Routine called by CppAD during :ref:`Reverse-name` mode. {xrst_spell_off} {xrst_code cpp} */ virtual bool reverse( const vector& parameter_x , const vector& type_x , size_t p , const vector& tx , const vector& ty , vector& px , const vector& py ) { size_t n_order = p + 1; size_t nr_left = size_t( tx[ 0 * n_order + 0 ] ); size_t n_middle = size_t( tx[ 1 * n_order + 0 ] ); size_t nc_right = size_t( tx[ 2 * n_order + 0 ] ); # ifndef NDEBUG size_t nx = 3 + (nr_left + nc_right) * n_middle; size_t ny = nr_left * nc_right; # endif assert( nx * n_order == tx.size() ); assert( ny * n_order == ty.size() ); assert( px.size() == tx.size() ); assert( py.size() == ty.size() ); // initialize summation for(size_t i = 0; i < px.size(); i++) px[i] = 0.0; // number of orders to differentiate size_t k = n_order; while(k--) { // differentiate the produces that result in order k for(size_t ell = 0; ell <= k; ell++) reverse_multiply( ell, k - ell, tx, ty, px, py, nr_left, n_middle, nc_right ); } // all orders are implemented, so always return true return true; } /* {xrst_code} {xrst_spell_on} jac_sparsity ************ {xrst_spell_off} {xrst_code cpp} */ // Jacobian sparsity routine called by CppAD virtual bool jac_sparsity( const vector& parameter_x , const vector& type_x , bool dependency , const vector& select_x , const vector& select_y , CppAD::sparse_rc< vector >& pattern_out ) { size_t n = select_x.size(); size_t m = select_y.size(); assert( parameter_x.size() == n ); assert( type_x.size() == n ); // size_t nr_left = size_t( parameter_x[0] ); size_t n_middle = size_t( parameter_x[1] ); size_t nc_right = size_t( parameter_x[2] ); size_t nk = 1; // only one order size_t k = 0; // order zero // // count number of non-zeros in sparsity pattern size_t nnz = 0; for(size_t i = 0; i < nr_left; ++i) { for(size_t j = 0; j < nc_right; ++j) { size_t i_result = result( i, j, k, nk, nr_left, n_middle, nc_right ); if( select_y[i_result] ) { for(size_t ell = 0; ell < n_middle; ++ell) { size_t i_left = left( i, ell, k, nk, nr_left, n_middle, nc_right ); size_t i_right = right( ell, j, k, nk, nr_left, n_middle, nc_right ); bool zero_left = type_x[i_left] == CppAD::constant_enum; zero_left &= parameter_x[i_left] == 0.0; bool zero_right = type_x[i_right] == CppAD::constant_enum; zero_right &= parameter_x[i_right] == 0.0; if( ! (zero_left | zero_right ) ) { bool var_left = type_x[i_left] == CppAD::variable_enum; bool var_right = type_x[i_right] == CppAD::variable_enum; if( select_x[i_left] & var_left ) ++nnz; if( select_x[i_right] & var_right ) ++nnz; } } } } } // // fill in the sparsity pattern pattern_out.resize(m, n, nnz); size_t idx = 0; for(size_t i = 0; i < nr_left; ++i) { for(size_t j = 0; j < nc_right; ++j) { size_t i_result = result( i, j, k, nk, nr_left, n_middle, nc_right ); if( select_y[i_result] ) { for(size_t ell = 0; ell < n_middle; ++ell) { size_t i_left = left( i, ell, k, nk, nr_left, n_middle, nc_right ); size_t i_right = right( ell, j, k, nk, nr_left, n_middle, nc_right ); bool zero_left = type_x[i_left] == CppAD::constant_enum; zero_left &= parameter_x[i_left] == 0.0; bool zero_right = type_x[i_right] == CppAD::constant_enum; zero_right &= parameter_x[i_right] == 0.0; if( ! (zero_left | zero_right ) ) { bool var_left = type_x[i_left] == CppAD::variable_enum; bool var_right = type_x[i_right] == CppAD::variable_enum; if( select_x[i_left] & var_left ) pattern_out.set(idx++, i_result, i_left); if( select_x[i_right] & var_right ) pattern_out.set(idx++, i_result, i_right); } } } } } assert( idx == nnz ); // return true; } /* {xrst_code} {xrst_spell_on} hes_sparsity ************ {xrst_spell_off} {xrst_code cpp} */ // Jacobian sparsity routine called by CppAD virtual bool hes_sparsity( const vector& parameter_x , const vector& type_x , const vector& select_x , const vector& select_y , CppAD::sparse_rc< vector >& pattern_out ) { size_t n = select_x.size(); assert( parameter_x.size() == n ); assert( type_x.size() == n ); // size_t nr_left = size_t( parameter_x[0] ); size_t n_middle = size_t( parameter_x[1] ); size_t nc_right = size_t( parameter_x[2] ); size_t nk = 1; // only one order size_t k = 0; // order zero // // count number of non-zeros in sparsity pattern size_t nnz = 0; for(size_t i = 0; i < nr_left; ++i) { for(size_t j = 0; j < nc_right; ++j) { size_t i_result = result( i, j, k, nk, nr_left, n_middle, nc_right ); if( select_y[i_result] ) { for(size_t ell = 0; ell < n_middle; ++ell) { // i_left depends on i, ell size_t i_left = left( i, ell, k, nk, nr_left, n_middle, nc_right ); // i_right depens on ell, j size_t i_right = right( ell, j, k, nk, nr_left, n_middle, nc_right ); bool var_left = select_x[i_left] && (type_x[i_left] == CppAD::variable_enum); bool var_right = select_x[i_right] && (type_x[i_right] == CppAD::variable_enum); if( var_left & var_right ) nnz += 2; } } } } // // fill in the sparsity pattern pattern_out.resize(n, n, nnz); size_t idx = 0; for(size_t i = 0; i < nr_left; ++i) { for(size_t j = 0; j < nc_right; ++j) { size_t i_result = result( i, j, k, nk, nr_left, n_middle, nc_right ); if( select_y[i_result] ) { for(size_t ell = 0; ell < n_middle; ++ell) { size_t i_left = left( i, ell, k, nk, nr_left, n_middle, nc_right ); size_t i_right = right( ell, j, k, nk, nr_left, n_middle, nc_right ); bool var_left = select_x[i_left] && (type_x[i_left] == CppAD::variable_enum); bool var_right = select_x[i_right] && (type_x[i_right] == CppAD::variable_enum); if( var_left & var_right ) { // Cannot possibly set the same (i_left, i_right) // pair twice. assert( i_left != i_right ); pattern_out.set(idx++, i_left, i_right); pattern_out.set(idx++, i_right, i_left); } } } } } assert( idx == nnz ); // return true; } /* {xrst_code} {xrst_spell_on} rev_depend ********** Routine called when a function using ``mat_mul`` is optimized. {xrst_spell_off} {xrst_code cpp} */ // calculate depend_x virtual bool rev_depend( const vector& parameter_x , const vector& type_x , vector& depend_x , const vector& depend_y ) { assert( parameter_x.size() == depend_x.size() ); assert( parameter_x.size() == type_x.size() ); bool ok = true; // size_t nr_left = size_t( parameter_x[0] ); size_t n_middle = size_t( parameter_x[1] ); size_t nc_right = size_t( parameter_x[2] ); // ok &= depend_x.size() == 3 + (nr_left + nc_right) * n_middle; ok &= depend_y.size() == n_middle * nc_right; if( ! ok ) return false; // // initialize depend_x for(size_t ell = 0; ell < 3; ++ell) depend_x[ell] = true; // always need these parameters for(size_t ell = 3; ell < depend_x.size(); ++ell) depend_x[ell] = false; // initialize as false // // compute depend_x size_t nk = 1; // number of orders size_t k = 0; // order for(size_t i = 0; i < nr_left; ++i) { for(size_t j = 0; j < nc_right; ++j) { // check depend for result[i, j] size_t i_result = result( i, j, k, nk, nr_left, n_middle, nc_right ); if( depend_y[i_result] ) { for(size_t ell = 0; ell < n_middle; ++ell) { // index for left(i, ell) size_t i_left = left( i, ell, k, nk, nr_left, n_middle, nc_right ); // index for right(ell, j) size_t i_right = right( ell, j, k, nk, nr_left, n_middle, nc_right ); bool zero_left = type_x[i_left] == CppAD::constant_enum; zero_left &= parameter_x[i_left] == 0.0; bool zero_right = type_x[i_right] == CppAD::constant_enum; zero_right &= parameter_x[i_right] == 0.0; if( ! zero_right ) depend_x[i_left] = true; if( ! zero_left ) depend_x[i_right] = true; } } } } return true; } /* {xrst_code} {xrst_spell_on} End Class Definition ******************** {xrst_spell_off} {xrst_code cpp} */ }; // End of mat_mul class } // End empty namespace /* {xrst_code} {xrst_spell_on} {xrst_end atomic_three_mat_mul.hpp} */ # endif ================================================ FILE: include/cppad/example/atomic_two/eigen_cholesky.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_TWO_EIGEN_CHOLESKY_HPP # define CPPAD_EXAMPLE_ATOMIC_TWO_EIGEN_CHOLESKY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_two_eigen_cholesky.hpp app} atomic_two Eigen Cholesky Factorization Class ############################################# Purpose ******* Construct an atomic operation that computes a lower triangular matrix :math:`L` such that :math:`L L^\R{T} = A` for any positive integer :math:`p` and symmetric positive definite matrix :math:`A \in \B{R}^{p \times p}`. Start Class Definition ********************** {xrst_spell_off} {xrst_code cpp} */ # include # include /* {xrst_code} {xrst_spell_on} Public ****** Types ===== {xrst_spell_off} {xrst_code cpp} */ namespace { // BEGIN_EMPTY_NAMESPACE template class atomic_eigen_cholesky : public CppAD::atomic_base { public: // ----------------------------------------------------------- // type of elements during calculation of derivatives typedef Base scalar; // type of elements during taping typedef CppAD::AD ad_scalar; // // type of matrix during calculation of derivatives typedef Eigen::Matrix< scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> matrix; // type of matrix during taping typedef Eigen::Matrix< ad_scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > ad_matrix; // // lower triangular scalar matrix typedef Eigen::TriangularView lower_view; /* {xrst_code} {xrst_spell_on} Constructor =========== {xrst_spell_off} {xrst_code cpp} */ // constructor atomic_eigen_cholesky(void) : CppAD::atomic_base( "atom_eigen_cholesky" , CppAD::atomic_base::set_sparsity_enum ) { } /* {xrst_code} {xrst_spell_on} op == {xrst_spell_off} {xrst_code cpp} */ // use atomic operation to invert an AD matrix ad_matrix op(const ad_matrix& arg) { size_t nr = size_t( arg.rows() ); size_t ny = ( (nr + 1 ) * nr ) / 2; size_t nx = 1 + ny; assert( nr == size_t( arg.cols() ) ); // ------------------------------------------------------------------- // packed version of arg CPPAD_TESTVECTOR(ad_scalar) packed_arg(nx); size_t index = 0; packed_arg[index++] = ad_scalar( nr ); // lower triangle of symmetric matrix A for(size_t i = 0; i < nr; i++) { for(size_t j = 0; j <= i; j++) packed_arg[index++] = arg( long(i), long(j) ); } assert( index == nx ); // ------------------------------------------------------------------- // packed version of result = arg^{-1}. // This is an atomic_base function call that CppAD uses to // store the atomic operation on the tape. CPPAD_TESTVECTOR(ad_scalar) packed_result(ny); (*this)(packed_arg, packed_result); // ------------------------------------------------------------------- // unpack result matrix L ad_matrix result = ad_matrix::Zero( long(nr), long(nr) ); index = 0; for(size_t i = 0; i < nr; i++) { for(size_t j = 0; j <= i; j++) result( long(i), long(j) ) = packed_result[index++]; } return result; } /* {xrst_code} {xrst_spell_on} Private ******* Variables ========= {xrst_spell_off} {xrst_code cpp} */ private: // ------------------------------------------------------------- // one forward mode vector of matrices for argument and result CppAD::vector f_arg_, f_result_; // one reverse mode vector of matrices for argument and result CppAD::vector r_arg_, r_result_; // ------------------------------------------------------------- /* {xrst_code} {xrst_spell_on} forward ======= {xrst_spell_off} {xrst_code cpp} */ // forward mode routine called by CppAD virtual bool forward( // lowest order Taylor coefficient we are evaluating size_t p , // highest order Taylor coefficient we are evaluating size_t q , // which components of x are variables const CppAD::vector& vx , // which components of y are variables CppAD::vector& vy , // tx [ j * (q+1) + k ] is x_j^k const CppAD::vector& tx , // ty [ i * (q+1) + k ] is y_i^k CppAD::vector& ty ) { size_t n_order = q + 1; size_t nr = size_t( CppAD::Integer( tx[ 0 * n_order + 0 ] ) ); size_t ny = ((nr + 1) * nr) / 2; # ifndef NDEBUG size_t nx = 1 + ny; # endif assert( vx.size() == 0 || nx == vx.size() ); assert( vx.size() == 0 || ny == vy.size() ); assert( nx * n_order == tx.size() ); assert( ny * n_order == ty.size() ); // // ------------------------------------------------------------------- // make sure f_arg_ and f_result_ are large enough assert( f_arg_.size() == f_result_.size() ); if( f_arg_.size() < n_order ) { f_arg_.resize(n_order); f_result_.resize(n_order); // for(size_t k = 0; k < n_order; k++) { f_arg_[k].resize( long(nr), long(nr) ); f_result_[k].resize( long(nr), long(nr) ); } } // ------------------------------------------------------------------- // unpack tx into f_arg_ for(size_t k = 0; k < n_order; k++) { size_t index = 1; // unpack arg values for this order for(long i = 0; i < long(nr); i++) { for(long j = 0; j <= i; j++) { f_arg_[k](i, j) = tx[ index * n_order + k ]; f_arg_[k](j, i) = f_arg_[k](i, j); index++; } } } // ------------------------------------------------------------------- // result for each order // (we could avoid recalculting f_result_[k] for k=0,...,p-1) // Eigen::LLT cholesky(f_arg_[0]); f_result_[0] = cholesky.matrixL(); lower_view L_0 = f_result_[0].template triangularView(); for(size_t k = 1; k < n_order; k++) { // initialize sum as A_k matrix f_sum = f_arg_[k]; // compute A_k - B_k for(size_t ell = 1; ell < k; ell++) f_sum -= f_result_[ell] * f_result_[k-ell].transpose(); // compute L_0^{-1} * (A_k - B_k) * L_0^{-T} matrix temp = L_0.template solve(f_sum); temp = L_0.transpose().template solve(temp); // divide the diagonal by 2 for(long i = 0; i < long(nr); i++) temp(i, i) /= scalar(2.0); // L_k = L_0 * low[ L_0^{-1} * (A_k - B_k) * L_0^{-T} ] lower_view view = temp.template triangularView(); f_result_[k] = f_result_[0] * view; } // ------------------------------------------------------------------- // pack result_ into ty for(size_t k = 0; k < n_order; k++) { size_t index = 0; for(long i = 0; i < long(nr); i++) { for(long j = 0; j <= i; j++) { ty[ index * n_order + k ] = f_result_[k](i, j); index++; } } } // ------------------------------------------------------------------- // check if we are computing vy if( vx.size() == 0 ) return true; // ------------------------------------------------------------------ // This is a very dumb algorithm that over estimates which // elements of the inverse are variables (which is not efficient). bool var = false; for(size_t i = 0; i < ny; i++) var |= vx[1 + i]; for(size_t i = 0; i < ny; i++) vy[i] = var; // return true; } /* {xrst_code} {xrst_spell_on} reverse ======= {xrst_spell_off} {xrst_code cpp} */ // reverse mode routine called by CppAD virtual bool reverse( // highest order Taylor coefficient that we are computing derivative of size_t q , // forward mode Taylor coefficients for x variables const CppAD::vector& tx , // forward mode Taylor coefficients for y variables const CppAD::vector& ty , // upon return, derivative of G[ F[ {x_j^k} ] ] w.r.t {x_j^k} CppAD::vector& px , // derivative of G[ {y_i^k} ] w.r.t. {y_i^k} const CppAD::vector& py ) { size_t n_order = q + 1; size_t nr = size_t( CppAD::Integer( tx[ 0 * n_order + 0 ] ) ); # ifndef NDEBUG size_t ny = ( (nr + 1 ) * nr ) / 2; size_t nx = 1 + ny; # endif // assert( nx * n_order == tx.size() ); assert( ny * n_order == ty.size() ); assert( px.size() == tx.size() ); assert( py.size() == ty.size() ); // ------------------------------------------------------------------- // make sure f_arg_ is large enough assert( f_arg_.size() == f_result_.size() ); // must have previous run forward with order >= n_order assert( f_arg_.size() >= n_order ); // ------------------------------------------------------------------- // make sure r_arg_, r_result_ are large enough assert( r_arg_.size() == r_result_.size() ); if( r_arg_.size() < n_order ) { r_arg_.resize(n_order); r_result_.resize(n_order); // for(size_t k = 0; k < n_order; k++) { r_arg_[k].resize( long(nr), long(nr) ); r_result_[k].resize( long(nr), long(nr) ); } } // ------------------------------------------------------------------- // unpack tx into f_arg_ for(size_t k = 0; k < n_order; k++) { size_t index = 1; // unpack arg values for this order for(long i = 0; i < long(nr); i++) { for(long j = 0; j <= i; j++) { f_arg_[k](i, j) = tx[ index * n_order + k ]; f_arg_[k](j, i) = f_arg_[k](i, j); index++; } } } // ------------------------------------------------------------------- // unpack py into r_result_ for(size_t k = 0; k < n_order; k++) { r_result_[k] = matrix::Zero( long(nr), long(nr) ); size_t index = 0; for(long i = 0; i < long(nr); i++) { for(long j = 0; j <= i; j++) { r_result_[k](i, j) = py[ index * n_order + k ]; index++; } } } // ------------------------------------------------------------------- // initialize r_arg_ as zero for(size_t k = 0; k < n_order; k++) r_arg_[k] = matrix::Zero( long(nr), long(nr) ); // ------------------------------------------------------------------- // matrix reverse mode calculation lower_view L_0 = f_result_[0].template triangularView(); // for(size_t k1 = n_order; k1 > 1; k1--) { size_t k = k1 - 1; // // L_0^T * bar{L}_k matrix tmp1 = L_0.transpose() * r_result_[k]; // //low[ L_0^T * bar{L}_k ] for(long i = 0; i < long(nr); i++) tmp1(i, i) /= scalar(2.0); matrix tmp2 = tmp1.template triangularView(); // // L_0^{-T} low[ L_0^T * bar{L}_k ] tmp1 = L_0.transpose().template solve( tmp2 ); // // M_k = L_0^{-T} * low[ L_0^T * bar{L}_k ]^{T} L_0^{-1} matrix M_k = L_0.transpose().template solve( tmp1.transpose() ); // // remove L_k and compute bar{B}_k matrix barB_k = scalar(0.5) * ( M_k + M_k.transpose() ); r_arg_[k] += barB_k; barB_k = scalar(-1.0) * barB_k; // // 2.0 * lower( bar{B}_k L_k ) matrix temp = scalar(2.0) * barB_k * f_result_[k]; temp = temp.template triangularView(); // // remove C_k r_result_[0] += temp; // // remove B_k for(size_t ell = 1; ell < k; ell++) { // bar{L}_ell = 2 * lower( \bar{B}_k * L_{k-ell} ) temp = scalar(2.0) * barB_k * f_result_[k-ell]; r_result_[ell] += temp.template triangularView(); } } // M_0 = L_0^{-T} * low[ L_0^T * bar{L}_0 ]^{T} L_0^{-1} matrix M_0 = L_0.transpose() * r_result_[0]; for(long i = 0; i < long(nr); i++) M_0(i, i) /= scalar(2.0); M_0 = M_0.template triangularView(); M_0 = L_0.template solve( M_0 ); M_0 = L_0.transpose().template solve( M_0 ); // remove L_0 r_arg_[0] += scalar(0.5) * ( M_0 + M_0.transpose() ); // ------------------------------------------------------------------- // pack r_arg into px // note that only the lower triangle of barA_k is stored in px for(size_t k = 0; k < n_order; k++) { size_t index = 0; px[ index * n_order + k ] = 0.0; index++; for(long i = 0; i < long(nr); i++) { for(long j = 0; j < i; j++) { px[ index * n_order + k ] = 2.0 * r_arg_[k](i, j); index++; } px[ index * n_order + k] = r_arg_[k](i, i); index++; } } // ------------------------------------------------------------------- return true; } /* {xrst_code} {xrst_spell_on} End Class Definition ******************** {xrst_spell_off} {xrst_code cpp} */ }; // End of atomic_eigen_cholesky class } // END_EMPTY_NAMESPACE /* {xrst_code} {xrst_spell_on} {xrst_end atomic_two_eigen_cholesky.hpp} */ # endif ================================================ FILE: include/cppad/example/atomic_two/eigen_mat_inv.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_TWO_EIGEN_MAT_INV_HPP # define CPPAD_EXAMPLE_ATOMIC_TWO_EIGEN_MAT_INV_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_two_eigen_mat_inv.hpp app} {xrst_spell tr } atomic_two Eigen Matrix Inversion Class ####################################### Purpose ******* Construct an atomic operation that computes the matrix inverse :math:`R = A^{-1}` for any positive integer :math:`p` and invertible matrix :math:`A \in \B{R}^{p \times p}`. Matrix Dimensions ***************** This example puts the matrix dimension :math:`p` in the atomic function arguments, instead of the :ref:`constructor` , so it can be different for different calls to the atomic function. Theory ****** Forward ======= The zero order forward mode Taylor coefficient is give by .. math:: R_0 = A_0^{-1} For :math:`k = 1 , \ldots`, the *k*-th order Taylor coefficient of :math:`A R` is given by .. math:: 0 = \sum_{\ell=0}^k A_\ell R_{k-\ell} Solving for :math:`R_k` in terms of the coefficients for :math:`A` and the lower order coefficients for :math:`R` we have .. math:: R_k = - R_0 \left( \sum_{\ell=1}^k A_\ell R_{k-\ell} \right) Furthermore, once we have :math:`R_k` we can compute the sum using .. math:: A_0 R_k = - \left( \sum_{\ell=1}^k A_\ell R_{k-\ell} \right) Product of Three Matrices ========================= Suppose :math:`\bar{E}` is the derivative of the scalar value function :math:`s(E)` with respect to :math:`E`; i.e., .. math:: \bar{E}_{i,j} = \frac{ \partial s } { \partial E_{i,j} } Also suppose that :math:`t` is a scalar valued argument and .. math:: E(t) = B(t) C(t) D(t) It follows that .. math:: E'(t) = B'(t) C(t) D(t) + B(t) C'(t) D(t) + B(t) C(t) D'(t) .. math:: (s \circ E)'(t) = \R{tr} [ \bar{E}^\R{T} E'(t) ] .. math:: = \R{tr} [ \bar{E}^\R{T} B'(t) C(t) D(t) ] + \R{tr} [ \bar{E}^\R{T} B(t) C'(t) D(t) ] + \R{tr} [ \bar{E}^\R{T} B(t) C(t) D'(t) ] .. math:: = \R{tr} [ B(t) D(t) \bar{E}^\R{T} B'(t) ] + \R{tr} [ D(t) \bar{E}^\R{T} B(t) C'(t) ] + \R{tr} [ \bar{E}^\R{T} B(t) C(t) D'(t) ] .. math:: \bar{B} = \bar{E} (C D)^\R{T} \W{,} \bar{C} = \B{R}^\R{T} \bar{E} D^\R{T} \W{,} \bar{D} = (B C)^\R{T} \bar{E} Reverse ======= For :math:`k > 0`, reverse mode eliminates :math:`R_k` and expresses the function values :math:`s` in terms of the coefficients of :math:`A` and the lower order coefficients of :math:`R`. The effect on :math:`\bar{R}_0` (of eliminating :math:`R_k`) is .. math:: \bar{R}_0 = \bar{R}_0 - \bar{R}_k \left( \sum_{\ell=1}^k A_\ell R_{k-\ell} \right)^\R{T} = \bar{R}_0 + \bar{R}_k ( A_0 R_k )^\R{T} For :math:`\ell = 1 , \ldots , k`, the effect on :math:`\bar{R}_{k-\ell}` and :math:`A_\ell` (of eliminating :math:`R_k`) is .. math:: \bar{A}_\ell = \bar{A}_\ell - R_0^\R{T} \bar{R}_k R_{k-\ell}^\R{T} .. math:: \bar{R}_{k-\ell} = \bar{R}_{k-\ell} - ( R_0 A_\ell )^\R{T} \bar{R}_k We note that .. math:: R_0 '(t) A_0 (t) + R_0 (t) A_0 '(t) = 0 .. math:: R_0 '(t) = - R_0 (t) A_0 '(t) R_0 (t) The reverse mode formula that eliminates :math:`R_0` is .. math:: \bar{A}_0 = \bar{A}_0 - R_0^\R{T} \bar{R}_0 R_0^\R{T} Start Class Definition ********************** {xrst_spell_off} {xrst_code cpp} */ # include # include # include /* {xrst_code} {xrst_spell_on} Public ****** Types ===== {xrst_spell_off} {xrst_code cpp} */ namespace { // BEGIN_EMPTY_NAMESPACE template class atomic_eigen_mat_inv : public CppAD::atomic_base { public: // ----------------------------------------------------------- // type of elements during calculation of derivatives typedef Base scalar; // type of elements during taping typedef CppAD::AD ad_scalar; // type of matrix during calculation of derivatives typedef Eigen::Matrix< scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> matrix; // type of matrix during taping typedef Eigen::Matrix< ad_scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > ad_matrix; /* {xrst_code} {xrst_spell_on} Constructor =========== {xrst_spell_off} {xrst_code cpp} */ // constructor atomic_eigen_mat_inv(void) : CppAD::atomic_base( "atom_eigen_mat_inv" , CppAD::atomic_base::set_sparsity_enum ) { } /* {xrst_code} {xrst_spell_on} op == {xrst_spell_off} {xrst_code cpp} */ // use atomic operation to invert an AD matrix ad_matrix op(const ad_matrix& arg) { size_t nr = size_t( arg.rows() ); size_t ny = nr * nr; size_t nx = 1 + ny; assert( nr == size_t( arg.cols() ) ); // ------------------------------------------------------------------- // packed version of arg CPPAD_TESTVECTOR(ad_scalar) packed_arg(nx); packed_arg[0] = ad_scalar( nr ); for(size_t i = 0; i < ny; i++) packed_arg[1 + i] = arg.data()[i]; // ------------------------------------------------------------------- // packed version of result = arg^{-1}. // This is an atomic_base function call that CppAD uses to // store the atomic operation on the tape. CPPAD_TESTVECTOR(ad_scalar) packed_result(ny); (*this)(packed_arg, packed_result); // ------------------------------------------------------------------- // unpack result matrix ad_matrix result(nr, nr); for(size_t i = 0; i < ny; i++) result.data()[i] = packed_result[i]; return result; } /* {xrst_code} {xrst_spell_on} Private ******* Variables ========= {xrst_spell_off} {xrst_code cpp} */ private: // ------------------------------------------------------------- // one forward mode vector of matrices for argument and result CppAD::vector f_arg_, f_result_; // one reverse mode vector of matrices for argument and result CppAD::vector r_arg_, r_result_; // ------------------------------------------------------------- /* {xrst_code} {xrst_spell_on} forward ======= {xrst_spell_off} {xrst_code cpp} */ // forward mode routine called by CppAD virtual bool forward( // lowest order Taylor coefficient we are evaluating size_t p , // highest order Taylor coefficient we are evaluating size_t q , // which components of x are variables const CppAD::vector& vx , // which components of y are variables CppAD::vector& vy , // tx [ j * (q+1) + k ] is x_j^k const CppAD::vector& tx , // ty [ i * (q+1) + k ] is y_i^k CppAD::vector& ty ) { size_t n_order = q + 1; size_t nr = size_t( CppAD::Integer( tx[ 0 * n_order + 0 ] ) ); size_t ny = nr * nr; # ifndef NDEBUG size_t nx = 1 + ny; # endif assert( vx.size() == 0 || nx == vx.size() ); assert( vx.size() == 0 || ny == vy.size() ); assert( nx * n_order == tx.size() ); assert( ny * n_order == ty.size() ); // // ------------------------------------------------------------------- // make sure f_arg_ and f_result_ are large enough assert( f_arg_.size() == f_result_.size() ); if( f_arg_.size() < n_order ) { f_arg_.resize(n_order); f_result_.resize(n_order); // for(size_t k = 0; k < n_order; k++) { f_arg_[k].resize( long(nr), long(nr) ); f_result_[k].resize( long(nr), long(nr) ); } } // ------------------------------------------------------------------- // unpack tx into f_arg_ for(size_t k = 0; k < n_order; k++) { // unpack arg values for this order for(size_t i = 0; i < ny; i++) f_arg_[k].data()[i] = tx[ (1 + i) * n_order + k ]; } // ------------------------------------------------------------------- // result for each order // (we could avoid recalculting f_result_[k] for k=0,...,p-1) // f_result_[0] = f_arg_[0].inverse(); for(size_t k = 1; k < n_order; k++) { // initialize sum matrix f_sum = matrix::Zero( long(nr), long(nr) ); // compute sum for(size_t ell = 1; ell <= k; ell++) f_sum -= f_arg_[ell] * f_result_[k-ell]; // result_[k] = arg_[0]^{-1} * sum_ f_result_[k] = f_result_[0] * f_sum; } // ------------------------------------------------------------------- // pack result_ into ty for(size_t k = 0; k < n_order; k++) { for(size_t i = 0; i < ny; i++) ty[ i * n_order + k ] = f_result_[k].data()[i]; } // ------------------------------------------------------------------- // check if we are computing vy if( vx.size() == 0 ) return true; // ------------------------------------------------------------------ // This is a very dumb algorithm that over estimates which // elements of the inverse are variables (which is not efficient). bool var = false; for(size_t i = 0; i < ny; i++) var |= vx[1 + i]; for(size_t i = 0; i < ny; i++) vy[i] = var; return true; } /* {xrst_code} {xrst_spell_on} reverse ======= {xrst_spell_off} {xrst_code cpp} */ // reverse mode routine called by CppAD virtual bool reverse( // highest order Taylor coefficient that we are computing derivative of size_t q , // forward mode Taylor coefficients for x variables const CppAD::vector& tx , // forward mode Taylor coefficients for y variables const CppAD::vector& ty , // upon return, derivative of G[ F[ {x_j^k} ] ] w.r.t {x_j^k} CppAD::vector& px , // derivative of G[ {y_i^k} ] w.r.t. {y_i^k} const CppAD::vector& py ) { size_t n_order = q + 1; size_t nr = size_t( CppAD::Integer( tx[ 0 * n_order + 0 ] ) ); size_t ny = nr * nr; # ifndef NDEBUG size_t nx = 1 + ny; # endif // assert( nx * n_order == tx.size() ); assert( ny * n_order == ty.size() ); assert( px.size() == tx.size() ); assert( py.size() == ty.size() ); // ------------------------------------------------------------------- // make sure f_arg_ is large enough assert( f_arg_.size() == f_result_.size() ); // must have previous run forward with order >= n_order assert( f_arg_.size() >= n_order ); // ------------------------------------------------------------------- // make sure r_arg_, r_result_ are large enough assert( r_arg_.size() == r_result_.size() ); if( r_arg_.size() < n_order ) { r_arg_.resize(n_order); r_result_.resize(n_order); // for(size_t k = 0; k < n_order; k++) { r_arg_[k].resize( long(nr), long(nr) ); r_result_[k].resize( long(nr), long(nr) ); } } // ------------------------------------------------------------------- // unpack tx into f_arg_ for(size_t k = 0; k < n_order; k++) { // unpack arg values for this order for(size_t i = 0; i < ny; i++) f_arg_[k].data()[i] = tx[ (1 + i) * n_order + k ]; } // ------------------------------------------------------------------- // unpack py into r_result_ for(size_t k = 0; k < n_order; k++) { for(size_t i = 0; i < ny; i++) r_result_[k].data()[i] = py[ i * n_order + k ]; } // ------------------------------------------------------------------- // initialize r_arg_ as zero for(size_t k = 0; k < n_order; k++) r_arg_[k] = matrix::Zero( long(nr), long(nr) ); // ------------------------------------------------------------------- // matrix reverse mode calculation // for(size_t k1 = n_order; k1 > 1; k1--) { size_t k = k1 - 1; // bar{R}_0 = bar{R}_0 + bar{R}_k (A_0 R_k)^T r_result_[0] += r_result_[k] * f_result_[k].transpose() * f_arg_[0].transpose(); // for(size_t ell = 1; ell <= k; ell++) { // bar{A}_l = bar{A}_l - R_0^T bar{R}_k R_{k-l}^T r_arg_[ell] -= f_result_[0].transpose() * r_result_[k] * f_result_[k-ell].transpose(); // bar{R}_{k-l} = bar{R}_{k-1} - (R_0 A_l)^T bar{R}_k r_result_[k-ell] -= f_arg_[ell].transpose() * f_result_[0].transpose() * r_result_[k]; } } r_arg_[0] -= f_result_[0].transpose() * r_result_[0] * f_result_[0].transpose(); // ------------------------------------------------------------------- // pack r_arg into px for(size_t k = 0; k < n_order; k++) { for(size_t i = 0; i < ny; i++) px[ (1 + i) * n_order + k ] = r_arg_[k].data()[i]; } // return true; } /* {xrst_code} {xrst_spell_on} End Class Definition ******************** {xrst_spell_off} {xrst_code cpp} */ }; // End of atomic_eigen_mat_inv class } // END_EMPTY_NAMESPACE /* {xrst_code} {xrst_spell_on} {xrst_end atomic_two_eigen_mat_inv.hpp} */ # endif ================================================ FILE: include/cppad/example/atomic_two/eigen_mat_mul.hpp ================================================ # ifndef CPPAD_EXAMPLE_ATOMIC_TWO_EIGEN_MAT_MUL_HPP # define CPPAD_EXAMPLE_ATOMIC_TWO_EIGEN_MAT_MUL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin atomic_two_eigen_mat_mul.hpp app} {xrst_spell nr tr } atomic_two Eigen Matrix Multiply Class ###################################### See Also ******** :ref:`atomic_three_mat_mul.hpp-name` Purpose ******* Construct an atomic operation that computes the matrix product, :math:`R = A \times \B{R}` for any positive integers :math:`r`, :math:`m`, :math:`c`, and any :math:`A \in \B{R}^{r \times m}`, :math:`B \in \B{R}^{m \times c}`. Matrix Dimensions ***************** This example puts the matrix dimensions in the atomic function arguments, instead of the :ref:`constructor` , so that they can be different for different calls to the atomic function. These dimensions are: .. list-table:: :widths: auto * - *nr_left* - number of rows in the left matrix; i.e, :math:`r` * - *n_middle* - rows in the left matrix and columns in right; i.e, :math:`m` * - *nc_right* - number of columns in the right matrix; i.e., :math:`c` Theory ****** Forward ======= For :math:`k = 0 , \ldots`, the *k*-th order Taylor coefficient :math:`R_k` is given by .. math:: R_k = \sum_{\ell = 0}^{k} A_\ell B_{k-\ell} Product of Two Matrices ======================= Suppose :math:`\bar{E}` is the derivative of the scalar value function :math:`s(E)` with respect to :math:`E`; i.e., .. math:: \bar{E}_{i,j} = \frac{ \partial s } { \partial E_{i,j} } Also suppose that :math:`t` is a scalar valued argument and .. math:: E(t) = C(t) D(t) It follows that .. math:: E'(t) = C'(t) D(t) + C(t) D'(t) .. math:: (s \circ E)'(t) = \R{tr} [ \bar{E}^\R{T} E'(t) ] .. math:: = \R{tr} [ \bar{E}^\R{T} C'(t) D(t) ] + \R{tr} [ \bar{E}^\R{T} C(t) D'(t) ] .. math:: = \R{tr} [ D(t) \bar{E}^\R{T} C'(t) ] + \R{tr} [ \bar{E}^\R{T} C(t) D'(t) ] .. math:: \bar{C} = \bar{E} D^\R{T} \W{,} \bar{D} = C^\R{T} \bar{E} Reverse ======= Reverse mode eliminates :math:`R_k` as follows: for :math:`\ell = 0, \ldots , k-1`, .. math:: \bar{A}_\ell = \bar{A}_\ell + \bar{R}_k B_{k-\ell}^\R{T} .. math:: \bar{B}_{k-\ell} = \bar{B}_{k-\ell} + A_\ell^\R{T} \bar{R}_k Start Class Definition ********************** {xrst_spell_off} {xrst_code cpp} */ # include # include /* {xrst_code} {xrst_spell_on} Public ****** Types ===== {xrst_spell_off} {xrst_code cpp} */ namespace { // BEGIN_EMPTY_NAMESPACE template class atomic_eigen_mat_mul : public CppAD::atomic_base { public: // ----------------------------------------------------------- // type of elements during calculation of derivatives typedef Base scalar; // type of elements during taping typedef CppAD::AD ad_scalar; // type of matrix during calculation of derivatives typedef Eigen::Matrix< scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> matrix; // type of matrix during taping typedef Eigen::Matrix< ad_scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > ad_matrix; /* {xrst_code} {xrst_spell_on} Constructor =========== {xrst_spell_off} {xrst_code cpp} */ // constructor atomic_eigen_mat_mul(void) : CppAD::atomic_base( "atom_eigen_mat_mul" , CppAD::atomic_base::set_sparsity_enum ) { } /* {xrst_code} {xrst_spell_on} op == {xrst_spell_off} {xrst_code cpp} */ // use atomic operation to multiply two AD matrices ad_matrix op( const ad_matrix& left , const ad_matrix& right ) { size_t nr_left = size_t( left.rows() ); size_t n_middle = size_t( left.cols() ); size_t nc_right = size_t( right.cols() ); assert( n_middle == size_t( right.rows() ) ); size_t nx = 3 + (nr_left + nc_right) * n_middle; size_t ny = nr_left * nc_right; size_t n_left = nr_left * n_middle; size_t n_right = n_middle * nc_right; size_t n_result = nr_left * nc_right; // assert( 3 + n_left + n_right == nx ); assert( n_result == ny ); // ----------------------------------------------------------------- // packed version of left and right CPPAD_TESTVECTOR(ad_scalar) packed_arg(nx); // packed_arg[0] = ad_scalar( nr_left ); packed_arg[1] = ad_scalar( n_middle ); packed_arg[2] = ad_scalar( nc_right ); for(size_t i = 0; i < n_left; i++) packed_arg[3 + i] = left.data()[i]; for(size_t i = 0; i < n_right; i++) packed_arg[ 3 + n_left + i ] = right.data()[i]; // ------------------------------------------------------------------ // Packed version of result = left * right. // This as an atomic_base function call that CppAD uses // to store the atomic operation on the tape. CPPAD_TESTVECTOR(ad_scalar) packed_result(ny); (*this)(packed_arg, packed_result); // ------------------------------------------------------------------ // unpack result matrix ad_matrix result(nr_left, nc_right); for(size_t i = 0; i < n_result; i++) result.data()[i] = packed_result[ i ]; // return result; } /* {xrst_code} {xrst_spell_on} Private ******* Variables ========= {xrst_spell_off} {xrst_code cpp} */ private: // ------------------------------------------------------------- // one forward mode vector of matrices for left, right, and result CppAD::vector f_left_, f_right_, f_result_; // one reverse mode vector of matrices for left, right, and result CppAD::vector r_left_, r_right_, r_result_; // ------------------------------------------------------------- /* {xrst_code} {xrst_spell_on} forward ======= {xrst_spell_off} {xrst_code cpp} */ // forward mode routine called by CppAD virtual bool forward( // lowest order Taylor coefficient we are evaluating size_t p , // highest order Taylor coefficient we are evaluating size_t q , // which components of x are variables const CppAD::vector& vx , // which components of y are variables CppAD::vector& vy , // tx [ 3 + j * (q+1) + k ] is x_j^k const CppAD::vector& tx , // ty [ i * (q+1) + k ] is y_i^k CppAD::vector& ty ) { size_t n_order = q + 1; size_t nr_left = size_t( CppAD::Integer( tx[ 0 * n_order + 0 ] ) ); size_t n_middle = size_t( CppAD::Integer( tx[ 1 * n_order + 0 ] ) ); size_t nc_right = size_t( CppAD::Integer( tx[ 2 * n_order + 0 ] ) ); # ifndef NDEBUG size_t nx = 3 + (nr_left + nc_right) * n_middle; size_t ny = nr_left * nc_right; # endif // assert( vx.size() == 0 || nx == vx.size() ); assert( vx.size() == 0 || ny == vy.size() ); assert( nx * n_order == tx.size() ); assert( ny * n_order == ty.size() ); // size_t n_left = nr_left * n_middle; size_t n_right = n_middle * nc_right; size_t n_result = nr_left * nc_right; assert( 3 + n_left + n_right == nx ); assert( n_result == ny ); // // ------------------------------------------------------------------- // make sure f_left_, f_right_, and f_result_ are large enough assert( f_left_.size() == f_right_.size() ); assert( f_left_.size() == f_result_.size() ); if( f_left_.size() < n_order ) { f_left_.resize(n_order); f_right_.resize(n_order); f_result_.resize(n_order); // for(size_t k = 0; k < n_order; k++) { f_left_[k].resize( long(nr_left), long(n_middle) ); f_right_[k].resize( long(n_middle), long(nc_right) ); f_result_[k].resize( long(nr_left), long(nc_right) ); } } // ------------------------------------------------------------------- // unpack tx into f_left and f_right for(size_t k = 0; k < n_order; k++) { // unpack left values for this order for(size_t i = 0; i < n_left; i++) f_left_[k].data()[i] = tx[ (3 + i) * n_order + k ]; // // unpack right values for this order for(size_t i = 0; i < n_right; i++) f_right_[k].data()[i] = tx[ ( 3 + n_left + i) * n_order + k ]; } // ------------------------------------------------------------------- // result for each order // (we could avoid recalculting f_result_[k] for k=0,...,p-1) for(size_t k = 0; k < n_order; k++) { // result[k] = sum_ell left[ell] * right[k-ell] f_result_[k] = matrix::Zero( long(nr_left), long(nc_right) ); for(size_t ell = 0; ell <= k; ell++) f_result_[k] += f_left_[ell] * f_right_[k-ell]; } // ------------------------------------------------------------------- // pack result_ into ty for(size_t k = 0; k < n_order; k++) { for(size_t i = 0; i < n_result; i++) ty[ i * n_order + k ] = f_result_[k].data()[i]; } // ------------------------------------------------------------------ // check if we are computing vy if( vx.size() == 0 ) return true; // ------------------------------------------------------------------ // compute variable information for y; i.e., vy // (note that the constant zero times a variable is a constant) scalar zero(0.0); assert( n_order == 1 ); for(size_t i = 0; i < nr_left; i++) { for(size_t j = 0; j < nc_right; j++) { bool var = false; for(size_t ell = 0; ell < n_middle; ell++) { // left information size_t index = 3 + i * n_middle + ell; bool var_left = vx[index]; bool nz_left = var_left | (f_left_[0]( long(i), long(ell) ) != zero); // right information index = 3 + n_left + ell * nc_right + j; bool var_right = vx[index]; bool nz_right = var_right | (f_right_[0]( long(ell), long(j) ) != zero); // effect of result var |= var_left & nz_right; var |= nz_left & var_right; } size_t index = i * nc_right + j; vy[index] = var; } } return true; } /* {xrst_code} {xrst_spell_on} reverse ======= {xrst_spell_off} {xrst_code cpp} */ // reverse mode routine called by CppAD virtual bool reverse( // highest order Taylor coefficient that we are computing derivative of size_t q , // forward mode Taylor coefficients for x variables const CppAD::vector& tx , // forward mode Taylor coefficients for y variables const CppAD::vector& ty , // upon return, derivative of G[ F[ {x_j^k} ] ] w.r.t {x_j^k} CppAD::vector& px , // derivative of G[ {y_i^k} ] w.r.t. {y_i^k} const CppAD::vector& py ) { size_t n_order = q + 1; size_t nr_left = size_t( CppAD::Integer( tx[ 0 * n_order + 0 ] ) ); size_t n_middle = size_t( CppAD::Integer( tx[ 1 * n_order + 0 ] ) ); size_t nc_right = size_t( CppAD::Integer( tx[ 2 * n_order + 0 ] ) ); # ifndef NDEBUG size_t nx = 3 + (nr_left + nc_right) * n_middle; size_t ny = nr_left * nc_right; # endif // assert( nx * n_order == tx.size() ); assert( ny * n_order == ty.size() ); assert( px.size() == tx.size() ); assert( py.size() == ty.size() ); // size_t n_left = nr_left * n_middle; size_t n_right = n_middle * nc_right; size_t n_result = nr_left * nc_right; assert( 3 + n_left + n_right == nx ); assert( n_result == ny ); // ------------------------------------------------------------------- // make sure f_left_, f_right_ are large enough assert( f_left_.size() == f_right_.size() ); assert( f_left_.size() == f_result_.size() ); // must have previous run forward with order >= n_order assert( f_left_.size() >= n_order ); // ------------------------------------------------------------------- // make sure r_left_, r_right_, and r_result_ are large enough assert( r_left_.size() == r_right_.size() ); assert( r_left_.size() == r_result_.size() ); if( r_left_.size() < n_order ) { r_left_.resize(n_order); r_right_.resize(n_order); r_result_.resize(n_order); // for(size_t k = 0; k < n_order; k++) { r_left_[k].resize( long(nr_left), long(n_middle) ); r_right_[k].resize( long(n_middle), long(nc_right) ); r_result_[k].resize( long(nr_left), long(nc_right) ); } } // ------------------------------------------------------------------- // unpack tx into f_left and f_right for(size_t k = 0; k < n_order; k++) { // unpack left values for this order for(size_t i = 0; i < n_left; i++) f_left_[k].data()[i] = tx[ (3 + i) * n_order + k ]; // // unpack right values for this order for(size_t i = 0; i < n_right; i++) f_right_[k].data()[i] = tx[ (3 + n_left + i) * n_order + k ]; } // ------------------------------------------------------------------- // unpack py into r_result_ for(size_t k = 0; k < n_order; k++) { for(size_t i = 0; i < n_result; i++) r_result_[k].data()[i] = py[ i * n_order + k ]; } // ------------------------------------------------------------------- // initialize r_left_ and r_right_ as zero for(size_t k = 0; k < n_order; k++) { r_left_[k] = matrix::Zero( long(nr_left), long(n_middle) ); r_right_[k] = matrix::Zero( long(n_middle), long(nc_right) ); } // ------------------------------------------------------------------- // matrix reverse mode calculation for(size_t k1 = n_order; k1 > 0; k1--) { size_t k = k1 - 1; for(size_t ell = 0; ell <= k; ell++) { // nr x nm = nr x nc * nc * nm r_left_[ell] += r_result_[k] * f_right_[k-ell].transpose(); // nm x nc = nm x nr * nr * nc r_right_[k-ell] += f_left_[ell].transpose() * r_result_[k]; } } // ------------------------------------------------------------------- // pack r_left and r_right int px for(size_t k = 0; k < n_order; k++) { // dimensions are integer constants px[ 0 * n_order + k ] = 0.0; px[ 1 * n_order + k ] = 0.0; px[ 2 * n_order + k ] = 0.0; // // pack left values for this order for(size_t i = 0; i < n_left; i++) px[ (3 + i) * n_order + k ] = r_left_[k].data()[i]; // // pack right values for this order for(size_t i = 0; i < n_right; i++) px[ (3 + i + n_left) * n_order + k] = r_right_[k].data()[i]; } // return true; } /* {xrst_code} {xrst_spell_on} for_sparse_jac ============== {xrst_spell_off} {xrst_code cpp} */ // forward Jacobian sparsity routine called by CppAD virtual bool for_sparse_jac( // number of columns in the matrix R size_t q , // sparsity pattern for the matrix R const CppAD::vector< std::set >& r , // sparsity pattern for the matrix S = f'(x) * R CppAD::vector< std::set >& s , const CppAD::vector& x ) { size_t nr_left = size_t( CppAD::Integer( x[0] ) ); size_t n_middle = size_t( CppAD::Integer( x[1] ) ); size_t nc_right = size_t( CppAD::Integer( x[2] ) ); # ifndef NDEBUG size_t nx = 3 + (nr_left + nc_right) * n_middle; size_t ny = nr_left * nc_right; # endif // assert( nx == r.size() ); assert( ny == s.size() ); // size_t n_left = nr_left * n_middle; for(size_t i = 0; i < nr_left; i++) { for(size_t j = 0; j < nc_right; j++) { // pack index for entry (i, j) in result size_t i_result = i * nc_right + j; s[i_result].clear(); for(size_t ell = 0; ell < n_middle; ell++) { // pack index for entry (i, ell) in left size_t i_left = 3 + i * n_middle + ell; // pack index for entry (ell, j) in right size_t i_right = 3 + n_left + ell * nc_right + j; // check if result of for this product is always zero // note that x is nan for components that are variables bool zero = x[i_left] == Base(0.0) || x[i_right] == Base(0); if( ! zero ) { s[i_result] = CppAD::set_union(s[i_result], r[i_left] ); s[i_result] = CppAD::set_union(s[i_result], r[i_right] ); } } } } return true; } /* {xrst_code} {xrst_spell_on} rev_sparse_jac ============== {xrst_spell_off} {xrst_code cpp} */ // reverse Jacobian sparsity routine called by CppAD virtual bool rev_sparse_jac( // number of columns in the matrix R^T size_t q , // sparsity pattern for the matrix R^T const CppAD::vector< std::set >& rt , // sparsity pattern for the matrix S^T = f'(x)^T * R^T CppAD::vector< std::set >& st , const CppAD::vector& x ) { size_t nr_left = size_t( CppAD::Integer( x[0] ) ); size_t n_middle = size_t( CppAD::Integer( x[1] ) ); size_t nc_right = size_t( CppAD::Integer( x[2] ) ); size_t nx = 3 + (nr_left + nc_right) * n_middle; # ifndef NDEBUG size_t ny = nr_left * nc_right; # endif // assert( nx == st.size() ); assert( ny == rt.size() ); // // initialize S^T as empty for(size_t i = 0; i < nx; i++) st[i].clear(); // sparsity for S(x)^T = f'(x)^T * R^T size_t n_left = nr_left * n_middle; for(size_t i = 0; i < nr_left; i++) { for(size_t j = 0; j < nc_right; j++) { // pack index for entry (i, j) in result size_t i_result = i * nc_right + j; st[i_result].clear(); for(size_t ell = 0; ell < n_middle; ell++) { // pack index for entry (i, ell) in left size_t i_left = 3 + i * n_middle + ell; // pack index for entry (ell, j) in right size_t i_right = 3 + n_left + ell * nc_right + j; // st[i_left] = CppAD::set_union(st[i_left], rt[i_result]); st[i_right] = CppAD::set_union(st[i_right], rt[i_result]); } } } return true; } /* {xrst_code} {xrst_spell_on} for_sparse_hes ============== {xrst_spell_off} {xrst_code cpp} */ virtual bool for_sparse_hes( // which components of x are variables for this call const CppAD::vector& vx, // sparsity pattern for the diagonal of R const CppAD::vector& r , // sparsity pattern for the vector S const CppAD::vector& s , // sparsity patternfor the Hessian H(x) CppAD::vector< std::set >& h , const CppAD::vector& x ) { size_t nr_left = size_t( CppAD::Integer( x[0] ) ); size_t n_middle = size_t( CppAD::Integer( x[1] ) ); size_t nc_right = size_t( CppAD::Integer( x[2] ) ); size_t nx = 3 + (nr_left + nc_right) * n_middle; # ifndef NDEBUG size_t ny = nr_left * nc_right; # endif // assert( vx.size() == nx ); assert( r.size() == nx ); assert( s.size() == ny ); assert( h.size() == nx ); // // initialize h as empty for(size_t i = 0; i < nx; i++) h[i].clear(); // size_t n_left = nr_left * n_middle; for(size_t i = 0; i < nr_left; i++) { for(size_t j = 0; j < nc_right; j++) { // pack index for entry (i, j) in result size_t i_result = i * nc_right + j; if( s[i_result] ) { for(size_t ell = 0; ell < n_middle; ell++) { // pack index for entry (i, ell) in left size_t i_left = 3 + i * n_middle + ell; // pack index for entry (ell, j) in right size_t i_right = 3 + n_left + ell * nc_right + j; if( r[i_left] && r[i_right] ) { h[i_left].insert(i_right); h[i_right].insert(i_left); } } } } } return true; } /* {xrst_code} {xrst_spell_on} rev_sparse_hes ============== {xrst_spell_off} {xrst_code cpp} */ // reverse Hessian sparsity routine called by CppAD virtual bool rev_sparse_hes( // which components of x are variables for this call const CppAD::vector& vx, // sparsity pattern for S(x) = g'[f(x)] const CppAD::vector& s , // sparsity pattern for d/dx g[f(x)] = S(x) * f'(x) CppAD::vector& t , // number of columns in R, U(x), and V(x) size_t q , // sparsity pattern for R const CppAD::vector< std::set >& r , // sparsity pattern for U(x) = g^{(2)} [ f(x) ] * f'(x) * R const CppAD::vector< std::set >& u , // sparsity pattern for // V(x) = f'(x)^T * U(x) + sum_{i=0}^{m-1} S_i(x) f_i^{(2)} (x) * R CppAD::vector< std::set >& v , // parameters as integers const CppAD::vector& x ) { size_t nr_left = size_t( CppAD::Integer( x[0] ) ); size_t n_middle = size_t( CppAD::Integer( x[1] ) ); size_t nc_right = size_t( CppAD::Integer( x[2] ) ); size_t nx = 3 + (nr_left + nc_right) * n_middle; # ifndef NDEBUG size_t ny = nr_left * nc_right; # endif // assert( vx.size() == nx ); assert( s.size() == ny ); assert( t.size() == nx ); assert( r.size() == nx ); assert( v.size() == nx ); // // initialize return sparsity patterns as false for(size_t j = 0; j < nx; j++) { t[j] = false; v[j].clear(); } // size_t n_left = nr_left * n_middle; for(size_t i = 0; i < nr_left; i++) { for(size_t j = 0; j < nc_right; j++) { // pack index for entry (i, j) in result size_t i_result = i * nc_right + j; for(size_t ell = 0; ell < n_middle; ell++) { // pack index for entry (i, ell) in left size_t i_left = 3 + i * n_middle + ell; // pack index for entry (ell, j) in right size_t i_right = 3 + n_left + ell * nc_right + j; // // back propagate T(x) = S(x) * f'(x). t[i_left] |= bool( s[i_result] ); t[i_right] |= bool( s[i_result] ); // // V(x) = f'(x)^T * U(x) + sum_i S_i(x) * f_i''(x) * R // U(x) = g''[ f(x) ] * f'(x) * R // S_i(x) = g_i'[ f(x) ] // // back propagate f'(x)^T * U(x) v[i_left] = CppAD::set_union(v[i_left], u[i_result] ); v[i_right] = CppAD::set_union(v[i_right], u[i_result] ); // // back propagate S_i(x) * f_i''(x) * R // (here is where we use vx to check for cross terms) if( s[i_result] && vx[i_left] && vx[i_right] ) { v[i_left] = CppAD::set_union(v[i_left], r[i_right] ); v[i_right] = CppAD::set_union(v[i_right], r[i_left] ); } } } } return true; } /* {xrst_code} {xrst_spell_on} End Class Definition ******************** {xrst_spell_off} {xrst_code cpp} */ }; // End of atomic_eigen_mat_mul class } // END_EMPTY_NAMESPACE /* {xrst_code} {xrst_spell_on} {xrst_end atomic_two_eigen_mat_mul.hpp} */ # endif ================================================ FILE: include/cppad/example/base_adolc.hpp ================================================ # ifndef CPPAD_EXAMPLE_BASE_ADOLC_HPP # define CPPAD_EXAMPLE_BASE_ADOLC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin base_adolc.hpp} {xrst_spell acosh adouble asinh atrig azmul codassign condassign erfc expm valgrind } Enable use of AD where Base is Adolc's adouble Type ######################################################### Syntax ****** | # ``include `` {xrst_toc_hidden example/general/mul_level_adolc.cpp } Example ******* The file :ref:`mul_level_adolc.cpp-name` contains an example use of Adolc's ``adouble`` type for a CppAD *Base* type. The file :ref:`mul_level_adolc_ode.cpp-name` contains a more realistic (and complex) example. Include Files ************* This file ``base_adolc.hpp`` requires ``adouble`` to be defined. In addition, it is included before ```` , but it needs to include parts of CppAD that are used by this file. This is done with the following include commands: {xrst_spell_off} {xrst_code cpp} */ # include # include /* {xrst_code} {xrst_spell_on} CondExpOp ********* The type ``adouble`` supports a conditional assignment function with the syntax ``condassign`` ( *a* , *b* , *c* , *d* ) which evaluates to *a* = ( *b* > 0) ? *c* : *d* ; This enables one to include conditionals in the recording of ``adouble`` operations and later evaluation for different values of the independent variables (in the same spirit as the CppAD :ref:`CondExp-name` function). {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline adouble CondExpOp( enum CppAD::CompareOp cop , const adouble &left , const adouble &right , const adouble &trueCase , const adouble &falseCase ) { adouble result; switch( cop ) { case CompareLt: // left < right condassign(result, right - left, trueCase, falseCase); break; case CompareLe: // left <= right condassign(result, left - right, falseCase, trueCase); break; case CompareEq: // left == right condassign(result, left - right, falseCase, trueCase); condassign(result, right - left, falseCase, result); break; case CompareGe: // left >= right condassign(result, right - left, falseCase, trueCase); break; case CompareGt: // left > right condassign(result, left - right, trueCase, falseCase); break; default: CppAD::ErrorHandler::Call( true , __LINE__ , __FILE__ , "CppAD::CondExp", "Error: for unknown reason." ); result = trueCase; } return result; } } /* {xrst_code} {xrst_spell_on} CondExpRel ********** The :ref:`CPPAD_COND_EXP_REL` macro invocation {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { CPPAD_COND_EXP_REL(adouble) } /* {xrst_code} {xrst_spell_on} EqualOpSeq ********** The Adolc user interface does not specify a way to determine if two ``adouble`` variables correspond to the same operations sequence. Make ``EqualOpSeq`` an error if it gets used: {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline bool EqualOpSeq(const adouble &x, const adouble &y) { CppAD::ErrorHandler::Call( true , __LINE__ , __FILE__ , "CppAD::EqualOpSeq(x, y)", "Error: adouble does not support EqualOpSeq." ); return false; } } /* {xrst_code} {xrst_spell_on} Identical ********* The Adolc user interface does not specify a way to determine if an ``adouble`` depends on the independent variables. To be safe (but slow) return ``false`` in all the cases below. {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline bool IdenticalCon(const adouble &x) { return false; } inline bool IdenticalZero(const adouble &x) { return false; } inline bool IdenticalOne(const adouble &x) { return false; } inline bool IdenticalEqualCon(const adouble &x, const adouble &y) { return false; } } /* {xrst_code} {xrst_spell_on} Integer ******* {xrst_spell_off} {xrst_code cpp} */ inline int Integer(const adouble &x) { return static_cast( x.getValue() ); } /* {xrst_code} {xrst_spell_on} azmul ***** {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { CPPAD_AZMUL( adouble ) } /* {xrst_code} {xrst_spell_on} Ordered ******* {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline bool GreaterThanZero(const adouble &x) { return (x > 0); } inline bool GreaterThanOrZero(const adouble &x) { return (x >= 0); } inline bool LessThanZero(const adouble &x) { return (x < 0); } inline bool LessThanOrZero(const adouble &x) { return (x <= 0); } inline bool abs_geq(const adouble& x, const adouble& y) { return fabs(x) >= fabs(y); } } /* {xrst_code} {xrst_spell_on} Unary Standard Math ******************* The following :ref:`required` functions are defined by the Adolc package for the ``adouble`` base case: ``acos`` , ``acosh`` , ``asin`` , ``asinh`` , ``atan`` , ``atanh`` , ``cos`` , ``cosh`` , ``erf`` , ``exp`` , ``fabs`` , ``log`` , ``sin`` , ``sinh`` , ``sqrt`` , ``tan`` . erfc **** If you provide ``--enable-atrig-erf`` on the configure command line, the adolc package supports all the c++11 math functions except ``erfc`` , ``expm1`` , and ``log1p`` . For the reason, we make using ``erfc`` an error: {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { # define CPPAD_BASE_ADOLC_NO_SUPPORT(fun) \ inline adouble fun(const adouble& x) \ { CPPAD_ASSERT_KNOWN( \ false, \ #fun ": adolc does not support this function" \ ); \ return 0.0; \ } CPPAD_BASE_ADOLC_NO_SUPPORT(erfc) CPPAD_BASE_ADOLC_NO_SUPPORT(expm1) CPPAD_BASE_ADOLC_NO_SUPPORT(log1p) # undef CPPAD_BASE_ADOLC_NO_SUPPORT } /* {xrst_code} {xrst_spell_on} sign **** This :ref:`required` function is defined using the ``codassign`` function so that its ``adouble`` operation sequence does not depend on the value of *x* . {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline adouble sign(const adouble& x) { adouble s_plus, s_minus, half(.5); // set s_plus to sign(x)/2, except for case x == 0, s_plus = -.5 condassign(s_plus, +x, -half, +half); // set s_minus to -sign(x)/2, except for case x == 0, s_minus = -.5 condassign(s_minus, -x, -half, +half); // set s to sign(x) return s_plus - s_minus; } } /* {xrst_code} {xrst_spell_on} abs *** This :ref:`required` function uses the adolc ``fabs`` function: {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline adouble abs(const adouble& x) { return fabs(x); } } /* {xrst_code} {xrst_spell_on} pow *** This :ref:`required` function is defined by the Adolc package for the ``adouble`` base case. numeric_limits ************** The following defines the CppAD :ref:`numeric_limits-name` for the type ``adouble`` : {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { CPPAD_NUMERIC_LIMITS(double, adouble) } /* {xrst_code} {xrst_spell_on} to_string ********* The following defines the CppAD :ref:`to_string-name` function for the type ``adouble`` : {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { template <> struct to_string_struct { std::string operator()(const adouble& x) { std::stringstream os; int n_digits = 1 + std::numeric_limits::digits10; os << std::setprecision(n_digits); os << x.value(); return os.str(); } }; } /* {xrst_code} {xrst_spell_on} hash_code ********* It appears that an ``adouble`` object can have fields that are not initialized. This results in a ``valgrind`` error when these fields are used by the :ref:`base_hash@Default` hashing function. For this reason, the ``adouble`` class overrides the default definition. {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { inline unsigned short hash_code(const adouble& x) { unsigned short code = 0; double value = x.value(); if( value == 0.0 ) return code; double log_x = std::log( fabs( value ) ); // assume log( std::numeric_limits::max() ) is near 700 code = static_cast( (CPPAD_HASH_TABLE_SIZE / 700 + 1) * log_x ); code = code % CPPAD_HASH_TABLE_SIZE; return code; } } /* {xrst_code} {xrst_spell_on} Note that after the hash codes match, the :ref:`base_adolc.hpp@Identical` function will be used to make sure two values are the same and one can replace the other. A more sophisticated implementation of the ``Identical`` function would detect which ``adouble`` values depend on the ``adouble`` independent variables (and hence can change). {xrst_end base_adolc.hpp} */ # endif ================================================ FILE: include/cppad/example/code_gen_fun.hpp ================================================ # ifndef CPPAD_EXAMPLE_CODE_GEN_FUN_HPP # define CPPAD_EXAMPLE_CODE_GEN_FUN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN C++ # include // See https://docs.microsoft.com/en-us/cpp/cpp/ // using-dllimport-and-dllexport-in-cpp-classes?view=msvc-160 // Also see define.hpp where CPPAD_LIB_EXPORTS is also defined and // undef.hpp where it gets undefined. # ifdef _MSC_VER # ifdef cppad_lib_EXPORTS # define CPPAD_LIB_EXPORT __declspec(dllexport) # else # define CPPAD_LIB_EXPORT __declspec(dllimport) # endif // cppad_lib_EXPORTS # else // _MSC_VER # define CPPAD_LIB_EXPORT # endif class CPPAD_LIB_EXPORT code_gen_fun { public: // type of evaluation for Jacobians (possibly Hessians in the future) enum evaluation_enum { none_enum, dense_enum, sparse_enum }; private: // dynamic_lib_ std::unique_ptr< CppAD::cg::DynamicLib > dynamic_lib_; // // model_ (contains a reference to dynamic_lib_) std::unique_ptr< CppAD::cg::GenericModel > model_; // public: // ----------------------------------------------------------------------- // constructors // ----------------------------------------------------------------------- // fun_name() code_gen_fun(void); // // fun_name( file_name ) code_gen_fun(const std::string& file_name); // // fun_name(file_name, cg_fun, eval_jac) code_gen_fun( const std::string& file_name , CppAD::ADFun< CppAD::cg::CG >& cg_fun , evaluation_enum eval_jac = none_enum ); // ----------------------------------------------------------------------- // operations // ----------------------------------------------------------------------- // swap(other_fun) void swap(code_gen_fun& other_fun); // // y = fun_name(x) CppAD::vector operator()(const CppAD::vector & x); // // J = fun_name.jacobian(x) CppAD::vector jacobian(const CppAD::vector & x); // // Jrcv = fun_name.sparse_jacobian(x) CppAD::sparse_rcv< CppAD::vector, CppAD::vector > sparse_jacobian(const CppAD::vector& x); }; // END C++ # endif ================================================ FILE: include/cppad/example/cppad_eigen.hpp ================================================ # ifndef CPPAD_EXAMPLE_CPPAD_EIGEN_HPP # define CPPAD_EXAMPLE_CPPAD_EIGEN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppad_eigen.hpp} {xrst_spell gitlab libeigen } Enable Use of Eigen Linear Algebra Package with CppAD ##################################################### Syntax ****** | # ``include `` {xrst_toc_hidden include/cppad/example/eigen_plugin.hpp example/general/eigen_array.cpp example/general/eigen_det.cpp } Purpose ******* Enables the use of the :ref:`eigen-name` linear algebra package with the type *AD* < ``Base`` > ; see `Using custom scalar types`_ . .. _Using custom scalar types: https:// libeigen.gitlab.io/eigen/docs-nightly/TopicCustomizing_CustomScalar.html Example ******* The files :ref:`eigen_array.cpp-name` and :ref:`eigen_det.cpp-name` contain an example and test of this include file. They return true if they succeed and false otherwise. CppAD Declarations ****************** First declare some items that are defined by cppad.hpp: {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { // AD template class AD; // numeric_limits template class numeric_limits; } /* {xrst_code} {xrst_spell_on} std Declarations **************** Next declare some template specializations in std namespace: {xrst_spell_off} {xrst_code cpp} */ namespace std { template bool isinf(const CppAD::AD &x); template bool isfinite(const CppAD::AD &x); template bool isnan(const CppAD::AD &x); } /* {xrst_code} {xrst_spell_on} Include Eigen/Core ****************** Next define the eigen plugin and then include Eigen/Core: {xrst_spell_off} {xrst_code cpp} */ # define EIGEN_MATRIXBASE_PLUGIN # include /* {xrst_code} {xrst_spell_on} Eigen NumTraits *************** Eigen needs the following definitions, in the Eigen namespace, to work properly with ``AD`` < *Base* > scalars: {xrst_spell_off} {xrst_code cpp} */ namespace Eigen { template struct NumTraits< CppAD::AD > { // type that corresponds to the real part of an AD value typedef CppAD::AD Real; // type for AD operations that result in non-integer values typedef CppAD::AD NonInteger; // type to use for numeric literals such as "2" or "0.5". typedef CppAD::AD Literal; // type for nested value inside an AD expression tree typedef CppAD::AD Nested; enum { // does not support complex Base types IsComplex = 0 , // does not support integer Base types IsInteger = 0 , // only support signed Base types IsSigned = 1 , // must initialize an AD object RequireInitialization = 1 , // computational cost of the corresponding operations ReadCost = 1 , AddCost = 2 , MulCost = 2 }; // machine epsilon with type of real part of x // (use assumption that Base is not complex) static CppAD::AD epsilon(void) { return CppAD::numeric_limits< CppAD::AD >::epsilon(); } // relaxed version of machine epsilon for comparison of different // operations that should result in the same value static CppAD::AD dummy_precision(void) { return 100. * CppAD::numeric_limits< CppAD::AD >::epsilon(); } // minimum normalized positive value static CppAD::AD lowest(void) { return CppAD::numeric_limits< CppAD::AD >::min(); } // maximum finite value static CppAD::AD highest(void) { return CppAD::numeric_limits< CppAD::AD >::max(); } // number of decimal digits that can be represented without change. static int digits10(void) { return CppAD::numeric_limits< CppAD::AD >::digits10; } // number of decimal digits necessary to uniquely represent values. static int max_digits10(void) { return CppAD::numeric_limits< CppAD::AD >::max_digits10; } // not a number static CppAD::AD quiet_NaN(void) { return CppAD::numeric_limits< CppAD::AD >::quiet_NaN(); } // positive infinite value static CppAD::AD infinity(void) { return CppAD::numeric_limits< CppAD::AD >::infinity(); } }; } /* {xrst_code} {xrst_spell_on} Eigen ScalarBinaryOpTraits ************************** {xrst_spell_off} {xrst_code cpp} */ namespace Eigen { // Inform Eigen that a binary operations between Base and AD // are allowed and thate the return type is AD template struct ScalarBinaryOpTraits, Base, BinOp>{ typedef CppAD::AD ReturnType; }; template struct ScalarBinaryOpTraits, BinOp> { typedef CppAD::AD ReturnType; }; } /* {xrst_code} {xrst_spell_on} CppAD Namespace *************** Eigen needs the following definitions, in the CppAD namespace, to work properly with ``AD`` < *Base* > scalars: {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { // functions that return references template const AD& conj(const AD& x) { return x; } template const AD& real(const AD& x) { return x; } // functions that return values (note abs is defined by cppad.hpp) template AD imag(const AD& x) { return CppAD::AD(0.); } template AD abs2(const AD& x) { return x * x; } } /* {xrst_code} {xrst_spell_on} eigen_vector ************ The class ``CppAD::eigen_vector`` is a wrapper for Eigen column vectors so that they are :ref:`simple vectors` . To be specific, it converts ``Eigen::Index`` arguments and return values to ``size_t`` . {xrst_spell_off} {xrst_code cpp} */ namespace CppAD { template class eigen_vector : public Eigen::Matrix { private: // base_class typedef Eigen::Matrix base_class; public: // constructor eigen_vector(size_t n) : base_class( Eigen::Index(n) ) { } eigen_vector(void) : base_class() { } // operator[] Scalar& operator[](size_t i) { return base_class::operator[]( Eigen::Index(i) ); } const Scalar& operator[](size_t i) const { return base_class::operator[]( Eigen::Index(i) ); } // size size_t size(void) const { return size_t( base_class::size() ); } // resize void resize(size_t n) { base_class::resize( Eigen::Index(n) ); } }; } /* {xrst_code} {xrst_spell_on} Include cppad.hpp ***************** {xrst_spell_off} {xrst_code cpp} */ # include /* {xrst_code} {xrst_spell_on} std Definitions *************** The definitions below use cppad.hpp. Note that :ref:`Value-name` function can only be used with a :ref:`constant parameter` argument. {xrst_spell_off} {xrst_code cpp} */ namespace std { template bool isinf(const CppAD::AD &x) { return isinf(CppAD::Value( CppAD::Var2Par(x) ) ); } template bool isfinite(const CppAD::AD &x) { return isfinite(CppAD::Value( CppAD::Var2Par(x) ) ); } template bool isnan(const CppAD::AD &x) { return CppAD::isnan(CppAD::Value( CppAD::Var2Par(x) ) ); } } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_eigen.hpp} */ # endif ================================================ FILE: include/cppad/example/eigen_plugin.hpp ================================================ # ifndef CPPAD_EXAMPLE_EIGEN_PLUGIN_HPP # define CPPAD_EXAMPLE_EIGEN_PLUGIN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /*$ {xrst_begin eigen_plugin.hpp} Source Code for eigen_plugin.hpp ################################ {xrst_spell_off} {xrst_code cpp} */ // Declaration needed, before eigen-3.3.3, so Eigen vector is a simple vector typedef Scalar value_type; /* {xrst_code} {xrst_spell_on} {xrst_end eigen_plugin.hpp} */ # endif ================================================ FILE: include/cppad/example/valvector/class.hpp ================================================ # ifndef CPPAD_EXAMPLE_VALVECTOR_CLASS_HPP # define CPPAD_EXAMPLE_VALVECTOR_CLASS_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin_parent valvector} {xrst_spell jax numpy pytorch } valvector: An Example Machine Learning Base Class ################################################# This CppAD Base class preforms numerical operations on vectors; e.g., only one CppAD operation represents the element-by-element addition of two vectors. This is similar to the Pytorch Tensors or Jax Numpy Arrays. scalar_type *********** The type ``valvector::scalar_type`` is the type corresponding to each element of a valvector . We use *scalar_type* to denote this type. Getting Started =============== The file :ref:`valvector_get_started.cpp-name` is an example that computes derivatives using valvector as the base class. Other Examples ============== The file :ref:`valvector.cpp-name` tests that all of the valvector examples git the expected results. Operations ********** {xrst_toc_table after include/cppad/example/valvector/split_join.hpp include/cppad/example/valvector/sum.hpp example/valvector/get_started.cpp example/valvector/llsq_obj.cpp } {xrst_end valvector} */ # include # include # include # include # include // ============================================================================ // Macros // ============================================================================ # define CPPAD_VALVECTOR_ASSERT_KNOWN(exp, msg) \ if( ! (exp ) ) \ { std::cerr << "valvector: " << msg << "\n"; \ assert( exp ); \ } // // BEGIN_NOT_AVAILABLE # define CPPAD_VALVECTOR_NOT_AVAILABLE(fun) \ inline bool fun(const valvector& x) \ { CPPAD_VALVECTOR_ASSERT_KNOWN( false, #fun " is not available" ) \ return false; \ } // END_NOT_AVAILABLE // # define CPPAD_VALVECTOR_UNARY_STD_MATH(fun) \ inline valvector fun(const valvector &x) \ { valvector result; \ result.resize( x.size() ); \ for(size_t i = 0; i < x.size(); ++i) \ result[i] = std::fun( x[i] ); \ return result; \ } // # define CPPAD_VALVECTOR_BINARY_NUMERIC_OP(op, compound_op) \ valvector operator op(const valvector& other) const \ { CPPAD_VALVECTOR_ASSERT_KNOWN( \ size() == 1 || other.size() == 1 || size() == other.size() , \ "size error using " #op " operator" \ ) \ valvector result; \ result.resize( std::max( size(), other.size() ) ); \ for(size_t i = 0; i < result.size(); ++i) \ result[i] = (*this)[i] op other[i]; \ return result; \ } \ valvector& operator compound_op(const valvector& other) \ { CPPAD_VALVECTOR_ASSERT_KNOWN( \ size() == 1 || other.size() == 1 || size() == other.size() , \ "size error using " #compound_op " operator" \ ) \ if( size() == 1 && 1 < other.size()) \ { vec_.resize( other.size() ); \ for(size_t i = 0; i < size(); ++i) \ vec_[i] = scalar_; \ } \ for(size_t i = 0; i < size(); ++i) \ (*this)[i] compound_op other[i]; \ return *this; \ } # define CPPAD_VALVECTOR_BINARY_ORDER_OP(op) \ bool operator op(const valvector& other) const \ { CPPAD_VALVECTOR_ASSERT_KNOWN( \ size() == 1 && other.size() == 1, \ "binary " #op " operator is only available when both sizes are one" \ ) \ return (*this)[0] op other[0]; \ } // ============================================================================ // valvector // ============================================================================ class valvector { public: // // scalar_type typedef double scalar_type; // private: // // vector_type typedef CppAD::vector vector_type; // // vec_ vector_type vec_; // // scalar_ scalar_type scalar_; public: /* --------------------------------------------------------------------------- {xrst_begin valvector_ctor} {xrst_spell initializer sj } The valvector Constructors ########################## Creates a valvector (called *x* below). Syntax ****** | ``valvector`` *x* | ``valvector`` *x*( *s* ) | ``valvector`` *x*( *v* ) | ``valvector`` *x*( { *s0* , *s1* , ... } ) Default ******* The default constructor (no argument) creates the valvector *x* with one element using the :ref:`valvector@scalar_type` default constructor. Scalar ****** The scalar constructor (argument is *s* ) creates the valvector *x* with one element that is equal to *scalar_type* ( *s* ), where *s* has type :ref:`valvector@scalar_type` , `int``, ``long int``, ``double``, ``long_double`` or ``size_t`` . Vector ****** The vector constructor (argument is *v*) creates a copy of the valvector *v* . List **** In the standard initializer list constructor ( argument is { *s0* , *s1* , .. } ) *s0* , *s1* , have valvector :ref:`valvector@scalar_type` . This create a valvector with size equal the length of the list and j-th element equal to *sj* . {xrst_toc_hidden example/valvector/ctor.cpp } Example ======= The file :ref:`valvector_ctor.cpp-name` is an example and test of these constructors. {xrst_end valvector_ctor} --------------------------------------------------------------------------- */ // default ctor valvector(void) : vec_(0), scalar_() { } // // ctor of scalar valvector(size_t s) : vec_(0), scalar_( scalar_type(s) ) { } valvector(int s) : vec_(0), scalar_( scalar_type(s) ) { } valvector(long int s) : vec_(0), scalar_( scalar_type(s) ) { } valvector(double s) : vec_(0), scalar_( scalar_type(s) ) { } valvector(long double s) : vec_(0), scalar_( scalar_type(s) ) { } // valvector(const valvector& other) : vec_( other.vec_), scalar_( other.scalar_) { } valvector(valvector&& other) { vec_.swap( other.vec_ ); scalar_ = other.scalar_; } valvector(std::initializer_list list) : vec_(0) { CPPAD_VALVECTOR_ASSERT_KNOWN( list.size() != 0, "Cannot create a valvector with size zero." ) std::initializer_list::iterator itr; if( list.size() == 1 ) { itr = list.begin(); scalar_ = *itr; } else { vec_.resize( list.size() ); itr = list.begin(); for(size_t i = 0; i < vec_.size(); ++i) { vec_[i] = *itr; ++itr; } } } /* ---------------------------------------------------------------------------- {xrst_begin valvector_resize} {xrst_spell valvectors } Resize a valvector ################## Sets the size of this valvector. Prototype ********* {xrst_literal , // BEGIN_RESIZE , // END_RESIZE } Use *** This size of a valvector directly after its :ref:`constructor` is always one. This function must be used to create valvectors with other sizes. n * The argument *n* must not be zero and specifies the number of elements in the valvector. Element Values ************** Directly after this operation, none of the element values are specified. {xrst_toc_hidden example/valvector/resize.cpp } Example ======= The file :ref:`valvector_resize.cpp-name` is an example and test of valvector resize. {xrst_end valvector_resize} ---------------------------------------------------------------------------- */ // BEGIN_RESIZE void resize(size_t n) // END_RESIZE { assert( n != 0 ); if( n == size() ) return; if( n == 1 ) vec_.clear(); else vec_.resize(n); } /* ---------------------------------------------------------------------------- {xrst_begin valvector_assign} The valvector Assignment Operator ################################# Set the value of this valvector equal to another. Prototype ********* {xrst_literal , // BEGIN_ASSIGN_ONE , // END_ASSIGN_ONE // BEGIN_ASSIGN_TWO , // END_ASSIGN_TWO } {xrst_toc_hidden example/valvector/assign.cpp } Example ======= The file :ref:`valvector_assign.cpp-name` is an example and test of valvector assignment. {xrst_end valvector_assign} ---------------------------------------------------------------------------- */ // BEGIN_ASSIGN_ONE valvector& operator=(const valvector& other) // END_ASSIGN_ONE { vec_ = other.vec_; scalar_ = other.scalar_; return *this; } // BEGIN_ASSIGN_TWO valvector& operator=(valvector&& other) // END_ASSIGN_TWO { vec_.swap( other.vec_ ); scalar_ = other.scalar_; return *this; } /* ---------------------------------------------------------------------------- {xrst_begin valvector_size} Size of a valvector ################### Returns the number or elements in this valvector. Prototype ********* {xrst_literal , // BEGIN_SIZE , // END_SIZE } {xrst_toc_hidden example/valvector/size.cpp } Example ======= The file :ref:`valvector_size.cpp-name` is an example and test of valvector size. {xrst_end valvector_size} ---------------------------------------------------------------------------- */ // // BEGIN_SIZE size_t size(void) const // END_SIZE { assert( vec_.size() != 1 ); if( vec_.size() == 0 ) return 1; return vec_.size(); } /* ---------------------------------------------------------------------------- {xrst_begin valvector_sum} Sum elements of a valvector ########################### Returns the sum of the elements in this valvector. Prototype ********* {xrst_literal , // BEGIN_SUM , // END_SUM } {xrst_toc_hidden example/valvector/sum.cpp } Example ======= The file :ref:`valvector_sum.cpp-name` is an example and test of valvector sum. {xrst_end valvector_sum} ---------------------------------------------------------------------------- */ // // BEGIN_SUM scalar_type sum(void) const // END_SUM { scalar_type result = 0.0; for(size_t i = 0; i < size(); ++i) result += (*this)[i]; return result; } /* ---------------------------------------------------------------------------- {xrst_begin valvector_element} Accessing Elements of a valvector ################################# Returns a reference to the specified element of this valvector. Prototype ********* {xrst_literal , // BEGIN_ELEMENT , // END_ELEMENT // BEGIN_CONST_ELEMENT , // END_CONST_ELEMENT } j * This is the index of the element we are accessing. #. If the size this valvector is one, *j* can have any value and the return is the single element in this valvector. #. If the size of this valvector is not one, *j* must be less than its size and the return is the j-th element in this valvector. {xrst_toc_hidden example/valvector/element.cpp } Example ======= The file :ref:`valvector_element.cpp-name` is an example and test of valvector element access. {xrst_end valvector_element} ---------------------------------------------------------------------------- */ // BEGIN_ELEMENT scalar_type& operator[](size_t j) // END_ELEMENT { CPPAD_VALVECTOR_ASSERT_KNOWN( size() == 1 || j < size(), "size is not one and index is greater than or equal size" ); if( size() == 1 ) return scalar_; return vec_[j]; } // BEGIN_CONST_ELEMENT const scalar_type& operator[](size_t j) const // END_CONST_ELEMENT { CPPAD_VALVECTOR_ASSERT_KNOWN( size() == 1 || j < size(), "size is not one and index is greater than or equal size" ); if( size() == 1 ) return scalar_; return vec_[j]; } /* ---------------------------------------------------------------------------- {xrst_begin valvector_unary_op} The valvector Numeric Unary Operators ##################################### Returns the element-by-element result of the unary operators for this valvector. Prototype ********* {xrst_literal , // BEGIN_PLUS , END_PLUS // BEGIN_MINUS , END_MINUS } {xrst_toc_hidden example/valvector/unary_op.cpp } Example ======= The file :ref:`valvector_unary_op.cpp-name` is an example and test of the valvector unary operators. {xrst_end valvector_unary_op} ---------------------------------------------------------------------------- */ // BEGIN_PLUS valvector operator+(void) const // END_PLUS { return *this; } // BEGIN_MINUS valvector operator-(void) const // END_MINUS { valvector result; result.resize( size() ); for(size_t i = 0; i < size(); ++i) result[i] = - (*this)[i]; return result; } /* ---------------------------------------------------------------------------- {xrst_begin valvector_binary_op} {xrst_spell valvectors } The valvector Numeric Binary Operators ###################################### Returns a valvector that is the element-by-element result of the numeric binary operators. Syntax ****** | *x* *op* *y* op ** The numeric binary operator *op* is ``+`` (addition) , ``-`` (subtraction), ``*`` (multiplication) , or ``/`` (division) . x, y **** Both *x* and *y* are ``const`` valvectors. {xrst_toc_hidden example/valvector/binary_op.cpp } Example ======= The file :ref:`valvector_binary_op.cpp-name` is an example and test of the valvector binary operators. {xrst_end valvector_binary_op} ---------------------------------------------------------------------------- {xrst_begin valvector_compound_op} The valvector Numeric Compound Assignment Operators ################################################### Computes the element-by-element result of the numeric compound assignment operators. Syntax ****** | *y* *op* *x* op ** The compound operator *op* is ``+=`` (compound addition) , ``-=`` (compound subtraction), ``*=`` (compound multiplication) , or ``/=`` (compound division) . x * The operand *x* is a ``const`` valvector. y * The operand and result *y* is a valvector. Return ****** The value returned by each of these operators is a reference to *y* . {xrst_toc_hidden example/valvector/compound_op.cpp } Example ======= The file :ref:`valvector_compound_op.cpp-name` is an example and test of the valvector compound assignment operators. {xrst_end valvector_compound_op} */ CPPAD_VALVECTOR_BINARY_NUMERIC_OP(+, +=) CPPAD_VALVECTOR_BINARY_NUMERIC_OP(-, -=) CPPAD_VALVECTOR_BINARY_NUMERIC_OP(*, *=) CPPAD_VALVECTOR_BINARY_NUMERIC_OP(/, /=) /* ---------------------------------------------------------------------------- {xrst_begin valvector_compare_op} The valvector Compare Operators ############################### Syntax ****** | *y* *op* *x* op ** Equality Operators ================== == , != Ordered Operators ================= < , <= , >= , > x * The operand *x* is a ``const`` valvector. If *op* is an equality operator, *x* can have any size. Otherwise it must be size one. y * The operand *y* is a ``const`` valvector. If *op* is an equality operator, *y* can have any size. Otherwise it must be size one. Return ****** The value returned by each of these operators is a bool {xrst_toc_hidden example/valvector/compare_op.cpp } Example ======= The file :ref:`valvector_compare_op.cpp-name` is an example and test of the valvector compare operators. {xrst_end valvector_compare_op} */ // bool operator==(const valvector& other) const { bool result = true; CPPAD_VALVECTOR_ASSERT_KNOWN( size() == 1 || other.size() == 1 || size() == other.size() , "size error using == operator" ) for(size_t i = 0; i < size(); ++i) result &= (*this)[i] == other[i]; return result; } bool operator!=(const valvector& other) const { return ! (*this == other); } CPPAD_VALVECTOR_BINARY_ORDER_OP(<) CPPAD_VALVECTOR_BINARY_ORDER_OP(<=) CPPAD_VALVECTOR_BINARY_ORDER_OP(>=) CPPAD_VALVECTOR_BINARY_ORDER_OP(>) }; /* ------------------------------------------------------------------------------ {xrst_begin valvector_output} Outputting a valvector ###################### Prototype ********* {xrst_literal , // BEGIN_OUTPUT , // END_OUTPUT } {xrst_toc_hidden example/valvector/output.cpp } Example ======= The file :ref:`valvector_output.cpp-name` is an example and test of the valvector output. {xrst_end valvector_output} */ // BEGIN_OUTPUT inline std::ostream& operator << (std::ostream& os, const valvector& x) // END_OUTPUT { os << "{"; for(size_t i = 0; i < x.size(); ++i) { os << x[i]; if( i + 1 < x.size() ) os << ", "; } os << "}"; return os; } // ============================================================================ // CppAD namespace // ============================================================================ namespace CppAD { /* -------------------------------------------------------------------------- {xrst_begin valvector_unary_math} {xrst_spell acosh asinh erfc expm signum } The valvector Unary Math Functions ################################## Syntax ****** | *y* = ``CppAD`` :: *fun* ( *x* ) x * The argument *x* is a ``const`` valvector that is passed by reference. y * The result *y* is a valvector with the same size as *x* . fun *** Standard Math Functions ======================= The function name *fun* can be any of the following: ``acos``, ``acosh``, ``asin``, ``asinh``, ``atan``, ``atanh``, ``cos``, ``cosh``, ``erf``, ``erfc``, ``exp``, ``expm1``, ``fabs``, ``log``, ``log1p``, ``log10``, ``sin``, ``sinh``, ``sqrt``, ``tan``, ``tanh`` abs === The function name *fun* can be ``abs`` , which acts the same as the standard function ``fabs`` . sign ==== The function name *fun* can be ``sign`` , which computes the sign (or signum) function. {xrst_toc_hidden example/valvector/unary_math.cpp } Example ======= The file :ref:`valvector_unary_math.cpp-name` is an example and test of the valvector unary math functions. {xrst_end valvector_unary_math} */ // // standard math function CPPAD_VALVECTOR_UNARY_STD_MATH(acos) CPPAD_VALVECTOR_UNARY_STD_MATH(acosh) CPPAD_VALVECTOR_UNARY_STD_MATH(asin) CPPAD_VALVECTOR_UNARY_STD_MATH(asinh) CPPAD_VALVECTOR_UNARY_STD_MATH(atan) CPPAD_VALVECTOR_UNARY_STD_MATH(atanh) CPPAD_VALVECTOR_UNARY_STD_MATH(cos) CPPAD_VALVECTOR_UNARY_STD_MATH(cosh) CPPAD_VALVECTOR_UNARY_STD_MATH(erf) CPPAD_VALVECTOR_UNARY_STD_MATH(erfc) CPPAD_VALVECTOR_UNARY_STD_MATH(exp) CPPAD_VALVECTOR_UNARY_STD_MATH(expm1) CPPAD_VALVECTOR_UNARY_STD_MATH(fabs) CPPAD_VALVECTOR_UNARY_STD_MATH(log) CPPAD_VALVECTOR_UNARY_STD_MATH(log1p) CPPAD_VALVECTOR_UNARY_STD_MATH(log10) CPPAD_VALVECTOR_UNARY_STD_MATH(sin) CPPAD_VALVECTOR_UNARY_STD_MATH(sinh) CPPAD_VALVECTOR_UNARY_STD_MATH(sqrt) CPPAD_VALVECTOR_UNARY_STD_MATH(tan) CPPAD_VALVECTOR_UNARY_STD_MATH(tanh) // // abs inline valvector abs(const valvector& x) { return fabs(x); } // // sign inline valvector sign(const valvector& x) { typedef valvector::scalar_type scalar_type; // valvector result; result.resize( x.size() ); scalar_type zero = scalar_type(0); for(size_t i = 0; i < x.size(); ++i) { if( x[i] < zero ) result[i] = scalar_type(-1); if( x[i] == zero ) result[i] = zero; if( x[i] > zero ) result[i] = scalar_type(1); } return result; } /* -------------------------------------------------------------------------- {xrst_begin valvector_pow} The valvector Pow Function ########################## Syntax ****** | *z* = ``CppAD::pow`` ( *x* , *y* ) x * The argument *x* is a ``const`` valvector that is passed by reference. y * The argument *y* is a ``const`` valvector that is passed by reference. If the size of *x* is not one, and the size of *y* is not one, the size of *x* and *y* must be equal. z * The result *z* is a valvector with size equal to the maximum of the size of *x* and the size of *y* . {xrst_toc_hidden example/valvector/pow.cpp } Example ======= The file :ref:`valvector_pow.cpp-name` is an example and test of the valvector pow function. {xrst_end valvector_pow} */ inline valvector pow(const valvector& x, const valvector& y) { CPPAD_VALVECTOR_ASSERT_KNOWN( x.size() == 1 || y.size() == 1 || x.size() == y.size() , "size error using pow function" ) valvector result; result.resize( std::max( x.size(), y.size() ) ); for(size_t i = 0; i < x.size(); ++i) result[i] = std::pow( x[i] , y[i] ); return result; } /* -------------------------------------------------------------------------- {xrst_begin valvector_azmul} {xrst_spell valvectors } Absolute Zero Multiply of valvectors #################################### Syntax ****** | *z* = ``CppAD::azmul`` ( *x* , *y* ) x * The argument *x* is a ``const`` valvector that is passed by reference. y * The argument *y* is a ``const`` valvector that is passed by reference. If the size of *x* is not one, and the size of *y* is not one, the size of *x* and *y* must be equal. z * In the special where all the elements of *z* are zero, the result *z* is a valvector with size one. Otherwise, *z* is a valvector with size equal to the maximum of the size of *x* and the size of *y* . {xrst_toc_hidden example/valvector/azmul.cpp } Example ======= The file :ref:`valvector_azmul.cpp-name` is an example and test of the valvector absolute zero multiply function. {xrst_end valvector_azmul} */ inline valvector azmul(const valvector& x, const valvector& y) { typedef valvector::scalar_type scalar_type; // CPPAD_VALVECTOR_ASSERT_KNOWN( x.size() == 1 || y.size() == 1 || x.size() == y.size() , "size error using azmul function" ) // // special case valvector vec_zero = valvector(0); if( x == vec_zero ) return vec_zero; // // element-by-element scalar_type scalar_zero(0); valvector result; result.resize( std::max( x.size(), y.size() ) ); for(size_t i = 0; i < result.size(); ++i) { if( x[i] == scalar_zero ) result[i] = scalar_zero; else result[i] = x[i] * y[i]; } return result; } /* ------------------------------------------------------------------------ {xrst_begin valvector_condexp} {xrst_spell valvectors } The valvector Conditional Expressions ##################################### Computes element-by-element :ref:`CondExp-name` values where the arguments are valvectors. Syntax ****** *result* = ``CondExp`` *Rel* ( *left* , *right* , *if_true* , *if_false* ) Discussion ********** For each valid index *i* , this computes the result | |tab| ``if`` ( *left* [ *i* ] *op* *right* [ *i* ] ) | |tab| |tab| *result* [ *i* ] = *if_true* [ *i* ] | |tab| ``else`` | |tab| |tab| *result* [ *i* ] = *if_false* [ *i* ] where the relational *Rel* and the operator *op* have the following correspondence: .. csv-table:: *Rel* , ``Lt`` , ``Le`` , ``Eq`` , ``Ge`` , ``Gt`` *op* , < , <= , == , >= , > Arguments ********* All of the argument are ``const`` valvectors. result ****** The result has size equal to the maximum of the size of *left* , *right* , *if_true* and *if_false* . The size of each argument must be one, or the same as the size of *result*. {xrst_toc_hidden example/valvector/condexp.cpp } Example ======= The file :ref:`valvector_condexp.cpp-name` is an example and test of the valvector conditional expressions. {xrst_end valvector_condexp} */ inline valvector CondExpOp( enum CompareOp cop , const valvector& left , const valvector& right , const valvector& if_true , const valvector& if_false ) { // // result_size size_t result_size = std::max(left.size(), right.size()); result_size = std::max(result_size, if_true.size()); result_size = std::max(result_size, if_false.size()); // // size_ok bool size_ok = true; size_ok &= left.size() == 1 || left.size() == result_size; size_ok &= right.size() == 1 || right.size() == result_size; size_ok &= if_true.size() == 1 || if_true.size() == result_size; size_ok &= if_false.size() == 1 || if_false.size() == result_size; CPPAD_VALVECTOR_ASSERT_KNOWN( size_ok, "argument sizes do not agree in conditional expression" ); // // result valvector result; result.resize(result_size); // for(size_t i = 0; i < result_size; ++i) { switch( cop ) { case CompareLt: if( left[i] < right[i] ) result[i] = if_true[i]; else result[i] = if_false[i];; break; case CompareLe: if( left[i] <= right[i] ) result[i] = if_true[i]; else result[i] = if_false[i];; break; case CompareEq: if( left[i] == right[i] ) result[i] = if_true[i]; else result[i] = if_false[i];; break; case CompareGe: if( left[i] >= right[i] ) result[i] = if_true[i]; else result[i] = if_false[i];; break; case CompareGt: if( left[i] > right[i] ) result[i] = if_true[i]; else result[i] = if_false[i];; break; default: assert(false); result[i] = if_true[i]; } } // result return result; } CPPAD_COND_EXP_REL(valvector) } // =========================================================================== /* {xrst_begin valvector_base_require} {xrst_spell geq } The valvector Implementation of CppAD Base Type Requirements ############################################################ Output Operator *************** The :ref:`base_require@Output Operator` requirement is satisfied by :ref:`valvector_output-name` . Constructors ************ The :ref:`base_member@Constructors` requirements are satisfied by :ref:`valvector_ctor-name` . Unary Operators *************** The :ref:`base_member@Unary Operators` requirements are satisfied by :ref:`valvector_unary_op-name` . Assignment Operators ******************** The :ref:`base_member@Assignment Operators` requirements are satisfied by :ref:`valvector_assign-name` and :ref:`valvector_compound_op-name` . Binary Operators **************** The :ref:`base_member@Binary Operators` requirements are satisfied by :ref:`valvector_binary_op-name` . Bool Operators ************** The :ref:`base_member@Bool Operators` requirements are satisfied by :ref:`valvector_compare_op-name` . Conditional Expressions *********************** The :ref:`base_cond_exp-name` requirements are satisfied by :ref:`valvector_condexp-name` . Standard Math ************* The :ref:`base_std_math-name` requirements are satisfied by :ref:`valvector_unary_math-name` and :ref:`valvector_pow-name` . azmul ***** The :ref:`base_require@Absolute Zero, azmul` requirement is satisfied by :ref:`valvector_azmul-name` . Features Implemented Here ************************* Integer ======= The :ref:`base_require@Integer` requirement is satisfied by: {xrst_code hpp} */ namespace CppAD { inline int Integer(const valvector& x) { return int( x[0] ); } } /* {xrst_code} numeric_limits ============== The :ref:`base_limits-name` requirement is satisfied by: {xrst_code hpp} */ namespace CppAD { CPPAD_NUMERIC_LIMITS(valvector::scalar_type, valvector) } /* {xrst_code} to_string ========= The :ref:`base_to_string-name` requirement is satisfied by: {xrst_code hpp} */ namespace CppAD { CPPAD_TO_STRING(valvector) } /* {xrst_code} EqualOpSeq ========== The :ref:`base_identical@EqualOpSeq` requirement is satisfied by: {xrst_code hpp} */ namespace CppAD { inline bool EqualOpSeq(const valvector& left, const valvector& right) { return left == right; } } /* {xrst_code} Identical ========= {xrst_code hpp} */ namespace CppAD { inline bool IdenticalCon(const valvector& x) { return true; } inline bool IdenticalZero(const valvector& x) { return x == valvector(0); } inline bool IdenticalOne(const valvector& x) { return x == valvector(1); } inline bool IdenticalEqualCon(const valvector& x, const valvector& y) { return x == y; } } /* {xrst_code} Not Ordered =========== The :ref:`base_ordered@Not Ordered` requirement is satisfied by: {xrst_literal , // BEGIN_NOT_AVAILABLE , // END_NOT_AVAILABLE } {xrst_code hpp} */ namespace CppAD { CPPAD_VALVECTOR_NOT_AVAILABLE(GreaterThanZero) CPPAD_VALVECTOR_NOT_AVAILABLE(GreaterThanOrZero) CPPAD_VALVECTOR_NOT_AVAILABLE(LessThanZero) CPPAD_VALVECTOR_NOT_AVAILABLE(LessThanOrZero) // // abs_geq inline bool abs_geq(const valvector& x, const valvector& y) { CPPAD_VALVECTOR_ASSERT_KNOWN( x.size() == 1 || y.size() == 1 || x.size() == y.size() , "size error in abs_geq functions" ) size_t max_size = std::max( x.size(), y.size() ); valvector::scalar_type abs_x(0), abs_y(0); for(size_t i = 0; i < max_size; ++i) { abs_x += fabs( x[i] ); abs_y += fabs( y[i] ); } return abs_x >= abs_y; } } /* {xrst_code} {xrst_toc_hidden example/valvector/base_require.cpp } Example ======= The file :ref:`valvector_base_require.cpp-name` is an example and test of the valvector :ref:`valvector_base_require@Features Implemented Here` . {xrst_end valvector_base_require} */ # undef CPPAD_VALVECTOR_ASSERT_KNOWN # undef CPPAD_VALVECTOR_UNARY_STD_MATH # undef CPPAD_VALVECTOR_BINARY_ORDER_OP # undef CPPAD_VALVECTOR_BINARY_NUMERIC_OP # undef CPPAD_VALVECTOR_NOT_AVAILABLE # endif ================================================ FILE: include/cppad/example/valvector/split_join.hpp ================================================ # ifndef CPPAD_EXAMPLE_VALVECTOR_SPLIT_JOIN_HPP # define CPPAD_EXAMPLE_VALVECTOR_SPLIT_JOIN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell /* ------------------------------------------------------------------------------- {xrst_begin valvector_ad_split} {xrst_spell asplit } Split A AD valvector #################### Split one AD into a vector of AD each with size one. Syntax ****** | valvector_ad_split *asplit* | *asplit( *ax* , *ay_vec* ) m * We use *m* to denote *ay_vec* .size() ax ** This CppAD::AD is ``const`` and passed by reference. The size *ax*.size() must be equal to one or *m* ay_vec ****** This is a :ref:`SimpleVector-name` with elements of type CppAD::AD and is passed by reference. The size of *ay_vec* is not changed. Upon return, for *i* = 0 , ... , *m* - 1 , the size *ay* [ *i* ].size() is one and:: ay[i][0] = ax[i] {xrst_toc_hidden example/valvector/ad_split.cpp } Example ******* The file :ref:`valvector_ad_split.cpp-name` is an example and test of this operation. {xrst_end valvector_ad_split} ------------------------------------------------------------------------------- {xrst_begin valvector_ad_join} {xrst_spell ajoin valvectors } Join a Vector of AD valvectors ############################## Join a vector of AD, each with size one, into one AD. Syntax ****** | valvector_ad_join *ajoin* | *ajoin( *ax_vec* , *ay* ) m * We use *m* to denote *ax_vec* .size() . ax_vec ****** This is a :ref:`SimpleVector-name` with elements of type CppAD::AD . It is ``const`` and is passed by reference. For *i* = 0 , ... , *m* - 1 , the size *ax* [ *i* ].size() is one. ay ** This CppAD::AD is passed by reference and its input value does not matter. Upon return, its size in *m* and for *i* = 0 , ... , *m* - 1 :: ay[i] = ax_vec[i][0] {xrst_toc_hidden example/valvector/ad_join.cpp } Example ******* The file :ref:`valvector_ad_join.cpp-name` is an example and test of this operation. {xrst_end valvector_ad_join} ------------------------------------------------------------------------------- */ # include # include // // valvector_split_join // split and join are in same atomic function so that in AD version of reverse // split can call join and join can call split. class valvector_split_join : public CppAD::atomic_four { public: // // ctor valvector_split_join(const std::string& name) : CppAD::atomic_four(name) { } // // is_split static bool is_split(size_t call_id) { return call_id == 1; } // // is_join static bool is_join(size_t call_id) { return call_id == 2; } private: // // ad_valvector typedef CppAD::AD ad_valvector; // // ------------------------------------------------------------------------ // for_type bool for_type( size_t call_id , const CppAD::vector& type_x , CppAD::vector& type_y ) override { if( is_split(call_id) ) { // assert( type_x.size() == 1 ); // n // // type_y size_t m = type_y.size(); for(size_t i = 0; i < m; ++i) type_y[i] = type_x[0]; } else { assert( is_join(call_id) ); // assert( type_y.size() == 1 ); // m // // type_y size_t n = type_x.size(); type_y[0] = CppAD::identical_zero_enum; for(size_t j = 0; j < n; ++j) type_y[0] = std::max( type_y[0], type_x[j] ); } return true; } // ------------------------------------------------------------------------ // forward bool forward( size_t call_id , const CppAD::vector& select_y , size_t order_low , size_t order_up , const CppAD::vector& taylor_x , CppAD::vector& taylor_y ) override { // if( is_split(call_id) ) { // // q, m size_t q = order_up + 1; size_t m = taylor_y.size() / q; // # ifndef NDEBUG size_t n = taylor_x.size() / q; assert( n == 1 ); assert( m == select_y.size() ); for(size_t k = 0; k < q; ++k) assert( taylor_x[k].size() == 1 || taylor_x[k].size() == m ); # endif // // taylor_y for(size_t i = 0; i < m; ++i) if( select_y[i] ) { for(size_t k = order_low; k < q; ++k) { // // taylor_y[i * q + k] taylor_y[i * q + k].resize(1); taylor_y[i * q + k][0] = taylor_x[k][i]; } } } else { assert( is_join(call_id) ); // // q, n size_t q = order_up + 1; size_t n = taylor_x.size() / q; // # ifndef NDEBUG size_t m = taylor_y.size() / q; assert( m == 1 ); assert( m == select_y.size() ); for(size_t k = 0; k < q; ++k) assert( taylor_x[k].size() == 1 ); # endif // // taylor_y if( select_y[0] ) { for(size_t k = order_low; k < q; ++k) { // // taylor_y[k] taylor_y[k].resize(n); for(size_t j = 0; j < n; ++j) taylor_y[k][j] = taylor_x[j * q + k][0]; } } // } return true; } // ------------------------------------------------------------------------ // forward bool forward( size_t call_id , const CppAD::vector& select_y , size_t order_low , size_t order_up , const CppAD::vector& ataylor_x , CppAD::vector& ataylor_y ) override { // if( is_split(call_id) ) { // // q, m size_t q = order_up + 1; size_t m = ataylor_y.size() / q; // # ifndef NDEBUG size_t n = ataylor_x.size() / q; assert( n == 1 ); assert( m == select_y.size() ); # endif // // ax_k, ay_k CppAD::vector ax_k(1); CppAD::vector ay_k(m); // // ataylor_y for(size_t k = order_low; k < q; ++k) { ax_k[0] = ataylor_x[k]; (*this)(call_id, ax_k, ay_k); { for(size_t i = 0; i < m; ++i) ataylor_y[i * q + k] = ay_k[i]; } } } else { assert( is_join(call_id) ); // // q, n size_t q = order_up + 1; size_t n = ataylor_x.size() / q; // # ifndef NDEBUG size_t m = ataylor_y.size() / q; assert( m == 1 ); assert( m == select_y.size() ); # endif if( select_y[0] ) { // // ax_k, ay_k CppAD::vector ax_k(n); CppAD::vector ay_k(1); // // ataylor_y for(size_t k = order_low; k < q; ++k) { for(size_t j = 0; j < n; ++j) ax_k[j] = ataylor_x[j * q + k]; (*this)(call_id, ax_k, ay_k); ataylor_y[k] = ay_k[0]; } } } return true; } // ------------------------------------------------------------------------ // reverse bool reverse( size_t call_id , const CppAD::vector& select_x , size_t order_up , const CppAD::vector& taylor_x , const CppAD::vector& taylor_y , CppAD::vector& partial_x , const CppAD::vector& partial_y ) override { // if( is_split(call_id) ) { // // q, m size_t q = order_up + 1; size_t m = taylor_y.size() / q; // # ifndef NDEBUG size_t n = taylor_x.size() / q; assert( n == 1 ); assert( 1 == select_x.size() ); # endif if( ! select_x[0] ) return true; // // partial_x for(size_t k = 0; k < q; ++k) { partial_x[k].resize(m); for(size_t i = 0; i < m; ++i) { // // partial_x[k][i] assert( taylor_y[i * q + k].size() == 1 ); partial_x[k][i] = partial_y[i * q + k][0]; } } } else { assert( is_join(call_id) ); // // q, m size_t q = order_up + 1; size_t n = taylor_x.size() / q; // # ifndef NDEBUG size_t m = taylor_y.size() / q; assert( m == 1 ); assert( n == select_x.size() ); # endif // // partial_x for(size_t k = 0; k < q; ++k) { assert( taylor_y[k].size() == n ); for(size_t j = 0; j < n; ++j) { partial_x[j * q + k].resize(1); partial_x[j * q + k][0] = partial_y[k][j]; } } } return true; } // ------------------------------------------------------------------------ // reverse bool reverse( size_t call_id , const CppAD::vector& select_x , size_t order_up , const CppAD::vector& ataylor_x , const CppAD::vector& ataylor_y , CppAD::vector& apartial_x , const CppAD::vector& apartial_y ) override { // if( is_split(call_id) ) { // // q, m size_t q = order_up + 1; size_t m = ataylor_y.size() / q; // # ifndef NDEBUG size_t n = ataylor_x.size() / q; assert( n == 1 ); assert( 1 == select_x.size() ); # endif if( ! select_x[0] ) return true; // // ay_k, ax_k CppAD::vector ay_k(m); CppAD::vector ax_k(1); // // join_call_id size_t join_call_id = 2; assert( is_join(join_call_id) ); // // apartial_x for(size_t k = 0; k < q; ++k) { for(size_t i = 0; i < m; ++i) ay_k[i] = apartial_y[i * q + k]; // split calls join here (*this)(join_call_id, ay_k, ax_k); apartial_x[k] = ax_k[0]; } } else { assert( is_join(call_id) ); // // q, m size_t q = order_up + 1; size_t n = ataylor_x.size() / q; // # ifndef NDEBUG size_t m = ataylor_y.size() / q; assert( m == 1 ); assert( n == select_x.size() ); # endif // // ay_k, ax_k CppAD::vector ay_k(1); CppAD::vector ax_k(n); // // split_call_id size_t split_call_id = 1; assert( is_split(split_call_id) ); // // apartial_x for(size_t k = 0; k < q; ++k) { ay_k[0] = apartial_y[k]; // Join calls split here (*this)(split_call_id, ay_k, ax_k); for(size_t j = 0; j < n; ++j) apartial_x[j * q + k] = ax_k[j]; } } return true; } // ------------------------------------------------------------------------ // jac_sparsity bool jac_sparsity( size_t call_id , bool dependency , const CppAD::vector& ident_zero_x , const CppAD::vector& select_x , const CppAD::vector& select_y , CppAD::sparse_rc< CppAD::vector >& pattern_out ) override { // if( is_split(call_id) ) { // // m, n size_t m = select_y.size(); size_t n = select_x.size(); // assert( n == 1 ); // // nnz size_t nnz = 0; if( select_x[0] ) { for(size_t i = 0; i < m; ++i) { if( select_y[i] ) ++nnz; } } // // pattern_out pattern_out.resize(m, n, nnz); size_t k = 0; if( select_x[0] ) { for(size_t i = 0; i < m; ++i) { if( select_y[i] ) pattern_out.set(k++, i, 0); } } } else { assert( is_join(call_id) ); // // m, n size_t m = select_y.size(); size_t n = select_x.size(); // assert( m == 1 ); // // nnz size_t nnz = 0; if( select_y[0] ) { for(size_t j = 0; j < n; ++j) { if( select_x[j] ) ++nnz; } } // // pattern_out pattern_out.resize(m, n, nnz); size_t k = 0; if( select_y[0] ) { for(size_t j = 0; j < n; ++j) { if( select_x[j] ) pattern_out.set(k++, 0, j); } } } return true; } // ------------------------------------------------------------------------ // hes_sparsity bool hes_sparsity( size_t call_id , const CppAD::vector& ident_zero_x , const CppAD::vector& select_x , const CppAD::vector& select_y , CppAD::sparse_rc< CppAD::vector >& pattern_out ) override { // if( is_split(call_id) ) { // // n size_t n = select_x.size(); // assert( n == 1 ); assert( n == select_x.size() ); // // pattern_out size_t nnz = 0; pattern_out.resize(n, n, nnz); } else { assert( is_join(call_id) ); // // n size_t n = select_x.size(); // assert( n == select_x.size() ); // // pattern_out size_t nnz = 0; pattern_out.resize(n, n, nnz); } return true; } // ------------------------------------------------------------------------ // rev_depend bool rev_depend( size_t call_id , const CppAD::vector& ident_zero_x , CppAD::vector& depend_x , const CppAD::vector& depend_y ) override { // if( is_split(call_id) ) { // // m size_t m = depend_y.size(); // # ifndef NDEBUG size_t n = depend_x.size(); assert( n == 1 ); # endif // // depend_x depend_x[0] = false; for(size_t i = 0; i < m; ++i) depend_x[0] |= depend_y[i]; } else { assert( is_join(call_id) ); // // n size_t n = depend_x.size(); // # ifndef NDEBUG size_t m = depend_y.size(); assert( m == 1 ); # endif // // depend_x for(size_t j = 0; j < n; ++j) depend_x[j] = depend_y[0]; } return true; } }; // --------------------------------------------------------------------------- // valvector_ad_split class valvector_ad_split { private: typedef CppAD::AD ad_valvector; valvector_split_join atomic_fun_; public: valvector_ad_split(void) : atomic_fun_("valvector_split_join") { } template void operator()(const ad_valvector& ax, ADVector& ay) { size_t call_id = 1; assert( atomic_fun_.is_split(call_id) ); ADVector ax_vec(1); ax_vec[0] = ax; atomic_fun_(call_id, ax_vec, ay); return; } }; // --------------------------------------------------------------------------- // valvector_ad_join class valvector_ad_join { private: typedef CppAD::AD ad_valvector; valvector_split_join atomic_fun_; public: valvector_ad_join(void) : atomic_fun_("valvector_split_join") { } template void operator()(const ADVector& ax, ad_valvector& ay) { size_t call_id = 2; ADVector ay_vec(1); assert( atomic_fun_.is_join(call_id) ); atomic_fun_(call_id, ax, ay_vec); ay = ay_vec[0]; return; } }; # endif ================================================ FILE: include/cppad/example/valvector/sum.hpp ================================================ # ifndef CPPAD_EXAMPLE_VALVECTOR_SUM_HPP # define CPPAD_EXAMPLE_VALVECTOR_SUM_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2024 Bradley M. Bell /* ------------------------------------------------------------------------------- {xrst_begin valvector_ad_sum} {xrst_spell asum } Sum The Elements of and AD valvector #################################### Sum the elements of an AD and return the result as one AD with size one. Syntax ****** | valvector_ad_sum *asum* | *asum* ( *ax* , *ay* ) ax ** This CppAD::AD is ``const`` and passed by reference. ay ** This CppAD::AD is passed by reference. Its input value does not matter. Upon return, it has size one and its element is the sum of the elements in *ax* . {xrst_toc_hidden example/valvector/ad_sum.cpp } Example ******* The file :ref:`valvector_ad_sum.cpp-name` is an example and test of this operation. {xrst_end valvector_ad_sum} */ # include # include // // valvector_atom_sum // split and join are in same atomic function so that in AD version of reverse // split can call join and join can call split. class valvector_atom_sum : public CppAD::atomic_four { public: // // ctor valvector_atom_sum(const std::string& name) : CppAD::atomic_four(name) { } private: // // ad_valvector typedef CppAD::AD ad_valvector; // // scalar_type typedef valvector::scalar_type scalar_type; // // ------------------------------------------------------------------------ // for_type bool for_type( size_t call_id , const CppAD::vector& type_x , CppAD::vector& type_y ) override { // assert( call_id == 0 ); assert( type_x.size() == 1 ); assert( type_y.size() == 1 ); // type_y[0] = type_x[0]; // return true; } // ------------------------------------------------------------------------ // forward bool forward( size_t call_id , const CppAD::vector& select_y , size_t order_low , size_t order_up , const CppAD::vector& taylor_x , CppAD::vector& taylor_y ) override { // assert( call_id == 0 ); assert( select_y.size() == 1 ); // // q size_t q = order_up + 1; assert( taylor_x.size() == q); assert( taylor_y.size() == q); // // taylor_y for(size_t k = order_low; k < q; ++k) { // taylor_y[k].resize(1); taylor_y[k][0] = taylor_x[k].sum(); } return true; } // ------------------------------------------------------------------------ // forward bool forward( size_t call_id , const CppAD::vector& select_y , size_t order_low , size_t order_up , const CppAD::vector& ataylor_x , CppAD::vector& ataylor_y ) override { // assert( call_id == 0 ); assert( select_y.size() == 1 ); // // q size_t q = order_up + 1; assert( ataylor_x.size() == q); assert( ataylor_y.size() == q); // // // ax_k, ay_k CppAD::vector ax_k(1); CppAD::vector ay_k(1); // // ataylor_y for(size_t k = 0; k < q; ++k) { ax_k[0] = ataylor_x[k]; (*this)(ax_k, ay_k); ataylor_y[k] = ay_k[0]; } // return true; } // ------------------------------------------------------------------------ // reverse bool reverse( size_t call_id , const CppAD::vector& select_x , size_t order_up , const CppAD::vector& taylor_x , const CppAD::vector& taylor_y , CppAD::vector& partial_x , const CppAD::vector& partial_y ) override { // assert( call_id == 0 ); assert( select_x.size() == 1 ); // // q size_t q = order_up + 1; assert( taylor_x.size() == q ); assert( taylor_y.size() == q ); // if( ! select_x[0] ) return true; // // partial_x for(size_t k = 0; k < q; ++k) { assert( partial_y[k].size() == 1 ); partial_x[k] = partial_y[k]; } // return true; } // ------------------------------------------------------------------------ // reverse bool reverse( size_t call_id , const CppAD::vector& select_x , size_t order_up , const CppAD::vector& ataylor_x , const CppAD::vector& ataylor_y , CppAD::vector& apartial_x , const CppAD::vector& apartial_y ) override { // assert( call_id == 0 ); assert( select_x.size() == 1 ); // // q size_t q = order_up + 1; assert( ataylor_x.size() == q ); assert( ataylor_y.size() == q ); // if( ! select_x[0] ) return true; // // apartial_x for(size_t k = 0; k < q; ++k) apartial_x[k] = apartial_y[k]; // return true; } // ------------------------------------------------------------------------ // jac_sparsity bool jac_sparsity( size_t call_id , bool dependency , const CppAD::vector& ident_zero_x , const CppAD::vector& select_x , const CppAD::vector& select_y , CppAD::sparse_rc< CppAD::vector >& pattern_out ) override { // assert( call_id == 0 ); assert( select_x.size() == 1 ); assert( select_y.size() == 1 ); // // m, n size_t m = select_y.size(); size_t n = select_x.size(); // // nnz size_t nnz = 0; if( select_x[0] && select_y[0] ) ++nnz; // // pattern_out pattern_out.resize(m, n, nnz); if( select_x[0] && select_y[0] ) pattern_out.set(0, 0, 0); // return true; } // ------------------------------------------------------------------------ // hes_sparsity bool hes_sparsity( size_t call_id , const CppAD::vector& ident_zero_x , const CppAD::vector& select_x , const CppAD::vector& select_y , CppAD::sparse_rc< CppAD::vector >& pattern_out ) override { // assert( call_id == 0 ); assert( select_x.size() == 1 ); assert( select_y.size() == 1 ); // size_t n = select_x.size(); // // pattern_out size_t nnz = 0; pattern_out.resize(n, n, nnz); // return true; } // ------------------------------------------------------------------------ // rev_depend bool rev_depend( size_t call_id , const CppAD::vector& ident_zero_x , CppAD::vector& depend_x , const CppAD::vector& depend_y ) override { // assert( call_id == 0 ); assert( depend_x.size() == 1 ); assert( depend_y.size() == 1 ); // depend_x[0] = depend_y[0]; // return true; } }; // --------------------------------------------------------------------------- // valvector_ad_sum class valvector_ad_sum { private: typedef CppAD::AD ad_valvector; valvector_atom_sum atomic_fun_; public: valvector_ad_sum(void) : atomic_fun_("valvector_ad_sum") { } void operator()(const ad_valvector& ax, ad_valvector& ay) { // // ay CppAD::vector ax_vec(1), ay_vec(1); ax_vec[0] = ax; atomic_fun_(ax_vec, ay_vec); ay = ay_vec[0]; return; } }; # endif ================================================ FILE: include/cppad/ipopt/solve.hpp ================================================ # ifndef CPPAD_IPOPT_SOLVE_HPP # define CPPAD_IPOPT_SOLVE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ipopt_solve} {xrst_spell bvector doesn fg gl gu maxiter naninf ng nx rll xl zl zu } Use Ipopt to Solve a Nonlinear Programming Problem ################################################## Syntax ****** | # ``include `` | ``ipopt::solve`` ( | |tab| *options* , *xi* , *xl* , *xu* , *gl* , *gu* , *fg_eval* , *solution* | ) Purpose ******* The function ``ipopt::solve`` solves nonlinear programming problems of the form .. math:: \begin{array}{rll} {\rm minimize} & f (x) \\ {\rm subject \; to} & gl \leq g(x) \leq gu \\ & xl \leq x \leq xu \end{array} This is done using `Ipopt `_ optimizer and CppAD for the derivative and sparsity calculations. Include File ************ If :ref:`cmake@include_ipopt` is on the cmake command line, the file ``cppad/ipopt/solve.hpp`` is included by ``cppad/cppad.hpp`` . Otherwise, ``cppad/ipopt/solve.hpp`` can be included directly (If ``cppad/cppad.hpp`` has not yet been included, ``cppad/ipopt/solve.hpp`` will automatically include it.) Bvector ******* The type *Bvector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` ``bool`` . Dvector ******* The type *DVector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` ``double`` . options ******* The argument *options* has prototype ``const std::string`` *options* It contains a list of options. Each option, including the last option, is terminated by the ``'\n'`` character. Each line consists of two or three tokens separated by one or more spaces. Retape ====== You can set the retape flag with the following syntax: ``Retape`` *value* If the value is ``true`` , ``ipopt::solve`` with retape the :ref:`operation sequence` for each new value of *x* . If the value is ``false`` , ``ipopt::solve`` will tape the operation sequence at the value of *xi* and use that sequence for the entire optimization process. The default value is ``false`` . Sparse ====== You can set the sparse Jacobian and Hessian flag with the following syntax: ``Sparse`` *value* *direction* If the value is ``true`` , ``ipopt::solve`` will use a sparse matrix representation for the computation of Jacobians and Hessians. Otherwise, it will use a full matrix representation for these calculations. The default for *value* is ``false`` . If sparse is true, retape must be false. It is unclear if :ref:`sparse_jacobian-name` would be faster user forward or reverse mode so you are able to choose the direction. If *value* == ``true &&`` *direction* == ``forward`` the Jacobians will be calculated using ``SparseJacobianForward`` . If *value* == ``true &&`` *direction* == ``reverse`` the Jacobians will be calculated using ``SparseJacobianReverse`` . String ====== You can set any Ipopt string option using a line with the following syntax: ``String`` *name* *value* Here *name* is any valid Ipopt string option and *value* is its setting. Numeric ======= You can set any Ipopt numeric option using a line with the following syntax: ``Numeric`` *name* *value* Here *name* is any valid Ipopt numeric option and *value* is its setting. Integer ======= You can set any Ipopt integer option using a line with the following syntax: ``Integer`` *name* *value* Here *name* is any valid Ipopt integer option and *value* is its setting. xi ** The argument *xi* has prototype ``const`` *Vector* & *xi* and its size is equal to *nx* . It specifies the initial point where Ipopt starts the optimization process. xl ** The argument *xl* has prototype ``const`` *Vector* & *xl* and its size is equal to *nx* . It specifies the lower limits for the argument in the optimization problem. xu ** The argument *xu* has prototype ``const`` *Vector* & *xu* and its size is equal to *nx* . It specifies the upper limits for the argument in the optimization problem. gl ** The argument *gl* has prototype ``const`` *Vector* & *gl* and its size is equal to *ng* . It specifies the lower limits for the constraints in the optimization problem. gu ** The argument *gu* has prototype ``const`` *Vector* & *gu* and its size is equal to *ng* . It specifies the upper limits for the constraints in the optimization problem. fg_eval ******* The argument *fg_eval* has prototype *FG_eval* *fg_eval* where the class *FG_eval* is unspecified except for the fact that it supports the syntax | |tab| *FG_eval* :: ``ADvector`` | |tab| *fg_eval* ( *fg* , *x* ) The type *ADvector* and the arguments to *fg* , *x* have the following meaning: ADvector ======== The type *FG_eval* :: ``ADvector`` must be a :ref:`SimpleVector-name` class with :ref:`elements of type` ``AD`` . x = The *fg_eval* argument *x* has prototype ``const`` *ADvector* & *x* where *nx* = *x* . ``size`` () . fg == The *fg_eval* argument *fg* has prototype *ADvector* & *fg* where 1 + *ng* = *fg* . ``size`` () . The input value of the elements of *fg* does not matter. Upon return from *fg_eval* , ``fg`` [0] = :math:`f (x)` and for :math:`i = 0, \ldots , ng-1`, ``fg`` [1 + ``i`` ] = :math:`g_i (x)` solution ******** The argument *solution* has prototype ``ipopt::solve_result<`` *Dvector* >& *solution* After the optimization process is completed, *solution* contains the following information: status ====== The *status* field of *solution* has prototype ``ipopt::solve_result<`` *Dvector* >:: ``status_type`` *solution* . ``status`` It is the final Ipopt status for the optimizer. Here is a list of the possible values for the status: .. list-table:: :widths: auto * - *status* - Meaning * - not_defined - The optimizer did not return a final status for this problem. * - unknown - The status returned by the optimizer is not defined in the Ipopt documentation for ``finalize_solution`` . * - success - Algorithm terminated successfully at a point satisfying the convergence tolerances (see Ipopt options). * - maxiter_exceeded - The maximum number of iterations was exceeded (see Ipopt options). * - stop_at_tiny_step - Algorithm terminated because progress was very slow. * - stop_at_acceptable_point - Algorithm stopped at a point that was converged, not to the 'desired' tolerances, but to 'acceptable' tolerances (see Ipopt options). * - local_infeasibility - Algorithm converged to a non-feasible point (problem may have no solution). * - user_requested_stop - This return value should not happen. * - diverging_iterates - It the iterates are diverging. * - restoration_failure - Restoration phase failed, algorithm doesn't know how to proceed. * - error_in_step_computation - An unrecoverable error occurred while Ipopt tried to compute the search direction. * - invalid_number_detected - Algorithm received an invalid number (such as ``nan`` or ``inf`` ) from the users function *fg_info* . ``eval`` or from the CppAD evaluations of its derivatives (see the Ipopt option ``check_derivatives_for_naninf`` ). * - internal_error - An unknown Ipopt internal error occurred. Contact the Ipopt authors through the mailing list. x = The ``x`` field of *solution* has prototype *Vector* *solution* . ``x`` and its size is equal to *nx* . It is the final :math:`x` value for the optimizer. zl == The ``zl`` field of *solution* has prototype *Vector* *solution* . ``zl`` and its size is equal to *nx* . It is the final Lagrange multipliers for the lower bounds on :math:`x`. zu == The ``zu`` field of *solution* has prototype *Vector* *solution* . ``zu`` and its size is equal to *nx* . It is the final Lagrange multipliers for the upper bounds on :math:`x`. g = The ``g`` field of *solution* has prototype *Vector* *solution* . ``g`` and its size is equal to *ng* . It is the final value for the constraint function :math:`g(x)`. lambda ====== The ``lambda`` field of *solution* has prototype *Vector* > *solution* . ``lambda`` and its size is equal to *ng* . It is the final value for the Lagrange multipliers corresponding to the constraint function. obj_value ========= The ``obj_value`` field of *solution* has prototype ``double`` *solution* . ``obj_value`` It is the final value of the objective function :math:`f(x)`. {xrst_toc_hidden example/ipopt_solve/get_started.cpp example/ipopt_solve/retape.cpp example/ipopt_solve/ode_inverse.cpp } Example ******* All the examples return true if it succeeds and false otherwise. get_started =========== The file :ref:`example/ipopt_solve/get_started.cpp` is an example and test of ``ipopt::solve`` taken from the Ipopt manual. retape ====== The file :ref:`example/ipopt_solve/retape.cpp` demonstrates when it is necessary to specify :ref:`ipopt_solve@options@Retape` as true. ode_inverse =========== The file :ref:`example/ipopt_solve/ode_inverse.cpp` demonstrates using Ipopt to solve for parameters in an ODE model. {xrst_end ipopt_solve} ------------------------------------------------------------------------------- */ # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE namespace ipopt { /*! \file solve.hpp \brief Implement the ipopt::solve Nonlinear Programming Solver */ /*! Use Ipopt to Solve a Nonlinear Programming Problem \tparam Bvector simple vector class with elements of type bool. \tparam Dvector simple vector class with elements of type double. \tparam FG_eval function object used to evaluate f(x) and g(x); see fg_eval below. It must also support \code FG_eval::ADvector \endcode to identify the type used for the arguments to fg_eval. \param options list of options, one for each line. Ipopt options (are optional) and have one of the following forms \code String name value Numeric name value Integer name value \endcode The following other possible options are listed below: \code Retape value \endcode \param xi initial argument value to start optimization procedure at. \param xl lower limit for argument during optimization \param xu upper limit for argument during optimization \param gl lower limit for g(x) during optimization. \param gu upper limit for g(x) during optimization. \param fg_eval function that evaluates the objective and constraints using the syntax \code fg_eval(fg, x) \endcode \param solution structure that holds the solution of the optimization. */ template void solve( const std::string& options , const Dvector& xi , const Dvector& xl , const Dvector& xu , const Dvector& gl , const Dvector& gu , FG_eval& fg_eval , ipopt::solve_result& solution ) { bool ok = true; typedef typename FG_eval::ADvector ADvector; CPPAD_ASSERT_KNOWN( xi.size() == xl.size() && xi.size() == xu.size() , "ipopt::solve: size of xi, xl, and xu are not all equal." ); CPPAD_ASSERT_KNOWN( gl.size() == gu.size() , "ipopt::solve: size of gl and gu are not equal." ); size_t nx = xi.size(); size_t ng = gl.size(); // Create an IpoptApplication using Ipopt::IpoptApplication; Ipopt::SmartPtr app = new IpoptApplication(); // process the options argument size_t begin_1, end_1, begin_2, end_2, begin_3, end_3; begin_1 = 0; bool retape = false; bool sparse_forward = false; bool sparse_reverse = false; while( begin_1 < options.size() ) { // split this line into tokens while( options[begin_1] == ' ') begin_1++; end_1 = options.find_first_of(" \n", begin_1); begin_2 = end_1; while( options[begin_2] == ' ') begin_2++; end_2 = options.find_first_of(" \n", begin_2); begin_3 = end_2; while( options[begin_3] == ' ') begin_3++; end_3 = options.find_first_of(" \n", begin_3); // check for errors CPPAD_ASSERT_KNOWN( (end_1 != std::string::npos) & (end_2 != std::string::npos) & (end_3 != std::string::npos) , "ipopt::solve: missing '\\n' at end of an option line" ); CPPAD_ASSERT_KNOWN( (end_1 > begin_1) && (end_2 > begin_2) , "ipopt::solve: an option line does not have two tokens" ); // get first two tokens std::string tok_1 = options.substr(begin_1, end_1 - begin_1); std::string tok_2 = options.substr(begin_2, end_2 - begin_2); // get third token std::string tok_3; bool three_tok = false; three_tok |= tok_1 == "Sparse"; three_tok |= tok_1 == "String"; three_tok |= tok_1 == "Numeric"; three_tok |= tok_1 == "Integer"; if( three_tok ) { CPPAD_ASSERT_KNOWN( (end_3 > begin_3) , "ipopt::solve: a Sparse, String, Numeric, or Integer\n" "option line does not have three tokens." ); tok_3 = options.substr(begin_3, end_3 - begin_3); } // switch on option type if( tok_1 == "Retape" ) { CPPAD_ASSERT_KNOWN( (tok_2 == "true") || (tok_2 == "false") , "ipopt::solve: Retape value is not true or false" ); retape = (tok_2 == "true"); } else if( tok_1 == "Sparse" ) { CPPAD_ASSERT_KNOWN( (tok_2 == "true") || (tok_2 == "false") , "ipopt::solve: Sparse value is not true or false" ); CPPAD_ASSERT_KNOWN( (tok_3 == "forward") || (tok_3 == "reverse") , "ipopt::solve: Sparse direction is not forward or reverse" ); if( tok_2 == "false" ) { sparse_forward = false; sparse_reverse = false; } else { sparse_forward = tok_3 == "forward"; sparse_reverse = tok_3 == "reverse"; } } else if ( tok_1 == "String" ) app->Options()->SetStringValue(tok_2.c_str(), tok_3.c_str()); else if ( tok_1 == "Numeric" ) { Ipopt::Number value = std::atof( tok_3.c_str() ); app->Options()->SetNumericValue(tok_2.c_str(), value); } else if ( tok_1 == "Integer" ) { Ipopt::Index value = std::atoi( tok_3.c_str() ); app->Options()->SetIntegerValue(tok_2.c_str(), value); } else CPPAD_ASSERT_KNOWN( false, "ipopt::solve: First token is not one of\n" "Retape, Sparse, String, Numeric, Integer" ); begin_1 = end_3; while( options[begin_1] == ' ') begin_1++; if( options[begin_1] != '\n' ) CPPAD_ASSERT_KNOWN( false, "ipopt::solve: either more than three tokens " "or no '\\n' at end of a line" ); begin_1++; } CPPAD_ASSERT_KNOWN( ! ( retape & (sparse_forward | sparse_reverse) ) , "ipopt::solve: retape and sparse both true is not supported." ); // Initialize the IpoptApplication and process the options Ipopt::ApplicationReturnStatus status = app->Initialize(); ok &= status == Ipopt::Solve_Succeeded; if( ! ok ) { solution.status = solve_result::unknown; return; } // Create an interface from Ipopt to this specific problem. // Note the assumption here that ADvector is same as cppd_ipopt::ADvector size_t nf = 1; Ipopt::SmartPtr cppad_nlp = new CppAD::ipopt::solve_callback( nf, nx, ng, xi, xl, xu, gl, gu, fg_eval, retape, sparse_forward, sparse_reverse, solution ); // Run the IpoptApplication app->OptimizeTNLP(cppad_nlp); return; } } // end ipopt namespace } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/ipopt/solve_callback.hpp ================================================ # ifndef CPPAD_IPOPT_SOLVE_CALLBACK_HPP # define CPPAD_IPOPT_SOLVE_CALLBACK_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE namespace ipopt { /*! \file solve_callback.hpp \brief Class that connects ipopt::solve to Ipopt */ /*! Class that Ipopt uses for obtaining information about this problem. \section Evaluation_Methods Evaluation Methods The set of evaluation methods for this class is \verbatim { eval_f, eval_grad_f, eval_g, eval_jac_g, eval_h } \endverbatim Note that the bool return flag for the evaluations methods does not appear in the Ipopt documentation. Looking at the code, it seems to be a flag telling Ipopt to abort when the flag is false. */ template class solve_callback : public Ipopt::TNLP { private: // ------------------------------------------------------------------ // Types used by this class // ------------------------------------------------------------------ /// A Scalar value used by Ipopt typedef Ipopt::Number Number; /// An index value used by Ipopt typedef Ipopt::Index Index; /// Indexing style used in Ipopt sparsity structure typedef Ipopt::TNLP::IndexStyleEnum IndexStyleEnum; // ------------------------------------------------------------------ // Values directly passed in to constructor // ------------------------------------------------------------------ /// dimension of the range space for f(x). /// The objective is sum_i f_i (x). /// Note that, at this point, there is no advantage having nf_ > 1. const size_t nf_; /// dimension of the domain space for f(x) and g(x) const size_t nx_; /// dimension of the range space for g(x) const size_t ng_; /// initial value for x const Dvector& xi_; /// lower limit for x const Dvector& xl_; /// upper limit for x const Dvector& xu_; /// lower limit for g(x) const Dvector& gl_; /// upper limit for g(x) const Dvector& gu_; /// object that evaluates f(x) and g(x) FG_eval& fg_eval_; /// should operation sequence be retaped for each new x. bool retape_; /// Should sparse methods be used to compute Jacobians and Hessians /// with forward mode used for Jacobian. bool sparse_forward_; /// Should sparse methods be used to compute Jacobians and Hessians /// with reverse mode used for Jacobian. bool sparse_reverse_; /// final results are returned to this structure solve_result& solution_; // ------------------------------------------------------------------ // Values that are initialized by the constructor // ------------------------------------------------------------------ /// AD function object that evaluates x -> [ f(x) , g(x) ] /// If retape is false, this object is initialized by constructor /// otherwise it is set by cache_new_x each time it is called. CppAD::ADFun adfun_; /// value of x corresponding to previous new_x Dvector x0_; /// value of fg corresponding to previous new_x Dvector fg0_; // ---------------------------------------------------------------------- // Jacobian information // ---------------------------------------------------------------------- /// Sparsity pattern for Jacobian of [f(x), g(x) ]. /// If sparse is true, this pattern set by constructor and does not change. /// Otherwise this vector has size zero. CppAD::vectorBool pattern_jac_; /// Row indices of [f(x), g(x)] for Jacobian of g(x) in row order. /// (Set by constructor and not changed.) CppAD::vector row_jac_; /// Column indices for Jacobian of g(x), same order as row_jac_. /// (Set by constructor and not changed.) CppAD::vector col_jac_; /// col_order_jac_ sorts row_jac_ and col_jac_ in column order. /// (Set by constructor and not changed.) CppAD::vector col_order_jac_; /// Work vector used by SparseJacobian, stored here to avoid recalculation. CppAD::sparse_jacobian_work work_jac_; // ---------------------------------------------------------------------- // Hessian information // ---------------------------------------------------------------------- /// Sparsity pattern for Hessian of Lagragian /// \f[ L(x) = \sigma \sum_i f_i (x) + \sum_i \lambda_i g_i (x) \f] /// If sparse is true, this pattern set by constructor and does not change. /// Otherwise this vector has size zero. CppAD::vectorBool pattern_hes_; /// Row indices of Hessian lower left triangle in row order. /// (Set by constructor and not changed.) CppAD::vector row_hes_; /// Column indices of Hessian left triangle in same order as row_hes_. /// (Set by constructor and not changed.) CppAD::vector col_hes_; /// Work vector used by SparseJacobian, stored here to avoid recalculation. CppAD::sparse_hessian_work work_hes_; // ------------------------------------------------------------------ // Private member functions // ------------------------------------------------------------------ /*! Cache information for a new value of x. \param x is the new value for x. \par x0_ the elements of this vector are set to the new value for x. \par fg0_ the elements of this vector are set to the new value for [f(x), g(x)] \par adfun_ If retape is true, the operation sequence for this function is changes to correspond to the argument x. If retape is false, the operation sequence is not changed. The zero order Taylor coefficients for this function are set so they correspond to the argument x. */ void cache_new_x(const Number* x) { size_t i; if( retape_ ) { // make adfun_, as well as x0_ and fg0_ correspond to this x ADvector a_x(nx_), a_fg(nf_ + ng_); for(i = 0; i < nx_; i++) { x0_[i] = x[i]; a_x[i] = x[i]; } CppAD::Independent(a_x); fg_eval_(a_fg, a_x); adfun_.Dependent(a_x, a_fg); } else { // make x0_ and fg0_ correspond to this x for(i = 0; i < nx_; i++) x0_[i] = x[i]; } fg0_ = adfun_.Forward(0, x0_); } public: // ---------------------------------------------------------------------- /*! Constructor for the interface between ipopt::solve and Ipopt \param nf dimension of the range space for f(x) \param nx dimension of the domain space for f(x) and g(x). \param ng dimension of the range space for g(x) \param xi initial value of x during the optimization procedure (size nx). \param xl lower limit for x (size nx). \param xu upper limit for x (size nx). \param gl lower limit for g(x) (size ng). \param gu upper limit for g(x) (size ng). \param fg_eval function object that evaluations f(x) and g(x) using fg_eval(fg, x) \param retape should the operation sequence be retaped for each argument value. \param sparse_forward should sparse matrix computations be used for Jacobians and Hessians with forward mode for Jacobian. \param sparse_reverse should sparse matrix computations be used for Jacobians and Hessians with reverse mode for Jacobian. (sparse_forward and sparse_reverse cannot both be true). \param solution object where final results are stored. */ solve_callback( size_t nf , size_t nx , size_t ng , const Dvector& xi , const Dvector& xl , const Dvector& xu , const Dvector& gl , const Dvector& gu , FG_eval& fg_eval , bool retape , bool sparse_forward , bool sparse_reverse , solve_result& solution ) : nf_ ( nf ), nx_ ( nx ), ng_ ( ng ), xi_ ( xi ), xl_ ( xl ), xu_ ( xu ), gl_ ( gl ), gu_ ( gu ), fg_eval_ ( fg_eval ), retape_ ( retape ), sparse_forward_ ( sparse_forward ), sparse_reverse_ ( sparse_reverse ), solution_ ( solution ) { CPPAD_ASSERT_UNKNOWN( ! ( sparse_forward_ & sparse_reverse_ ) ); size_t i, j; size_t nfg = nf_ + ng_; // initialize x0_ and fg0_ with proper dimensions and value nan x0_.resize(nx); fg0_.resize(nfg); for(i = 0; i < nx_; i++) x0_[i] = std::numeric_limits::quiet_NaN(); for(i = 0; i < nfg; i++) fg0_[i] = std::numeric_limits::quiet_NaN(); if( ! retape_ ) { // make adfun_ correspond to x -> [ f(x), g(x) ] ADvector a_x(nx_), a_fg(nfg); for(i = 0; i < nx_; i++) a_x[i] = xi_[i]; CppAD::Independent(a_x); fg_eval_(a_fg, a_x); adfun_.Dependent(a_x, a_fg); // optimize because we will make repeated use of this tape adfun_.optimize(); } if( sparse_forward_ | sparse_reverse_ ) { CPPAD_ASSERT_UNKNOWN( ! retape ); size_t m = nf_ + ng_; // // ----------------------------------------------------------- // Jacobian pattern_jac_.resize( m * nx_ ); if( nx_ <= m ) { // use forward mode to compute sparsity // number of bits that are packed into one unit in vectorBool size_t n_column = vectorBool::bit_per_unit(); // sparsity patterns for current columns vectorBool r(nx_ * n_column), s(m * n_column); // compute the sparsity pattern n_column columns at a time size_t n_loop = (nx_ - 1) / n_column + 1; for(size_t i_loop = 0; i_loop < n_loop; i_loop++) { // starting column index for this iteration size_t i_column = i_loop * n_column; // pattern that picks out the appropriate columns for(i = 0; i < nx_; i++) { for(j = 0; j < n_column; j++) r[i * n_column + j] = (i == i_column + j); } s = adfun_.ForSparseJac(n_column, r); // fill in the corresponding columns of total_sparsity for(i = 0; i < m; i++) { for(j = 0; j < n_column; j++) { if( i_column + j < nx_ ) pattern_jac_[i * nx_ + i_column + j] = s[i * n_column + j]; } } } } else { // use reverse mode to compute sparsity // number of bits that are packed into one unit in vectorBool size_t n_row = vectorBool::bit_per_unit(); // sparsity patterns for current rows vectorBool r(n_row * m), s(n_row * nx_); // compute the sparsity pattern n_row row at a time size_t n_loop = (m - 1) / n_row + 1; for(size_t i_loop = 0; i_loop < n_loop; i_loop++) { // starting row index for this iteration size_t i_row = i_loop * n_row; // pattern that picks out the appropriate rows for(i = 0; i < n_row; i++) { for(j = 0; j < m; j++) r[i * m + j] = (i_row + i == j); } s = adfun_.RevSparseJac(n_row, r); // fill in corresponding rows of total sparsity for(i = 0; i < n_row; i++) { for(j = 0; j < nx_; j++) if( i_row + i < m ) pattern_jac_[ (i_row + i) * nx_ + j ] = s[ i * nx_ + j]; } } } /* { // use reverse mode to compute sparsity CppAD::vectorBool s(m * m); for(i = 0; i < m; i++) { for(j = 0; j < m; j++) s[i * m + j] = (i == j); } pattern_jac_ = adfun_.RevSparseJac(m, s); } */ // Set row and column indices in Jacoian of [f(x), g(x)] // for Jacobian of g(x). These indices are in row major order. for(i = nf_; i < nfg; i++) { for(j = 0; j < nx_; j++) { if( pattern_jac_[ i * nx_ + j ] ) { row_jac_.push_back(i); col_jac_.push_back(j); } } } // Set row and column indices in Jacoian of [f(x), g(x)] // for Jacobian of g(x). These indices are in row major order. // ----------------------------------------------------------- // Hessian pattern_hes_.resize(nx_ * nx_); // number of bits that are packed into one unit in vectorBool size_t n_column = vectorBool::bit_per_unit(); // sparsity patterns for current columns vectorBool r(nx_ * n_column), h(nx_ * n_column); // sparsity pattern for range space of function vectorBool s(m); for(i = 0; i < m; i++) s[i] = true; // compute the sparsity pattern n_column columns at a time size_t n_loop = (nx_ - 1) / n_column + 1; for(size_t i_loop = 0; i_loop < n_loop; i_loop++) { // starting column index for this iteration size_t i_column = i_loop * n_column; // pattern that picks out the appropriate columns for(i = 0; i < nx_; i++) { for(j = 0; j < n_column; j++) r[i * n_column + j] = (i == i_column + j); } adfun_.ForSparseJac(n_column, r); // sparsity pattern corresponding to paritls w.r.t. (theta, u) // of partial w.r.t. the selected columns bool transpose = true; h = adfun_.RevSparseHes(n_column, s, transpose); // fill in the corresponding columns of total_sparsity for(i = 0; i < nx_; i++) { for(j = 0; j < n_column; j++) { if( i_column + j < nx_ ) pattern_hes_[i * nx_ + i_column + j] = h[i * n_column + j]; } } } // Set row and column indices for Lower triangle of Hessian // of Lagragian. These indices are in row major order. for(i = 0; i < nx_; i++) { for(j = 0; j < nx_; j++) { if( pattern_hes_[ i * nx_ + j ] ) if( j <= i ) { row_hes_.push_back(i); col_hes_.push_back(j); } } } } else { // Set row and column indices in Jacoian of [f(x), g(x)] // for Jacobian of g(x). These indices are in row major order. for(i = nf_; i < nfg; i++) { for(j = 0; j < nx_; j++) { row_jac_.push_back(i); col_jac_.push_back(j); } } // Set row and column indices for lower triangle of Hessian. // These indices are in row major order. for(i = 0; i < nx_; i++) { for(j = 0; j <= i; j++) { row_hes_.push_back(i); col_hes_.push_back(j); } } } // Column order indirect sort of the Jacobian indices col_order_jac_.resize( col_jac_.size() ); index_sort( col_jac_, col_order_jac_ ); } // ----------------------------------------------------------------------- /*! Return dimension information about optimization problem. \param[out] n is set to the value nx_. \param[out] m is set to the value ng_. \param[out] nnz_jac_g is set to ng_ * nx_ (sparsity not yet implemented) \param[out] nnz_h_lag is set to nx_*(nx_+1)/2 (sparsity not yet implemented) \param[out] index_style is set to C_STYLE; i.e., zeoro based indexing is used in the information passed to Ipopt. */ virtual bool get_nlp_info( Index& n , Index& m , Index& nnz_jac_g , Index& nnz_h_lag , IndexStyleEnum& index_style ) { n = static_cast(nx_); m = static_cast(ng_); nnz_jac_g = static_cast(row_jac_.size()); nnz_h_lag = static_cast(row_hes_.size()); # ifndef NDEBUG if( ! (sparse_forward_ | sparse_reverse_) ) { size_t nnz = static_cast(nnz_jac_g); CPPAD_ASSERT_UNKNOWN( nnz == ng_ * nx_); // nnz = static_cast(nnz_h_lag); CPPAD_ASSERT_UNKNOWN( nnz == (nx_ * (nx_ + 1)) / 2 ); } # endif // use the fortran index style for row/col entries index_style = C_STYLE; return true; } // ----------------------------------------------------------------------- /*! Return bound information about optimization problem. \param[in] n is the dimension of the domain space for f(x) and g(x); i.e., it must be equal to nx_. \param[out] x_l is a vector of size nx_. The input value of its elements does not matter. On output, it is a copy of the lower bound for \f$ x \f$; i.e., xl_. \param[out] x_u is a vector of size nx_. The input value of its elements does not matter. On output, it is a copy of the upper bound for \f$ x \f$; i.e., xu_. \param[in] m is the dimension of the range space for g(x). i.e., it must be equal to ng_. \param[out] g_l is a vector of size ng_. The input value of its elements does not matter. On output, it is a copy of the lower bound for \f$ g(x) \f$; i.e., gl_. \param[out] g_u is a vector of size ng_. The input value of its elements does not matter. On output, it is a copy of the upper bound for \f$ g(x) \f$; i.e, gu_. */ virtual bool get_bounds_info( Index n , Number* x_l , Number* x_u , Index m , Number* g_l , Number* g_u ) { size_t i; // here, the n and m we gave IPOPT in get_nlp_info are passed back CPPAD_ASSERT_UNKNOWN(static_cast(n) == nx_); CPPAD_ASSERT_UNKNOWN(static_cast(m) == ng_); // pass back bounds for(i = 0; i < nx_; i++) { x_l[i] = xl_[i]; x_u[i] = xu_[i]; } for(i = 0; i < ng_; i++) { g_l[i] = gl_[i]; g_u[i] = gu_[i]; } return true; } // ----------------------------------------------------------------------- /*! Return initial x value where optimization is started. \param[in] n must be equal to the domain dimension for f(x) and g(x); i.e., it must be equal to nx_. \param[in] init_x must be equal to true. \param[out] x is a vector of size nx_. The input value of its elements does not matter. On output, it is a copy of the initial value for \f$ x \f$; i.e. xi_. \param[in] init_z must be equal to false. \param z_L is not used. \param z_U is not used. \param[in] m must be equal to the domain dimension for f(x) and g(x); i.e., it must be equal to ng_. \param init_lambda must be equal to false. \param lambda is not used. */ virtual bool get_starting_point( Index n , bool init_x , Number* x , bool init_z , Number* z_L , Number* z_U , Index m , bool init_lambda , Number* lambda ) { size_t j; CPPAD_ASSERT_UNKNOWN(static_cast(n) == nx_ ); CPPAD_ASSERT_UNKNOWN(static_cast(m) == ng_ ); CPPAD_ASSERT_UNKNOWN(init_x == true); CPPAD_ASSERT_UNKNOWN(init_z == false); CPPAD_ASSERT_UNKNOWN(init_lambda == false); for(j = 0; j < nx_; j++) x[j] = xi_[j]; return true; } // ----------------------------------------------------------------------- /*! Evaluate the objective function f(x). \param[in] n is the dimension of the argument space for f(x); i.e., must be equal nx_. \param[in] x is a vector of size nx_ containing the point at which to evaluate the function sum_i f_i (x). \param[in] new_x is false if the previous call to any one of the \ref Evaluation_Methods used the same value for x. \param[out] obj_value is the value of the objective sum_i f_i (x) at this value of x. \return The return value is always true; see \ref Evaluation_Methods. */ virtual bool eval_f( Index n , const Number* x , bool new_x , Number& obj_value ) { size_t i; if( new_x ) cache_new_x(x); // double sum = 0.0; for(i = 0; i < nf_; i++) sum += fg0_[i]; obj_value = static_cast(sum); return true; } // ----------------------------------------------------------------------- /*! Evaluate the gradient of f(x). \param[in] n is the dimension of the argument space for f(x); i.e., must be equal nx_. \param[in] x has a vector of size nx_ containing the point at which to evaluate the gradient of f(x). \param[in] new_x is false if the previous call to any one of the \ref Evaluation_Methods used the same value for x. \param[out] grad_f is a vector of size nx_. The input value of its elements does not matter. The output value of its elements is the gradient of f(x) at this value of. \return The return value is always true; see \ref Evaluation_Methods. */ virtual bool eval_grad_f( Index n , const Number* x , bool new_x , Number* grad_f ) { size_t i; if( new_x ) cache_new_x(x); // Dvector w(nf_ + ng_), dw(nx_); for(i = 0; i < nf_; i++) w[i] = 1.0; for(i = 0; i < ng_; i++) w[nf_ + i] = 0.0; dw = adfun_.Reverse(1, w); for(i = 0; i < nx_; i++) grad_f[i] = dw[i]; return true; } // ----------------------------------------------------------------------- /*! Evaluate the function g(x). \param[in] n is the dimension of the argument space for g(x); i.e., must be equal nx_. \param[in] x has a vector of size n containing the point at which to evaluate the gradient of g(x). \param[in] new_x is false if the previous call to any one of the \ref Evaluation_Methods used the same value for x. \param[in] m is the dimension of the range space for g(x); i.e., must be equal to ng_. \param[out] g is a vector of size ng_. The input value of its elements does not matter. The output value of its elements is the value of the function g(x) at this value of x. \return The return value is always true; see \ref Evaluation_Methods. */ virtual bool eval_g( Index n , const Number* x , bool new_x , Index m , Number* g ) { size_t i; if( new_x ) cache_new_x(x); // for(i = 0; i < ng_; i++) g[i] = fg0_[nf_ + i]; return true; } // ----------------------------------------------------------------------- /*! Evaluate the Jacobian of g(x). \param[in] n is the dimension of the argument space for g(x); i.e., must be equal nx_. \param x If values is not NULL, x is a vector of size nx_ containing the point at which to evaluate the gradient of g(x). \param[in] new_x is false if the previous call to any one of the \ref Evaluation_Methods used the same value for x. \param[in] m is the dimension of the range space for g(x); i.e., must be equal to ng_. \param[in] nele_jac is the number of possibly non-zero elements in the Jacobian of g(x); i.e., must be equal to ng_ * nx_. \param iRow if values is not NULL, iRow is not defined. if values is NULL, iRow is a vector with size nele_jac. The input value of its elements does not matter. On output, For k = 0 , ... , nele_jac-1, iRow[k] is the base zero row index for the k-th possibly non-zero entry in the Jacobian of g(x). \param jCol if values is not NULL, jCol is not defined. if values is NULL, jCol is a vector with size nele_jac. The input value of its elements does not matter. On output, For k = 0 , ... , nele_jac-1, jCol[k] is the base zero column index for the k-th possibly non-zero entry in the Jacobian of g(x). \param values if values is not NULL, values is a vector with size nele_jac. The input value of its elements does not matter. On output, For k = 0 , ... , nele_jac-1, values[k] is the value for the k-th possibly non-zero entry in the Jacobian of g(x). \return The return value is always true; see \ref Evaluation_Methods. */ virtual bool eval_jac_g( Index n, const Number* x, bool new_x, Index m, Index nele_jac, Index* iRow, Index *jCol, Number* values) { size_t i, j, k, ell; CPPAD_ASSERT_UNKNOWN(static_cast(m) == ng_ ); CPPAD_ASSERT_UNKNOWN(static_cast(n) == nx_ ); // size_t nk = row_jac_.size(); CPPAD_ASSERT_UNKNOWN( static_cast(nele_jac) == nk ); // if( new_x ) cache_new_x(x); if( values == NULL ) { for(k = 0; k < nk; k++) { i = row_jac_[k]; j = col_jac_[k]; CPPAD_ASSERT_UNKNOWN( i >= nf_ ); iRow[k] = static_cast(i - nf_); jCol[k] = static_cast(j); } return true; } // if( nk == 0 ) return true; // if( sparse_forward_ ) { Dvector jac(nk); adfun_.SparseJacobianForward( x0_ , pattern_jac_, row_jac_, col_jac_, jac, work_jac_ ); for(k = 0; k < nk; k++) values[k] = jac[k]; } else if( sparse_reverse_ ) { Dvector jac(nk); adfun_.SparseJacobianReverse( x0_ , pattern_jac_, row_jac_, col_jac_, jac, work_jac_ ); for(k = 0; k < nk; k++) values[k] = jac[k]; } else if( nx_ < ng_ ) { // use forward mode Dvector x1(nx_), fg1(nf_ + ng_); for(j = 0; j < nx_; j++) x1[j] = 0.0; // index in col_order_jac_ of next entry ell = 0; k = col_order_jac_[ell]; for(j = 0; j < nx_; j++) { // compute j-th column of Jacobian of g(x) x1[j] = 1.0; fg1 = adfun_.Forward(1, x1); while( ell < nk && col_jac_[k] <= j ) { CPPAD_ASSERT_UNKNOWN( col_jac_[k] == j ); i = row_jac_[k]; CPPAD_ASSERT_UNKNOWN( i >= nf_ ) values[k] = fg1[i]; ell++; if( ell < nk ) k = col_order_jac_[ell]; } x1[j] = 0.0; } } else { // user reverse mode size_t nfg = nf_ + ng_; // user reverse mode Dvector w(nfg), dw(nx_); for(i = 0; i < nfg; i++) w[i] = 0.0; // index in row_jac_ of next entry k = 0; for(i = nf_; i < nfg; i++) { // compute i-th row of Jacobian of g(x) w[i] = 1.0; dw = adfun_.Reverse(1, w); while( k < nk && row_jac_[k] <= i ) { CPPAD_ASSERT_UNKNOWN( row_jac_[k] == i ); j = col_jac_[k]; values[k] = dw[j]; k++; } w[i] = 0.0; } } return true; } // ----------------------------------------------------------------------- /*! Evaluate the Hessian of the Lagragian \section The_Hessian_of_the_Lagragian The Hessian of the Lagragian The Hessian of the Lagragian is defined as \f[ H(x, \sigma, \lambda ) = \sigma \nabla^2 f(x) + \sum_{i=0}^{m-1} \lambda_i \nabla^2 g(x)_i \f] \param[in] n is the dimension of the argument space for g(x); i.e., must be equal nx_. \param x if values is not NULL, x is a vector of size nx_ containing the point at which to evaluate the Hessian of the Lagragian. \param[in] new_x is false if the previous call to any one of the \ref Evaluation_Methods used the same value for x. \param[in] obj_factor the value \f$ \sigma \f$ multiplying the Hessian of f(x) in the expression for \ref The_Hessian_of_the_Lagragian. \param[in] m is the dimension of the range space for g(x); i.e., must be equal to ng_. \param[in] lambda if values is not NULL, lambda is a vector of size ng_ specifying the value of \f$ \lambda \f$ in the expression for \ref The_Hessian_of_the_Lagragian. \param[in] new_lambda is true if the previous call to eval_h had the same value for lambda and false otherwise. (Not currently used.) \param[in] nele_hess is the number of possibly non-zero elements in the Hessian of the Lagragian; i.e., must be equal to nx_*(nx_+1)/2. \param iRow if values is not NULL, iRow is not defined. if values is NULL, iRow is a vector with size nele_hess. The input value of its elements does not matter. On output, For k = 0 , ... , nele_hess-1, iRow[k] is the base zero row index for the k-th possibly non-zero entry in the Hessian of the Lagragian. \param jCol if values is not NULL, jCol is not defined. if values is NULL, jCol is a vector with size nele_hess. The input value of its elements does not matter. On output, For k = 0 , ... , nele_hess-1, jCol[k] is the base zero column index for the k-th possibly non-zero entry in the Hessian of the Lagragian. \param values if values is not NULL, it is a vector with size nele_hess. The input value of its elements does not matter. On output, For k = 0 , ... , nele_hess-1, values[k] is the value for the k-th possibly non-zero entry in the Hessian of the Lagragian. \return The return value is always true; see \ref Evaluation_Methods. */ virtual bool eval_h( Index n , const Number* x , bool new_x , Number obj_factor , Index m , const Number* lambda , bool new_lambda , Index nele_hess , Index* iRow , Index* jCol , Number* values ) { size_t i, j, k; CPPAD_ASSERT_UNKNOWN(static_cast(m) == ng_ ); CPPAD_ASSERT_UNKNOWN(static_cast(n) == nx_ ); // size_t nk = row_hes_.size(); CPPAD_ASSERT_UNKNOWN( static_cast(nele_hess) == nk ); // if( new_x ) cache_new_x(x); // if( values == NULL ) { for(k = 0; k < nk; k++) { i = row_hes_[k]; j = col_hes_[k]; iRow[k] = static_cast(i); jCol[k] = static_cast(j); } return true; } // if( nk == 0 ) return true; // weigting vector for Lagragian Dvector w(nf_ + ng_); for(i = 0; i < nf_; i++) w[i] = obj_factor; for(i = 0; i < ng_; i++) w[i + nf_] = lambda[i]; // if( sparse_forward_ | sparse_reverse_ ) { Dvector hes(nk); adfun_.SparseHessian( x0_, w, pattern_hes_, row_hes_, col_hes_, hes, work_hes_ ); for(k = 0; k < nk; k++) values[k] = hes[k]; } else { Dvector hes(nx_ * nx_); hes = adfun_.Hessian(x0_, w); for(k = 0; k < nk; k++) { i = row_hes_[k]; j = col_hes_[k]; values[k] = hes[i * nx_ + j]; } } return true; } // ---------------------------------------------------------------------- /*! Pass solution information from Ipopt to users solution structure. \param[in] status is value that the Ipopt solution status which gets mapped to a corresponding value for \n solution_.status \param[in] n is the dimension of the domain space for f(x) and g(x); i.e., it must be equal to nx_. \param[in] x is a vector with size nx_ specifying the final solution. This is the output value for \n solution_.x \param[in] z_L is a vector with size nx_ specifying the Lagragian multipliers for the constraint \f$ x^l \leq x \f$. This is the output value for \n solution_.zl \param[in] z_U is a vector with size nx_ specifying the Lagragian multipliers for the constraint \f$ x \leq x^u \f$. This is the output value for \n solution_.zu \param[in] m is the dimension of the range space for g(x). i.e., it must be equal to ng_. \param[in] g is a vector with size ng_ containing the value of the constraint function g(x) at the final solution x. This is the output value for \n solution_.g \param[in] lambda is a vector with size ng_ specifying the Lagragian multipliers for the constraints \f$ g^l \leq g(x) \leq g^u \f$. This is the output value for \n solution_.lambda \param[in] obj_value is the value of the objective function f(x) at the final solution x. This is the output value for \n solution_.obj_value \param[in] ip_data is unspecified (by Ipopt) and hence not used. \param[in] ip_cq is unspecified (by Ipopt) and hence not used. \par solution_[out] this is a reference to the solution argument in the constructor for solve_callback. The results are stored here (see documentation above). */ virtual void finalize_solution( Ipopt::SolverReturn status , Index n , const Number* x , const Number* z_L , const Number* z_U , Index m , const Number* g , const Number* lambda , Number obj_value , const Ipopt::IpoptData* ip_data , Ipopt::IpoptCalculatedQuantities* ip_cq ) { size_t i, j; CPPAD_ASSERT_UNKNOWN(static_cast(n) == nx_ ); CPPAD_ASSERT_UNKNOWN(static_cast(m) == ng_ ); switch(status) { // convert status from Ipopt enum to solve_result enum case Ipopt::SUCCESS: solution_.status = solve_result::success; break; case Ipopt::MAXITER_EXCEEDED: solution_.status = solve_result::maxiter_exceeded; break; case Ipopt::STOP_AT_TINY_STEP: solution_.status = solve_result::stop_at_tiny_step; break; case Ipopt::STOP_AT_ACCEPTABLE_POINT: solution_.status = solve_result::stop_at_acceptable_point; break; case Ipopt::LOCAL_INFEASIBILITY: solution_.status = solve_result::local_infeasibility; break; case Ipopt::USER_REQUESTED_STOP: solution_.status = solve_result::user_requested_stop; break; case Ipopt::DIVERGING_ITERATES: solution_.status = solve_result::diverging_iterates; break; case Ipopt::RESTORATION_FAILURE: solution_.status = solve_result::restoration_failure; break; case Ipopt::ERROR_IN_STEP_COMPUTATION: solution_.status = solve_result::error_in_step_computation; break; case Ipopt::INVALID_NUMBER_DETECTED: solution_.status = solve_result::invalid_number_detected; break; case Ipopt::INTERNAL_ERROR: solution_.status = solve_result::internal_error; break; default: solution_.status = solve_result::unknown; } solution_.x.resize(nx_); solution_.zl.resize(nx_); solution_.zu.resize(nx_); for(j = 0; j < nx_; j++) { solution_.x[j] = x[j]; solution_.zl[j] = z_L[j]; solution_.zu[j] = z_U[j]; } solution_.g.resize(ng_); solution_.lambda.resize(ng_); for(i = 0; i < ng_; i++) { solution_.g[i] = g[i]; solution_.lambda[i] = lambda[i]; } solution_.obj_value = obj_value; return; } }; } // end namespace ipopt } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/ipopt/solve_result.hpp ================================================ # ifndef CPPAD_IPOPT_SOLVE_RESULT_HPP # define CPPAD_IPOPT_SOLVE_RESULT_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { // BEGIN_CPPAD_NAMESPACE namespace ipopt { /*! \file solve_result.hpp Class that contains information about solve problem result */ /*! Class that contains information about solve problem result \tparam Dvector a simple vector with elements of type double */ template class solve_result { public: /// possible values for the result status enum status_type { not_defined, success, maxiter_exceeded, stop_at_tiny_step, stop_at_acceptable_point, local_infeasibility, user_requested_stop, feasible_point_found, diverging_iterates, restoration_failure, error_in_step_computation, invalid_number_detected, too_few_degrees_of_freedom, internal_error, unknown }; /// possible values for solution status status_type status; /// the approximation solution Dvector x; /// Lagrange multipliers corresponding to lower bounds on x Dvector zl; /// Lagrange multipliers corresponding to upper bounds on x Dvector zu; /// value of g(x) Dvector g; /// Lagrange multipliers correspondiing constraints on g(x) Dvector lambda; /// value of f(x) double obj_value; /// constructor initializes solution status as not yet defined solve_result(void) { status = not_defined; } }; } // end namespace ipopt } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/local/ad_tape.hpp ================================================ # ifndef CPPAD_LOCAL_AD_TAPE_HPP # define CPPAD_LOCAL_AD_TAPE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL__NAMESPACE /*! Class used to hold tape that records AD operations. \tparam Base An AD object is used to recording AD operations. */ template class ADTape { // Friends ============================================================= // classes ------------------------------------------------------------- friend class AD; friend class ADFun; friend class atomic_base; friend class atomic_three; friend class atomic_four; friend class discrete; friend class VecAD; friend class VecAD_reference; // functions ----------------------------------------------------------- // PrintFor friend void CppAD::PrintFor ( const AD& flag , const char* before , const AD& var , const char* after ); // CondExpOp friend AD CppAD::CondExpOp ( enum CompareOp cop , const AD &left , const AD &right , const AD &trueCase , const AD &falseCase ); // pow friend AD CppAD::pow (const AD &x, const AD &y); // azmul friend AD CppAD::azmul (const AD &x, const AD &y); // Parameter friend bool CppAD::Parameter (const AD &u); // Variable friend bool CppAD::Variable (const AD &u); // operators ----------------------------------------------------------- // arithematic binary operators # if _MSC_VER && !defined(__clang__) // see https://stackoverflow.com/questions/63288453 template friend AD CppAD::operator * (const AD &left, const AD &right); # else friend AD CppAD::operator * (const AD &left, const AD &right); # endif friend AD CppAD::operator + (const AD &left, const AD &right); friend AD CppAD::operator - (const AD &left, const AD &right); friend AD CppAD::operator / (const AD &left, const AD &right); // comparison operators # if _MSC_VER && !defined(__clang__) template friend bool CppAD::operator == (const AD &left, const AD &right); template friend bool CppAD::operator != (const AD &left, const AD &right); # else friend bool CppAD::operator == (const AD &left, const AD &right); friend bool CppAD::operator != (const AD &left, const AD &right); # endif friend bool CppAD::operator < (const AD &left, const AD &right); friend bool CppAD::operator <= (const AD &left, const AD &right); friend bool CppAD::operator > (const AD &left, const AD &right); friend bool CppAD::operator >= (const AD &left, const AD &right); // ====================================================================== // -------------------------------------------------------------------------- private: // ---------------------------------------------------------------------- // private data /*! Unique identifier for this tape. It is always greater than CPPAD_MAX_NUM_THREADS, and different for every tape (even ones that have been deleted). In addition, id_ % CPPAD_MAX_NUM_THREADS is the thread number for this tape. Set by Independent and effectively const */ tape_id_t id_; /// Number of independent variables in this tapes reconding. /// Set by Independent and effectively const size_t size_independent_; /// This is where the information is recorded. local::recorder Rec_; // ---------------------------------------------------------------------- // private functions // // add a parameter to the tape addr_t RecordParOp(const AD& y); // see CondExp.h void RecordCondExp( enum CompareOp cop , AD &returnValue , const AD &left , const AD &right , const AD &trueCase , const AD &falseCase ); public: // public function only used by CppAD::Independent template void Independent( ADBaseVector& x , size_t abort_op_index , bool record_compare , ADBaseVector& dynamic ); }; // --------------------------------------------------------------------------- // Private functions // /*! Place a parameter in the tape as a variable. On rare occasions it is necessary to place a parameter in the tape; e.g., when it is one of the dependent variables. \param y value of the parameter that we are placing in the tape as a variable. \return variable index (for this recording) corresponding to the parameter. \par 2DO All these operates are preformed in Rec_, so we should move this routine from ADTape to recorder. */ template addr_t ADTape::RecordParOp(const AD& y) { CPPAD_ASSERT_UNKNOWN( NumRes(ParOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumArg(ParOp) == 1 ); addr_t z_taddr = Rec_.PutOp(ParOp); if( Dynamic(y) ) { addr_t ind = y.taddr_; Rec_.PutArg(ind); } else { addr_t ind = Rec_.put_con_par(y.value_); Rec_.PutArg(ind); } return z_taddr; } } } // END_CPPAD_LOCAL_NAMESPACE # endif ================================================ FILE: include/cppad/local/atom_state.hpp ================================================ # ifndef CPPAD_LOCAL_ATOM_STATE_HPP # define CPPAD_LOCAL_ATOM_STATE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE enum enum_atom_state { /// next AFunOp marks beginning of a atomic function call start_atom, /// next FunapOp (FunavOp) is a parameter (variable) argument arg_atom, /// next FunrpOp (FunrvOp) is a parameter (variable) result ret_atom, /// next AFunOp marks end of a atomic function call end_atom }; } } // END_CPPAD_LOCAL_NAMESPACE # endif ================================================ FILE: include/cppad/local/atomic_index.hpp ================================================ # ifndef CPPAD_LOCAL_ATOMIC_INDEX_HPP # define CPPAD_LOCAL_ATOMIC_INDEX_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /*! {xrst_begin atomic_index dev} Store and Retrieve Atomic Function Information by Index ####################################################### Syntax ****** | *index_out* = ``local::atomic_index<`` *Base* >( | |tab| *set_null* , *index_in* , *type* , *name* , *ptr* | ) Prototype ********* {xrst_literal // BEGIN_ATOMIC_INDEX // END_PROTOTYPE } Base **** Is the base type for the tape for the atomic functions that we are using an index to identify. Get Number Case *************** The get number case is defined by *set_null* is true and *index_in* is zero. For this case, *index_out* is set to the number of atomic functions stored in ``atomic_index`` < *Base* > and no information is stored or changed. In this case, the atomic functions correspond to *index_in* from one to *index_out* inclusive. set_null ******** If *set_null* is true and *index_in* is zero, this argument is just used to signal the get number case. Otherwise, *set_null* should only be true during a call to an atomic function destructor. In this case, the *ptr* corresponding to *index_in* is set to null (so that CppAD knows the corresponding atomic function no longer works). index_in ******** If *index_in* is zero and *set_null* is true, this argument is just used to signal the get number case. Otherwise, see below: zero ==== The value *index_in* should only be zero during a call to an atomic function constructor. In this case, a copy of the input value of *type* , * *name* , and *ptr* are stored. The value *index_out* is the *index_in* value corresponding to these input values. non-zero ======== If *index_in* is non-zero, the information corresponding to this index is returned. type **** This argument is not used in the get number case. Otherwise if *index_in* is zero, *type* is an input. Otherwise it is set to the value corresponding to *index_in* . The type corresponding to an index is intended to be 2 for :ref:`atomic_two-name` functions, 3 for :ref:`atomic_three-name` functions, and 4 for :ref:`atomic_four-name` functions, name **** This argument is not used in the get number case. Otherwise if *index_in* is zero, *name* is an input and must not be null. Otherwise, if *name* is not null, * *name* is set to the name corresponding to *index_in* . Allowing for *name* to be null avoids a string copy when it is not needed. ptr *** This argument is not used in the get number case. Otherwise if *index_in* is zero, *ptr* is an input. Otherwise it is set to the value corresponding to *index_in* . In the special case where *set_null* is true, *ptr* is set to the null pointer and this is the *ptr* value corresponding to *index_in* for future calls to ``atomic_index`` . index_out ********* In the get number case, this is the number of atomic functions. Otherwise if *index_in* is zero, *index_out* is non-zero and is the *index_in* value corresponding to the input values for *type* , * *name* , and *ptr* . Otherwise, *index_out* is zero. {xrst_end atomic_index} */ # include # include namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE struct atomic_index_info { size_t type; std::string name; void* ptr; }; // BEGIN_ATOMIC_INDEX template size_t atomic_index( bool set_null , const size_t& index_in , size_t& type , std::string* name , void*& ptr ) // END_PROTOTYPE { // // information for each index static std::vector vec; # ifndef NDEBUG if( index_in == 0 || set_null ) { CPPAD_ASSERT_KNOWN( ! thread_alloc::in_parallel(), "calling atomic function constructor or destructor in parallel mode" ); } # endif if( set_null & (index_in == 0) ) return vec.size(); // // case were we are retrieving information for an atomic function if( 0 < index_in ) { CPPAD_ASSERT_UNKNOWN( index_in <= vec.size() ) // // case where we are setting the pointer to null if( set_null ) vec[index_in-1].ptr = nullptr; // atomic_index_info& entry = vec[index_in - 1]; type = entry.type; ptr = entry.ptr; if( name != nullptr ) *name = entry.name; return 0; } // // case where we are storing information for an atomic function atomic_index_info entry; entry.type = type; entry.name = *name; entry.ptr = ptr; vec.push_back(entry); // return vec.size(); } } } // END_CPPAD_LOCAL_NAMESPACE # endif ================================================ FILE: include/cppad/local/color_general.hpp ================================================ # ifndef CPPAD_LOCAL_COLOR_GENERAL_HPP # define CPPAD_LOCAL_COLOR_GENERAL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE /*! \file color_general.hpp Coloring algorithm for a general sparse matrix. */ // -------------------------------------------------------------------------- /*! Determine which rows of a general sparse matrix can be computed together; i.e., do not have non-zero entries with the same column index. \tparam SizeVector is a simple vector class with elements of type size_t. \tparam SetVector is vector_of_sets class. \param pattern [in] Is a representation of the sparsity pattern for the matrix. \param row [in] is a vector specifying which row indices to compute. \param col [in] is a vector, with the same size as row, that specifies which column indices to compute. For each valid index k, the index pair (row[k], col[k]) must be present in the sparsity pattern. It may be that some entries in the sparsity pattern do not need to be computed; i.e, do not appear in the set of (row[k], col[k]) entries. \param color [out] is a vector with size m. The input value of its elements does not matter. Upon return, it is a coloring for the rows of the sparse matrix. \n \n If for some i, color[i] == m, then the i-th row does not appear in the vector row. Otherwise, color[i] < m. \n \n Suppose two different rows, i != r have the same color and column index j is such that both of the pairs (i, j) and (r, j) appear in the sparsity pattern. It follows that neither of these pairs appear in the set of (row[k], col[k]) entries. \n \n This routine tries to minimize, with respect to the choice of colors, the maximum, with respct to k, of color[ row[k] ] (not counting the indices k for which row[k] == m). */ template void color_general_cppad( const SetVector& pattern , const SizeVector& row , const SizeVector& col , CppAD::vector& color ) { size_t K = row.size(); size_t m = pattern.n_set(); size_t n = pattern.end(); CPPAD_ASSERT_UNKNOWN( size_t( col.size() ) == K ); CPPAD_ASSERT_UNKNOWN( size_t( color.size() ) == m ); // We define the set of rows, columns, and pairs that appear // by the set ( row[k], col[k] ) for k = 0, ... , K-1. // initialize rows that appear CppAD::vector row_appear(m); for(size_t i = 0; i < m; i++) row_appear[i] = false; // rows and columns that appear SetVector c2r_appear, r2c_appear; c2r_appear.resize(n, m); r2c_appear.resize(m, n); for(size_t k = 0; k < K; k++) { CPPAD_ASSERT_KNOWN( pattern.is_element(row[k], col[k]) , "color_general_cppad: requesting value for a matrix element\n" "that is not in the matrice's sparsity pattern.\n" "Such a value must be zero." ); row_appear[ row[k] ] = true; c2r_appear.post_element(col[k], row[k]); r2c_appear.post_element(row[k], col[k]); } // process posts for(size_t j = 0; j < n; ++j) c2r_appear.process_post(j); for(size_t i = 0; i < m; ++i) r2c_appear.process_post(i); // for each column, which rows are non-zero and do not appear SetVector not_appear; not_appear.resize(n, m); for(size_t i = 0; i < m; i++) { typename SetVector::const_iterator pattern_itr(pattern, i); size_t j = *pattern_itr; while( j != pattern.end() ) { if( ! c2r_appear.is_element(j , i) ) not_appear.post_element(j, i); j = *(++pattern_itr); } } // process posts for(size_t j = 0; j < n; ++j) not_appear.process_post(j); // initial coloring color.resize(m); size_t ell = 0; for(size_t i = 0; i < m; i++) { if( row_appear[i] ) color[i] = ell++; else color[i] = m; } /* See GreedyPartialD2Coloring Algorithm Section 3.6.2 of Graph Coloring in Optimization Revisited by Assefaw Gebremedhin, Fredrik Maane, Alex Pothen The algorithm above was modified (by Brad Bell) to take advantage of the fact that only the entries (subset of the sparsity pattern) specified by row and col need to be computed. */ CppAD::vector forbidden(m); for(size_t i = 1; i < m; i++) // for each row that appears if( color[i] < m ) { // initial all colors as ok for this row // (value of forbidden for ell > initial color[i] does not matter) for(ell = 0; ell <= color[i]; ell++) forbidden[ell] = false; // ----------------------------------------------------- // Forbid colors for which this row would destroy results: // // for each column that is non-zero for this row typename SetVector::const_iterator pattern_itr(pattern, i); size_t j = *pattern_itr; while( j != pattern.end() ) { // for each row that appears with this column typename SetVector::const_iterator c2r_itr(c2r_appear, j); size_t r = *c2r_itr; while( r != c2r_appear.end() ) { // if this is not the same row, forbid its color if( (r < i) && (color[r] < m) ) forbidden[ color[r] ] = true; r = *(++c2r_itr); } j = *(++pattern_itr); } // ----------------------------------------------------- // Forbid colors that destroy results needed for this row. // // for each column that appears with this row typename SetVector::const_iterator r2c_itr(r2c_appear, i); j = *r2c_itr; while( j != r2c_appear.end() ) { // For each row that is non-zero for this column // (the appear rows have already been checked above). typename SetVector::const_iterator not_itr(not_appear, j); size_t r = *not_itr; while( r != not_appear.end() ) { // if this is not the same row, forbid its color if( (r < i) && (color[r] < m) ) forbidden[ color[r] ] = true; r = *(++not_itr); } j = *(++r2c_itr); } // pick the color with smallest index ell = 0; while( forbidden[ell] ) { ell++; CPPAD_ASSERT_UNKNOWN( ell <= color[i] ); } color[i] = ell; } return; } # if CPPAD_HAS_COLPACK /*! Colpack version of determining which rows of a sparse matrix can be computed together. \copydetails color_general */ template void color_general_colpack( const SetVector& pattern , const SizeVector& row , const SizeVector& col , CppAD::vector& color ) { size_t m = pattern.n_set(); size_t n = pattern.end(); // Determine number of non-zero entries in each row CppAD::vector n_nonzero(m); size_t n_nonzero_total = 0; for(size_t i = 0; i < m; i++) { n_nonzero[i] = 0; typename SetVector::const_iterator pattern_itr(pattern, i); size_t j = *pattern_itr; while( j != pattern.end() ) { n_nonzero[i]++; j = *(++pattern_itr); } n_nonzero_total += n_nonzero[i]; } // Allocate memory and fill in Adolc sparsity pattern CppAD::vector adolc_pattern(m); CppAD::vector adolc_memory(m + n_nonzero_total); size_t i_memory = 0; for(size_t i = 0; i < m; i++) { adolc_pattern[i] = adolc_memory.data() + i_memory; CPPAD_ASSERT_KNOWN( std::numeric_limits::max() >= n_nonzero[i], "Matrix is too large for colpack" ); adolc_pattern[i][0] = static_cast( n_nonzero[i] ); typename SetVector::const_iterator pattern_itr(pattern, i); size_t j = *pattern_itr; size_t k = 1; while(j != pattern.end() ) { CPPAD_ASSERT_KNOWN( std::numeric_limits::max() >= j, "Matrix is too large for colpack" ); adolc_pattern[i][k++] = static_cast( j ); j = *(++pattern_itr); } CPPAD_ASSERT_UNKNOWN( k == 1 + n_nonzero[i] ); i_memory += k; } CPPAD_ASSERT_UNKNOWN( i_memory == m + n_nonzero_total ); // Must use an external routine for this part of the calculation because // ColPack/ColPackHeaders.h has as 'using namespace std' at global level. cppad_colpack_general(color, m, n, adolc_pattern); return; } # endif // CPPAD_HAS_COLPACK } } // END_CPPAD_LOCAL_NAMESPACE # endif ================================================ FILE: include/cppad/local/color_symmetric.hpp ================================================ # ifndef CPPAD_LOCAL_COLOR_SYMMETRIC_HPP # define CPPAD_LOCAL_COLOR_SYMMETRIC_HPP # include # include // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE /*! \file color_symmetric.hpp Coloring algorithm for a symmetric sparse matrix. */ // -------------------------------------------------------------------------- /*! CppAD algorithm for determining which rows of a symmetric sparse matrix can be computed together. \tparam SizeVector is a simple vector class with elements of type size_t. \tparam SetVector is a vector_of_sets class. \param pattern [in] Is a representation of the sparsity pattern for the matrix. \param row [in/out] is a vector specifying which row indices to compute. \param col [in/out] is a vector, with the same size as row, that specifies which column indices to compute. \n \n Input: For each valid index k, the index pair (row[k], col[k]) must be present in the sparsity pattern. It may be that some entries in the sparsity pattern do not need to be computed; i.e, do not appear in the set of (row[k], col[k]) entries. \n \n Output: On output, some of row and column indices may have been swapped \code std::swap( row[k], col[k] ) \endcode So the the the color for row[k] can be used to compute entry (row[k], col[k]). \param color [out] is a vector with size m. The input value of its elements does not matter. Upon return, it is a coloring for the rows of the sparse matrix. Note that if color[i] == m, then there is no index k for which row[k] == i (for the return value of row). \n \n Fix any (i, j) in the sparsity pattern. Suppose that there is a row index i1 with i1 != i, color[i1] == color[i] and (i1, j) is in the sparsity pattern. If follows that for all j1 with j1 != j and color[j1] == color[j], (j1, i ) is not in the sparsity pattern. \n \n This routine tries to minimize, with respect to the choice of colors, the maximum, with respect to k, of color[ row[k] ]. */ template void color_symmetric_cppad( const SetVector& pattern , CppAD::vector& row , CppAD::vector& col , CppAD::vector& color ) { size_t K = row.size(); size_t m = pattern.n_set(); CPPAD_ASSERT_UNKNOWN( m == pattern.end() ); CPPAD_ASSERT_UNKNOWN( color.size() == m ); CPPAD_ASSERT_UNKNOWN( col.size() == K ); // row, column pairs that appear in ( row[k], col[k] ) CppAD::vector< std::set > pair_needed(m); std::set::iterator itr1, itr2; for(size_t k1 = 0; k1 < K; k1++) { CPPAD_ASSERT_UNKNOWN( pattern.is_element(row[k1], col[k1]) ); pair_needed[ row[k1] ].insert( col[k1] ); pair_needed[ col[k1] ].insert( row[k1] ); } // order the rows descending by number of pairs needed CppAD::vector key(m), order2row(m); for(size_t i1 = 0; i1 < m; i1++) { CPPAD_ASSERT_UNKNOWN( pair_needed[i1].size() <= m ); key[i1] = m - pair_needed[i1].size(); } CppAD::index_sort(key, order2row); // mapping from order index to row index CppAD::vector row2order(m); for(size_t o1 = 0; o1 < m; o1++) row2order[ order2row[o1] ] = o1; // initial coloring color.resize(m); size_t c1 = 0; for(size_t o1 = 0; o1 < m; o1++) { size_t i1 = order2row[o1]; if( pair_needed[i1].empty() ) color[i1] = m; else color[i1] = c1++; } // which colors are forbidden for this row CppAD::vector forbidden(m); // must start with row zero so that we remove results computed for it for(size_t o1 = 0; o1 < m; o1++) // for each row that appears (in order) if( color[ order2row[o1] ] < m ) { size_t i1 = order2row[o1]; c1 = color[i1]; // initial all colors as ok for this row // (value of forbidden for c > c1 does not matter) for(size_t c2 = 0; c2 <= c1; c2++) forbidden[c2] = false; // ----------------------------------------------------- // Forbid grouping with rows that would destroy results that are // needed for this row. itr1 = pair_needed[i1].begin(); while( itr1 != pair_needed[i1].end() ) { // entry (i1, j1) is needed for this row size_t j1 = *itr1; // Forbid rows i2 != i1 that have non-zero sparsity at (i2, j1). // Note that this is the same as non-zero sparsity at (j1, i2) typename SetVector::const_iterator pattern_itr(pattern, j1); size_t i2 = *pattern_itr; while( i2 != pattern.end() ) { size_t c2 = color[i2]; if( c2 < c1 ) forbidden[c2] = true; i2 = *(++pattern_itr); } itr1++; } // ----------------------------------------------------- // Forbid grouping with rows that this row would destroy results for for(size_t o2 = 0; o2 < o1; o2++) { size_t i2 = order2row[o2]; size_t c2 = color[i2]; itr2 = pair_needed[i2].begin(); while( itr2 != pair_needed[i2].end() ) { size_t j2 = *itr2; // row i2 needs pair (i2, j2). // Forbid grouping with i1 if (i1, j2) has non-zero sparsity if( pattern.is_element(i1, j2) ) forbidden[c2] = true; itr2++; } } // pick the color with smallest index size_t c2 = 0; while( forbidden[c2] ) { c2++; CPPAD_ASSERT_UNKNOWN( c2 <= c1 ); } color[i1] = c2; // no longer need results that are computed by this row itr1 = pair_needed[i1].begin(); while( itr1 != pair_needed[i1].end() ) { size_t j1 = *itr1; if( row2order[j1] > o1 ) { itr2 = pair_needed[j1].find(i1); if( itr2 != pair_needed[j1].end() ) { pair_needed[j1].erase(itr2); if( pair_needed[j1].empty() ) color[j1] = m; } } itr1++; } } // determine which sparsity entries need to be reflected for(size_t k1 = 0; k1 < row.size(); k1++) { size_t i1 = row[k1]; size_t j1 = col[k1]; itr1 = pair_needed[i1].find(j1); if( itr1 == pair_needed[i1].end() ) { row[k1] = j1; col[k1] = i1; # ifndef NDEBUG itr1 = pair_needed[j1].find(i1); CPPAD_ASSERT_UNKNOWN( itr1 != pair_needed[j1].end() ); # endif } } return; } // -------------------------------------------------------------------------- /*! Colpack algorithm for determining which rows of a symmetric sparse matrix can be computed together. \copydetails CppAD::local::color_symmetric_cppad */ template void color_symmetric_colpack( const SetVector& pattern , CppAD::vector& row , CppAD::vector& col , CppAD::vector& color ) { # if ! CPPAD_HAS_COLPACK CPPAD_ASSERT_UNKNOWN(false); return; # else size_t i, j, k; size_t m = pattern.n_set(); CPPAD_ASSERT_UNKNOWN( m == pattern.end() ); CPPAD_ASSERT_UNKNOWN( row.size() == col.size() ); // Determine number of non-zero entries in each row CppAD::vector n_nonzero(m); size_t n_nonzero_total = 0; for(i = 0; i < m; i++) { n_nonzero[i] = 0; typename SetVector::const_iterator pattern_itr(pattern, i); j = *pattern_itr; while( j != pattern.end() ) { n_nonzero[i]++; j = *(++pattern_itr); } n_nonzero_total += n_nonzero[i]; } // Allocate memory and fill in Adolc sparsity pattern CppAD::vector adolc_pattern(m); CppAD::vector adolc_memory(m + n_nonzero_total); size_t i_memory = 0; for(i = 0; i < m; i++) { adolc_pattern[i] = adolc_memory.data() + i_memory; CPPAD_ASSERT_KNOWN( std::numeric_limits::max() >= n_nonzero[i], "Matrix is too large for colpack" ); adolc_pattern[i][0] = static_cast( n_nonzero[i] ); typename SetVector::const_iterator pattern_itr(pattern, i); j = *pattern_itr; k = 1; while(j != pattern.end() ) { CPPAD_ASSERT_KNOWN( std::numeric_limits::max() >= j, "Matrix is too large for colpack" ); adolc_pattern[i][k++] = static_cast( j ); j = *(++pattern_itr); } CPPAD_ASSERT_UNKNOWN( k == 1 + n_nonzero[i] ); i_memory += k; } CPPAD_ASSERT_UNKNOWN( i_memory == m + n_nonzero_total ); // Must use an external routine for this part of the calculation because // ColPack/ColPackHeaders.h has as 'using namespace std' at global level. cppad_colpack_symmetric(color, m, adolc_pattern); // determine which sparsity entries need to be reflected for(size_t k1 = 0; k1 < row.size(); k1++) { size_t i1 = row[k1]; size_t j1 = col[k1]; bool reflect = false; for(size_t i2 = 0; i2 < m; i2++) if( (i1 != i2) && (color[i1]==color[i2]) ) { for(size_t k2 = 1; k2 <= adolc_pattern[i2][0]; k2++) { size_t j2 = adolc_pattern[i2][k2]; reflect |= (j1 == j2); } } if( reflect ) { row[k1] = j1; col[k1] = i1; } } return; # endif // CPPAD_HAS_COLPACK } } } // END_CPPAD_LOCAL_NAMESPACE # endif ================================================ FILE: include/cppad/local/cppad_colpack.hpp ================================================ # ifndef CPPAD_LOCAL_CPPAD_COLPACK_HPP # define CPPAD_LOCAL_CPPAD_COLPACK_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # if CPPAD_HAS_COLPACK namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE /*! \file cppad_colpack.hpp External interface to Colpack routines used by cppad. */ // --------------------------------------------------------------------------- /*! Link from CppAD to ColPack used for general sparse matrices. This CppAD library routine is necessary because ColPack/ColPackHeaders.h has a using namespace std at the global level. \param m [in] is the number of rows in the sparse matrix \param n [in] is the number of columns in the sparse matrix. \param adolc_pattern [in] This vector has size m, adolc_pattern[i][0] is the number of non-zeros in row i. For j = 1 , ... , adolc_sparsity[i], adolc_pattern[i][j] is the column index (base zero) for the non-zeros in row i. \param color [out] is a vector with size m. The input value of its elements does not matter. Upon return, it is a coloring for the rows of the sparse matrix. \n \n If for some i, color[i] == m, then adolc_pattern[i][0] == 0. Otherwise, color[i] < m. \n \n Suppose two different rows, i != r have the same color. It follows that for all column indices j; it is not the case that both (i, j) and (r, j) appear in the sparsity pattern. \n \n This routine tries to minimize, with respect to the choice of colors, the number of colors. */ CPPAD_LIB_EXPORT void cppad_colpack_general( CppAD::vector& color , size_t m , size_t n , const CppAD::vector& adolc_pattern ); /*! Link from CppAD to ColPack used for symmetric sparse matrices (not yet used or tested). This CppAD library routine is necessary because ColPack/ColPackHeaders.h has a using namespace std at the global level. \param n [in] is the number of rows and columns in the symmetric sparse matrix. \param adolc_pattern [in] This vector has size n, adolc_pattern[i][0] is the number of non-zeros in row i. For j = 1 , ... , adolc_sparsity[i], adolc_pattern[i][j] is the column index (base zero) for the non-zeros in row i. \param color [out] The input value of its elements does not matter. Upon return, it is a coloring for the rows of the sparse matrix. The properties of this coloring have not yet been determined; see Efficient Computation of Sparse Hessians Using Coloring and Automatic Differentiation (pdf/ad/gebemedhin14.pdf) */ CPPAD_LIB_EXPORT void cppad_colpack_symmetric( CppAD::vector& color , size_t n , const CppAD::vector& adolc_pattern ); } } // END_CPPAD_LOCAL_NAMESPACE # endif # endif ================================================ FILE: include/cppad/local/declare_ad.hpp ================================================ # ifndef CPPAD_LOCAL_DECLARE_AD_HPP # define CPPAD_LOCAL_DECLARE_AD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include /*! \file declare_ad.hpp CppAD forward declarations; i.e., before definition */ namespace CppAD { namespace local { template class ADTape; template class player; template class dyn_player; template class recorder; } } namespace CppAD { namespace local { namespace val_graph { template class tape_t; } } } namespace CppAD { // BEGIN_COMPARE_OP enum CompareOp { // operator // Rel: description CompareLt, // Lt: less than CompareLe, // Le: less than or equal CompareEq, // Eq: equal CompareGe, // Ge: greater than or equal CompareGt, // Gt: greater than CompareNe // Ne: not equal }; // END_COMPARE_OP // simple typedefs typedef CPPAD_TAPE_ADDR_TYPE addr_t; typedef CPPAD_TAPE_ID_TYPE tape_id_t; // classes class sparse_hes_work; class sparse_jac_work; class sparse_jacobian_work; class sparse_hessian_work; template class AD; template class ADFun; template class atomic_base; template class atomic_three; template class atomic_four; template class discrete; template class VecAD; template class VecAD_reference; // functions with one VecAD argument template bool Constant (const VecAD &u); template bool Dynamic (const VecAD &u); template bool Parameter (const VecAD &u); template bool Variable (const VecAD &u); // functions with one AD argument template bool Constant (const AD &u); template bool Dynamic (const AD &u); template bool Parameter (const AD &u); template bool Variable (const AD &u); // template int Integer (const AD &u); template bool IdenticalZero (const AD &u); template bool IdenticalOne (const AD &u); template bool IdenticalCon (const AD &u); template bool LessThanZero (const AD &u); template bool LessThanOrZero (const AD &u); template bool GreaterThanZero (const AD &u); template bool GreaterThanOrZero (const AD &u); template AD Var2Par (const AD &u); template AD abs (const AD &u); template AD acos (const AD &u); template AD asin (const AD &u); template AD atan (const AD &u); template AD cos (const AD &u); template AD cosh (const AD &u); template AD exp (const AD &u); template AD log (const AD &u); template AD log10 (const AD &u); template AD sin (const AD &u); template AD sinh (const AD &u); template AD sqrt (const AD &u); template AD tan (const AD &u); // template unsigned short hash_code(const AD& u); // arithematic operators template AD operator + ( const AD &left, const AD &right); template AD operator - ( const AD &left, const AD &right); template AD operator * ( const AD &left, const AD &right); template AD operator / ( const AD &left, const AD &right); // comparison operators template bool operator < ( const AD &left, const AD &right); template bool operator <= ( const AD &left, const AD &right); template bool operator > ( const AD &left, const AD &right); template bool operator >= ( const AD &left, const AD &right); template bool operator == ( const AD &left, const AD &right); template bool operator != ( const AD &left, const AD &right); // pow template AD pow ( const AD &x, const AD &y); // azmul template AD azmul ( const AD &x, const AD &y); // NearEqual template bool NearEqual( const AD &x, const AD &y, const Base &r, const Base &a); template bool NearEqual( const Base &x, const AD &y, const Base &r, const Base &a); template bool NearEqual( const AD &x, const Base &y, const Base &r, const Base &a); // CondExpOp template AD CondExpOp ( enum CompareOp cop , const AD &left , const AD &right , const AD &trueCase , const AD &falseCase ); // IdenticalEqualCon template bool IdenticalEqualCon (const AD &u, const AD &v); // EqualOpSeq template bool EqualOpSeq (const AD &u, const AD &v); // PrintFor template void PrintFor( const AD& flag , const char* before , const AD& var , const char* after ); // Value template Base Value(const AD &x); // Pow function template AD pow (const AD &x, const AD &y); // input operator template std::istream& operator >> (std::istream &is, AD &x); // output operator template std::ostream& operator << (std::ostream &os, const AD &x); template std::ostream& operator << (std::ostream &os, const VecAD_reference &e); template std::ostream& operator << (std::ostream &os, const VecAD &vec); } # endif ================================================ FILE: include/cppad/local/define.hpp ================================================ # ifndef CPPAD_LOCAL_DEFINE_HPP # define CPPAD_LOCAL_DEFINE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /*! \file define.hpp Define processor symbols and macros that are used by CppAD. */ /*! \def CPPAD_VEC_ENUM_TYPE Is the type used to store vectors of enum values when the vector may be large and we want to conserve memory. The following must hold for any enum_value that is stored using the type CPPAD_VEC_ENUM_TYPE: size_t(enum_value) <= std::numeric_limits::max() && is_pod */ # define CPPAD_VEC_ENUM_TYPE unsigned char // ---------------------------------------------------------------------------- /*! \def CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION A version of the inline command that works with MC compiler. Microsoft Visual C++ version 9.0 generates a warning if a template function is declared as a friend (this was not a problem for version 7.0). The warning identifier is \verbatim warning C4396 \endverbatim and it contains the text \verbatim the inline specifier cannot be used when a friend declaration refers to a specialization of a function template \endverbatim This happens even if the function is not a specialization. This macro is defined as empty for Microsoft compilers. */ # ifdef _MSC_VER # define CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION # else # define CPPAD_INLINE_FRIEND_TEMPLATE_FUNCTION inline # endif // ---------------------------------------------------------------------------- /*! \def CPPAD_LIB_EXPORT Special macro for exporting windows DLL symbols; see https://gitlab.kitware.com/cmake/community/wikis/doc/tutorials/BuildingWinDLL */ /* This commented out code is for building windows shared libraries which currently does not work for CppAD: # ifdef _MSC_VER # ifdef cppad_lib_EXPORTS # define CPPAD_LIB_EXPORT __declspec(dllexport) # else # define CPPAD_LIB_EXPORT __declspec(dllimport) # endif // cppad_lib_EXPORTS # else // _MSC_VER # define CPPAD_LIB_EXPORT # endif */ # define CPPAD_LIB_EXPORT // ============================================================================ /*! \def CPPAD_FOLD_ASSIGNMENT_OPERATOR(Op) Declares automatic coercion for certain AD assignment operations. This macro assumes that the operator \verbatim left Op right \endverbatim is defined for the case where left and right have type AD. It uses this case to define the cases where left has type AD and right has type VecAD_reference, Base, or double. The argument right is const and call by reference. This macro converts the operands to AD and then uses the definition of the same operation for that case. */ # define CPPAD_FOLD_ASSIGNMENT_OPERATOR(Op) \ /* ----------------------------------------------------------------*/ \ template \ AD& operator Op \ (AD &left, double right) \ { return left Op AD(right); } \ \ template \ AD& operator Op \ (AD &left, const Base &right) \ { return left Op AD(right); } \ \ inline AD& operator Op \ (AD &left, const double &right) \ { return left Op AD(right); } \ \ template \ AD& operator Op \ (AD &left, const VecAD_reference &right) \ { return left Op right.ADBase(); } // ===================================================================== /*! \def CPPAD_FOLD_AD_VALUED_BINARY_OPERATOR(Op) Declares automatic coercion for certain binary operations with AD result. This macro assumes that the operator \verbatim left Op right \endverbatim is defined for the case where left and right and the result of the operation all have type AD. It uses this case to define the cases either left or right has type VecAD_reference or AD and the type of the other operand is one of the following: VecAD_reference, AD, Base, double. All of the arguments are const and call by reference. This macro converts the operands to AD and then uses the definition of the same operation for that case. */ # define CPPAD_FOLD_AD_VALUED_BINARY_OPERATOR(Op) \ /* ----------------------------------------------------------------*/ \ /* Operations with VecAD_reference and AD only*/ \ \ template \ AD operator Op \ (const AD &left, const VecAD_reference &right) \ { return left Op right.ADBase(); } \ \ template \ AD operator Op \ (const VecAD_reference &left, const VecAD_reference &right)\ { return left.ADBase() Op right.ADBase(); } \ \ template \ AD operator Op \ (const VecAD_reference &left, const AD &right) \ { return left.ADBase() Op right; } \ /* ----------------------------------------------------------------*/ \ /* Operations Base */ \ \ template \ AD operator Op \ (const Base &left, const AD &right) \ { return AD(left) Op right; } \ \ template \ AD operator Op \ (const Base &left, const VecAD_reference &right) \ { return AD(left) Op right.ADBase(); } \ \ template \ AD operator Op \ (const AD &left, const Base &right) \ { return left Op AD(right); } \ \ template \ AD operator Op \ (const VecAD_reference &left, const Base &right) \ { return left.ADBase() Op AD(right); } \ \ /* ----------------------------------------------------------------*/ \ /* Operations double */ \ \ template \ AD operator Op \ (const double &left, const AD &right) \ { return AD(left) Op right; } \ \ template \ AD operator Op \ (const double &left, const VecAD_reference &right) \ { return AD(left) Op right.ADBase(); } \ \ template \ AD operator Op \ (const AD &left, const double &right) \ { return left Op AD(right); } \ \ template \ AD operator Op \ (const VecAD_reference &left, const double &right) \ { return left.ADBase() Op AD(right); } \ /* ----------------------------------------------------------------*/ \ /* Special case to avoid ambiguity when Base is double */ \ \ inline AD operator Op \ (const double &left, const AD &right) \ { return AD(left) Op right; } \ \ inline AD operator Op \ (const double &left, const VecAD_reference &right) \ { return AD(left) Op right.ADBase(); } \ \ inline AD operator Op \ (const AD &left, const double &right) \ { return left Op AD(right); } \ \ inline AD operator Op \ (const VecAD_reference &left, const double &right) \ { return left.ADBase() Op AD(right); } // ======================================================================= /*! \def CPPAD_FOLD_BOOL_VALUED_BINARY_OPERATOR(Op) Declares automatic coercion for certain binary operations with bool result. This macro assumes that the operator \verbatim left Op right \endverbatim is defined for the case where left and right have type AD and the result has type bool. It uses this case to define the cases either left or right has type VecAD_reference or AD and the type of the other operand is one of the following: VecAD_reference, AD, Base, double. All of the arguments are const and call by reference. This macro converts the operands to AD and then uses the definition of the same operation for that case. */ # define CPPAD_FOLD_BOOL_VALUED_BINARY_OPERATOR(Op) \ /* ----------------------------------------------------------------*/ \ /* Operations with VecAD_reference and AD only*/ \ \ template \ bool operator Op \ (const AD &left, const VecAD_reference &right) \ { return left Op right.ADBase(); } \ \ template \ bool operator Op \ (const VecAD_reference &left, const VecAD_reference &right)\ { return left.ADBase() Op right.ADBase(); } \ \ template \ bool operator Op \ (const VecAD_reference &left, const AD &right) \ { return left.ADBase() Op right; } \ /* ----------------------------------------------------------------*/ \ /* Operations Base */ \ \ template \ bool operator Op \ (const Base &left, const AD &right) \ { return AD(left) Op right; } \ \ template \ bool operator Op \ (const Base &left, const VecAD_reference &right) \ { return AD(left) Op right.ADBase(); } \ \ template \ bool operator Op \ (const AD &left, const Base &right) \ { return left Op AD(right); } \ \ template \ bool operator Op \ (const VecAD_reference &left, const Base &right) \ { return left.ADBase() Op AD(right); } \ \ /* ----------------------------------------------------------------*/ \ /* Operations double */ \ \ template \ bool operator Op \ (const double &left, const AD &right) \ { return AD(left) Op right; } \ \ template \ bool operator Op \ (const double &left, const VecAD_reference &right) \ { return AD(left) Op right.ADBase(); } \ \ template \ bool operator Op \ (const AD &left, const double &right) \ { return left Op AD(right); } \ \ template \ bool operator Op \ (const VecAD_reference &left, const double &right) \ { return left.ADBase() Op AD(right); } \ /* ----------------------------------------------------------------*/ \ /* Special case to avoid ambiguity when Base is double */ \ \ inline bool operator Op \ (const double &left, const AD &right) \ { return AD(left) Op right; } \ \ inline bool operator Op \ (const double &left, const VecAD_reference &right) \ { return AD(left) Op right.ADBase(); } \ \ inline bool operator Op \ (const AD &left, const double &right) \ { return left Op AD(right); } \ \ inline bool operator Op \ (const VecAD_reference &left, const double &right) \ { return left.ADBase() Op AD(right); } # endif ================================================ FILE: include/cppad/local/graph/cpp_graph_itr.hpp ================================================ # ifndef CPPAD_LOCAL_GRAPH_CPP_GRAPH_ITR_HPP # define CPPAD_LOCAL_GRAPH_CPP_GRAPH_ITR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include // BEGIN_CPPAD_LOCAL_GRAPH_NAMESPACE namespace CppAD { namespace local { namespace graph { class cpp_graph_itr { /* {xrst_begin cpp_graph_itr_data dev} C++ AD Graph Iterator Private Member Data ######################################### {xrst_spell_off} {xrst_code hpp} */ private: // values set by constructor const vector* operator_vec_; const vector* operator_arg_; // // set by constructor and ++ size_t op_index_; size_t first_arg_; // // set by set_value graph_op_enum op_enum_; size_t first_node_; size_t n_result_; size_t call_id_; vector str_index_; vector arg_node_; /* {xrst_code} {xrst_spell_on} {xrst_end cpp_graph_itr_data} ------------------------------------------------------------------------------ {xrst_begin cpp_graph_itr_set_value dev} {xrst_spell str } C++ AD Graph Iterator set_value() ################################# Syntax ****** | *itr* . ``set_value`` () op_index\_ ********** This input is the operator index for the value we are retrieving. first_arg\_ *********** This input is the first argument index for the value we are retrieving. first_node\_ ************ The input value of this argument does not matter. It is set to the index in ``operator_arg_`` of the first node argument for this operator. op_enum\_ ********* The input value of this argument does not matter. It is set to the :ref:`graph_op_enum-name` for the operator str_index\_ *********** The input value of this argument does not matter. Upon return its size is zero except for the special cases listed below: atom_graph_op, atom4_graph_op ============================= If *op_enum_* is ``atom_graph_op`` or ``atom4_graph_op`` , ``str_index_.size() == 1`` and ``str_index_[0]`` is the index in :ref:`cpp_ad_graph@atomic_name_vec` for the function called by this operator. discrete_graph_op ================= If *op_enum_* is ``discrete_graph_op`` , ``str_index_.size() == 1`` and ``str_index_[0]`` is the index in :ref:`cpp_ad_graph@discrete_name_vec` for the function called by this operator. print_graph_op ============== If *op_enum_* is ``print_graph_op`` , ``str_index_.size() == 2`` and ``str_index_[0]`` ( ``str_index_[1]`` ) is the index in :ref:`cpp_ad_graph@print_text_vec` for the :ref:`PrintFor@before` (:ref:`PrintFor@after` ) text. n_result\_ ********** The input value of this argument does not matter. This is set to the number of result nodes for this operator. call_id\_ ********* If *op_enum_* is ``atom4_graph_op`` , ``call_id_`` is set to the :ref:`atomic_four_call@call_id` for this function call. If *op_enum_* is ``atom_graph_op`` , ``call_id`` is set to zero. arg_node\_ ********** The input value of this argument does not matter. Upon return, its size is the number of arguments, that are node indices, for this operator usage. The value of the elements are the node indices. Prototype ********* {xrst_spell_off} {xrst_code hpp} */ void set_value(void) /* {xrst_code} {xrst_spell_on} {xrst_end cpp_graph_itr_set_value} */ { // initialize output values size_t invalid_index = std::numeric_limits::max(); size_t n_arg = invalid_index; first_node_ = invalid_index; n_result_ = invalid_index; call_id_ = invalid_index; str_index_.resize(0); arg_node_.resize(0); // // op_enum op_enum_ = (*operator_vec_)[op_index_]; // // n_result_, n_arg, call_id_, str_index_ switch( op_enum_ ) { // unary operators case abs_graph_op: case acos_graph_op: case acosh_graph_op: case asin_graph_op: case asinh_graph_op: case atan_graph_op: case atanh_graph_op: case cos_graph_op: case cosh_graph_op: case erf_graph_op: case erfc_graph_op: case exp_graph_op: case expm1_graph_op: case log1p_graph_op: case log_graph_op: case neg_graph_op: case sign_graph_op: case sin_graph_op: case sinh_graph_op: case sqrt_graph_op: case tan_graph_op: case tanh_graph_op: first_node_ = first_arg_; n_result_ = 1; n_arg = 1; break; // binary operators case add_graph_op: case azmul_graph_op: case div_graph_op: case mul_graph_op: case pow_graph_op: case sub_graph_op: first_node_ = first_arg_; n_result_ = 1; n_arg = 2; break; // discrete_graph_op case discrete_graph_op: first_node_ = first_arg_ + 1; str_index_.push_back( (*operator_arg_)[first_node_ - 1] ); n_result_ = 1; n_arg = 1; break; // atom_graph_op case atom_graph_op: first_node_ = first_arg_ + 3; str_index_.push_back( (*operator_arg_)[first_node_ - 3] ); call_id_ = 0; n_result_ = (*operator_arg_)[first_node_ - 2]; n_arg = (*operator_arg_)[first_node_ - 1]; break; // atom4_graph_op case atom4_graph_op: first_node_ = first_arg_ + 4; str_index_.push_back( (*operator_arg_)[first_node_ - 4] ); call_id_ = (*operator_arg_)[first_node_ - 3]; n_result_ = (*operator_arg_)[first_node_ - 2]; n_arg = (*operator_arg_)[first_node_ - 1]; break; // print_graph_op case print_graph_op: first_node_ = first_arg_ + 2; str_index_.push_back( (*operator_arg_)[first_node_ - 2] ); str_index_.push_back( (*operator_arg_)[first_node_ - 1] ); n_result_ = 0; n_arg = 2; break; // conditional expressions case cexp_eq_graph_op: case cexp_le_graph_op: case cexp_lt_graph_op: first_node_ = first_arg_; n_result_ = 1; n_arg = 4; break; // comparison operators case comp_eq_graph_op: case comp_le_graph_op: case comp_lt_graph_op: case comp_ne_graph_op: first_node_ = first_arg_; n_result_ = 0; n_arg = 2; break; // sum_graph_op case sum_graph_op: first_node_ = first_arg_ + 1; n_result_ = 1; n_arg = (*operator_arg_)[first_node_ - 1]; break; default: CPPAD_ASSERT_UNKNOWN(false); break; } // set arg_node arg_node_.resize(n_arg); for(size_t i = 0; i < n_arg; i++) arg_node_[i] = (*operator_arg_)[first_node_ + i]; return; } /* %$$ ------------------------------------------------------------------------------- {xrst_begin cpp_graph_itr_types dev} C++ AD Graph Iterator Types ########################### {xrst_spell_off} {xrst_code hpp} */ public: typedef struct { graph_op_enum op_enum; size_t n_result; size_t call_id; const vector* str_index_ptr; const vector* arg_node_ptr; } value_type; typedef std::input_iterator_tag iterator_category; /* {xrst_code} {xrst_spell_on} {xrst_end cpp_graph_itr_types} ------------------------------------------------------------------------------ {xrst_begin cpp_graph_itr_ctor dev} C++ AD Graph Iterator Constructors ################################## Syntax ****** | ``cpp_graph_itr`` *default* | ``cpp_graph_itr`` *itr* ( *operator_vec* , *operator_arg* , *op_index* Prototype ********* {xrst_literal // BEGIN_CTOR // END_CTOR } default ******* The result of the default constructor can only be used as a target for the assignment operator. operator_vec ************ Is the :ref:`cpp_ad_graph@operator_vec` for the ``cpp_graph`` container that this iterator refers to. operator_arg ************ Is the :ref:`operator_arg` for the ``cpp_graph`` container that this iterator refers to. op_index ******** This must be either zero (the ``begin()`` for the container) or equal to the size of *operator_vec* (the ``end()`` for the container). {xrst_end cpp_graph_itr_ctor} */ cpp_graph_itr(void) : operator_vec_(nullptr), operator_arg_(nullptr) { } // BEGIN_CTOR cpp_graph_itr( const vector& operator_vec , const vector& operator_arg , size_t op_index ) // END_CTOR : operator_vec_(&operator_vec) , operator_arg_(&operator_arg) , op_index_(op_index) { // end constructor if( op_index == operator_vec.size() ) return; // // begin constructor CPPAD_ASSERT_KNOWN( op_index == 0, "cpp_graph_itr: constructor op_index not 0 or operator_vec.size()" ); // start at the beginning of operator_vec first_arg_ = 0; // // get the value, and first_node_, for this operator set_value(); } /* %$$ ------------------------------------------------------------------------------ {xrst_begin cpp_graph_itr_input dev} C++ AD Graph Iterator Input Operations ###################################### {xrst_spell_off} {xrst_code hpp} */ // itr == other bool operator==(const cpp_graph_itr& other) const { return op_index_ == other.op_index_; } // itr != other bool operator!=(const cpp_graph_itr& other) const { return op_index_ != other.op_index_; } // *itr value_type operator*(void) { CPPAD_ASSERT_KNOWN( operator_vec_ != nullptr, "cpp_graph_itr: attempt to dereference default iterator" ); CPPAD_ASSERT_KNOWN( op_index_ < operator_vec_->size(), "cpp_graph_itr: attempt to dereference past last element in graph" ); value_type ret; ret.op_enum = op_enum_; ret.n_result = n_result_; ret.call_id = call_id_; ret.str_index_ptr = &str_index_; ret.arg_node_ptr = &arg_node_; return ret; } // ++itr cpp_graph_itr& operator++(void) { ++op_index_; first_arg_ = first_node_ + arg_node_.size(); set_value(); return *this; } // itr++ cpp_graph_itr operator++(int) { cpp_graph_itr ret(*this); ++op_index_; first_arg_ = first_node_ + arg_node_.size(); set_value(); return ret; } /* {xrst_code} {xrst_spell_on} {xrst_end cpp_graph_itr_input} */ }; } } } // END_CPPAD_LOCAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/graph/cpp_graph_itr.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin cpp_graph_itr dev} C++ AD Graph Iterator Class ########################### Contents ******** {xrst_toc_table include/cppad/local/graph/cpp_graph_itr.hpp } {xrst_end cpp_graph_itr} ================================================ FILE: include/cppad/local/graph/cpp_graph_op.hpp ================================================ # ifndef CPPAD_LOCAL_GRAPH_CPP_GRAPH_OP_HPP # define CPPAD_LOCAL_GRAPH_CPP_GRAPH_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include # include namespace CppAD { namespace local { namespace graph { /* {xrst_begin cpp_graph_op dev} C++ AD Graph Operators ###################### Namespace ********* All of these definitions are in the ``CppAD::local::graph`` namespace. CppAD::graph ************ {xrst_spell_off} {xrst_code hpp} */ using namespace CppAD::graph; /* {xrst_code} {xrst_spell_on} addr_t ****** {xrst_spell_off} {xrst_code hpp} */ typedef CPPAD_TAPE_ADDR_TYPE addr_t; /* {xrst_code} {xrst_spell_on} op_name2enum ************ This is a mapping from the operator name to its enum value. The name is the operator enum without the ``_operator`` at the end. {xrst_spell_off} {xrst_code hpp} */ extern CPPAD_LIB_EXPORT std::map< std::string, graph_op_enum > op_name2enum; /* {xrst_code} {xrst_spell_on} op_enum2fixed_n_arg ******************* This is the number of arguments for the operators that have a fixed number of arguments and one result. For other operators, this value is zero. {xrst_spell_off} {xrst_code hpp} */ extern CPPAD_LIB_EXPORT size_t op_enum2fixed_n_arg[]; /* {xrst_code} {xrst_spell_on} op_enum2name ************ This is mapping from operator enum value to its name. In the ``local::graph`` namespace: {xrst_spell_off} {xrst_code hpp} */ extern CPPAD_LIB_EXPORT const char* op_enum2name[]; /* {xrst_code} {xrst_spell_on} set_operator_info ***************** This routine sets the values in ``op_enum2fixed_n_arg`` , ``op_enum2name`` , and ``op_name2enum`` . {xrst_spell_off} {xrst_code hpp} */ extern CPPAD_LIB_EXPORT void set_operator_info(void); /* {xrst_code} {xrst_spell_on} {xrst_end cpp_graph_op} */ } } } // END_CPPAD_LOCAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/graph/csrc_writer.hpp ================================================ # ifndef CPPAD_LOCAL_GRAPH_CSRC_WRITER_HPP # define CPPAD_LOCAL_GRAPH_CSRC_WRITER_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include /* {xrst_begin csrc_writer dev} Prototype for csrc_writer ######################### Syntax ****** | ``csrc_writer`` ( *csrc* , *graph_obj* , *type* ) Prototype ********* {xrst_spell_off} {xrst_code hpp} */ namespace CppAD { namespace local { namespace graph { CPPAD_LIB_EXPORT void csrc_writer( std::ostream& os , const cpp_graph& graph_obj , const std::string& type ); } } } /* {xrst_code} {xrst_spell_on} See *** :ref:`cpp_csrc_writer-name` {xrst_end csrc_writer} */ # endif ================================================ FILE: include/cppad/local/graph/json_lexer.hpp ================================================ # ifndef CPPAD_LOCAL_GRAPH_JSON_LEXER_HPP # define CPPAD_LOCAL_GRAPH_JSON_LEXER_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include // BEGIN_NAMESPACE_CPPAD_LOCAL_GRAPH namespace CppAD { namespace local { namespace graph { // =========================================================================== class json_lexer { // =========================================================================== /* ------------------------------------------------------------------------------- {xrst_begin json_lexer_member_data dev} json lexer: Private Data ######################## Member Variables **************** graph\_ ======= The :ref:`json_ad_graph-name` . index\_ ======= is the index in the graph for the current character. If a token is returned, this corresponds to the last character it the token. line_number\_ ============= line number in the graph for the current character char_number\_ ============= character number in the graph for the current character token\_ ======= used to return tokens. function_name\_ =============== is the function name for this graph. This is initialized as empty, should be set as soon as it is parsed, and is used for error reporting. token ***** returns current value of ``token_`` . line_number *********** returns current value of ``line_number_`` (which corresponds to last character in the token). char_number *********** returns current value of ``char_number_`` . (which corresponds to last character in the token). set_function_name ***************** sets the value of ``function_name_`` . Source Code *********** {xrst_spell_off} {xrst_code hpp} */ private: const std::string& json_; size_t index_; size_t line_number_; size_t char_number_; std::string token_; std::string function_name_; public: const std::string& token(void) const; size_t line_number(void) const; size_t char_number(void) const; void set_function_name(const std::string& function_name); /* {xrst_code} {xrst_spell_on} {xrst_end json_lexer_member_data} ------------------------------------------------------------------------------- {xrst_begin json_lexer_report_error dev} json lexer: Report an Error ########################### Syntax ****** | *json_lexer* . ``report_error`` ( *expected* , *found* ) json_lexer ********** is a ``local::graph::json_lexer`` object. expected ******** is the token that is expected. found ***** is the token or text that was found. Report ****** The current CppAD :ref:`ErrorHandler-name` is used to report an error parsing this Json AD graph. Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void report_error(const std::string& expected, const std::string& found); /* {xrst_code} {xrst_spell_on} {xrst_end json_lexer_report_error} ------------------------------------------------------------------------------- {xrst_begin json_lexer_next_index dev} json lexer: Advance Index by One ################################ Syntax ****** *json_lexer* . ``next_index`` () json_lexer ********** is a ``local::graph::json_lexer`` object. index\_ ******* The input value of ``index_`` is increased by one. It is an error to call this routine when the input value of ``index_`` is greater than or equal ``json_.size()`` . line_number\_ ************* If the previous character, before the call, was a new line, ``line_number_`` is increased by one. char_number\_ ************* If the previous character, before the call, was a new line, ``char_number`` is set to one. Otherwise, ``char_number_`` is increased by one. Prototype ********* {xrst_spell_off} {xrst_code hpp} */ private: void next_index(void); /* {xrst_code} {xrst_spell_on} {xrst_end json_lexer_next_index} ------------------------------------------------------------------------------- {xrst_begin json_lexer_skip_white_space dev} json lexer: Skip White Space That Separates Tokens ################################################## Syntax ****** *json_lexer* . ``skip_white_space`` () json_lexer ********** is a json lexer object. Discussion ********** This member functions is used to increase ``index_`` until either a non-white space character is found or ``index_`` is equal to ``json_.size()`` . Prototype ********* {xrst_spell_off} {xrst_code hpp} */ private: void skip_white_space(void); /* {xrst_code} {xrst_spell_on} {xrst_end json_lexer_skip_white_space} ------------------------------------------------------------------------------- {xrst_begin json_lexer_constructor dev} json lexer: Constructor ####################### Syntax ****** ``local::graph::lexer`` *json_lexer* ( *json* ) json **** The argument *json* is an :ref:`json_ad_graph-name` and it is assumed that *json* does not change for as long as *json_lexer* exists. Initialization ************** The current token, index, line number, and character number are set to the first non white space character in ``json_`` . If this is not a left brace character ``'{'`` , the error is reported and the constructor does not return. Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: json_lexer(const std::string& json); /* {xrst_code} {xrst_spell_on} {xrst_end json_lexer_constructor} ------------------------------------------------------------------------------- {xrst_begin json_lexer_check_next_char dev} Get and Check Next Single Character Token ######################################### Syntax ****** *json_lexer* . ``check_next_char`` ( *ch* ) index\_ ******* The search for the character starts at one greater than the input value for ``index_`` and skips white space. ch ** Is a non white space single character token that is expected. If this character is not found, the error is reported and this function does not return. In the special case where *ch* is ``'\0'`` , any non-white space character will be accepted (but there must be such a character). token\_ ******* If this routine returns, ``token_`` has size one and contains the character that is found. Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void check_next_char(char ch); /* {xrst_code} {xrst_spell_on} {xrst_end json_lexer_check_next_char} ------------------------------------------------------------------------------- {xrst_begin json_lexer_check_next_string dev} Get and Check Next Single Character Token ######################################### Syntax ****** *json_lexer* . ``check_next_string`` ( *expected* ) index\_ ******* The search for the string starts at one greater than the input value for ``index_`` and skips white space. expected ******** Is the value (not including double quotes) for the string that is expected. If this string is not found, the error is reported and this function does not return. In the special case where *expected* is empty, any string will be accepted. token\_ ******* If this routine returns, *token_* is the string that was found. Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void check_next_string(const std::string& expected); /* {xrst_code} {xrst_spell_on} {xrst_end json_lexer_check_next_string} ------------------------------------------------------------------------------- {xrst_begin json_lexer_next_non_neg_int dev} Get Next Non-Negative Integer ############################# Syntax ****** | |tab| *json_lexer* . ``next_non_neg_int`` () | |tab| *value* = *json_lexer* . ``token2size_t`` () index\_ ******* The search for the non-negative integer starts at one greater than the input value for ``index_`` and skips white space. token\_ ******* is set to the non-negative integer. If the next token is not a non-negative integer, the error is reported and this function does not return. value ***** If the current token is a non-negative integer, *value* is the corresponding value. Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void next_non_neg_int(void); size_t token2size_t(void) const; /* {xrst_code} {xrst_spell_on} {xrst_end json_lexer_next_non_neg_int} ------------------------------------------------------------------------------- {xrst_begin json_lexer_next_float dev} Get Next Floating Point Number ############################## Syntax ****** | |tab| *ok* = *json_lexer* . ``next_float`` () | |tab| *value* = *json_lexer* . ``token2double`` () index\_ ******* The search for the floating point number starts at one greater than the input value for ``index_`` and skips white space. token\_ ******* is set to the floating point number. If the next token is not a floating point number, the error is reported and this function does not return. value ***** If the current token is a floating point number, *value* is the corresponding value. Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void next_float(void); double token2double(void) const; /* {xrst_code} {xrst_spell_on} {xrst_end json_lexer_next_float} */ // ========================================================================== }; // end class lexer // ========================================================================== } } } // END_NAMESPACE_CPPAD_LOCAL_GRAPH # endif ================================================ FILE: include/cppad/local/graph/json_lexer.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin json_lexer dev} Lexical Analysis Class for a Json AD Graph ########################################## Contents ******** {xrst_toc_table include/cppad/local/graph/json_lexer.hpp } {xrst_end json_lexer} ================================================ FILE: include/cppad/local/graph/json_parser.hpp ================================================ # ifndef CPPAD_LOCAL_GRAPH_JSON_PARSER_HPP # define CPPAD_LOCAL_GRAPH_JSON_PARSER_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include /* {xrst_begin json_parser dev} Json AD Graph Parser #################### Syntax ****** | ``json_parser`` ( *json* , *graph_obj* ) json **** The :ref:`json_ad_graph-name` . graph_obj ********* This is a ``cpp_graph`` object. The input value of the object does not matter. Upon return it is a :ref:`cpp_ad_graph-name` representation of this function. Prototype ********* {xrst_spell_off} {xrst_code hpp} */ namespace CppAD { namespace local { namespace graph { CPPAD_LIB_EXPORT void json_parser( const std::string& json , cpp_graph& graph_obj ); } } } /* {xrst_code} {xrst_spell_on} {xrst_end json_parser} */ # endif ================================================ FILE: include/cppad/local/graph/json_writer.hpp ================================================ # ifndef CPPAD_LOCAL_GRAPH_JSON_WRITER_HPP # define CPPAD_LOCAL_GRAPH_JSON_WRITER_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include /* {xrst_begin json_writer dev} Json AD Graph Writer #################### Syntax ****** | ``json_writer`` ( *json* , *graph_obj* ) json **** The input value of *json* does not matter, upon return it a :ref:`json` representation of the AD graph. graph_obj ********* This is a ``cpp_graph`` object. Prototype ********* {xrst_spell_off} {xrst_code hpp} */ namespace CppAD { namespace local { namespace graph { CPPAD_LIB_EXPORT void json_writer( std::string& json , const cpp_graph& graph_obj ); } } } /* {xrst_code} {xrst_spell_on} {xrst_end json_writer} */ # endif ================================================ FILE: include/cppad/local/hash_code.hpp ================================================ # ifndef CPPAD_LOCAL_HASH_CODE_HPP # define CPPAD_LOCAL_HASH_CODE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include /*! \file local/hash_code.hpp CppAD hashing utility. */ namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE /*! General purpose hash code for an arbitrary value. \tparam Value is the type of the argument being hash coded. It should be a plain old data class; i.e., the values included in the equality operator in the object and not pointed to by the object. \param value the value that we are generating a hash code for. All of the fields in value should have been set before the hash code is computed (otherwise undefined values are used). \return is a hash code that is between zero and CPPAD_HASH_TABLE_SIZE - 1. \par Checked Assertions \li std::numeric_limits::max() >= CPPAD_HASH_TABLE_SIZE \li sizeof(value) is even \li sizeof(unsigned short) == 2 */ template unsigned short local_hash_code(const Value& value) { CPPAD_ASSERT_UNKNOWN( std::numeric_limits::max() >= CPPAD_HASH_TABLE_SIZE ); CPPAD_ASSERT_UNKNOWN( sizeof(unsigned short) == 2 ); CPPAD_ASSERT_UNKNOWN( sizeof(value) % 2 == 0 ); // const unsigned short* v = reinterpret_cast(& value); // size_t i = sizeof(value) / 2 - 1; // size_t sum = v[i]; // while(i--) sum += v[i]; // unsigned short code = static_cast( sum % CPPAD_HASH_TABLE_SIZE ); return code; } /*! Specialized hash code for a CppAD operator and its arguments. \param op is the operator that we are computing a hash code for. If it is not one of the following operartors, the operator is not hash coded and zero is returned: \li unary operators: AbsOp, AcosOp, AcoshOp, AsinOp, AsinhOp, AtanOp, AtanhOp, CosOp, CoshOp ExpOp, Expm1Op, LogOp, Log1pOp, SinOp, SinhOp, SqrtOp, TanOp, TanhOp \li binary operators where first argument is a parameter: AddpvOp, DivpvOp, MulpvOp, PowpvOp, SubpvOp, ZmulpvOp \li binary operators where second argument is a parameter: DivvpOp, PowvpOp, SubvpOp, Zmulvp \li binary operators where first is an index and second is a variable: DisOp \li binary operators where both arguments are variables: AddvvOp, DivvvOp, MulvvOp, PowvvOp, SubvvOp, ZmulvvOp \param arg is a vector of length NumArg(op) or 2 (which ever is smaller), containing the corresponding argument indices for this operator. \param npar is the number of parameters corresponding to this operation sequence. \param par is a vector of length npar containing the parameters for this operation sequence; i.e., given a parameter index of i, the corresponding parameter value is par[i]. \return is a hash code that is between zero and CPPAD_HASH_TABLE_SIZE - 1. \par Checked Assertions op must be one of the operators specified above. In addition, \li std::numeric_limits::max() >= CPPAD_HASH_TABLE_SIZE \li sizeof(size_t) is even \li sizeof(Base) is even \li sizeof(unsigned short) == 2 \li size_t(op) < size_t(NumberOp) <= CPPAD_HASH_TABLE_SIZE \li if the j-th argument for this operation is a parameter, arg[j] < npar. */ template unsigned short local_hash_code( op_code_var op , const addr_t* arg , size_t npar , const Base* par ) { CPPAD_ASSERT_UNKNOWN( std::numeric_limits::max() >= CPPAD_HASH_TABLE_SIZE ); CPPAD_ASSERT_UNKNOWN( size_t (op) < size_t(NumberOp) ); CPPAD_ASSERT_UNKNOWN( sizeof(unsigned short) == 2 ); CPPAD_ASSERT_UNKNOWN( sizeof(addr_t) % 2 == 0 ); CPPAD_ASSERT_UNKNOWN( sizeof(Base) % 2 == 0 ); unsigned short op_fac = static_cast ( CPPAD_HASH_TABLE_SIZE / static_cast(NumberOp) ); CPPAD_ASSERT_UNKNOWN( op_fac > 0 ); // number of shorts per addr_t value size_t short_addr_t = sizeof(addr_t) / 2; // initialize with value that separates operators as much as possible unsigned short code = static_cast( static_cast(op) * op_fac ); // now code in the operands size_t i; const unsigned short* v; // first argument switch(op) { // Binary operators where first argument is a parameter. // Code parameters by value instead of // by index for two reasons. One, it gives better separation. // Two, different indices can be same parameter value. case AddpvOp: case DivpvOp: case MulpvOp: case PowpvOp: case SubpvOp: case ZmulpvOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 2 ); code += hash_code( par[arg[0]] ); // v = reinterpret_cast(arg + 1); i = short_addr_t; while(i--) code += v[i]; break; // Binary operator where first argument is an index and // second is a variable (same as both variables). case DisOp: // Binary operators where both arguments are variables case AddvvOp: case DivvvOp: case MulvvOp: case PowvvOp: case SubvvOp: case ZmulvvOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 2 ); v = reinterpret_cast(arg + 0); i = 2 * short_addr_t; while(i--) code += v[i]; break; // Binary operators where second argument is a parameter. case DivvpOp: case PowvpOp: case SubvpOp: case ZmulvpOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 2 ); v = reinterpret_cast(arg + 0); i = short_addr_t; while(i--) code += v[i]; code += hash_code( par[arg[1]] ); break; // Unary operators case AbsOp: case AcosOp: case AcoshOp: case AsinOp: case AsinhOp: case AtanOp: case AtanhOp: case CosOp: case CoshOp: case ErfOp: case ErfcOp: case ExpOp: case Expm1Op: case LogOp: case Log1pOp: case SignOp: case SinOp: case SinhOp: case SqrtOp: case TanOp: case TanhOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 1 || op == ErfOp ); v = reinterpret_cast(arg + 0); i = short_addr_t; while(i--) code += v[i]; break; // should have been one of he cases above default: CPPAD_ASSERT_UNKNOWN(false); } return code % CPPAD_HASH_TABLE_SIZE; } } } // END_CPPAD_LOCAL_NAMESPACE # endif ================================================ FILE: include/cppad/local/independent.hpp ================================================ # ifndef CPPAD_LOCAL_INDEPENDENT_HPP # define CPPAD_LOCAL_INDEPENDENT_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE /* \file local/independent.hpp Start recording AD operations. */ /*! Start recording AD operations: Implementation in local namespace. \tparam ADVector This is simple vector type with elements of type AD. \param x Vector of the independent variables. \param abort_op_index operator index at which execution will be aborted (during the recording \param record_compare should comparison operators be recorded. of operations). The value zero corresponds to not aborting (will not match). \param dynamic Vector of dynamic parameters. */ template template void ADTape::Independent( ADVector& x , size_t abort_op_index , bool record_compare , ADVector& dynamic ) { // check ADVector is Simple Vector class with AD elements CheckSimpleVector< AD, ADVector>(); // dimension of the domain space size_t n = x.size(); CPPAD_ASSERT_KNOWN( n > 0, "Independent: the argument vector x has zero size" ); CPPAD_ASSERT_UNKNOWN( Rec_.num_var() == 0 ); CPPAD_ASSERT_UNKNOWN( Rec_.get_abort_op_index() == 0 ); CPPAD_ASSERT_UNKNOWN( Rec_.get_record_compare() == true ); CPPAD_ASSERT_UNKNOWN( Rec_.n_dyn_independent() == 0 ); // set record_compare and abort_op_index before doing anything else Rec_.set_record_compare(record_compare); Rec_.set_abort_op_index(abort_op_index); Rec_.set_n_dyn_independent( dynamic.size() ); // mark the beginning of the tape and skip the first variable index // (zero) because parameters use taddr zero CPPAD_ASSERT_NARG_NRES(BeginOp, 1, 1); Rec_.PutOp(BeginOp); Rec_.PutArg(0); // place each of the independent variables in the tape CPPAD_ASSERT_NARG_NRES(InvOp, 0, 1); for(size_t j = 0; j < n; j++) { // tape address for this independent variable CPPAD_ASSERT_UNKNOWN( ! Variable(x[j] ) ); x[j].taddr_ = Rec_.PutOp(InvOp); x[j].tape_id_ = id_; x[j].ad_type_ = variable_enum; CPPAD_ASSERT_UNKNOWN( size_t(x[j].taddr_) == j+1 ); CPPAD_ASSERT_UNKNOWN( Variable(x[j] ) ); } // done specifying all of the independent variables size_independent_ = n; // parameter index zero is used by dynamic parameter tape // to indicate that an argument is a variable Base nan = CppAD::numeric_limits::quiet_NaN(); # ifndef NDEBUG CPPAD_ASSERT_UNKNOWN( Rec_.put_con_par(nan) == 0 ); # else Rec_.put_con_par(nan); # endif // Place independent dynamic parameters at beginning of parameter vector, // just after the nan at index zero. CPPAD_ASSERT_UNKNOWN( Rec_.n_dyn_independent() <= dynamic.size() ); for(size_t j = 0; j < Rec_.n_dyn_independent(); ++j) { CPPAD_ASSERT_UNKNOWN( ! Dynamic( dynamic[j] ) ); CPPAD_ASSERT_UNKNOWN( Parameter( dynamic[j] ) ); // // dynamic parameters are placed at the end, so i == j # ifndef NDEBUG addr_t i = Rec_.put_dyn_par(dynamic[j].value_ , ind_dyn); CPPAD_ASSERT_UNKNOWN( size_t(i) == j+1 ); # else Rec_.put_dyn_par(dynamic[j].value_ , ind_dyn); # endif // // make this parameter dynamic dynamic[j].taddr_ = static_cast(j+1); dynamic[j].tape_id_ = id_; dynamic[j].ad_type_ = dynamic_enum; CPPAD_ASSERT_UNKNOWN( Dynamic( dynamic[j] ) ); } } } } // END_CPPAD_LOCAL_NAMESPACE # endif ================================================ FILE: include/cppad/local/is_pod.hpp ================================================ # ifndef CPPAD_LOCAL_IS_POD_HPP # define CPPAD_LOCAL_IS_POD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin is_pod dev} {xrst_spell nullptr } The is_pod Template Function ############################ Default Definition ****************** The default template definition is that ``is_pod`` < *Type* >() is false for all types. Fundamental Types ***************** This file specializes ``is_pod`` < *Type* > to be true where *Type* is any of the c++11 fundamental types that hold data; i.e., ``void`` and ``nullptr_t`` are excluded. Other Type ********** You can inform CppAD that a particular *Type* is plain old data by defining | |tab| ``namespace CppAD`` { ``namespace local`` { | |tab| |tab| ``template <> inline bool is_pod<`` *Type* >( ``void`` ) { ``return true`` ; } | |tab| } } {xrst_end is_pod} */ namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE // template inline bool is_pod(void) { return false; } // bool template <> inline bool is_pod(void) {return true;} // short template <> inline bool is_pod(void) {return true;} template <> inline bool is_pod(void) {return true;} // int template <> inline bool is_pod(void) {return true;} template <> inline bool is_pod(void) {return true;} // long template <> inline bool is_pod(void) {return true;} template <> inline bool is_pod(void) {return true;} // long long template <> inline bool is_pod(void) {return true;} template <> inline bool is_pod(void) {return true;} // Character types template <> inline bool is_pod(void) {return true;} template <> inline bool is_pod(void) {return true;} template <> inline bool is_pod(void) {return true;} template <> inline bool is_pod(void) {return true;} template <> inline bool is_pod(void) {return true;} template <> inline bool is_pod(void) {return true;} // floating point types template <> inline bool is_pod(void) {return true;} template <> inline bool is_pod(void) {return true;} template <> inline bool is_pod(void) {return true;} } } // END_CPPAD_LOCAL_NAMESPACE # endif ================================================ FILE: include/cppad/local/is_pod.hpp.in ================================================ # ifndef CPPAD_LOCAL_IS_POD_HPP # define CPPAD_LOCAL_IS_POD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // make sure size_t is defined because autotools version of // is_pod_specialize_98 uses it # include /*! \file is_pod.hpp File that defines is_pod(void) */ namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE /*! Is this type plain old data; i.e., its constructor need not be called. The default definition is false. This include file defines it as true for all the fundamental types except for void and nullptr_t. */ template bool is_pod(void) { return false; } // The following command suppresses doxygen processing for the code below /// \cond // C++98 Fundamental types @is_pod_specialize_98@ // C++11 Fundamental types @is_pod_specialize_11@ /// \endcond } } // END_CPPAD_LOCAL_NAMESPACE # endif ================================================ FILE: include/cppad/local/op_code_dyn.hpp ================================================ # ifndef CPPAD_LOCAL_OP_CODE_DYN_HPP # define CPPAD_LOCAL_OP_CODE_DYN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE /*! {xrst_begin op_code_dyn dev} {xrst_spell zmul } Dynamic Parameter Op Codes ########################## Namespace ********* The ``op_code_dyn`` enum type is in the ``CppAD::local`` namespace. AD Type ******* All the operators below have no variable arguments, at least one dynamic parameter argument, and at most one constant argument; see :ref:`ad_type_enum-name` . For example, all the unary operators have one dynamic parameter argument and one dynamic parameter result. Unary ***** The number of arguments for a unary operator is one and it is a parameter index. All the unary operators have one result that is a dynamic parameter. Binary ****** The number of arguments for a binary operator is two and they are parameter indices. All the binary operators have one result that is a dynamic parameter. For binary operators the first argument is the left operand and the second is the right operand. zmul_dyn ======== This binary operator has a non-standard name; see :ref:`azmul-name` for its definition. ind_dyn ******* This is an independent dynamic parameter operator. It has no arguments and one result which is the value of the corresponding independent dynamic parameter in the call to :ref:`new_dynamic-name` . {xrst_comment ------------------------------------------------------------ } atom_dyn ******** This operator is a call to an atomic function. The number of arguments to this operator is *arg* [4+ *n* + *m* ] ; see below. arg[0] ====== This is the index that identifies this atomic function; see ``local/atomic_index.hpp`` . arg[1] ====== This is the :ref:`atomic_four_call@call_id` for this function call. arg[2] ====== This is the number of arguments to this atomic function. We use the notation *n* = *arg* [2] below. arg[3] ====== This is the number of results for this atomic function. We use the notation *m* = *arg* [3] below. arg[4] ====== This is the number of result values that are dynamic parameters for this function call. arg[5+j] ======== For *j* = 0 , ... , *n* ``-1`` , this is the parameter index for the *j*-th argument to this atomic function call. arg[5+n+i] ========== For *i* = 0 , ... , *m* ``-1`` , this is the parameter index for the *i*-th result to this atomic function call. arg[5+n+m] ========== This is the number of arguments to this operator; i.e., 6+ *n* + *m* . result_dyn ********** This is a place holder for a result of an atomic function call that is a dynamic parameter. It has no arguments, no results, and is only there so that the number of dynamic parameters and the number of dynamic operators are equal. {xrst_comment ------------------------------------------------------------ } cond_exp_dyn ************ This is a conditional expression operator and has five arguments and one result. arg[0] ====== This is the :ref:`base_cond_exp@CompareOp` value for this operator. arg[1] ====== This is the parameter index for the left operand to the comparison. arg[2] ====== This is the parameter index for the right operand to the comparison. arg[3] ====== This is the index of the parameter equal to the operator result if the comparison result is true. arg[4] ====== This is the index of the parameter equal to the operator result if the comparison result is false. {xrst_comment ------------------------------------------------------------ } dis_dyn ******* This is a call to a discrete function. The discrete function has one argument and one result. This operator has two arguments and one result. It is not a binary operator because the first argument is not the index of a parameter. arg[0] ====== Is the discrete function index which depends on the *Base* type used when this function was recorded. arg[1] ====== Is the parameter index for the argument to the function. {xrst_comment ------------------------------------------------------------ } Source ****** {xrst_literal // BEGIN_OP_CODE_DYN // END_OP_CODE_DYN } {xrst_end op_code_dyn} */ // BEGIN_SORT_THIS_LINE_PLUS_3 // BEGIN_OP_CODE_DYN enum op_code_dyn { abs_dyn, // unary acos_dyn, // unary acosh_dyn, // unary add_dyn, // binary asin_dyn, // unary asinh_dyn, // unary atan_dyn, // unary atanh_dyn, // unary atom_dyn, // ? arguments: atomic function call cond_exp_dyn, // 5 arguments: conditional expression cos_dyn, // unary cosh_dyn, // unary dis_dyn, // 2 arguments: discrete function div_dyn, // binary erf_dyn, // unary erfc_dyn, // unary exp_dyn, // unary expm1_dyn, // unary fabs_dyn, // unary ind_dyn, // 0 arguments: independent parameter log1p_dyn, // unary log_dyn, // unary mul_dyn, // binary neg_dyn, // unary pow_dyn, // binary result_dyn, // 0 arguments: atomic function result sign_dyn, // unary sin_dyn, // unary sinh_dyn, // unary sqrt_dyn, // unary sub_dyn, // binary tan_dyn, // unary tanh_dyn, // unary zmul_dyn, // binary number_dyn // number of operator codes and invalid operator value }; // END_OP_CODE_DYN // END_SORT_THIS_LINE_MINUS_4 /* {xrst_begin num_arg_dyn dev} Number of Arguments to a Dynamic Parameter Operator ################################################### Syntax ****** | *n_arg* = ``local::num_arg_dyn`` ( *op* ) Prototype ********* {xrst_literal // BEGIN_NUM_ARG_DYN_PROTOTYPE // END_NUM_ARG_DYN_PROTOTYPE } Parallel Mode ************* This routine has static data so its first call cannot be in Parallel mode. op ** is the operator in question. n_arg ***** The return value is the number of arguments as commented in the :ref:`op_code_dyn@Source` for ``enum op_code_dyn`` . There is one exception: if *op* is ``atom_dyn`` , *n_arg* is zero; see :ref:`op_code_dyn@atom_dyn` for the true number of arguments in this case. atom_dyn ******** All of the dynamic parameter operators have a fixed number of arguments except for the :ref:`op_code_dyn@atom_dyn` operator which calls an atomic functions. In this special case the return value *n_arg* is zero which is not correct. {xrst_end num_arg_dyn} */ // BEGIN_NUM_ARG_DYN_PROTOTYPE inline size_t num_arg_dyn(op_code_dyn op) // END_NUM_ARG_DYN_PROTOTYPE { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; // BEGIN_SORT_THIS_LINE_PLUS_2 static const size_t num_arg_table[] = { /* abs_dyn */ 1, /* acos_dyn */ 1, /* acosh_dyn */ 1, /* add_dyn */ 2, /* asin_dyn */ 1, /* asinh_dyn */ 1, /* atan_dyn */ 1, /* atanh_dyn */ 1, /* atom_dyn */ 0, /* cond_exp_dyn */ 5, /* cos_dyn */ 1, /* cosh_dyn */ 1, /* dis_dyn */ 2, /* div_dyn */ 2, /* erf_dyn */ 1, /* erfc_dyn */ 1, /* exp_dyn */ 1, /* expm1_dyn */ 1, /* fabs_dyn */ 1, /* ind_dyn */ 0, /* log1p_dyn */ 1, /* log_dyn */ 1, /* mul_dyn */ 2, /* neg_dyn */ 1, /* pow_dyn */ 2, /* result_dyn */ 0, /* sign_dyn */ 1, /* sin_dyn */ 1, /* sinh_dyn */ 1, /* sqrt_dyn */ 1, /* sub_dyn */ 2, /* tan_dyn */ 1, /* tanh_dyn */ 1, /* zmul_dyn */ 2, 0 // number_dyn (not used) }; // END_SORT_THIS_LINE_MINUS_3 // static bool first = true; if( first ) { CPPAD_ASSERT_UNKNOWN( size_t(number_dyn)+1 == sizeof(num_arg_table)/sizeof(num_arg_table[0]) ); first = false; } return num_arg_table[op]; } /* {xrst_begin op_name_dyn dev} Number of Arguments to a Dynamic Parameter Operator ################################################### Syntax ****** *name* = ``local::op_name_dyn`` ( *op* ) Prototype ********* {xrst_literal // BEGIN_OP_NAME_DYN_PROTOTYPE // END_OP_NAME_DYN_PROTOTYPE } Parallel Mode ************* This routine has static data so its first call cannot be in Parallel mode. op ** is the operator in question. name **** The return value *name* is the same as the operator enum symbol (see :ref:`op_code_dyn@Source` for ``enum op_code_dyn`` ) without the ``_dyn`` at the end. For example, the name corresponding to the :ref:`op_code_dyn@cond_exp_dyn` operator is ``cond_exp`` . {xrst_end op_name_dyn} */ // BEGIN_OP_NAME_DYN_PROTOTYPE inline const char* op_name_dyn(op_code_dyn op) // END_OP_NAME_DYN_PROTOTYPE { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; // BEGIN_SORT_THIS_LINE_PLUS_2 static const char* op_name_table[] = { /* abs_dyn */ "abs", /* acos_dyn */ "acos", /* acosh_dyn */ "acosh", /* add_dyn */ "add", /* asin_dyn */ "asin", /* asinh_dyn */ "asinh", /* atan_dyn */ "atan", /* atanh_dyn */ "atanh", /* atom_dyn */ "call", /* cond_exp_dyn */ "cond_exp", /* cos_dyn */ "cos", /* cosh_dyn */ "cosh", /* dis_dyn */ "dis", /* div_dyn */ "div", /* erf_dyn */ "erf", /* erfc_dyn */ "erfc", /* exp_dyn */ "exp", /* expm1_dyn */ "expm1", /* fabs_dyn */ "fabs", /* ind_dyn */ "ind", /* log1p_dyn */ "log1p", /* log_dyn */ "log", /* mul_dyn */ "mul", /* neg_dyn */ "neg", /* pow_dyn */ "pow", /* result_dyn */ "result", /* sign_dyn */ "sign", /* sin_dyn */ "sin", /* sinh_dyn */ "sinh", /* sqrt_dyn */ "sqrt", /* sub_dyn */ "sub", /* tan_dyn */ "tan", /* tanh_dyn */ "tanh", /* zmul_dyn */ "zmul", /* number_dyn */ "number" }; // END_SORT_THIS_LINE_MINUS_3 static bool first = true; if( first ) { CPPAD_ASSERT_UNKNOWN( size_t(number_dyn)+1 == sizeof(op_name_table)/sizeof(op_name_table[0]) ); first = false; } return op_name_table[op]; } /* {xrst_begin num_non_par_arg_dyn dev} Number Non-Parameter Arguments to a Dynamic Parameters Operator ############################################################### Syntax ****** *num* = ``local::num_non_par_arg_dyn`` ( *op* ) Prototype ********* {xrst_literal // BEGIN_NUM_NON_PAR_ARG_DYN // END_NUM_NON_PAR_ARG_DYN } op ** is the operator in question. num *** The return value *num* is the number of arguments, for this operator *op* , that are not parameters indices. All of the non-parameter arguments come first so *num* is also the offset for the first argument that is a parameter index. Special Case ************ If :ref:`num_arg_dyn-name` ( *op* ) is zero and *num* is non-zero, this operator has a variable number of arguments. (The only such operator so far is ``atom_dyn`` .) In this case the last argument is not a parameter index and there are *num* - 1 arguments that are not parameter indices before the first parameter index. {xrst_end num_non_par_arg_dyn} */ // BEGIN_NUM_NON_PAR_ARG_DYN inline size_t num_non_par_arg_dyn(op_code_dyn op) // END_NUM_NON_PAR_ARG_DYN { size_t num; switch(op) { case atom_dyn: num = 6; break; case cond_exp_dyn: case dis_dyn: num = 1; break; default: num = 0; } // return num; } } } // END_CPPAD_LOCAL_NAMESPACE # endif ================================================ FILE: include/cppad/local/op_code_var.hpp ================================================ # ifndef CPPAD_LOCAL_OP_CODE_VAR_HPP # define CPPAD_LOCAL_OP_CODE_VAR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include # include # include // needed before one can use CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL # include namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE /*! {xrst_begin op_code_var dev} {xrst_spell funap funav funrp funrv ldp ldv opcode pri } Variable Op Codes ################# Namespace ********* All of these definitions are in the ``CppAD::local`` namespace. opcode_t ******** This type is used to save space when storing operator enum type in vectors. {xrst_spell_off} {xrst_code hpp} */ typedef CPPAD_VEC_ENUM_TYPE opcode_t; /* {xrst_code} {xrst_spell_on} op_code_var *********** This enum type is used to distinguish different ``AD`` < *Base* > atomic operations. Each value in the enum type ends with the characters ``Op`` . Ignoring the ``Op`` at the end, the operators appear in alphabetical order. arg[i] ****** We use the notation *arg* [ ``i`` ] below for the *i*-th operator argument which is a position integer represented using the type ``addr_t`` . {xrst_comment ------------------------------------------------------------- } Unary ***** see :ref:`var_unary_op-name` {xrst_comment ------------------------------------------------------------- } Binary ****** see :ref:`var_binary_op-name` {xrst_comment ------------------------------------------------------------- } {xrst_spell_off} EqppOp, LeppOp, LtppOp, NeppOp ****************************** see :ref:`var_compare_op@op_code@EqppOp, LeppOp, LtppOp, NeppOp` EqpvOp, LepvOp, LtpvOp, NepvOp ****************************** see :ref:`var_compare_op@op_code@EqpvOp, LepvOp, LtpvOp, NepvOp` LevpOp, LtvpOp ************** see :ref:`var_compare_op@op_code@LevpOp, LtvpOp` EqvvOp, LevvOp, LtvvOp, NevvOp ****************************** see :ref:`var_compare_op@op_code@EqvvOp, LevvOp, LtvvOp, NevvOp` {xrst_spell_on} {xrst_comment ------------------------------------------------------------- } AFunOp ****** see :ref:`var_atomic_op@AFunOp` FunapOp, FunavOp **************** see :ref:`var_atomic_op@FunapOp, FunavOp` FunrpOp, FunrvOp **************** see :ref:`var_atomic_op@FunrpOp, FunrvOp` {xrst_comment ------------------------------------------------------------- } BeginOp ******* This operator marks the start of the tape. It has one parameter index argument that is nan and corresponds to parameter index zero. It also has one variable result that has index zero which is used to indicate that a value is not a variable. for indicate an parameter. {xrst_comment ------------------------------------------------------------- } CExpOp ****** see :ref:`var_cexp_op@CExpOp` {xrst_comment ------------------------------------------------------------- } CSkipOp ******* see :ref:`var_cskip_op@CSkipOp` . {xrst_comment ------------------------------------------------------------- } CSumOp ****** see :ref:`var_csum_op@CSumOp` {xrst_comment ------------------------------------------------------------- } DisOp ***** see :ref:`var_dis_op@DisOp` {xrst_comment ------------------------------------------------------------- } LdpOp, LdvOp ============ see :ref:`var_load_op@LdpOp, LdvOp` {xrst_comment ------------------------------------------------------------- } {xrst_spell_off} StppOp, StpvOp, StvpOp, StvvOp ============================== see :ref:`var_store_op@StppOp, StpvOp, StvpOp, StvvOp` {xrst_spell_on} {xrst_comment ------------------------------------------------------------- } ParOp ===== see :ref:`var_par_op@ParOp` {xrst_comment ------------------------------------------------------------- } PriOp ***** see :ref:`var_pri_op@PriOp` {xrst_comment ------------------------------------------------------------- } {xrst_comment // BEGIN_SORT_THIS_LINE_PLUS_2 } {xrst_toc_table include/cppad/local/var_op/atomic_op.hpp include/cppad/local/var_op/binary_op.xrst include/cppad/local/var_op/cexp_op.hpp include/cppad/local/var_op/compare_op.hpp include/cppad/local/var_op/cskip_op.hpp include/cppad/local/var_op/csum_op.hpp include/cppad/local/var_op/dis_op.hpp include/cppad/local/var_op/load_op.hpp include/cppad/local/var_op/one_var.hpp include/cppad/local/var_op/par_op.hpp include/cppad/local/var_op/pri_op.hpp include/cppad/local/var_op/store_op.hpp include/cppad/local/var_op/two_var.hpp include/cppad/local/var_op/unary_op.xrst } {xrst_comment // END_SORT_THIS_LINE_MINUS_2 } Source ****** {xrst_spell_off} {xrst_code hpp} */ // BEGIN_SORT_THIS_LINE_PLUS_2 enum op_code_var { AFunOp, // see its heading above AbsOp, // unary fabs AcosOp, // unary acos AcoshOp, // unary acosh AddpvOp, // binary + AddvvOp, // ... AsinOp, // unary asin AsinhOp, // unary asinh AtanOp, // unary atan AtanhOp, // unary atanh BeginOp, // see its heading above CExpOp, // ... CSkipOp, // see its heading above CSumOp, // ... CosOp, // unary cos CoshOp, // unary cosh DisOp, // ... DivpvOp, // binary / DivvpOp, // ... DivvvOp, // ... EndOp, // used to mark the end of the tape EqppOp, // compare equal EqpvOp, // ... EqvvOp, // ... ErfOp, // unary erf ErfcOp, // unary erfc ExpOp, // unary exp Expm1Op, // unary expm1 FunapOp, // see AFun heading above FunavOp, // ... FunrpOp, // ... FunrvOp, // ... InvOp, // independent variable, no arguments, one result variable LdpOp, // see its heading above LdvOp, // ... LeppOp, // compare <= LepvOp, // ... LevpOp, // ... LevvOp, // ... Log1pOp, // unary log1p LogOp, // unary log LtppOp, // compare < LtpvOp, // ... LtvpOp, // ... LtvvOp, // ... MulpvOp, // binary * MulvvOp, // ... NegOp, // unary negative NeppOp, // compare != NepvOp, // ... NevvOp, // ... ParOp, // see its heading above PowpvOp, // see its heading above PowvpOp, // binary pow PowvvOp, // .. PriOp, // .. SignOp, // unary sign SinOp, // unary sin SinhOp, // unary sinh SqrtOp, // unary sqrt StppOp, // see its heading above StpvOp, // ... StvpOp, // ... StvvOp, // ... SubpvOp, // binary - SubvpOp, // ... SubvvOp, // ... TanOp, // unary tan TanhOp, // unary tanh ZmulpvOp, // binary azmul ZmulvpOp, // ... ZmulvvOp, // ... NumberOp // number of operator codes (not an operator) }; // END_SORT_THIS_LINE_MINUS_3 /* {xrst_code} {xrst_spell_on} {xrst_end op_code_var} */ // Note that bin/check_op_code.sh assumes the pattern NumberOp occurs // at the end of this list and only at the end of this list. /*! Number of arguments for a specified operator. \return Number of arguments corresponding to the specified operator. \param op Operator for which we are fetching the number of arguments. \par NumArgTable this table specifies the number of arguments stored for each occurrence of the operator that is the i-th value in the op_code_var enum type. For example, for the first three op_code_var enum values we have \verbatim op_code_var j NumArgTable[j] Meaning AbsOp 0 1 index of variable we are taking absolute value of AcosOp 1 1 index of variable we are taking acos of AcoshOp 2 1 index of variable we are taking acosh of \endverbatim Note that the meaning of the arguments depends on the operator. */ inline size_t NumArg( op_code_var op) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; // agreement with op_code_var is checked by bin/check_op_code.sh // BEGIN_SORT_THIS_LINE_PLUS_2 static const size_t NumArgTable[] = { /* AFunOp */ 4, /* AbsOp */ 1, /* AcosOp */ 1, /* AcoshOp */ 1, /* AddpvOp */ 2, /* AddvvOp */ 2, /* AsinOp */ 1, /* AsinhOp */ 1, /* AtanOp */ 1, /* AtanhOp */ 1, /* BeginOp */ 1, // offset first real argument to have index 1 /* CExpOp */ 6, /* CSkipOp */ 0, // (has a variable number of arguments, not zero) /* CSumOp */ 0, // (has a variable number of arguments, not zero) /* CosOp */ 1, /* CoshOp */ 1, /* DisOp */ 2, /* DivpvOp */ 2, /* DivvpOp */ 2, /* DivvvOp */ 2, /* EndOp */ 0, /* EqppOp */ 2, /* EqpvOp */ 2, /* EqvvOp */ 2, /* ErfOp */ 3, /* ErfcOp */ 3, /* ExpOp */ 1, /* Expm1Op */ 1, /* FunapOp */ 1, /* FunavOp */ 1, /* FunrpOp */ 1, /* FunrvOp */ 0, /* InvOp */ 0, /* LdpOp */ 3, /* LdvOp */ 3, /* LeppOp */ 2, /* LepvOp */ 2, /* LevpOp */ 2, /* LevvOp */ 2, /* Log1pOp */ 1, /* LogOp */ 1, /* LtppOp */ 2, /* LtpvOp */ 2, /* LtvpOp */ 2, /* LtvvOp */ 2, /* MulpvOp */ 2, /* MulvvOp */ 2, /* NegOp */ 1, /* NeppOp */ 2, /* NepvOp */ 2, /* NevvOp */ 2, /* ParOp */ 1, /* PowpvOp */ 2, /* PowvpOp */ 2, /* PowvvOp */ 2, /* PriOp */ 5, /* SignOp */ 1, /* SinOp */ 1, /* SinhOp */ 1, /* SqrtOp */ 1, /* StppOp */ 3, /* StpvOp */ 3, /* StvpOp */ 3, /* StvvOp */ 3, /* SubpvOp */ 2, /* SubvpOp */ 2, /* SubvvOp */ 2, /* TanOp */ 1, /* TanhOp */ 1, /* ZmulpvOp */ 2, /* ZmulvpOp */ 2, /* ZmulvvOp */ 2, 0 // NumberOp not used }; // END_SORT_THIS_LINE_MINUS_3 # ifndef NDEBUG // only do these checks once to save time static bool first = true; if( first ) { first = false; // check that NumberOp is last value in op code table CPPAD_ASSERT_UNKNOWN( size_t(NumberOp) + 1 == sizeof(NumArgTable)/sizeof(NumArgTable[0]) ); //Check that the type CPPAD_VEC_ENUM_TYPE as required by define.hpp CPPAD_ASSERT_UNKNOWN( is_pod() ); size_t number_op_size_t = size_t( NumberOp ); CPPAD_ASSERT_UNKNOWN( number_op_size_t < std::numeric_limits::max() ); } // do this check every time CPPAD_ASSERT_UNKNOWN( size_t(op) < size_t(NumberOp) ); # endif return NumArgTable[op]; } /*! Number of variables resulting from the specified operation. \param op Operator for which we are fecching the number of results. \par NumResTable table specifies the number of variables that result for each occurrence of the operator that is the i-th value in the op_code_var enum type. For example, for the first three op_code_var enum values we have \verbatim op_code_var j NumResTable[j] Meaning AbsOp 0 1 variable that is the result of the absolute value AcosOp 1 2 acos(x) and sqrt(1-x*x) are required for this op AcoshOp 2 2 acosh(x) and sqrt(x*x-1) are required for this op \endverbatim */ inline size_t NumRes(op_code_var op) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; // agreement with op_code_var is checked by bin/check_op_code.sh // BEGIN_SORT_THIS_LINE_PLUS_2 static const size_t NumResTable[] = { /* AFunOp */ 0, /* AbsOp */ 1, /* AcosOp */ 2, /* AcoshOp */ 2, /* AddpvOp */ 1, /* AddvvOp */ 1, /* AsinOp */ 2, /* AsinhOp */ 2, /* AtanOp */ 2, /* AtanhOp */ 2, /* BeginOp */ 1, // offsets first variable to have index one (not zero) /* CExpOp */ 1, /* CSkipOp */ 0, /* CSumOp */ 1, /* CosOp */ 2, /* CoshOp */ 2, /* DisOp */ 1, /* DivpvOp */ 1, /* DivvpOp */ 1, /* DivvvOp */ 1, /* EndOp */ 0, /* EqppOp */ 0, /* EqpvOp */ 0, /* EqvvOp */ 0, /* ErfOp */ 5, /* ErfcOp */ 5, /* ExpOp */ 1, /* Expm1Op */ 1, /* FunapOp */ 0, /* FunavOp */ 0, /* FunrpOp */ 0, /* FunrvOp */ 1, /* InvOp */ 1, /* LdpOp */ 1, /* LdvOp */ 1, /* LeppOp */ 0, /* LepvOp */ 0, /* LevpOp */ 0, /* LevvOp */ 0, /* Log1pOp */ 1, /* LogOp */ 1, /* LtppOp */ 0, /* LtpvOp */ 0, /* LtvpOp */ 0, /* LtvvOp */ 0, /* MulpvOp */ 1, /* MulvvOp */ 1, /* NegOp */ 1, /* NeppOp */ 0, /* NepvOp */ 0, /* NevvOp */ 0, /* ParOp */ 1, /* PowpvOp */ 3, /* PowvpOp */ 1, /* PowvvOp */ 3, /* PriOp */ 0, /* SignOp */ 1, /* SinOp */ 2, /* SinhOp */ 2, /* SqrtOp */ 1, /* StppOp */ 0, /* StpvOp */ 0, /* StvpOp */ 0, /* StvvOp */ 0, /* SubpvOp */ 1, /* SubvpOp */ 1, /* SubvvOp */ 1, /* TanOp */ 2, /* TanhOp */ 2, /* ZmulpvOp */ 1, /* ZmulvpOp */ 1, /* ZmulvvOp */ 1, 0 // NumberOp not used and avoids g++ 4.3.2 warn when pycppad builds }; // END_SORT_THIS_LINE_MINUS_3 // check ensuring conversion to size_t is as expected CPPAD_ASSERT_UNKNOWN( size_t(NumberOp) + 1 == sizeof(NumResTable) / sizeof(NumResTable[0]) ); // this test ensures that all indices are within the table CPPAD_ASSERT_UNKNOWN( size_t(op) < size_t(NumberOp) ); return NumResTable[op]; } /*! Fetch the name for a specified operation. \return name of the specified operation. \param op Operator for which we are fetching the name */ inline std::string OpName(op_code_var op) { // agreement with op_code_var is checked by bin/check_op_code.sh // BEGIN_SORT_THIS_LINE_PLUS_2 static const char *OpNameTable[] = { "AFunOp" , "AbsOp" , "AcosOp" , "AcoshOp" , "AddpvOp" , "AddvvOp" , "AsinOp" , "AsinhOp" , "AtanOp" , "AtanhOp" , "BeginOp" , "CExpOp" , "CSkipOp" , "CSumOp" , "CosOp" , "CoshOp" , "DisOp" , "DivpvOp" , "DivvpOp" , "DivvvOp" , "EndOp" , "EqppOp" , "EqpvOp" , "EqvvOp" , "ErfOp" , "ErfcOp" , "ExpOp" , "Expm1Op" , "FunapOp" , "FunavOp" , "FunrpOp" , "FunrvOp" , "InvOp" , "LdpOp" , "LdvOp" , "LeppOp" , "LepvOp" , "LevpOp" , "LevvOp" , "Log1pOp" , "LogOp" , "LtppOp" , "LtpvOp" , "LtvpOp" , "LtvvOp" , "MulpvOp" , "MulvvOp" , "NegOp" , "NeppOp" , "NepvOp" , "NevvOp" , "ParOp" , "PowpvOp" , "PowvpOp" , "PowvvOp" , "PriOp" , "SignOp" , "SinOp" , "SinhOp" , "SqrtOp" , "StppOp" , "StpvOp" , "StvpOp" , "StvvOp" , "SubpvOp" , "SubvpOp" , "SubvvOp" , "TanOp" , "TanhOp" , "ZmulpvOp", "ZmulvpOp", "ZmulvvOp", "Number" // not used }; // END_SORT_THIS_LINE_MINUS_3 // check ensuring conversion to size_t is as expected CPPAD_ASSERT_UNKNOWN( size_t(NumberOp) + 1 == sizeof(OpNameTable)/sizeof(OpNameTable[0]) ); // this test ensures that all indices are within the table CPPAD_ASSERT_UNKNOWN( size_t(op) < size_t(NumberOp) ); // result std::string result = OpNameTable[op]; result = result.substr(0, result.size() - 2); return result; } /*! Prints a single field corresponding to an operator. A specified leader is printed in front of the value and then the value is left justified in the following width character. \tparam Type is the type of the value we are printing. \param os is the stream that we are printing to. \param leader are characters printed before the value. \param value is the value being printed. \param width is the number of character to print the value in. If the value does not fit in the width, the value is replace by width '*' characters. */ template void printOpField( std::ostream &os , const char * leader , const Type &value , size_t width ) { std::ostringstream buffer; std::string str; // first print the leader os << leader; // print the value into an internal buffer buffer << value; str = buffer.str(); // length of the string size_t len = str.size(); if( len > width ) { for(size_t i = 0; i < width-1; i++) os << str[i]; os << "*"; return; } // count number of spaces at beginning size_t nspace = 0; while(str[nspace] == ' ' && nspace < len) nspace++; // left justify the string size_t i = nspace; while( i < len ) os << str[i++]; i = width - len + nspace; while(i--) os << " "; } /*! Prints a single operator and its operands \tparam Base Is the base type for these AD< Base > operations. \param os is the output stream that the information is printed on. \param play Is the entire recording for the tape that this operator is in. \param i_op is the index for the operator corresponding to this operation. \param i_var is the index for the variable corresponding to the result of this operation (if NumRes(op) > 0). \param op The operator code (op_code_var) for this operation. \param arg is the vector of argument indices for this operation (must have NumArg(op) elements). */ template void printOp( std::ostream& os , const local::player* play, size_t i_op , size_t i_var , op_code_var op , const addr_t* arg ) { CPPAD_ASSERT_KNOWN( ! thread_alloc::in_parallel() , "cannot print trace of AD operations in parallel mode" ); static const char *CompareOpName[] = { "Lt", "Le", "Eq", "Ge", "Gt", "Ne" }; // print operator printOpField(os, "o=", i_op, 5); if( NumRes(op) > 0 && op != BeginOp ) printOpField(os, "v=", i_var, 5); else printOpField(os, "v=", "", 5); if( op == CExpOp || op == CSkipOp ) { printOpField(os, "", OpName(op).c_str(), 5); printOpField(os, "", CompareOpName[ arg[0] ], 3); } else printOpField(os, "", OpName(op).c_str(), 8); // print other fields size_t ncol = 5; switch( op ) { case CSkipOp: /* arg[0] = the Rel operator: Lt, Le, Eq, Ge, Gt, or Ne arg[1] & 1 = is left a variable arg[1] & 2 = is right a variable arg[2] = index corresponding to left arg[3] = index corresponding to right arg[4] = number of operations to skip if CExpOp comparison is true arg[5] = number of operations to skip if CExpOp comparison is false arg[6] -> arg[5+arg[4]] = skip operations if true arg[6+arg[4]] -> arg[5+arg[4]+arg[5]] = skip operations if false arg[6+arg[4]+arg[5]] = 6+arg[4]+arg[5]+1 */ CPPAD_ASSERT_UNKNOWN( arg[6+arg[4]+arg[5]] == 6+arg[4]+arg[5]+1 ); CPPAD_ASSERT_UNKNOWN(arg[1] != 0); if( arg[1] & 1 ) printOpField(os, " vl=", arg[2], ncol); else printOpField(os, " pl=", play->par_one( size_t(arg[2]) ), ncol); if( arg[1] & 2 ) printOpField(os, " vr=", arg[3], ncol); else printOpField(os, " pr=", play->par_one( size_t(arg[3]) ), ncol); if( size_t(arg[4]) < 3 ) { for(addr_t i = 0; i < arg[4]; i++) printOpField(os, " ot=", arg[6+i], ncol); } else { printOpField(os, "\n\tot=", arg[6+0], ncol); for(addr_t i = 1; i < arg[4]; i++) printOpField(os, " ot=", arg[6+i], ncol); } if( size_t(arg[5]) < 3 ) { for(addr_t i = 0; i < arg[5]; i++) printOpField(os, " of=", arg[6+arg[4]+i], ncol); } else { printOpField(os, "\n\tof=", arg[6+arg[4]+0], ncol); { for(addr_t i = 1; i < arg[5]; i++) printOpField(os, " of=", arg[6+arg[4]+i], ncol); } } break; case CSumOp: /* arg[0] = index of parameter that initializes summation arg[1] = end in arg of addition variables in summation arg[2] = end in arg of subtraction variables in summation arg[3] = end in arg of addition dynamic parameters in summation arg[4] = end in arg of subtraction dynamic parameters in summation arg[5], ... , arg[arg[1]-1]: indices for addition variables arg[arg[1]], ... , arg[arg[2]-1]: indices for subtraction variables arg[arg[2]], ... , arg[arg[3]-1]: indices for addition dynamics arg[arg[3]], ... , arg[arg[4]-1]: indices for subtraction dynamics arg[arg[4]] = arg[4] + 1 */ CPPAD_ASSERT_UNKNOWN( arg[arg[4]] == arg[4] + 1 ); printOpField(os, " pr=", play->par_one( size_t(arg[0]) ), ncol); for(addr_t i = 5; i < arg[1]; i++) printOpField(os, " +v=", arg[i], ncol); for(addr_t i = arg[1]; i < arg[2]; i++) printOpField(os, " -v=", arg[i], ncol); for(addr_t i = arg[2]; i < arg[3]; i++) printOpField(os, " +d=", play->par_one( size_t(arg[i]) ), ncol); for(addr_t i = arg[3]; i < arg[4]; i++) printOpField(os, " -d=", play->par_one( size_t(arg[i]) ), ncol); break; case LdpOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); printOpField(os, "off=", arg[0], ncol); printOpField(os, " p=", play->par_one( size_t(arg[1]) ), ncol); break; case LdvOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); printOpField(os, "off=", arg[0], ncol); printOpField(os, " v=", arg[1], ncol); break; case StppOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); printOpField(os, "off=", arg[0], ncol); printOpField(os, " pl=", play->par_one( size_t(arg[1]) ), ncol); printOpField(os, " pr=", play->par_one( size_t(arg[2]) ), ncol); break; case StpvOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); printOpField(os, "off=", arg[0], ncol); printOpField(os, " p=", play->par_one( size_t(arg[1]) ), ncol); printOpField(os, " v=", arg[2], ncol); break; case StvpOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); printOpField(os, "off=", arg[0], ncol); printOpField(os, " v=", arg[1], ncol); printOpField(os, " p=", play->par_one( size_t(arg[2]) ), ncol); break; case StvvOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); printOpField(os, "off=", arg[0], ncol); printOpField(os, " vl=", arg[1], ncol); printOpField(os, " vr=", arg[2], ncol); break; case AddvvOp: case DivvvOp: case EqvvOp: case LevvOp: case LtvvOp: case NevvOp: case MulvvOp: case PowvvOp: case SubvvOp: case ZmulvvOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 2 ); printOpField(os, " vl=", arg[0], ncol); printOpField(os, " vr=", arg[1], ncol); break; case AddpvOp: case EqpvOp: case DivpvOp: case LepvOp: case LtpvOp: case NepvOp: case SubpvOp: case MulpvOp: case PowpvOp: case ZmulpvOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 2 ); printOpField(os, " pl=", play->par_one( size_t(arg[0]) ), ncol); printOpField(os, " vr=", arg[1], ncol); break; case DivvpOp: case LevpOp: case LtvpOp: case PowvpOp: case SubvpOp: case ZmulvpOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 2 ); printOpField(os, " vl=", arg[0], ncol); printOpField(os, " pr=", play->par_one( size_t(arg[1]) ), ncol); break; case AbsOp: case AcosOp: case AcoshOp: case AsinOp: case AsinhOp: case AtanOp: case AtanhOp: case CosOp: case CoshOp: case ExpOp: case Expm1Op: case LogOp: case Log1pOp: case NegOp: case SignOp: case SinOp: case SinhOp: case SqrtOp: case FunavOp: case TanOp: case TanhOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 1 ); printOpField(os, " v=", arg[0], ncol); break; case ErfOp: case ErfcOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); // arg[1] points to the parameter 0 // arg[2] points to the parameter 2 / sqrt(pi) printOpField(os, " v=", arg[0], ncol); break; case ParOp: case FunapOp: case FunrpOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 1 ); printOpField(os, " p=", play->par_one( size_t(arg[0]) ), ncol); break; case AFunOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 4 ); { // get the name of this atomic function bool set_null = false; size_t atom_index = size_t( arg[0] ); size_t type = 0; // set to avoid warning std::string name; void* v_ptr = nullptr; // set to avoid warning atomic_index(set_null, atom_index, type, &name, v_ptr); printOpField(os, " f=", name.c_str(), ncol); printOpField(os, " i=", arg[1], ncol); printOpField(os, " n=", arg[2], ncol); printOpField(os, " m=", arg[3], ncol); } break; case PriOp: CPPAD_ASSERT_NARG_NRES(op, 5, 0); if( arg[0] & 1 ) printOpField(os, " v=", arg[1], ncol); else printOpField(os, " p=", play->par_one( size_t(arg[1]) ), ncol); os << "before=\"" << play->GetTxt( size_t(arg[2]) ) << "\""; if( arg[0] & 2 ) printOpField(os, " v=", arg[3], ncol); else printOpField(os, " p=", play->par_one( size_t(arg[3]) ), ncol); os << "after=\"" << play->GetTxt( size_t(arg[4]) ) << "\""; break; case BeginOp: // argument not used (created by independent) CPPAD_ASSERT_UNKNOWN( NumArg(op) == 1 ); break; case EndOp: case InvOp: case FunrvOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 0 ); break; case DisOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 2 ); { const std::string name = discrete::name( size_t(arg[0]) ); printOpField(os, " f=", name.c_str(), ncol); printOpField(os, " x=", arg[1], ncol); } break; case CExpOp: CPPAD_ASSERT_UNKNOWN(arg[1] != 0); CPPAD_ASSERT_UNKNOWN( NumArg(op) == 6 ); if( arg[1] & 1 ) printOpField(os, " vl=", arg[2], ncol); else printOpField(os, " pl=", play->par_one( size_t(arg[2]) ), ncol); if( arg[1] & 2 ) printOpField(os, " vr=", arg[3], ncol); else printOpField(os, " pr=", play->par_one( size_t(arg[3]) ), ncol); if( arg[1] & 4 ) printOpField(os, " vt=", arg[4], ncol); else printOpField(os, " pt=", play->par_one( size_t(arg[4]) ), ncol); if( arg[1] & 8 ) printOpField(os, " vf=", arg[5], ncol); else printOpField(os, " pf=", play->par_one( size_t(arg[5]) ), ncol); break; case EqppOp: case LeppOp: case LtppOp: case NeppOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 2 ); printOpField(os, " pl=", play->par_one( size_t(arg[0]) ), ncol); printOpField(os, " pr=", play->par_one( size_t(arg[1]) ), ncol); break; default: CPPAD_ASSERT_UNKNOWN(0); } } /*! Prints the result values corresponding to an operator. \tparam Base Is the base type for these AD< Base > operations. \tparam Value Determines the type of the values that we are printing. \param os is the output stream that the information is printed on. \param nfz is the number of forward sweep calculated values of type Value that correspond to this operation (ignored if NumRes(op) == 0). \param fz points to the first forward calculated value that correspond to this operation (ignored if NumRes(op) == 0). \param nrz is the number of reverse sweep calculated values of type Value that correspond to this operation (ignored if NumRes(op) == 0). \param rz points to the first reverse calculated value that correspond to this operation (ignored if NumRes(op) == 0). */ template void printOpResult( std::ostream &os , size_t nfz , const Value *fz , size_t nrz , const Value *rz ) { size_t k; for(k = 0; k < nfz; k++) os << "| fz[" << k << "]=" << fz[k]; for(k = 0; k < nrz; k++) os << "| rz[" << k << "]=" << rz[k]; } /*! Determines which arguments are variaibles for an operator. \param op is the operator. Note that CSkipOp and CSumOp are special cases because the true number of arguments is not equal to NumArg(op) and the true number of arguments num_arg can be large. It may be more efficient to handle these cases separately (see below). \param arg is the argument vector for this operator. \param is_variable If the input value of the elements in this vector do not matter. Upon return, resize has been used to set its size to the true number of arguments to this operator. If op != CSkipOp and op != CSumOp, is_variable.size() = NumArg(op). The j-th argument for this operator is a variable index if and only if is_variable[j] is true. Note that the variable index 0, for the BeginOp, does not correspond to a real variable and false is returned for this case. \par CSkipOp In the case of CSkipOp, \code is_variable.size() = 7 + arg[4] + arg[5]; is_variable[2] = (arg[1] & 1) != 0; is_variable[3] = (arg[1] & 2) != 0; \endcode and all the other is_variable[j] values are false. \par CSumOp In the case of CSumOp, \code is_variable.size() = arg[4] for(size_t j = 5; j < arg[2]; ++j) is_variable[j] = true; \endcode and all the other is_variable values are false. */ template void arg_is_variable( op_code_var op , const Addr* arg , pod_vector& is_variable ) { size_t num_arg = NumArg(op); is_variable.resize( num_arg ); // switch(op) { // ------------------------------------------------------------------- // cases where true number of arguments = NumArg(op) == 0 case EndOp: case InvOp: case FunrvOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 0 ); break; // ------------------------------------------------------------------- // cases where NumArg(op) == 1 case AbsOp: case AcoshOp: case AcosOp: case AsinhOp: case AsinOp: case AtanhOp: case AtanOp: case CoshOp: case CosOp: case Expm1Op: case ExpOp: case Log1pOp: case LogOp: case NegOp: case SignOp: case SinhOp: case SinOp: case SqrtOp: case TanhOp: case TanOp: case FunavOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 1 ); is_variable[0] = true; break; case BeginOp: case ParOp: case FunapOp: case FunrpOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 1 ); is_variable[0] = false; break; // ------------------------------------------------------------------- // cases where NumArg(op) == 2 case AddpvOp: case DisOp: case DivpvOp: case EqpvOp: case LepvOp: case LtpvOp: case MulpvOp: case NepvOp: case PowpvOp: case SubpvOp: case ZmulpvOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 2 ); is_variable[0] = false; is_variable[1] = true; break; case DivvpOp: case LevpOp: case LtvpOp: case PowvpOp: case SubvpOp: case ZmulvpOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 2 ); is_variable[0] = true; is_variable[1] = false; break; case AddvvOp: case DivvvOp: case EqvvOp: case LevvOp: case LtvvOp: case MulvvOp: case NevvOp: case PowvvOp: case SubvvOp: case ZmulvvOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 2 ); is_variable[0] = true; is_variable[1] = true; break; case ErfOp: case ErfcOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); is_variable[0] = true; is_variable[1] = false; // parameter index corresponding to zero is_variable[2] = false; // parameter index corresponding to one break; // -------------------------------------------------------------------- // cases where NumArg(op) == 3 case LdpOp: case StppOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); is_variable[0] = false; is_variable[1] = false; is_variable[2] = false; break; case LdvOp: case StvpOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); is_variable[0] = false; is_variable[1] = true; is_variable[2] = false; break; case StpvOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); is_variable[0] = false; is_variable[1] = false; is_variable[2] = true; break; case StvvOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); is_variable[0] = false; is_variable[1] = true; is_variable[2] = true; break; // -------------------------------------------------------------------- // case where NumArg(op) == 4 case AFunOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 4 ); for(size_t i = 0; i < 4; i++) is_variable[i] = false; break; // -------------------------------------------------------------------- // case where NumArg(op) == 5 case PriOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 5 ); is_variable[0] = false; is_variable[1] = (arg[0] & 1) != 0; is_variable[2] = false; is_variable[3] = (arg[0] & 2) != 0; is_variable[4] = false; break; // -------------------------------------------------------------------- // case where NumArg(op) == 6 case CExpOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 6 ); is_variable[0] = false; is_variable[1] = false; is_variable[2] = (arg[1] & 1) != 0; is_variable[3] = (arg[1] & 2) != 0; is_variable[4] = (arg[1] & 4) != 0; is_variable[5] = (arg[1] & 8) != 0; break; // ------------------------------------------------------------------- // CSkipOp: case CSkipOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 0 ) // // true number of arguments num_arg = size_t(7 + arg[4] + arg[5]); is_variable.resize(num_arg); is_variable[0] = false; is_variable[1] = false; is_variable[2] = (arg[1] & 1) != 0; is_variable[3] = (arg[1] & 2) != 0; for(size_t i = 4; i < num_arg; ++i) is_variable[i] = false; break; // ------------------------------------------------------------------- // CSumOp: case CSumOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 0 ) // // true number of arguments num_arg = size_t(arg[4] + 1); // is_variable.resize( num_arg ); for(size_t i = 0; i < num_arg; ++i) is_variable[i] = (5 <= i) && (i < size_t(arg[2])); break; case EqppOp: case LeppOp: case LtppOp: case NeppOp: CPPAD_ASSERT_UNKNOWN( NumArg(op) == 2 ); is_variable[0] = false; is_variable[1] = false; break; // -------------------------------------------------------------------- default: CPPAD_ASSERT_UNKNOWN(false); break; } return; } } } // END_CPPAD_LOCAL_NAMESPACE # endif ================================================ FILE: include/cppad/local/optimize/cexp_info.hpp ================================================ # ifndef CPPAD_LOCAL_OPTIMIZE_CEXP_INFO_HPP # define CPPAD_LOCAL_OPTIMIZE_CEXP_INFO_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include // defines CompareOp # include /*! {xrst_begin optimize_cexp_info dev} {xrst_spell cskip struct } Optimization Information About Conditional Expressions ###################################################### struct_cexp_info **************** information about a conditional expression in the old operation sequence (before optimization). {xrst_literal // BEGIN_STRUCT_CEXP_INFO // END_STRUCT_CEXP_INFO } i_op ==== is the operator index for this conditional expression. left ==== is the variable or parameter index (depending on flag) for left operand in the comparison. right ===== is the variable or parameter index (depending on flag) for right operand in the comparison. max_left_right ============== is the maximum of the left and right variable indices. This is a variable index, so parameters correspond to index zero. cop === is the comparison operator for this conditional expression. flag ==== #. (flag & 1) is true if and only if left is a variable #. (flag & 2) is true if and only if right is a variable struct_cskip_new **************** information about a conditional expression in thew new operation sequence (after optimization). {xrst_literal // BEGIN_STRUCT_CSKIP_NEW // END_STRUCT_CSKIP_NEW } left ==== is the variable or parameter index (depending on flag) for left operand in the comparison. right ===== is the variable or parameter index (depending on flag) for right operand in the comparison. max_left_right ============== is the maximum of the left and right variable indices. This is a variable index, so parameters correspond to index zero. i_arg ===== index where this conditional skips arguments start (in the vector or arguments for all operators). {xrst_end optimize_cexp_info} */ // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE namespace CppAD { namespace local { namespace optimize { /*! Information about one conditional expression. */ // BEGIN_STRUCT_CEXP_INFO struct struct_cexp_info { addr_t i_op; addr_t left; addr_t right; addr_t max_left_right; CompareOp cop; unsigned char flag; }; // END_STRUCT_CEXP_INFO // BEGIN_STRUCT_CSKIP_NEW struct struct_cskip_new { size_t left; size_t right; size_t max_left_right; size_t i_arg; }; // END_STRUCT_CSKIP_NEW } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE namespace CppAD { namespace local { template <> inline bool is_pod(void) { return true; } } } # endif ================================================ FILE: include/cppad/local/optimize/csum_op_info.hpp ================================================ # ifndef CPPAD_LOCAL_OPTIMIZE_CSUM_OP_INFO_HPP # define CPPAD_LOCAL_OPTIMIZE_CSUM_OP_INFO_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include // defines addr_t /*! \file csum_op_info.hpp Information about one old variable that is part of a new CSumOp operation. */ // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE namespace CppAD { namespace local { namespace optimize { /*! Information about one old variable that is part of a new CSumOp operation. */ struct struct_csum_op_info { /// Pointer to first argument (child) for this old operator. /// Set by the reverse sweep at beginning of optimization. const addr_t* arg; /// Was this old variable added to the summation /// (if not it was subtracted) bool add; /// Operator for which this old variable is the result, NumRes(op) > 0. op_code_var op; }; } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE # endif ================================================ FILE: include/cppad/local/optimize/csum_stacks.hpp ================================================ # ifndef CPPAD_LOCAL_OPTIMIZE_CSUM_STACKS_HPP # define CPPAD_LOCAL_OPTIMIZE_CSUM_STACKS_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include /*! \file csum_stacks.hpp Information about one cumulative summation operation. */ // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE namespace CppAD { namespace local { namespace optimize { /*! Information about one cumulative summation operation. */ struct struct_csum_stacks { /// old operator indices for this cumulative summation std::stack op_info; /// old variable indices to be added std::stack add_var; /// old variable indices to be subtracted std::stack sub_var; /// dynamic parameter indices to be added std::stack add_dyn; /// dynamic parameter indices to be subtracted std::stack sub_dyn; }; } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE # endif ================================================ FILE: include/cppad/local/optimize/extract_option.hpp ================================================ # ifndef CPPAD_LOCAL_OPTIMIZE_EXTRACT_OPTION_HPP # define CPPAD_LOCAL_OPTIMIZE_EXTRACT_OPTION_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /*! {xrst_begin optimize_extract_option dev} {xrst_spell struct } Convert Optimizer Options From String to Struct ############################################### Prototype ********* {xrst_literal // BEGIN_OPTIMIZE_OPTIONS // END_OPTIMIZE_OPTIONS } result ****** The return value, *result* below, has the following type {xrst_literal // BEGIN_OPTIONS_T // END_OPTIONS_T } options ******* See :ref:`optimize@options` {xrst_end optimize_extract_option} */ # include // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE namespace CppAD { namespace local { namespace optimize { // BEGIN_SORT_THIS_LINE_PLUS_3 // BEGIN_OPTIONS_T struct options_t { bool compare_op; bool conditional_skip; bool cumulative_sum_op; bool print_for_op; bool val_graph; size_t collision_limit; }; // END_OPTIONS_T // END_SORT_THIS_LINE_MINUS_3 // BEGIN_OPTIMIZE_OPTIONS inline options_t extract_option(const std::string& options) // END_OPTIMIZE_OPTIONS { // // result: default value struct options_t result = { true, // compare_op true, // conditional_skip true, // cumulative_sum_op true, // print_for_op false, // val_graph 10 // collision_limit }; size_t index = 0; while( index < options.size() ) { while( index < options.size() && options[index] == ' ' ) ++index; std::string option; while( index < options.size() && options[index] != ' ' ) option += options[index++]; if( option != "" ) { if( option == "no_compare_op" ) result.compare_op = false; else if( option == "no_conditional_skip" ) result.conditional_skip = false; else if( option == "no_cumulative_sum_op" ) result.cumulative_sum_op = false; else if( option == "no_print_for_op" ) result.print_for_op = false; else if( option == "val_graph" ) result.val_graph = true; else if( option.substr(0, 16) == "collision_limit=" ) { std::string value = option.substr(16, option.size()); bool value_ok = value.size() > 0; for(size_t i = 0; i < value.size(); ++i) { value_ok &= '0' <= value[i]; value_ok &= value[i] <= '9'; } if( ! value_ok ) { option += " value is not a sequence of decimal digits"; CPPAD_ASSERT_KNOWN( false , option.c_str() ); } result.collision_limit = size_t( std::atoi( value.c_str() ) ); if( result.collision_limit < 1 ) { option += " value must be greater than zero"; CPPAD_ASSERT_KNOWN( false , option.c_str() ); } } else { option += " is not a valid optimize option"; CPPAD_ASSERT_KNOWN( false , option.c_str() ); } } } return result; } } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE # endif ================================================ FILE: include/cppad/local/optimize/get_cexp_info.hpp ================================================ # ifndef CPPAD_LOCAL_OPTIMIZE_GET_CEXP_INFO_HPP # define CPPAD_LOCAL_OPTIMIZE_GET_CEXP_INFO_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE namespace CppAD { namespace local { namespace optimize { /*! {xrst_begin optimize_get_cexp_info.hpp dev} {xrst_spell deallocate funap funav funrp funrv } Information for Each Conditional Expression ########################################### Syntax ****** | ``get_cexp_info`` ( | |tab| *play* , | |tab| *random_itr* , | |tab| *op_previous* , | |tab| *op_usage* , | |tab| *cexp2op* , | |tab| *cexp_set* , | |tab| *cexp_info* , | |tab| *skip_op_true* , | |tab| *skip_op_false* | ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } Restrictions ************ Do not call this routine unless you are optimizing conditional expressions and there are conditional expressions in the operation sequence. Base **** base type for the operator; i.e., this operation was recorded using AD and computations by this routine are done using type Base. play **** This is the old operation sequence. random_itr ********** This is a random iterator for the old operation sequence. cexp2op ******* This is the number of conditional expressions in the operation sequence and must be non-zero. cexp_set ******** This is a vector of sets, the i-th set, set[i], is a set of elements for the i-th operator. If e is an element of set[i], let j = e / 2 and k = e % 2. If the comparison for the j-th conditional expression is equal to bool(k), the i-th operator can be skipped (is not used by any of the results). Note that j indexes the subset of operators that are conditional expressions in the old operation sequence. cexp_info ********* The input size of this vector must be zero. Upon return cexp_info has size equal to the number of conditional expressions in the operation sequence; i.e., the number of CExpOp operators. The value cexp_info[j] is the information corresponding to the j-th conditional expression in the operation sequence. This vector is in the same order as the operation sequence; i.e. if j1 > j2, cexp_info[j1].i_op > cexp_info[j2].i_op. Note that skip_op_true and skip_op_false could be part of this structure, but then we would allocate and deallocate two vectors for each conditional expression in the operation sequence. skip_op_true ************ This vector of sets is empty on input. Upon return, the j-th set is the operators that are not used when comparison result for cexp_info[j] is true. Note that FunapOp, FunavOp, FunrpOp, and FunrvOp, are not in this set and should be skipped when the corresponding AFunOp are skipped. skip_op_false ************* This vector of sets is empty on input. Upon return, the j-th set is the operators that are not used when comparison result for cexp_info[j] is false. Note that FunapOp, FunavOp, FunrpOp, and FunrvOp, are not in this set and should be skipped when the corresponding AFunOp are skipped. op_previous *********** This argument has size equal to the number of operators in the operation sequence; i.e., num_op = play->nun_var_rec(). If op_previous[i] == 0, no replacement was found for the i-th operator. If op_previous[i] != 0, op_usage[ op_previous[i] ] == usage_t(yes_usage). op_usage ******** This argument has size equal to the number of operators in the operation sequence; i.e., num_op = play->nun_var_rec(). The value op_usage[i] is the usage for the i-th operator in the operation sequence. {xrst_end optimize_get_cexp_info.hpp} */ // BEGIN_PROTOTYPE template void get_cexp_info( const player* play , const play::const_random_iterator& random_itr , const pod_vector& op_previous , const pod_vector& op_usage , const pod_vector& cexp2op , const sparse::list_setvec& cexp_set , vector& cexp_info , sparse::list_setvec& skip_op_true , sparse::list_setvec& skip_op_false ) // END_PROTOTYPE { CPPAD_ASSERT_UNKNOWN( cexp_set.n_set() > 0 ); CPPAD_ASSERT_UNKNOWN( cexp_info.size() == 0 ); // number of operators in the tape const size_t num_op = play->num_var_op(); CPPAD_ASSERT_UNKNOWN( op_usage.size() == num_op ); CPPAD_ASSERT_UNKNOWN( op_previous.size() == num_op ); // // number of conditional expressions in the tape size_t num_cexp_op = cexp2op.size(); // // initialize mapping from variable index to operator index CPPAD_ASSERT_UNKNOWN( size_t( (std::numeric_limits::max)() ) >= num_op ); // ---------------------------------------------------------------------- // compute cexp_info // ---------------------------------------------------------------------- // // initialize information for each conditional expression cexp_info.resize(num_cexp_op); skip_op_true.resize(num_cexp_op, num_op); skip_op_false.resize(num_cexp_op, num_op); // for(size_t i = 0; i < num_cexp_op; i++) { size_t i_op = size_t( cexp2op[i] ); CPPAD_ASSERT_UNKNOWN( op_previous[i_op] == 0 || op_usage[i_op] == usage_t(yes_usage) ); op_code_var op; // operator const addr_t* arg; // arguments size_t i_var; // variable index of first result random_itr.op_info(i_op, op, arg, i_var); CPPAD_ASSERT_UNKNOWN( op == CExpOp ); // struct_cexp_info info; info.i_op = addr_t(i_op); info.cop = CompareOp( arg[0] ); info.flag = static_cast(arg[1]); info.left = arg[2]; info.right = arg[3]; // // max_left_right addr_t index = 0; if( arg[1] & 1 ) index = std::max(index, info.left); if( arg[1] & 2 ) index = std::max(index, info.right); info.max_left_right = index; // cexp_info[i] = info; }; // Determine which operators can be conditionally skipped size_t i_op = 0; while(i_op < num_op) { size_t j_op = i_op; bool keep = op_usage[i_op] != usage_t(no_usage); keep &= op_usage[i_op] != usage_t(csum_usage); keep &= op_previous[i_op] == 0; if( keep ) { sparse::list_setvec_const_iterator itr(cexp_set, i_op); if( *itr != cexp_set.end() ) { if( play->GetOp(i_op) == AFunOp ) { // i_op is the first operations in this atomic function call. // Find the last operation in this call. ++j_op; while( play->GetOp(j_op) != AFunOp ) { switch( play->GetOp(j_op) ) { case FunapOp: case FunavOp: case FunrpOp: case FunrvOp: break; default: CPPAD_ASSERT_UNKNOWN(false); } ++j_op; } } } while( *itr != cexp_set.end() ) { size_t element = *itr; size_t index = element / 2; bool compare = bool( element % 2 ); if( compare == false ) { // cexp_info[index].skip_op_false.push_back(i_op); skip_op_false.post_element(index, i_op); if( j_op != i_op ) { // cexp_info[index].skip_op_false.push_back(j_op); skip_op_false.post_element(index, j_op); } } else { // cexp_info[index].skip_op_true.push_back(i_op); skip_op_true.post_element(index, i_op); if( j_op != i_op ) { // cexp_info[index].skip_op_true.push_back(j_op); skip_op_true.post_element(index, j_op); } } ++itr; } } CPPAD_ASSERT_UNKNOWN( i_op <= j_op ); i_op += (1 + j_op) - i_op; } // process postings for skip_op_false, skip_op_true for(size_t i = 0; i < num_cexp_op; ++i) { skip_op_false.process_post(i); skip_op_true.process_post(i); } return; } } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE # endif ================================================ FILE: include/cppad/local/optimize/get_dyn_previous.hpp ================================================ # ifndef CPPAD_LOCAL_OPTIMIZE_GET_DYN_PREVIOUS_HPP # define CPPAD_LOCAL_OPTIMIZE_GET_DYN_PREVIOUS_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- /*! \file get_cexp_info.hpp Create operator information tables */ # include # include # include // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE namespace CppAD { namespace local { namespace optimize { /*! mapping from a dynamic parameter index to its arguments \param i_dyn is the dynamic parameter index \param dyn2par_index is the mapping from dynamic parameter index to parameter index (size is number of dynamic parameters). \param par_is_dyn i-th element is true (false) if i-th parameter is (is not) dynamic (size is number of parameters). \param dyn_arg_offset j-th element is the offset in dyn_par_arg of the first argument for j-th dynamic parameter's operator (size is number of dynamic parameters). This is only defined for dynamic parameters indices less than or equal i_dyn. \param dyn_par_arg it the vector of arguments for all the dynamic parameter operators. This is only defined for dynamic parameters indices less than or equal i_dyn. \param par_ind2dyn_ind is the mapping from parameter index to dynamic parameter index (size is number of parameters). This is only defined for parameter indices less than or equal the parameter index corresponding to i_dyn. \param dyn_previous is the mapping from dynamic parameter index to previous dynamic parameter that can be used as a replacement (size is number of dynamic parameters). This is only defined for dynamic parameters indices less than or equal i_dyn. \param arg_match Size of this vector must be number of arguments for operator for i_dyn. The input value of its elements does not matter. Upn return it contains the parameter indices for the arguments to use when matching this operator Arguments that are dynamic parameters, and have previous matches, have been replaced by their previous matches. */ inline void dyn_arg_match( size_t i_dyn , const pod_vector& dyn2par_index , const pod_vector & par_is_dyn , const pod_vector& dyn_arg_offset , const pod_vector& dyn_par_arg , const pod_vector& par_ind2dyn_ind , const pod_vector& dyn_previous , pod_vector& arg_match ) { // number of dynamic parameters addr_t num_dynamic_par = addr_t( dyn2par_index.size() ); // // check some assumptions CPPAD_ASSERT_UNKNOWN( size_t( num_dynamic_par ) == dyn_arg_offset.size() ); CPPAD_ASSERT_UNKNOWN( size_t( num_dynamic_par ) == dyn_previous.size() ); CPPAD_ASSERT_UNKNOWN( par_is_dyn.size() == par_ind2dyn_ind.size() ); // // number of arguments for this operator addr_t n_arg = addr_t( arg_match.size() ); // // index in dyn_par_arg of first argument for this operator addr_t i_arg = dyn_arg_offset[i_dyn]; // // loop over arguments for this operator for(addr_t j = 0; j < n_arg; ++j) { // parameter index for this argument addr_t j_par = dyn_par_arg[i_arg + j]; CPPAD_ASSERT_UNKNOWN( j_par < dyn2par_index[i_dyn] ); // // map dynamic parameters arguments to previous matches if( par_is_dyn[j_par] ) { addr_t j_dyn = par_ind2dyn_ind[j_par]; if( dyn_previous[j_dyn] != num_dynamic_par ) { CPPAD_ASSERT_UNKNOWN( dyn_previous[j_dyn] < j_dyn ); // previous dynamic parameter j_dyn = dyn_previous[j_dyn]; // corresponding parameter j_par = dyn2par_index[j_dyn]; } } arg_match[j] = j_par; } return; } /*! Get mapping from each dynamic parameter to a previous dynamic parameter that can be used to replace it (if one exists). \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base. \param play This is the old operation sequence. \param par_usage The size of this vector is the number of parameters in the operation sequence.i.e., play->nun_var_rec(). It is the usage counting previous operator optimization of operators. \param dyn_previous The input size of this vector must be zero. Upon return it has size equal to the number of dynamic parameters in the operation sequence; i.e., num_dyn = play->num_dynamic_par(). Let k = dyn_parvious[j]. If k == num_dyn, no replacement was found for the j-th dynamic parameter. If k != num_dyn, the k-th dynamic parameter can be used in place of the j-th dynamic parameter, k < j, dyn_previous[k] != num_dyn, par_usage[dyn2par_index[k]] == true. */ template void get_dyn_previous( const player* play , pod_vector& par_usage , pod_vector& dyn_previous ) { // number of parameters in the recording size_t num_par = play->num_par_all(); // number of dynamic parameters in the recording size_t num_dynamic_par = play->num_dynamic_par(); // number of independent dynamic parameters in the recording size_t n_dyn_independent= play->n_dyn_independent(); // check some assumptions CPPAD_ASSERT_UNKNOWN( dyn_previous.size() == 0 ); CPPAD_ASSERT_UNKNOWN( par_usage.size() == num_par ); CPPAD_ASSERT_UNKNOWN( num_dynamic_par <= num_par ); CPPAD_ASSERT_UNKNOWN( n_dyn_independent <= num_dynamic_par ); CPPAD_ASSERT_UNKNOWN( num_arg_dyn( ind_dyn ) == 0 ); // dynamic parameter information dyn_previous.resize( num_dynamic_par ); const pod_vector& dyn2par_index( play->dyn2par_index() ); const pod_vector& par_is_dyn( play->par_is_dyn() ); const pod_vector& dyn_par_op( play->dyn_par_op() ); const pod_vector& dyn_par_arg( play->dyn_par_arg() ); // mapping from parameter index to dynamic parameter index // only defined when par_is_dyn is true pod_vector par_ind2dyn_ind(num_par); // mapping from dynamic parameter index to first argument index pod_vector dyn_arg_offset(num_dynamic_par); // ---------------------------------------------------------------------- // compute dyn_previous // ---------------------------------------------------------------------- sparse::list_setvec hash_table_dyn; hash_table_dyn.resize(CPPAD_HASH_TABLE_SIZE, num_dynamic_par); // // Initialize in dyn_par_arg // (independent dynamic parameters do not have any arguments) size_t i_arg = 0; // // independent dynamic parameters for(size_t i_dyn = 0; i_dyn < n_dyn_independent; ++i_dyn) { // parameter index size_t i_par = size_t( dyn2par_index[i_dyn] ); // dynamic parameter index is one greater because phantom parameter // at index 0 is not dynamic CPPAD_ASSERT_UNKNOWN( i_par == i_dyn + 1 ); // mapping from parameter index to dynamic parameter index par_ind2dyn_ind[i_par] = addr_t( i_dyn ); // never get optimized out dyn_previous[i_dyn] = addr_t( num_dynamic_par ); } // // other dynamic parameters for(size_t i_dyn = n_dyn_independent; i_dyn < num_dynamic_par; ++i_dyn) { // Initialize previous for this dynamic parameter. This is only // defined for dynamic parameter indices less than or equal i_dyn dyn_previous[i_dyn] = addr_t( num_dynamic_par ); // // mapping from dynamic parameter index to argument offset // is only defined for j_dyn <= i_dyn dyn_arg_offset[i_dyn] = addr_t( i_arg ); // // parameter index for this dynamic parameter size_t i_par = size_t( dyn2par_index[i_dyn] ); // // mapping from parameter indices to dynamic parameter indices // is only defined when par_is_dyn[i_par] is true and for parameter // indices less than or equal i_par CPPAD_ASSERT_UNKNOWN( par_is_dyn[i_par] ); par_ind2dyn_ind[i_par] = addr_t( i_dyn ); // // operator for this dynamic parameter op_code_dyn op = op_code_dyn( dyn_par_op[i_dyn] ); // // temporary used below and decaled here to reduce memory allocation pod_vector arg_match; // // temporaries used below and decaled here to reduce indentation level bool match; size_t code; size_t count; // // check for a previous match for i_dyn if( par_usage[i_par] ) switch( op ) { // --------------------------------------------------------------- // unary operators case abs_dyn: case acos_dyn: case acosh_dyn: case asin_dyn: case asinh_dyn: case atan_dyn: case atanh_dyn: case cos_dyn: case cosh_dyn: case erf_dyn: case erfc_dyn: case exp_dyn: case expm1_dyn: case fabs_dyn: case log_dyn: case log1p_dyn: case neg_dyn: case sign_dyn: case sin_dyn: case sinh_dyn: case sqrt_dyn: case tan_dyn: case tanh_dyn: CPPAD_ASSERT_UNKNOWN( num_arg_dyn(op) == 1); CPPAD_ASSERT_UNKNOWN( par_is_dyn[i_par] ); { size_t num_arg = 1; arg_match.resize(num_arg); dyn_arg_match( i_dyn, dyn2par_index, par_is_dyn, dyn_arg_offset, dyn_par_arg, par_ind2dyn_ind, dyn_previous, arg_match ); opcode_t op_t = opcode_t(op); code = optimize_hash_code( op_t, num_arg, arg_match.data() ); // // iterator for the set with this hash code sparse::list_setvec_const_iterator itr(hash_table_dyn, code); // // check for a match count = 0; match = false; while( ! match && *itr != num_dynamic_par ) { ++count; // // candidate for current dynamic parameter size_t k_dyn = *itr; CPPAD_ASSERT_UNKNOWN( k_dyn < i_dyn ); // // argument offset for the candidate addr_t k_arg = dyn_arg_offset[k_dyn]; // match = op_t == dyn_par_op[k_dyn]; match &= arg_match[0] == dyn_par_arg[k_arg + 0]; if( ! match ) ++itr; } if( match ) { size_t k_dyn = *itr; CPPAD_ASSERT_UNKNOWN( k_dyn < i_dyn ); dyn_previous[i_dyn] = addr_t( k_dyn ); } else { CPPAD_ASSERT_UNKNOWN( count < 11 ); if( count == 10 ) { // restart list for this hash code hash_table_dyn.clear(code); } // Add this entry to hash table. // Not using post_element because we need to iterate for // this code before adding another element for this code. hash_table_dyn.add_element(code, i_dyn); } } break; // --------------------------------------------------------------- // binary operators case add_dyn: case div_dyn: case mul_dyn: case pow_dyn: case sub_dyn: case zmul_dyn: CPPAD_ASSERT_UNKNOWN( num_arg_dyn(op) == 2); CPPAD_ASSERT_UNKNOWN( par_is_dyn[i_par] ); match = false; { size_t num_arg = 2; arg_match.resize(num_arg); dyn_arg_match( i_dyn, dyn2par_index, par_is_dyn, dyn_arg_offset, dyn_par_arg , par_ind2dyn_ind, dyn_previous, arg_match ); opcode_t op_t = opcode_t(op); code = optimize_hash_code( op_t, num_arg, arg_match.data() ); // // iterator for the set with this hash code sparse::list_setvec_const_iterator itr(hash_table_dyn, code); // // check for a match count = 0; while( ! match && *itr != num_dynamic_par ) { ++count; // // candidate for current dynamic parameter size_t k_dyn = *itr; CPPAD_ASSERT_UNKNOWN( k_dyn < i_dyn ); // // argument offset for the candidate addr_t k_arg = dyn_arg_offset[k_dyn]; // match = op_t == dyn_par_op[k_dyn]; match &= arg_match[0] == dyn_par_arg[k_arg + 0]; match &= arg_match[1] == dyn_par_arg[k_arg + 1]; if( ! match ) ++itr; } if( match ) { size_t k_dyn = *itr; CPPAD_ASSERT_UNKNOWN( k_dyn < i_dyn ); dyn_previous[i_dyn] = addr_t( k_dyn ); } } if( (! match) && ( (op == add_dyn) || (op == mul_dyn) ) ) { size_t num_arg = 2; std::swap( arg_match[0], arg_match[1] ); opcode_t op_t = opcode_t(op); size_t code_swp = optimize_hash_code( op_t, num_arg, arg_match.data() ); // // iterator for the set with this hash code sparse::list_setvec_const_iterator itr(hash_table_dyn, code_swp); // // check for a match while( ! match && *itr != num_dynamic_par ) { // // candidate for current dynamic parameter size_t k_dyn = *itr; CPPAD_ASSERT_UNKNOWN( k_dyn < i_dyn ); // // argument offset for the candidate addr_t k_arg = dyn_arg_offset[k_dyn]; // match = op_t == dyn_par_op[k_dyn]; match &= arg_match[0] == dyn_par_arg[k_arg + 0]; match &= arg_match[1] == dyn_par_arg[k_arg + 1]; if( ! match ) ++itr; } if( match ) { size_t k_dyn = *itr; CPPAD_ASSERT_UNKNOWN( k_dyn < i_dyn ); dyn_previous[i_dyn] = addr_t( k_dyn ); } } if( ! match ) { CPPAD_ASSERT_UNKNOWN( count < 11 ); if( count == 10 ) { // restart list for this hash code hash_table_dyn.clear(code); } // Add the entry to hash table // Not using post_element because we need to iterate for // this code before adding another element for this code. hash_table_dyn.add_element(code, i_dyn); } // -------------------------------------------------------------- // skipping these cases for now case dis_dyn: case cond_exp_dyn: case atom_dyn: case result_dyn: break; // -------------------------------------------------------------- // should be no other cases; e.g., no ind_dyn or number_dyn. default: CPPAD_ASSERT_UNKNOWN(false); break; } i_arg += num_arg_dyn(op); if( op == atom_dyn ) { CPPAD_ASSERT_UNKNOWN( num_arg_dyn(op) == 0 ); size_t n = size_t( dyn_par_arg[i_arg + 2] ); size_t m = size_t( dyn_par_arg[i_arg + 3] ); size_t n_arg = 6 + n + m; i_arg += n_arg; } } } } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE # endif ================================================ FILE: include/cppad/local/optimize/get_op_previous.hpp ================================================ # ifndef CPPAD_LOCAL_OPTIMIZE_GET_OP_PREVIOUS_HPP # define CPPAD_LOCAL_OPTIMIZE_GET_OP_PREVIOUS_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE namespace CppAD { namespace local { namespace optimize { /* {xrst_begin optimize_get_op_previous dev} {xrst_spell cexp } Get Mapping From Op to Previous Op That is Equivalent ##################################################### Syntax ****** | *exceed_collision_limit* = ``get_op_previous`` ( | |tab| *collision_limit* , | |tab| *play* , | |tab| *random_itr* , | |tab| *cexp_set* , | |tab| *op_previous* , | |tab| *op_usage* | ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } Base **** base type for the operator; i.e., this operation was recorded using AD and computations by this routine are done using type Base. collision_limit *************** is the maximum number of collisions (matches) allowed in the hash expression has table. play **** is the old operation sequence. random_itr ********** is a random iterator for the old operation sequence. cexp_set ******** set[i] is a set of elements for the i-th operator. Suppose that e is an element of set[i], j = e / 2, k = e % 2. If the comparison for the j-th conditional expression is equal to bool(k), the i-th operator can be skipped (is not used by any of the results). Note the j indexes the CExpOp operators in the operation sequence. On input, cexp_set is does not count previous optimization. On output, it does count previous optimization. op_previous *********** The input size of this vector must be zero. Upon return it has size equal to the number of operators in the operation sequence; i.e., num_op = play->nun_var_rec(). Let j = op_previous[i]. If j = 0, no replacement was found for i-th operator. If j != 0: #. j < i #. op_previous[j] == 0 #. op_usage[j] == usage_t(yes_usage) #. i-th operator has NumArg(op) <= 3 #. i-th operator has 0 < NumRes(op) #. i-th operator is not one of the following: {xrst_spell_off} PriOp, ParOp, InvOp, EndOp, CexpOp, BeginOp. {xrst_spell_on} #. i-th operator is not one of the load store operator: {xrst_spell_off} LtpvOp, LtvpOp, LtvvOp, StppOp, StpvOp, StvpOp, StvvOp. {xrst_spell_on} #. i-th operator is not a atomic function operator: {xrst_spell_off} AFunOp, FunapOp, FunavOp, FunrpOp, FunrvOp. {xrst_spell_on} op_usage ******** The size of this vector is the number of operators in the old operation sequence.i.e., play->nun_var_rec(). On input, op_usage[i] is the usage for the i-th operator in the operation sequence not counting previous optimization. On output, it is the usage counting previous operator optimization. exceed_collision_limit ********************** If the *collision_limit* is exceeded (is not exceeded), the return value is true (false). {xrst_end optimize_get_op_previous} */ // BEGIN_PROTOTYPE template bool get_op_previous( size_t collision_limit , const player* play , const play::const_random_iterator& random_itr , sparse::list_setvec& cexp_set , pod_vector& op_previous , pod_vector& op_usage ) // END_PROTOTYPE { bool exceed_collision_limit = false; // // number of operators in the tape const size_t num_op = random_itr.num_op(); CPPAD_ASSERT_UNKNOWN( op_previous.size() == 0 ); CPPAD_ASSERT_UNKNOWN( op_usage.size() == num_op ); op_previous.resize( num_op ); // // number of conditional expressions in the tape // // initialize mapping from variable index to operator index CPPAD_ASSERT_UNKNOWN( size_t( (std::numeric_limits::max)() ) >= num_op ); // ---------------------------------------------------------------------- // compute op_previous // ---------------------------------------------------------------------- sparse::list_setvec hash_table_op; hash_table_op.resize(CPPAD_HASH_TABLE_SIZE, num_op); // pod_vector work_bool; pod_vector work_addr_t; for(size_t i_op = 0; i_op < num_op; ++i_op) { op_previous[i_op] = 0; if( op_usage[i_op] == usage_t(yes_usage) ) switch( random_itr.get_op(i_op) ) { // ---------------------------------------------------------------- // these operators never match previous operators case BeginOp: case CExpOp: case CSkipOp: case CSumOp: case EndOp: case InvOp: case LdpOp: case LdvOp: case ParOp: case PriOp: case StppOp: case StpvOp: case StvpOp: case StvvOp: case AFunOp: case FunapOp: case FunavOp: case FunrpOp: case FunrvOp: break; // ---------------------------------------------------------------- // check for a previous match // BEGIN_SORT_THIS_LINE_PLUS_1 case AbsOp: case AcosOp: case AcoshOp: case AddpvOp: case AddvvOp: case AsinOp: case AsinhOp: case AtanOp: case AtanhOp: case CosOp: case CoshOp: case DisOp: case DivpvOp: case DivvpOp: case DivvvOp: case EqppOp: case EqpvOp: case EqvvOp: case ErfOp: case ErfcOp: case ExpOp: case Expm1Op: case LeppOp: case LepvOp: case LevpOp: case LevvOp: case Log1pOp: case LogOp: case LtppOp: case LtpvOp: case LtvpOp: case LtvvOp: case MulpvOp: case MulvvOp: case NegOp: case NeppOp: case NepvOp: case NevvOp: case PowpvOp: case PowvpOp: case PowvvOp: case SignOp: case SinOp: case SinhOp: case SqrtOp: case SubpvOp: case SubvpOp: case SubvvOp: case TanOp: case TanhOp: case ZmulpvOp: case ZmulvpOp: case ZmulvvOp: // END_SORT_THIS_LINE_MINUS_1 exceed_collision_limit |= match_op( collision_limit, random_itr, op_previous, i_op, hash_table_op, work_bool, work_addr_t ); if( op_previous[i_op] != 0 ) { // like a unary operator that assigns i_op equal to previous. size_t previous = size_t( op_previous[i_op] ); bool sum_op = false; CPPAD_ASSERT_UNKNOWN( previous < i_op ); op_inc_arg_usage( play, sum_op, i_op, previous, op_usage, cexp_set ); } break; // ---------------------------------------------------------------- default: CPPAD_ASSERT_UNKNOWN(false); break; } } /* --------------------------------------------------------------------- // Print out hash code usage summary CppAD::vector count(collision_limit + 1); for(size_t i = 0; i <= collision_limit; ++i) count[i] = 0; for(size_t code = 0; code < CPPAD_HASH_TABLE_SIZE; ++code) { size_t size = hash_table_op.number_elements(code); ++count[size]; } std::cout << "count = " << count << "\n"; --------------------------------------------------------------------- */ return exceed_collision_limit; } } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE # endif ================================================ FILE: include/cppad/local/optimize/get_op_usage.hpp ================================================ # ifndef CPPAD_LOCAL_OPTIMIZE_GET_OP_USAGE_HPP # define CPPAD_LOCAL_OPTIMIZE_GET_OP_USAGE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE namespace CppAD { namespace local { namespace optimize { /// Is this an addition or subtraction operator inline bool op_add_or_sub( op_code_var op ///< operator we are checking ) { bool result; switch(op) { case AddpvOp: case AddvvOp: case SubpvOp: case SubvpOp: case SubvvOp: result = true; break; default: result = false; break; } return result; } /*! {xrst_begin optimize_op_inc_arg_usage dev} {xrst_spell cexp csum } Increase Argument Usage and Propagate cexp_set From Result to Argument ###################################################################### Prototype ********* {xrst_literal // BEGIN_OP_INC_ARG_USAGE // END_OP_INC_ARG_USAGE } play **** is the player for the old operation sequence. check_csum ********** is result an addition or subtraction operator, and the optimizer is allowed to generate cumulative sum operators. i_result ******** is the operator index for the result operator. There are no posting waiting to be processed for the corresponding cexp_set. i_arg ***** is the operator index for the argument to the result operator. There may be postings waiting to be processed for the corresponding cexp_set. op_usage ******** structure that holds the information for each of the operators. The output value of op_usage[i_arg] is increased; to be specific, If check_csum is true and the input value of op_usage[i_arg] is usage_t(no_usage), its output value is usage_t(csum_usage). Otherwise, the output value of op_usage[i_arg] is usage_t(yes_usage). cexp_set ******** This is a vector of sets with one set for each operator. We denote the i-th set by set[i]. These are the conditional expression conditions that must be satisfied for this argument to be used. #. In the special case where cexp_set.n_set() is zero, cexp_set is not changed. #. If cexp_set.n_set() != 0 and op_usage[i_arg] == usage_t(no_usage), the input value of set[i_arg] must be empty. In this case the output value if set[i_arg] is equal to set[i_result] (which may also be empty). #. If cexp_set.n_set() != 0 and op_usage[i_arg] != usage_t(no_usage), the output value of set[i_arg] is the intersection of its input value and set[i_result]. {xrst_end optimize_op_inc_arg_usage} */ // BEGIN_OP_INC_ARG_USAGE template void op_inc_arg_usage( const player* play , bool check_csum , size_t i_result , size_t i_arg , pod_vector& op_usage , sparse::list_setvec& cexp_set ) // END_OP_INC_ARG_USAGE { // value of argument input on input to this routine enum_usage arg_usage = enum_usage( op_usage[i_arg] ); // // new value for usage op_usage[i_arg] = usage_t(yes_usage); if( check_csum ) { if( arg_usage == no_usage ) { op_code_var op_a = play->GetOp(i_arg); if( op_add_or_sub( op_a ) ) { op_usage[i_arg] = usage_t(csum_usage); } } } // // cexp_set if( cexp_set.n_set() == 0 ) return; // if( arg_usage == no_usage ) { // set[i_arg] = set[i_result] // not necessary to process posts for set i_result cexp_set.assignment(i_arg, i_result, cexp_set); } else { // set[i_arg] = set[i_arg] intersect set[i_result] // is necessary to process postts for set i_arg cexp_set.process_post(i_arg); cexp_set.binary_intersection(i_arg, i_arg, i_result, cexp_set); } // return; } /*! {xrst_begin optimize_get_op_usage dev} {xrst_spell cexp dep pri taddr } Use Reverse Activity Analysis to Get Usage Information for Each Operator ######################################################################## Prototype ********* {xrst_literal // BEGIN_GET_OP_USAGE // END_GET_OP_USAGE } Base **** Base type for the operator; i.e., this operation was recorded using ``AD`` < *Base* > and computations by this routine are done using type *Base* . Addr **** Type used by random iterator for the player. cumulative_sum_op ***************** If this is true (false), cumulative summation operator are allowed (not allowed) to be generated by the optimization. compare_op ********** if this is true, arguments are considered used if they appear in compare operators. This is a side effect because compare operators have boolean results (and the result is not in the tape; i.e. NumRes(op) is zero for these operators. (This is an example of a side effect.) print_for_op ************ if this is true, arguments are considered used if they appear in print forward operators; i.e., PriOp. This is also a side effect; i.e. NumRes(PriOp) is zero. conditional_skip **************** If this is true, the conditional expression information cexp_info will be calculated. This may be time intensive and may not have much benefit in the optimized recording. play **** This is the operation sequence. random_itr ********** This is a random iterator for the operation sequence. dep_taddr ********* is a vector of indices for the dependent variables (where the reverse activity analysis starts). cexp2op ******* The input size of this vector must be zero. Upon return it has size equal to the number of conditional expressions, CExpOp operators. The value *cexp2op* [ ``j`` ] is the operator index corresponding to the *j*-th conditional expressions. cexp_set ******** This is a vector of sets that is empty on input. If *conditional_skip* is false, *cexp_usage* is not modified. Otherwise, set[i] is a set of elements for the i-th operator. Suppose that e is an element of set[i], j = e / 2, k = e % 2. If the comparison for the j-th conditional expression is equal to bool(k), the i-th operator can be skipped (is not used by any of the results). Note that j indexes the CExpOp operators in the operation sequence. vecad_used ********** The input size of this vector must be zero. Upon return it has size equal to the number of VecAD vectors in the operations sequences; i.e., play->num_var_vecad(). The VecAD vectors are indexed in the order that their indices appear in the one large play->GetVecInd that holds all the VecAD vectors. op_usage ******** The input size of this vector must be zero. Upon return it has size equal to the number of operators in the operation sequence; i.e., num_op = play->nun_var_rec(). The value *op_usage* [ *i* ] has been set to the usage for the i-th operator in the operation sequence. Atomic function calls are a special case, the first and second AFunOp have usage corresponding to the entire call. The arguments have the usage for particular parameter or variable. This usage is only for creating variables, not for creating dynamic parameters. {xrst_end optimize_get_op_usage} */ // BEGIN_GET_OP_USAGE template void get_op_usage( bool conditional_skip , bool compare_op , bool print_for_op , bool cumulative_sum_op , const player* play , const play::const_random_iterator& random_itr , const pod_vector& dep_taddr , pod_vector& cexp2op , sparse::list_setvec& cexp_set , pod_vector& vecad_used , pod_vector& op_usage ) // END_GET_OP_USAGE { CPPAD_ASSERT_UNKNOWN( cexp_set.n_set() == 0 ); CPPAD_ASSERT_UNKNOWN( vecad_used.size() == 0 ); CPPAD_ASSERT_UNKNOWN( op_usage.size() == 0 ); // number of operators in the tape const size_t num_op = play->num_var_op(); // // initialize mapping from variable index to operator index CPPAD_ASSERT_UNKNOWN( size_t( (std::numeric_limits::max)() ) >= num_op ); // ----------------------------------------------------------------------- // information about current operator op_code_var op; // operator const addr_t* arg; // arguments size_t i_op; // operator index size_t i_var; // variable index of first result // ----------------------------------------------------------------------- // information about atomic function calls size_t atom_index=0, atom_old=0, atom_m=0, atom_n=0, atom_i=0, atom_j=0; enum_atom_state atom_state; // // work space used by user atomic functions vector atom_x; // value of parameters in x vector type_x; // type for each argument vector atom_ix; // variables indices for argument vector vector depend_y; // results that are used vector depend_x; // arguments that are used // // parameter information (used by atomic function calls) # ifndef NDEBUG size_t num_par = play->num_par_all(); # endif CPPAD_ASSERT_UNKNOWN( num_par > 0 ) const Base* parameter = play->par_ptr(); // ----------------------------------------------------------------------- // vecad information size_t num_vecad = play->num_var_vecad(); size_t num_vecad_ind = play->num_var_vec_ind(); // vecad_used.resize(num_vecad); for(size_t i = 0; i < num_vecad; i++) vecad_used[i] = false; // vector arg2vecad(num_vecad_ind); for(size_t i = 0; i < num_vecad_ind; i++) arg2vecad[i] = num_vecad; // invalid value size_t arg_0 = 1; // value of arg[0] for theh first vecad for(size_t i = 0; i < num_vecad; i++) { // mapping from arg[0] value to index for this vecad object. arg2vecad[arg_0] = i; // // length of this vecad object size_t length = play->GetVecInd(arg_0 - 1); // // set to proper index in GetVecInd for next VecAD arg[0] value arg_0 += length + 1; } CPPAD_ASSERT_UNKNOWN( arg_0 == num_vecad_ind + 1 ); // ----------------------------------------------------------------------- // conditional expression information // size_t num_cexp_op = 0; if( conditional_skip ) { for(i_op = 0; i_op < num_op; ++i_op) { if( random_itr.get_op(i_op) == CExpOp ) { // count the number of conditional expressions. ++num_cexp_op; } } } // cexp2op.resize( num_cexp_op ); // // number of sets size_t num_set = 0; if( conditional_skip && num_cexp_op > 0) num_set = num_op; // // conditional expression index = element / 2 // conditional expression compare = bool ( element % 2) size_t end_set = 2 * num_cexp_op; // if( num_set > 0 ) cexp_set.resize(num_set, end_set); // ----------------------------------------------------------------------- // initialize operator usage for reverse dependency analysis. op_usage.resize( num_op ); for(i_op = 0; i_op < num_op; ++i_op) op_usage[i_op] = usage_t(no_usage); for(size_t i = 0; i < dep_taddr.size(); i++) { i_op = random_itr.var2op(dep_taddr[i]); op_usage[i_op] = usage_t(yes_usage); // dependent variables } // ---------------------------------------------------------------------- // Reverse pass to compute usage and cexp_set for each operator // ---------------------------------------------------------------------- // // Initialize reverse pass size_t last_atom_i_op = 0; size_t cexp_index = num_cexp_op; atom_state = end_atom; i_op = num_op; while(i_op != 0 ) { --i_op; if( num_set > 0 ) { // no more elements will be added to this set cexp_set.process_post(i_op); } // // this operator information random_itr.op_info(i_op, op, arg, i_var); // // Is the result of this operation used. // (This only makes sense when NumRes(op) > 0.) usage_t use_result = op_usage[i_op]; // bool check_csum = false; switch( op ) { // ============================================================= // normal operators // ============================================================= // Only one variable with index arg[0] case SubvpOp: check_csum = cumulative_sum_op; // case AbsOp: case AcosOp: case AcoshOp: case AsinOp: case AsinhOp: case AtanOp: case AtanhOp: case CosOp: case CoshOp: case DivvpOp: case ErfOp: case ErfcOp: case ExpOp: case Expm1Op: case LogOp: case Log1pOp: case NegOp: case PowvpOp: case SignOp: case SinOp: case SinhOp: case SqrtOp: case TanOp: case TanhOp: case ZmulvpOp: CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 ); if( use_result != usage_t(no_usage) ) { size_t j_op = random_itr.var2op(size_t(arg[0])); op_inc_arg_usage( play, check_csum, i_op, j_op, op_usage, cexp_set ); } break; // -------------------------------------------- // Only one variable with index arg[1] case AddpvOp: case SubpvOp: check_csum = cumulative_sum_op; // case DisOp: case DivpvOp: case MulpvOp: case PowpvOp: case ZmulpvOp: CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 ); if( use_result != usage_t(no_usage) ) { size_t j_op = random_itr.var2op(size_t(arg[1])); op_inc_arg_usage( play, check_csum, i_op, j_op, op_usage, cexp_set ); } break; // -------------------------------------------- // arg[0] and arg[1] are the only variables case AddvvOp: case SubvvOp: check_csum = cumulative_sum_op; // case DivvvOp: case MulvvOp: case PowvvOp: case ZmulvvOp: CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 ); if( use_result != usage_t(no_usage) ) { for(size_t i = 0; i < 2; i++) { size_t j_op = random_itr.var2op(size_t(arg[i])); op_inc_arg_usage( play, check_csum, i_op, j_op, op_usage, cexp_set ); } } break; // -------------------------------------------- // Conditional expression operators // arg[2], arg[3], arg[4], arg[5] are parameters or variables case CExpOp: CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 ); if( conditional_skip ) { --cexp_index; cexp2op[ cexp_index ] = addr_t(i_op); } if( use_result != usage_t(no_usage) ) { CPPAD_ASSERT_UNKNOWN( NumArg(CExpOp) == 6 ); // propagate from result to left argument if( arg[1] & 1 ) { size_t j_op = random_itr.var2op(size_t(arg[2])); op_inc_arg_usage( play, check_csum, i_op, j_op, op_usage, cexp_set ); } // propagate from result to right argument if( arg[1] & 2 ) { size_t j_op = random_itr.var2op(size_t(arg[3])); op_inc_arg_usage( play, check_csum, i_op, j_op, op_usage, cexp_set ); } // are if_true and if_false cases the same variable bool same_variable = (arg[1] & 4) != 0; same_variable &= (arg[1] & 8) != 0; same_variable &= arg[4] == arg[5]; // // if_true if( arg[1] & 4 ) { size_t j_op = random_itr.var2op(size_t(arg[4])); bool can_skip = conditional_skip & (! same_variable); can_skip &= op_usage[j_op] == usage_t(no_usage); op_inc_arg_usage( play, check_csum, i_op, j_op, op_usage, cexp_set ); if( can_skip ) { // j_op corresponds to the value used when the // comparison result is true. It can be skipped when // the comparison is false (0). size_t element = 2 * cexp_index + 0; cexp_set.post_element(j_op, element); // op_usage[j_op] = usage_t(yes_usage); } } // // if_false if( arg[1] & 8 ) { size_t j_op = random_itr.var2op(size_t(arg[5])); bool can_skip = conditional_skip & (! same_variable); can_skip &= op_usage[j_op] == usage_t(no_usage); op_inc_arg_usage( play, check_csum, i_op, j_op, op_usage, cexp_set ); if( can_skip ) { // j_op corresponds to the value used when the // comparison result is false. It can be skipped when // the comparison is true (0). size_t element = 2 * cexp_index + 1; cexp_set.post_element(j_op, element); // op_usage[j_op] = usage_t(yes_usage); } } } break; // -------------------------------------------- // Operations that are never used // (new CSkip options are generated if conditional_skip is true) case CSkipOp: case ParOp: break; // Operators that are always used case InvOp: case BeginOp: case EndOp: op_usage[i_op] = usage_t(yes_usage); break; // ----------------------------------------------- // The print forward operator case PriOp: CPPAD_ASSERT_NARG_NRES(op, 5, 0); if( print_for_op ) { op_usage[i_op] = usage_t(yes_usage); if( arg[0] & 1 ) { // arg[1] is a variable size_t j_op = random_itr.var2op(size_t(arg[1])); op_inc_arg_usage( play, check_csum, i_op, j_op, op_usage, cexp_set ); } if( arg[0] & 2 ) { // arg[3] is a variable size_t j_op = random_itr.var2op(size_t(arg[3])); op_inc_arg_usage( play, check_csum, i_op, j_op, op_usage, cexp_set ); } } break; // ----------------------------------------------------- // ============================================================= // Comparison operators // ============================================================= // Compare operator were none of the operatros are variables case EqppOp: case LeppOp: case LtppOp: case NeppOp: if( compare_op ) op_usage[i_op] = usage_t(yes_usage); break; // ---------------------------------------------- // Compare operators where arg[1] is only variable case LepvOp: case LtpvOp: case EqpvOp: case NepvOp: CPPAD_ASSERT_UNKNOWN( NumRes(op) == 0 ); if( compare_op ) { op_usage[i_op] = usage_t(yes_usage); // size_t j_op = random_itr.var2op(size_t(arg[1])); op_inc_arg_usage( play, check_csum, i_op, j_op, op_usage, cexp_set ); } break; // ---------------------------------------------- // Compare operators where arg[0] is only variable case LevpOp: case LtvpOp: CPPAD_ASSERT_UNKNOWN( NumRes(op) == 0 ); if( compare_op ) { op_usage[i_op] = usage_t(yes_usage); // size_t j_op = random_itr.var2op(size_t(arg[0])); op_inc_arg_usage( play, check_csum, i_op, j_op, op_usage, cexp_set ); } break; // ---------------------------------------------- // Compare operators where arg[0] and arg[1] are variables case LevvOp: case LtvvOp: case EqvvOp: case NevvOp: CPPAD_ASSERT_UNKNOWN( NumRes(op) == 0 ); if( compare_op ) { op_usage[i_op] = usage_t(yes_usage); // for(size_t i = 0; i < 2; i++) { size_t j_op = random_itr.var2op(size_t(arg[i])); op_inc_arg_usage( play, check_csum, i_op, j_op, op_usage, cexp_set ); } } break; // ---------------------------------------------- // ============================================================= // VecAD operators // ============================================================= // load operator using a parameter index case LdpOp: CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 ); if( use_result != usage_t(no_usage) ) { size_t i_vec = arg2vecad[ arg[0] ]; vecad_used[i_vec] = true; } break; // -------------------------------------------- // load operator using a variable index case LdvOp: CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 ); if( use_result != usage_t(no_usage) ) { size_t i_vec = arg2vecad[ arg[0] ]; vecad_used[i_vec] = true; // size_t j_op = random_itr.var2op(size_t(arg[1])); op_usage[j_op] = usage_t(yes_usage); } break; // -------------------------------------------- // Store a variable using a parameter index case StpvOp: CPPAD_ASSERT_UNKNOWN( NumRes(op) == 0 ); if( vecad_used[ arg2vecad[ arg[0] ] ] ) { op_usage[i_op] = usage_t(yes_usage); // size_t j_op = random_itr.var2op(size_t(arg[2])); op_usage[j_op] = usage_t(yes_usage); } break; // -------------------------------------------- // Store a variable using a variable index case StvvOp: CPPAD_ASSERT_UNKNOWN( NumRes(op) == 0 ); if( vecad_used[ arg2vecad[ arg[0] ] ] ) { op_usage[i_op] = usage_t(yes_usage); // size_t j_op = random_itr.var2op(size_t(arg[1])); op_usage[j_op] = usage_t(yes_usage); size_t k_op = random_itr.var2op(size_t(arg[2])); op_usage[k_op] = usage_t(yes_usage); } break; // ----------------------------------------------------- // ============================================================= // cumulative summation operator // ============================================================ case CSumOp: CPPAD_ASSERT_UNKNOWN( NumRes(op) == 1 ); { for(size_t i = 5; i < size_t(arg[2]); i++) { size_t j_op = random_itr.var2op(size_t(arg[i])); op_inc_arg_usage( play, check_csum, i_op, j_op, op_usage, cexp_set ); } } break; // ============================================================= // user defined atomic operators // ============================================================ case AFunOp: // start or end atomic operation sequence if( atom_state == end_atom ) { // reverse_user using random_itr instead of play atom_index = size_t(arg[0]); atom_old = size_t(arg[1]); atom_n = size_t(arg[2]); atom_m = size_t(arg[3]); atom_j = atom_n; atom_i = atom_m; atom_state = ret_atom; // ------------------------------------------------------- last_atom_i_op = i_op; CPPAD_ASSERT_UNKNOWN( i_op > atom_n + atom_m + 1 ); CPPAD_ASSERT_UNKNOWN( op_usage[last_atom_i_op] == usage_t(no_usage) ); # ifndef NDEBUG if( cexp_set.n_set() > 0 ) { cexp_set.process_post(last_atom_i_op); CPPAD_ASSERT_UNKNOWN( cexp_set.number_elements(last_atom_i_op) == 0 ); } # endif // atom_x.resize( atom_n ); type_x.resize( atom_n ); atom_ix.resize( atom_n ); // depend_y.resize( atom_m ); depend_x.resize( atom_n ); for(size_t i = 0; i < atom_m; i++) depend_y[ i ] = false; } else { // reverse_user using random_itr instead of play CPPAD_ASSERT_UNKNOWN( atom_state == start_atom ); CPPAD_ASSERT_UNKNOWN( atom_n == size_t(arg[2]) ); CPPAD_ASSERT_UNKNOWN( atom_m == size_t(arg[3]) ); CPPAD_ASSERT_UNKNOWN( atom_j == 0 ); CPPAD_ASSERT_UNKNOWN( atom_i == 0 ); atom_state = end_atom; // ------------------------------------------------------- CPPAD_ASSERT_UNKNOWN( i_op + atom_n + atom_m + 1 == last_atom_i_op ); if( op_usage[last_atom_i_op] != usage_t(no_usage) ) { // call atomic function for this operation sweep::call_atomic_rev_depend( atom_index, atom_old, atom_x, type_x, depend_x, depend_y ); for(size_t j = 0; j < atom_n; j++) if( depend_x[j] ) { // The parameter or variable corresponding to the j-th // argument gets used op_usage[i_op + 1 + j] = true; if( type_x[j] == variable_enum ) { CPPAD_ASSERT_UNKNOWN( atom_ix[j] > 0 ); if( depend_x[j] ) { size_t j_op = random_itr.var2op(atom_ix[j]); op_inc_arg_usage(play, check_csum, last_atom_i_op, j_op, op_usage, cexp_set ); } } } } // copy set information from last to first if( cexp_set.n_set() > 0 ) { cexp_set.process_post(last_atom_i_op); cexp_set.assignment(i_op, last_atom_i_op, cexp_set); } // copy usage information from last to first op_usage[i_op] = op_usage[last_atom_i_op]; } break; // ------------------------------------------------------- case FunapOp: // parameter argument in an atomic operation sequence CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); // // reverse_user using random_itr instead of play CPPAD_ASSERT_NARG_NRES(op, 1, 0); CPPAD_ASSERT_UNKNOWN( 0 < atom_j && atom_j <= atom_n ); --atom_j; if( atom_j == 0 ) atom_state = start_atom; // ------------------------------------------------------------- atom_ix[atom_j] = 0; // // parameter arguments atom_x[atom_j] = parameter[arg[0]]; if( play->par_is_dyn()[arg[0]] ) type_x[atom_j] = dynamic_enum; else type_x[atom_j] = constant_enum; // break; case FunavOp: // variable argument in an atomic operation sequence CPPAD_ASSERT_UNKNOWN( 0 < arg[0] ); // // reverse_user using random_itr instead of play CPPAD_ASSERT_NARG_NRES(op, 1, 0); CPPAD_ASSERT_UNKNOWN( 0 < atom_j && atom_j <= atom_n ); --atom_j; if( atom_j == 0 ) atom_state = start_atom; // ------------------------------------------------------------- atom_ix[atom_j] = size_t(arg[0]); // // variable arguments as parameters atom_x[atom_j] = CppAD::numeric_limits::quiet_NaN(); type_x[atom_j] = variable_enum; // break; case FunrvOp: // variable result in an atomic operation sequence // // reverse_user using random_itr instead of play CPPAD_ASSERT_NARG_NRES(op, 0, 1); CPPAD_ASSERT_UNKNOWN( 0 < atom_i && atom_i <= atom_m ); --atom_i; if( atom_i == 0 ) atom_state = arg_atom; // ------------------------------------------------------------- if( use_result ) { depend_y[atom_i] = true; op_inc_arg_usage( play, check_csum, i_op, last_atom_i_op, op_usage, cexp_set ); } break; // -------------------------------------------------------- case FunrpOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); // // reverse_user using random_itr instead of play CPPAD_ASSERT_NARG_NRES(op, 1, 0); CPPAD_ASSERT_UNKNOWN( 0 < atom_i && atom_i <= atom_m ); --atom_i; if( atom_i == 0 ) atom_state = arg_atom; break; // ============================================================ // all cases should be handled above default: CPPAD_ASSERT_UNKNOWN(0); } } return; } } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE # endif ================================================ FILE: include/cppad/local/optimize/get_par_usage.hpp ================================================ # ifndef CPPAD_LOCAL_OPTIMIZE_GET_PAR_USAGE_HPP # define CPPAD_LOCAL_OPTIMIZE_GET_PAR_USAGE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- /*! \file get_cexp_info.hpp Create operator information tables */ # include // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE namespace CppAD { namespace local { namespace optimize { /*! {xrst_begin optimize_get_par_usage dev} Use Reverse Activity Analysis to Get Usage for Each Parameter ############################################################# Prototype ********* {xrst_literal // BEGIN_GET_PAR_USAGE // END_PROTOTYPE } Base **** Base type for the operator; i.e., this operation was recorded using ``AD`` < *Base* > and computations by this routine are done using type *Base* . Addr **** Type used by random iterator for the player. play **** This is the operation sequence. random_itr ********** This is a random iterator for the operation sequence. op_usage ******** This argument has size equal to the number of operators in the operation sequence; i.e., num_op = play->nun_var_rec(). The value *op_usage* [ *i* ] have been set to the usage for the i-th operator in the operation sequence. vecad_used ********** This argument has size equal to the number of VecAD vectors in the operations sequences; i.e., play->num_var_vecad(). The VecAD vectors are indexed in the order that their indices appear in the one large play->GetVecInd that holds all the VecAD vectors. par_usage ********* The input size of this vector must be zero. Upon return it has size equal to the number of parameters in the operation sequence; i.e., play->num_par_all(); The value *par_usage* [ *i* ] is true if an only if the i-th parameter is used to compute a dependent variable or parameter. The nan at the beginning of the parameter vector and the independent dynamic parameters are always used. {xrst_end optimize_get_par_usage} */ // BEGIN_GET_PAR_USAGE template void get_par_usage( const player* play , const play::const_random_iterator& random_itr , const pod_vector& op_usage , pod_vector& vecad_used , pod_vector& par_usage ) // END_PROTOTYPE { CPPAD_ASSERT_UNKNOWN( op_usage.size() == play->num_var_op() ); CPPAD_ASSERT_UNKNOWN( par_usage.size() == 0 ); // // number of operators in the tape const size_t num_op = play->num_var_op(); // // number of parameters in the tape const size_t num_par = play->num_par_all(); // // number of dynamic parameters const size_t num_dynamic_par = play->num_dynamic_par(); // // number of independent dynamic parameters size_t n_dyn_independent = play->n_dyn_independent(); // // number of VecAD vectors size_t num_vecad_vec = play->num_var_vecad(); // // dynamic parameter information const pod_vector& par_is_dyn( play->par_is_dyn() ); const pod_vector& dyn_par_op( play->dyn_par_op() ); const pod_vector& dyn_par_arg( play->dyn_par_arg() ); const pod_vector& dyn2par_index( play->dyn2par_index() ); const pod_vector_maybe& par_all( play->par_all() ); // ----------------------------------------------------------------------- // initialize par_usage par_usage.resize(num_par); par_usage[0] = true; // true for nan at beginning of parameter vector for(size_t i_par = 1; i_par <= n_dyn_independent; ++i_par) par_usage[i_par] = true; // true for independent dynamic parameters for(size_t i_par = n_dyn_independent+1; i_par < num_par; ++i_par) par_usage[i_par] = false; // initialize as false for other parameters // // ----------------------------------------------------------------------- // set usage to true for VecAD parameters that get used size_t start_this_vector = 0; for(size_t i_vec = 0; i_vec < num_vecad_vec; ++i_vec) { // length of this vector (note length is not a parameter) size_t length = play->GetVecInd(start_this_vector); // if( vecad_used[i_vec] ) { // this vector gets used for(size_t k = 1; k <= length; ++k) { // index of parameter used by this VecAD vector size_t i_par = play->GetVecInd(start_this_vector + k); // must not be a dynamic parameter CPPAD_ASSERT_UNKNOWN( ! par_is_dyn[i_par] ); // set usage for this parameter par_usage[i_par] = true; } } start_this_vector += length + 1; } CPPAD_ASSERT_UNKNOWN( start_this_vector == play->num_var_vec_ind() ); // // ----------------------------------------------------------------------- // forward pass to mark which parameters are used by necessary operators // ----------------------------------------------------------------------- // // information about atomic function calls size_t atom_index=0, call_id=0, atom_m=0, atom_n=0, atom_i=0, atom_j=0; enum_atom_state atom_state = start_atom; // // work space used by user atomic functions vector parameter_x; // value of parameters in x vector type_x; // type for each component of z vector atom_ix; // variables indices for argument vector vector depend_y; // results that are used vector depend_x; // arguments that are used // for(size_t i_op = 0; i_op < num_op; ++i_op) { // information about current operator op_code_var op; // operator const addr_t* arg; // arguments size_t i_var; // variable index of first result random_itr.op_info(i_op, op, arg, i_var); // bool skip = op_usage[i_op] == usage_t(no_usage); skip &= atom_state == start_atom; if( ! skip ) switch( op ) { // add or subtract with left a parameter and right a variable case AddpvOp: case SubpvOp: if( par_is_dyn[ arg[0] ] ) par_usage[ arg[0] ] = true; else { // determine if this parameter will be absorbed by csum if( ! (op_usage[i_op] == csum_usage) ) { // determine operator corresponding to variable size_t j_op = random_itr.var2op(size_t(arg[1])); CPPAD_ASSERT_UNKNOWN( op_usage[j_op] != no_usage ); if( op_usage[j_op] != csum_usage ) par_usage[ arg[0] ] = true; } } break; // subtract with left a variable and right a parameter case SubvpOp: if( par_is_dyn[ arg[1] ] ) par_usage[ arg[1] ] = true; else { // determine if this parameter will be absorbed by csum if( ! (op_usage[i_op] == csum_usage) ) { // determine operator corresponding to variable size_t j_op = random_itr.var2op(size_t(arg[0])); CPPAD_ASSERT_UNKNOWN( op_usage[j_op] != no_usage ); if( op_usage[j_op] != csum_usage ) par_usage[ arg[1] ] = true; } } break; // cases with no parameter arguments case AbsOp: case AcosOp: case AcoshOp: case AddvvOp: case AsinOp: case AsinhOp: case AtanOp: case AtanhOp: case BeginOp: case CosOp: case CoshOp: case CSkipOp: case DisOp: case DivvvOp: case EndOp: case EqvvOp: case ExpOp: case Expm1Op: case InvOp: case LdvOp: case LevvOp: case LogOp: case Log1pOp: case LtvvOp: case MulvvOp: case NegOp: case NevvOp: case PowvvOp: case SignOp: case SinOp: case SinhOp: case SqrtOp: case StvvOp: case SubvvOp: case TanOp: case TanhOp: case ZmulvvOp: break; // cases where first and second arguments are parameters case EqppOp: case LeppOp: case LtppOp: case NeppOp: CPPAD_ASSERT_UNKNOWN( 2 <= NumArg(op) ) par_usage[arg[0]] = true; par_usage[arg[1]] = true; break; // cases where only first argument is a parameter case CSumOp: case EqpvOp: case DivpvOp: case LepvOp: case LtpvOp: case MulpvOp: case NepvOp: case ParOp: case PowpvOp: case ZmulpvOp: CPPAD_ASSERT_UNKNOWN( 1 <= NumArg(op) || op == CSumOp ) par_usage[arg[0]] = true; break; // cases where only second argument is a parameter case DivvpOp: case LevpOp: case LdpOp: case LtvpOp: case PowvpOp: case StpvOp: case ZmulvpOp: CPPAD_ASSERT_UNKNOWN( 2 <= NumArg(op) ) par_usage[arg[1]] = true; break; // cases where second and third arguments are parameters case ErfOp: case ErfcOp: case StppOp: CPPAD_ASSERT_UNKNOWN( 3 <= NumArg(op) ) par_usage[arg[1]] = true; par_usage[arg[2]] = true; break; // cases where only third argument is a parameter case StvpOp: CPPAD_ASSERT_UNKNOWN( 3 <= NumArg(op) ) par_usage[arg[2]] = true; break; // conditional expression operator case CExpOp: CPPAD_ASSERT_UNKNOWN( 6 == NumArg(op) ) if( (arg[1] & 1) == 0 ) par_usage[arg[2]] = true; if( (arg[1] & 2) == 0 ) par_usage[arg[3]] = true; if( (arg[1] & 4) == 0 ) par_usage[arg[4]] = true; if( (arg[1] & 8) == 0 ) par_usage[arg[5]] = true; break; // print function case PriOp: if( (arg[0] & 1) == 0 ) par_usage[arg[1]] = true; if( (arg[0] & 2) == 0 ) par_usage[arg[3]] = true; CPPAD_ASSERT_UNKNOWN( 5 == NumArg(op) ) break; // -------------------------------------------------------------- // atomic function calls case AFunOp: if( atom_state == start_atom ) { atom_index = size_t(arg[0]); call_id = size_t(arg[1]); atom_n = size_t(arg[2]); atom_m = size_t(arg[3]); atom_j = 0; atom_i = 0; atom_state = arg_atom; // ------------------------------------------------------- parameter_x.resize( atom_n ); type_x.resize( atom_n ); atom_ix.resize( atom_n ); // depend_y.resize( atom_m ); depend_x.resize( atom_n ); } else { CPPAD_ASSERT_UNKNOWN( atom_state == end_atom ); CPPAD_ASSERT_UNKNOWN( atom_n == size_t(arg[2]) ); CPPAD_ASSERT_UNKNOWN( atom_m == size_t(arg[3]) ); CPPAD_ASSERT_UNKNOWN( atom_j == atom_n ); CPPAD_ASSERT_UNKNOWN( atom_i == atom_m ); atom_state = start_atom; // // call atomic function for this operation sweep::call_atomic_rev_depend( atom_index, call_id, parameter_x, type_x, depend_x, depend_y ); for(size_t j = 0; j < atom_n; j++) if( depend_x[j] && type_x[j] != variable_enum ) { // This user argument is a parameter that is needed CPPAD_ASSERT_UNKNOWN( atom_ix[j] > 0 ); par_usage[ atom_ix[j] ] = true; } } break; case FunavOp: // this argument is a variable CPPAD_ASSERT_UNKNOWN( atom_state == arg_atom ); atom_ix[atom_j] = 0; parameter_x[atom_j] = par_all[0]; // variables get value nan type_x[atom_j] = variable_enum; ++atom_j; if( atom_j == atom_n ) atom_state = ret_atom; break; case FunapOp: // this argument is a parameter CPPAD_ASSERT_UNKNOWN( atom_state == arg_atom ); atom_ix[atom_j] = size_t( arg[0] ); parameter_x[atom_j] = par_all[arg[0]]; // parameter value if( par_is_dyn[arg[0]] ) type_x[atom_j] = dynamic_enum; else type_x[atom_j] = constant_enum; ++atom_j; if( atom_j == atom_n ) atom_state = ret_atom; break; case FunrpOp: // this result is a parameter CPPAD_ASSERT_UNKNOWN( atom_state == ret_atom ); depend_y[atom_i] = op_usage[i_op] != usage_t(no_usage); ++atom_i; if( atom_i == atom_m ) atom_state = end_atom; break; case FunrvOp: // this result is a variable CPPAD_ASSERT_UNKNOWN( atom_state == ret_atom ); depend_y[atom_i] = op_usage[i_op] != usage_t(no_usage); ++atom_i; if( atom_i == atom_m ) atom_state = end_atom; break; // -------------------------------------------------------------- default: CPPAD_ASSERT_UNKNOWN(false); } } // ----------------------------------------------------------------------- // reverse pass to determine which dynamic parameters are necessary // ----------------------------------------------------------------------- size_t i_arg = dyn_par_arg.size(); // index in dyn_par_arg size_t i_dyn = num_dynamic_par; // index in dyn2par_index while(i_dyn) { // next dynamic parameter in reverse order --i_dyn; op_code_dyn op = op_code_dyn( dyn_par_op[i_dyn] ); while( op == result_dyn ) { --i_dyn; op = op_code_dyn( dyn_par_op[i_dyn] ); CPPAD_ASSERT_UNKNOWN( op == result_dyn || op == atom_dyn ); } if( op == atom_dyn ) { // number of arguments for this operator size_t n_arg = size_t( dyn_par_arg[i_arg - 1] ); // // index of first argument for this operation i_arg -= n_arg; // atom_index = size_t( dyn_par_arg[i_arg + 0] ); call_id = size_t( dyn_par_arg[i_arg + 1] ); size_t n = size_t( dyn_par_arg[i_arg + 2] ); size_t m = size_t( dyn_par_arg[i_arg + 3] ); CPPAD_ASSERT_UNKNOWN( n_arg == 6 + n + m ); // // parameter_x, type_x parameter_x.resize(n); type_x.resize(n); for(size_t j = 0; j < n; ++j) { // parameter index zero is used for variable CPPAD_ASSERT_UNKNOWN( CppAD::isnan( par_all[0] ) ); addr_t arg_j = dyn_par_arg[i_arg + 5 + j]; parameter_x[j] = par_all[arg_j]; if( arg_j == 0 ) type_x[j] = variable_enum; else if( par_is_dyn[arg_j] ) type_x[j] = dynamic_enum; else type_x[j] = constant_enum; } // // depend_y depend_y.resize(m); for(size_t i = 0; i < m; ++i) { // a constant parameter cannot depend on a dynamic parameter // so do not worry about constant parameters in depend_y size_t i_par = size_t( dyn_par_arg[i_arg + 5 + n + i] ); depend_y[i] = par_usage[i_par]; } // // call back to atomic function for this operation depend_x.resize(n); sweep::call_atomic_rev_depend( atom_index, call_id, parameter_x, type_x, depend_x, depend_y ); // // transfer depend_x to par_usage for(size_t j = 0; j < n; ++j) { size_t i_par = size_t( dyn_par_arg[i_arg + 5 + j] ); par_usage[i_par] = par_usage[i_par] || depend_x[j]; } } else { // corresponding parameter index size_t i_par = size_t( dyn2par_index[i_dyn] ); CPPAD_ASSERT_UNKNOWN( par_is_dyn[i_par] ); // // number of arguments to this operator size_t n_arg = num_arg_dyn(op); // // index of first argument for this operator CPPAD_ASSERT_UNKNOWN( op != atom_dyn ); i_arg -= n_arg; // // if this dynamic parameter is needed if( par_usage[i_par] ) { // need dynamic parameters that are used to generate this one size_t offset = num_non_par_arg_dyn(op); for(size_t i = offset; i < n_arg; ++i) par_usage[ dyn_par_arg[i_arg + i] ] = true; } } } CPPAD_ASSERT_UNKNOWN( i_arg == 0 ); // return; } } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE # endif ================================================ FILE: include/cppad/local/optimize/hash_code.hpp ================================================ # ifndef CPPAD_LOCAL_OPTIMIZE_HASH_CODE_HPP # define CPPAD_LOCAL_OPTIMIZE_HASH_CODE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /*! \file local/optimize/hash_code.hpp CppAD hashing utility. */ // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE namespace CppAD { namespace local { namespace optimize { /*! Specialized hash code for a CppAD operator and its arguments (used during optimization). \param op is the operator that we are computing a hash code for. \param num_arg number of elements of arg to include in the hash code. \param arg is a vector of length num_arg containing the corresponding argument indices for this operator. \return is a hash code that is between zero and CPPAD_HASH_TABLE_SIZE - 1. */ inline size_t optimize_hash_code( opcode_t op , size_t num_arg , const addr_t* arg ) { CPPAD_ASSERT_UNKNOWN( num_arg < 4 ); size_t prime = 1; size_t sum = prime * size_t(op); for(size_t i = 0; i < num_arg; i++) { prime = prime + 2; // 3, 5, 7 in that order sum += prime * size_t(arg[i]); } // return sum % CPPAD_HASH_TABLE_SIZE; } } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE # endif ================================================ FILE: include/cppad/local/optimize/match_op.hpp ================================================ # ifndef CPPAD_LOCAL_OPTIMIZE_MATCH_OP_HPP # define CPPAD_LOCAL_OPTIMIZE_MATCH_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE namespace CppAD { namespace local { namespace optimize { /* {xrst_begin optimize_match_op dev} {xrst_spell erfc } Search for a Previous Operator that Matches Current Operator ############################################################ Syntax ****** | *exceed_collision_limit* = ``match_op`` ( | |tab| ``collision_limit`` , | |tab| ``random_itr`` , | |tab| ``op_previous`` , | |tab| ``current`` , | |tab| ``hash_tape_op`` , | |tab| ``work_bool`` , | |tab| ``work_addr_t`` | ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } Operator Arguments ****************** If an argument for the current operator is a variable, and the argument has previous match, the previous match for the argument is used when checking for a match for the current operator. collision_limit *************** is the maximum number of collisions (matches) allowed for one expression hash code value. random_itr ********** is a random iterator for the old operation sequence. op_previous *********** Mapping from operator index to previous operator that can replace this one. The input value of *previous* = *op_previous* [ *current* ] is assumed to be zero. If a match if found, the output value of *previous* is set to the matching operator index, otherwise it is left as is. Note that *previous* < *current* and *op_previous* [ ``previous`` ] is zero. current ******* is the index of the current operator which cannot be any of the operators in the list below: {xrst_literal // BEGIN_INVALID_OP // END_INVALID_OP } After this initialization, the value of *current* increases with each call to match_op. erf === The operators ``ErfOp`` and ``ErfcOp`` have three arguments, but only one true argument (the others are always the same). hash_table_op ************* is assumed to be initialized as a vector of empty sets before the first call to match_op (for a pass of the operation sequence). | |tab| *hash_table_op* . ``n_set`` () == ``CPPAD_HASH_TABLE_SIZE`` | |tab| *hash_table_op* . ``end`` () == *op_previous* . ``size`` () If *i_op* is an element of the j-th set, then the operation *op_previous* [ *i_op* ] has hash code j, and does not match any other element of the j-th set. An entry to j-th set for the current operator is added each time match_op is called and a match for the current operator is not found. work_bool ********* work space that is used by match_op between calls to increase speed. Should be empty on first call for this forward pass of the operation sequence and not modified until forward pass is done work_addr_t *********** work space that is used by match_op between calls to increase speed. Should be empty on first call for this forward pass of the operation sequence and not modified until forward pass is done exceed_collision_limit ********************** If the *collision_limit* is exceeded (is not exceeded), the return value is true (false). {xrst_end optimize_match_op} */ // BEGIN_PROTOTYPE template bool match_op( size_t collision_limit , const play::const_random_iterator& random_itr , pod_vector& op_previous , size_t current , sparse::list_setvec& hash_table_op , pod_vector& work_bool , pod_vector& work_addr_t ) // END_PROTOTYPE { # ifndef NDEBUG switch( random_itr.get_op(current) ) { // BEGIN_INVALID_OP case BeginOp: case CExpOp: case CSkipOp: case CSumOp: case EndOp: case InvOp: case LdpOp: case LdvOp: case ParOp: case PriOp: case StppOp: case StpvOp: case StvpOp: case StvvOp: case AFunOp: case FunapOp: case FunavOp: case FunrpOp: case FunrvOp: // END_INVALID_OP CPPAD_ASSERT_UNKNOWN(false); break; default: break; } # endif // initialize return value bool exceed_collision_limit = false; // num_op size_t num_op = random_itr.num_op(); // // num_var size_t num_var = random_itr.num_var(); // // variable is a reference to, and better name for, work_bool pod_vector& variable(work_bool); // // var2previous_var is a reference to, and better name for, work_addr_t pod_vector& var2previous_var(work_addr_t); if( var2previous_var.size() == 0 ) { var2previous_var.resize(num_var); for(size_t i = 0; i < num_var; ++i) var2previous_var[i] = addr_t(i); } // CPPAD_ASSERT_UNKNOWN( var2previous_var.size() == num_var ); CPPAD_ASSERT_UNKNOWN( num_op == op_previous.size() ); CPPAD_ASSERT_UNKNOWN( op_previous[current] == 0 ); CPPAD_ASSERT_UNKNOWN( hash_table_op.n_set() == CPPAD_HASH_TABLE_SIZE ); CPPAD_ASSERT_UNKNOWN( hash_table_op.end() == num_op ); CPPAD_ASSERT_UNKNOWN( current < num_op ); // // op, arg, i_var op_code_var op; const addr_t* arg; size_t i_var; random_itr.op_info(current, op, arg, i_var); // // num_arg size_t num_arg = NumArg(op); CPPAD_ASSERT_UNKNOWN( 0 < num_arg ); CPPAD_ASSERT_UNKNOWN( (num_arg < 3) || ( (num_arg == 3) && (op == ErfOp || op == ErfcOp) ) ); // arg_is_variable(op, arg, variable); CPPAD_ASSERT_UNKNOWN( variable.size() == num_arg ); // // If j-th argument to this operator is a variable, and a previous // variable will be used in its place, use the previous variable for // hash coding and matching. addr_t arg_match[] = { // Invalid value that will not be used. This initialization avoid // a warning on some compilers std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max() }; if( (op == AddvvOp) || (op == MulvvOp ) ) { // in special case where operator is commutative and operands are variables, // put lower index first so hash code does not depend on operator order CPPAD_ASSERT_UNKNOWN( num_arg == 2 ); arg_match[0] = var2previous_var[ arg[0] ]; arg_match[1] = var2previous_var[ arg[1] ]; if( arg_match[1] < arg_match[0] ) std::swap( arg_match[0], arg_match[1] ); } else for(size_t j = 0; j < num_arg; ++j) { arg_match[j] = arg[j]; if( variable[j] ) arg_match[j] = var2previous_var[ arg[j] ]; } // size_t code = optimize_hash_code(opcode_t(op), num_arg, arg_match); // // iterator for the set with this hash code sparse::list_setvec_const_iterator itr(hash_table_op, code); // // check for a match size_t count = 0; while( *itr != num_op ) { ++count; // // candidate previous for current operator size_t candidate = *itr; CPPAD_ASSERT_UNKNOWN( candidate < current ); CPPAD_ASSERT_UNKNOWN( op_previous[candidate] == 0 ); // op_code_var op_c; const addr_t* arg_c; size_t i_var_c; random_itr.op_info(candidate, op_c, arg_c, i_var_c); // // check for a match bool match = op == op_c; size_t j = 0; while( match & (j < num_arg) ) { if( variable[j] ) match &= arg_match[j] == var2previous_var[ arg_c[j] ]; else match &= arg_match[j] == arg_c[j]; ++j; } if( (! match) && ( (op == AddvvOp) || (op == MulvvOp) ) ) { // communative so check for reverse order match match = op == op_c; // // 2024-02-14: // If op_c is not AddvvOp or MulvvOp, its arguments may not be // variables and the code below could attempt to access // var2previous_var out of range. See 2024@mm-dd@02-14. if( match ) { match &= arg_match[0] == var2previous_var[ arg_c[1] ]; match &= arg_match[1] == var2previous_var[ arg_c[0] ]; } } if( match ) { op_previous[current] = static_cast( candidate ); if( NumRes(op) > 0 ) { CPPAD_ASSERT_UNKNOWN( i_var_c < i_var ); var2previous_var[i_var] = addr_t( i_var_c ); } return exceed_collision_limit; } ++itr; } // see print (that is commented out) at bottom of get_op_previous.hpp CPPAD_ASSERT_UNKNOWN( count <= collision_limit ); if( count == collision_limit ) { // restart the list hash_table_op.clear(code); // limit has been exceeded exceed_collision_limit = true; } // No match was found. Add this operator to the set for this hash code // Not using post_element because we need to iterate for // this code before adding another element for this code. hash_table_op.add_element(code, current); // return exceed_collision_limit; } } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE # endif ================================================ FILE: include/cppad/local/optimize/optimize_run.hpp ================================================ # ifndef CPPAD_LOCAL_OPTIMIZE_OPTIMIZE_RUN_HPP # define CPPAD_LOCAL_OPTIMIZE_OPTIMIZE_RUN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE namespace CppAD { namespace local { namespace optimize { /*! {xrst_begin optimize_run dev} {xrst_spell dep pri substring taddr } Convert a player object to an optimized recorder object ####################################################### Syntax ****** | *exceed_collision_limit* = ``local::optimize::optimize_run`` ( | |tab| ``options`` , ``n`` , ``dep_taddr`` , ``play`` , ``rec`` | ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } Addr **** Type to use for array elements in ``const_random_iterator`` . Base **** Base type for the operator; i.e., this operation was recorded using ``AD`` < *Base* > and computations by this routine are done using type *Base* . options ******* no_conditional_skip =================== If this sub-string appears, conditional skip operations will not be generated. This may make the optimize routine use significantly less memory and take significantly less time. no_compare_op ============= If this sub-string appears, then comparison operators will be removed from the optimized tape. These operators are necessary for the :ref:`compare_change-name` feature to be meaningful in the resulting recording. On the other hand, they are not necessary and take extra time when this feature is not needed. no_print_for_op =============== If this sub-string appears, then :ref:`printfor-name` operators ``PriOp`` will be removed from the optimized tape. These operators are useful for reporting problems evaluating derivatives at independent variable values different from those used to record a function. no_cumulative_sum_op ==================== If this sub-string appears, no cumulative sum operations will be generated during the optimization; see :ref:`optimize_cumulative_sum.cpp-name` . collision_limit=value ===================== If this substring appears, where *value* is a sequence of decimal digits, the optimizer's hash code collision limit will be set to *value* . When the collision limit is exceeded, the expressions with that hash code are removed and a new lists of expressions with that has code is started. The larger *value* , the more identical expressions the optimizer can recognize, but the slower the optimizer may run. The default for *value* is ``10`` . n * is the number of independent variables on the tape. dep_taddr ********* On input this vector contains the indices for each of the dependent variable values in the operation sequence corresponding to *play* . Upon return it contains the indices for the same variables but in the operation sequence corresponding to *rec* . play **** This is the operation sequence that we are optimizing. It is ``const`` except for the fact that *play* ``->setup_random`` () is called. rec *** The input contents of this recording must be empty; i.e., it corresponds to directly after the default constructor. Upon return, it contains an optimized version of the operation sequence corresponding to *play* . exceed_collision_limit ********************** If the *collision_limit* is exceeded (is not exceeded), the return value is true (false). Contents ******** {xrst_toc_table include/cppad/local/optimize/extract_option.hpp include/cppad/local/optimize/cexp_info.hpp include/cppad/local/optimize/get_cexp_info.hpp include/cppad/local/optimize/get_op_usage.hpp include/cppad/local/optimize/get_par_usage.hpp include/cppad/local/optimize/record_csum.hpp include/cppad/local/optimize/match_op.hpp include/cppad/local/optimize/get_op_previous.hpp } {xrst_end optimize_run} */ // BEGIN_PROTOTYPE template bool optimize_run( const std::string& options , size_t n , pod_vector& dep_taddr , player* play , recorder* rec ) // END_PROTOTYPE { bool exceed_collision_limit = false; // // check that recorder is empty CPPAD_ASSERT_UNKNOWN( rec->num_var_op() == 0 ); // // get a random iterator for this player Addr not_used; play->setup_random( not_used ); local::play::const_random_iterator random_itr = play->get_random( not_used ); // // compare_op, conditional_skip, cumulative_sum_op, print_for_op, // collision_limit options_t result = extract_option(options); bool compare_op = result.compare_op; bool conditional_skip = result.conditional_skip; bool cumulative_sum_op = result.cumulative_sum_op; bool print_for_op = result.print_for_op; size_t collision_limit = result.collision_limit; CPPAD_ASSERT_UNKNOWN( result.val_graph == false ); // // number of operators in the player const size_t num_op = play->num_var_op(); CPPAD_ASSERT_UNKNOWN( num_op < size_t( (std::numeric_limits::max)() ) ); // number of variables in the player # ifndef NDEBUG const size_t num_var = play->num_var(); # endif // number of parameter in the player const size_t num_par = play->num_par_all(); // number of VecAD indices size_t num_vecad_ind = play->num_var_vec_ind(); // number of VecAD vectors size_t num_vecad_vec = play->num_var_vecad(); // number of independent dynamic parameters size_t n_dyn_independent = play->n_dyn_independent(); // number of dynamic parameters size_t num_dynamic_par = play->num_dynamic_par(); // mapping from dynamic parameter index to parameter index const pod_vector& dyn2par_index( play->dyn2par_index() ); // number of dynamic parameters CPPAD_ASSERT_UNKNOWN( n_dyn_independent <= play->num_dynamic_par () ); // ----------------------------------------------------------------------- // operator information pod_vector cexp2op; sparse::list_setvec cexp_set; pod_vector vecad_used; pod_vector op_usage; get_op_usage( conditional_skip, compare_op, print_for_op, cumulative_sum_op, play, random_itr, dep_taddr, cexp2op, cexp_set, vecad_used, op_usage ); pod_vector op_previous; exceed_collision_limit |= get_op_previous( collision_limit, play, random_itr, cexp_set, op_previous, op_usage ); size_t num_cexp = cexp2op.size(); CPPAD_ASSERT_UNKNOWN( conditional_skip || num_cexp == 0 ); vector cexp_info; // struct_cexp_info not POD sparse::list_setvec skip_op_true; sparse::list_setvec skip_op_false; // if( cexp2op.size() > 0 ) get_cexp_info( play, random_itr, op_previous, op_usage, cexp2op, cexp_set, cexp_info, skip_op_true, skip_op_false ); // We no longer need cexp_set, and cexp2op, so free their memory cexp_set.resize(0, 0); cexp2op.clear(); // ----------------------------------------------------------------------- // dynamic parameter information pod_vector par_usage; get_par_usage( play, random_itr, op_usage, vecad_used, par_usage ); pod_vector dyn_previous; get_dyn_previous( play , par_usage , dyn_previous ); // ----------------------------------------------------------------------- // conditional expression information // // Size of the conditional expression information structure. // This is equal to the number of conditional expressions when // conditional_skip is true, otherwise it is zero. // // sort the conditional expression information by max_left_right // this is the conditional skip order vector cskip_order(num_cexp); if( num_cexp > 0 ) { vector keys(num_cexp); for(size_t i = 0; i < num_cexp; i++) keys[i] = size_t( cexp_info[i].max_left_right ); CppAD::index_sort(keys, cskip_order); } // initial index in conditional skip order size_t cskip_order_next = 0; // // initialize index in conditional expression order size_t cexp_next = 0; // mapping from conditional expression index to conditional skip // information on new tape pod_vector cskip_new(num_cexp); // // flag used to indicate that there is no conditional skip // for this conditional expression for(size_t i = 0; i < num_cexp; i++) cskip_new[i].i_arg = 0; // ======================================================================= // Create new recording // ======================================================================= // // dynamic parameter information in player const pod_vector& par_is_dyn( play->par_is_dyn() ); const pod_vector& dyn_par_op( play->dyn_par_op() ); const pod_vector& dyn_par_arg( play->dyn_par_arg() ); // // start mapping from old parameter indices to new parameter indices // for all parameters that get used. pod_vector new_par( num_par ); addr_t addr_t_max = (std::numeric_limits::max)(); for(size_t i_par = 0; i_par < num_par; ++i_par) new_par[i_par] = addr_t_max; // initialize as not used // // start new recording CPPAD_ASSERT_UNKNOWN( rec->num_var_op() == 0 ); rec->set_n_dyn_independent(n_dyn_independent); rec->set_abort_op_index(0); rec->set_record_compare( compare_op ); // copy parameters with index 0 CPPAD_ASSERT_UNKNOWN( ! par_is_dyn[0] && CppAD::isnan( play->par_one(0) ) ); rec->put_con_par( play->par_one(0) ); new_par[0] = 0; // set new_par for the independent dynamic parameters for(size_t i_par = 1; i_par <= n_dyn_independent; i_par++) { CPPAD_ASSERT_UNKNOWN( par_is_dyn[i_par] ); addr_t i = rec->put_dyn_par(play->par_one(i_par), ind_dyn); CPPAD_ASSERT_UNKNOWN( size_t(i) == i_par ); new_par[i_par] = i; } // set new_par for the constant parameters that are used for(size_t i_par = n_dyn_independent + 1; i_par < num_par; ++i_par) if( ! par_is_dyn[i_par] ) { CPPAD_ASSERT_UNKNOWN( i_par == 0 || n_dyn_independent < i_par ); if( par_usage[i_par] ) { // value of this parameter Base par = play->par_one(i_par); new_par[i_par] = rec->put_con_par(par); } } // index corresponding to the parameter zero addr_t zero_par_index = rec->put_con_par( Base(0) ); // set new_par for the dependent dynamic parameters size_t i_dyn = n_dyn_independent;// dynamic parameter index size_t i_arg = 0; // dynamic parameter argument index pod_vector arg_vec; for(size_t i_par = n_dyn_independent + 1; i_par < num_par; ++i_par) if( par_is_dyn[i_par] ) { // operator for this dynamic parameter op_code_dyn op = op_code_dyn( dyn_par_op[i_dyn] ); // // number of arguments for this dynamic parameter size_t n_arg = num_arg_dyn(op); // // number of dynamic parameter results for this operator size_t n_dyn = 1; // if( op == atom_dyn ) { size_t atom_index = size_t( dyn_par_arg[i_arg + 0] ); size_t call_id = size_t( dyn_par_arg[i_arg + 1] ); size_t atom_n = size_t( dyn_par_arg[i_arg + 2] ); size_t atom_m = size_t( dyn_par_arg[i_arg + 3] ); n_dyn = size_t( dyn_par_arg[i_arg + 4] ); n_arg = 6 + atom_n + atom_m; // // check if any dynamic parameter result for this operator is used bool call_used = false; # ifndef NDEBUG bool found_i_par = false; for(size_t i = 0; i < atom_m; ++i) { size_t j_par = size_t( dyn_par_arg[i_arg + 5 + atom_n + i] ); if( par_is_dyn[j_par] ) { call_used |= par_usage[j_par]; CPPAD_ASSERT_UNKNOWN( j_par == i_par || found_i_par ); // j_par > i_par corresponds to result_dyn operator CPPAD_ASSERT_UNKNOWN( j_par >= i_par ); found_i_par |= j_par == i_par; } } CPPAD_ASSERT_UNKNOWN( found_i_par ); # else for(size_t i = 0; i < atom_m; ++i) { size_t j_par = size_t( dyn_par_arg[i_arg + 5 + atom_n + i] ); if( par_is_dyn[j_par] ) call_used |= par_usage[j_par]; } # endif if( call_used ) { arg_vec.resize(0); arg_vec.push_back( addr_t( atom_index ) ); arg_vec.push_back( addr_t( call_id ) ); arg_vec.push_back( addr_t( atom_n ) ); arg_vec.push_back( addr_t( atom_m ) ); arg_vec.push_back( addr_t( n_dyn ) ); for(size_t j = 0; j < atom_n; ++j) { addr_t arg_j = dyn_par_arg[i_arg + 5 + j]; if( arg_j > 0 && par_usage[arg_j] ) arg_vec.push_back( new_par[ arg_j ] ); else arg_vec.push_back(0); } bool first_dynamic_result = true; for(size_t i = 0; i < atom_m; ++i) { addr_t res_i = dyn_par_arg[i_arg + 5 + atom_n + i]; CPPAD_ASSERT_UNKNOWN( par_is_dyn[res_i] || res_i == 0 ); // if( par_is_dyn[res_i] ) { Base par = play->par_one( size_t(res_i) ); if( first_dynamic_result ) { first_dynamic_result = false; new_par[res_i] = rec->put_dyn_par(par, atom_dyn); } else new_par[res_i] = rec->put_dyn_par(par, result_dyn); arg_vec.push_back( new_par[res_i] ); } else { // this result is a constant parameter if( new_par[res_i] != addr_t_max ) arg_vec.push_back( new_par[res_i] ); else { // this constant parameter is not used arg_vec.push_back(0); // phantom parameter } } } arg_vec.push_back( addr_t(6 + atom_n + atom_m ) ); rec->put_dyn_arg_vec( arg_vec ); } } else if( par_usage[i_par] && (op != result_dyn) ) { size_t j_dyn = size_t( dyn_previous[i_dyn] ); if( j_dyn != num_dynamic_par ) { size_t j_par = size_t( dyn2par_index[j_dyn] ); CPPAD_ASSERT_UNKNOWN( j_par < i_par ); new_par[i_par] = new_par[j_par]; } else { // value of this parameter Base par = play->par_one(i_par); // if( op == cond_exp_dyn ) { // cond_exp_dyn CPPAD_ASSERT_UNKNOWN( n_dyn_independent <= i_par ); CPPAD_ASSERT_UNKNOWN( n_arg == 5 ); new_par[i_par] = rec->put_dyn_cond_exp( par , // par CompareOp( dyn_par_arg[i_arg + 0] ), // cop new_par[ dyn_par_arg[i_arg + 1] ] , // left new_par[ dyn_par_arg[i_arg + 2] ] , // right new_par[ dyn_par_arg[i_arg + 3] ] , // if_true new_par[ dyn_par_arg[i_arg + 4] ] // if_false ); } else if( op == dis_dyn ) { // dis_dyn CPPAD_ASSERT_UNKNOWN( n_arg == 2 ); new_par[i_par] = rec->put_dyn_par( par , // par op , // op dyn_par_arg[i_arg + 0] , // index new_par[ dyn_par_arg[i_arg + 1] ] // parameter ); } else if( n_arg == 1 ) { // cases with one argument CPPAD_ASSERT_UNKNOWN( num_non_par_arg_dyn(op) == 0 ); CPPAD_ASSERT_UNKNOWN( n_dyn_independent <= i_par ); new_par[i_par] = rec->put_dyn_par( par, op, new_par[ dyn_par_arg[i_arg + 0] ] ); } else if( n_arg == 2 ) { // cases with two arguments CPPAD_ASSERT_UNKNOWN( n_dyn_independent <= i_par ); CPPAD_ASSERT_UNKNOWN( num_non_par_arg_dyn(op) == 0 ); new_par[i_par] = rec->put_dyn_par( par, op, new_par[ dyn_par_arg[i_arg + 0] ], new_par[ dyn_par_arg[i_arg + 1] ] ); } else { // independent dynamic parameter case CPPAD_ASSERT_UNKNOWN( op == ind_dyn ) CPPAD_ASSERT_UNKNOWN( i_par <= n_dyn_independent ); CPPAD_ASSERT_UNKNOWN( n_arg == 0 ); new_par[i_par] = rec->put_dyn_par( par, op); } } } ++i_dyn; i_arg += n_arg; } // ----------------------------------------------------------------------- // There is an additional constant parameter for each cumulative summation // (that does not have a corresponding old parameter index). // ------------------------------------------------------------------------ // initialize mapping from old VecAD index to new VecAD index CPPAD_ASSERT_UNKNOWN( size_t( (std::numeric_limits::max)() ) >= num_vecad_ind ); pod_vector new_vecad_ind(num_vecad_ind); for(size_t i = 0; i < num_vecad_ind; i++) new_vecad_ind[i] = addr_t( num_vecad_ind ); // invalid index { size_t j = 0; // index into the old set of indices for(size_t i = 0; i < num_vecad_vec; i++) { // length of this VecAD size_t length = play->GetVecInd(j); if( vecad_used[i] ) { // Put this VecAD vector in new recording CPPAD_ASSERT_UNKNOWN(length < num_vecad_ind); new_vecad_ind[j] = rec->put_var_vecad_ind( addr_t(length) ); for(size_t k = 1; k <= length; k++) new_vecad_ind[j+k] = rec->put_var_vecad_ind( new_par[ play->GetVecInd(j+k) ] ); } // start of next VecAD j += length + 1; } CPPAD_ASSERT_UNKNOWN( j == num_vecad_ind ); } // temporary buffer for new argument values addr_t new_arg[6]; // temporary work space used by record_csum // (declared here to avoid realloaction of memory) struct_csum_stacks csum_work; // temporary used to hold a size_pair struct_size_pair size_pair; // // Mapping from old operator index to new variable index, // zero is invalid except for new_var[0]. pod_vector new_var(num_op); // // Mapping from old operator index to new operator index will share // memory with op_previous. Must get op_previous[i_op] for this operator // before over writing it with new_op[i_op]. pod_vector& new_op( op_previous ); CPPAD_ASSERT_UNKNOWN( new_op.size() == num_op ); // ------------------------------------------------------------- // information for current operator size_t i_op; // index op_code_var op; // operator const addr_t* arg; // arguments size_t i_var; // variable index of primary (last) result // // information about atomic function enum_atom_state atom_state = start_atom; size_t atom_i = 0; size_t atom_j = 0; // i_var = 0; for(i_op = 0; i_op < num_op; ++i_op) { // if non-zero, use previous result in place of this operator. // Must get this information before writing new_op[i_op]. size_t previous = size_t( op_previous[i_op] ); // // zero is invalid except for new_op[0]. new_op[i_op] = 0; // // Zero is invalid except for new_var[0] and previous is zero unless // this operator is replace by a previous operator. new_var[i_op] = 0; if( op_usage[i_op] == usage_t(yes_usage) ) new_var[i_op] = new_var[previous]; // // temporary used in some switch cases addr_t mask; // // this operator information size_t i_tmp; random_itr.op_info(i_op, op, arg, i_tmp); if( NumRes(op) > 0 ) i_var = i_tmp; // // is this new result the top of a cumulative summation bool top_csum; // // determine if we should insert a conditional skip here bool skip = conditional_skip; if( skip ) { skip &= cskip_order_next < num_cexp; skip &= op != BeginOp; skip &= op != InvOp; skip &= atom_state == start_atom; if( skip ) { size_t j = cskip_order[cskip_order_next]; if( NumRes(op) > 0 ) skip &= size_t( cexp_info[j].max_left_right ) < i_var; else skip &= size_t( cexp_info[j].max_left_right ) <= i_var; } if( skip ) { size_t j = cskip_order[cskip_order_next]; cskip_order_next++; size_t n_true = skip_op_true.number_elements(j); size_t n_false = skip_op_false.number_elements(j); skip &= n_true > 0 || n_false > 0; if( skip ) { CPPAD_ASSERT_UNKNOWN( NumRes(CSkipOp) == 0 ); size_t n_arg = 7 + size_t(n_true) + size_t(n_false); // reserve space for the arguments to this operator but // delay setting them until we have all the new addresses cskip_new[j].i_arg = rec->ReserveArg(n_arg); // i_arg == 0 is used to check if conditional expression // has been skipped. CPPAD_ASSERT_UNKNOWN( cskip_new[j].i_arg > 0 ); // There is no corresponding old operator in this case rec->PutOp(CSkipOp); } } } // CPPAD_ASSERT_UNKNOWN( size_t( (std::numeric_limits::max)() ) >= rec->num_var_op() ); // // For each call, first and second AFunOp will have same op_usage skip = op_usage[i_op] != usage_t( yes_usage ); skip &= atom_state != arg_atom && atom_state != ret_atom; if( skip ) { if( op == CExpOp ) ++cexp_next; // if( op == AFunOp ) { if( atom_state == start_atom ) atom_state = end_atom; else { CPPAD_ASSERT_UNKNOWN( atom_state == end_atom ); atom_state = start_atom; } } } else switch( op ) { // op_usage[i_op] == usage_t(yes_usage) case BeginOp: CPPAD_ASSERT_UNKNOWN( previous == 0 ); CPPAD_ASSERT_NARG_NRES(op, 1, 1); // Put BeginOp at beginning of recording new_op[i_op] = addr_t( rec->num_var_op() ); new_var[i_op] = rec->PutOp(BeginOp); rec->PutArg(arg[0]); break; // -------------------------------------------------------------- // Unary operators, argument a variable, one result case AbsOp: case AcosOp: case AcoshOp: case AsinOp: case AsinhOp: case AtanOp: case AtanhOp: case CosOp: case CoshOp: case ErfOp: case ErfcOp: case ExpOp: case Expm1Op: case LogOp: case Log1pOp: case NegOp: case SignOp: case SinOp: case SinhOp: case SqrtOp: case TanOp: case TanhOp: if( previous == 0 ) { // new_arg[0] = new_var[ random_itr.var2op(size_t(arg[0])) ]; rec->PutArg( new_arg[0] ); // new_op[i_op] = addr_t( rec->num_var_op() ); new_var[i_op] = rec->PutOp(op); CPPAD_ASSERT_UNKNOWN( new_arg[0] < new_var[random_itr.var2op(i_var)] ); if( op == ErfOp || op == ErfcOp ) { CPPAD_ASSERT_NARG_NRES(op, 3, 5); // Error function is a special case // second argument is always the parameter 0 // third argument is always the parameter 2 / sqrt(pi) CPPAD_ASSERT_UNKNOWN( NumArg(ErfOp) == 3 ); rec->PutArg( rec->put_con_par( Base(0.0) ) ); rec->PutArg( rec->put_con_par( Base( 1.0 / std::sqrt( std::atan(1.0) ) ) ) ); } else { // some of these operators have an auxiliary result; // e.g. sine and cosine are computed together. CPPAD_ASSERT_UNKNOWN( NumArg(op) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(op) ==1 || NumRes(op) == 2 ); } } break; // --------------------------------------------------- // Binary operators, left variable, right parameter, one result case SubvpOp: // check if this is the top of a csum connection i_tmp = random_itr.var2op(size_t(arg[0])); top_csum = op_usage[i_tmp] == usage_t(csum_usage); if( top_csum ) { CPPAD_ASSERT_UNKNOWN( previous == 0 ); // // convert to a sequence of summation operators size_pair = record_csum( play , random_itr , op_usage , new_par , new_var , i_var , rec , csum_work ); new_op[i_op] = addr_t( size_pair.i_op ); new_var[i_op] = addr_t( size_pair.i_var ); // abort rest of this case break; } case DivvpOp: case PowvpOp: case ZmulvpOp: if( previous == 0 ) { // size_pair = record_vp( play , random_itr , new_par , new_var , i_op , rec ); new_op[i_op] = addr_t( size_pair.i_op ); new_var[i_op] = addr_t( size_pair.i_var ); } break; // --------------------------------------------------- // Binary operators, left index, right variable, one result case DisOp: CPPAD_ASSERT_NARG_NRES(op, 2, 1); if( previous == 0 ) { // new_arg[0] = arg[0]; new_arg[1] = new_var[ random_itr.var2op(size_t(arg[1])) ]; rec->PutArg( new_arg[0], new_arg[1] ); // new_op[i_op] = addr_t( rec->num_var_op() ); new_var[i_op] = rec->PutOp(op); CPPAD_ASSERT_UNKNOWN( new_arg[1] < new_var[random_itr.var2op(i_var)] ); } break; // --------------------------------------------------- // Binary operators, left parameter, right variable, one result case SubpvOp: case AddpvOp: // check if this is the top of a csum connection i_tmp = random_itr.var2op(size_t(arg[1])); top_csum = op_usage[i_tmp] == usage_t(csum_usage); if( top_csum ) { CPPAD_ASSERT_UNKNOWN( previous == 0 ); // // convert to a sequence of summation operators size_pair = record_csum( play , random_itr , op_usage , new_par , new_var , i_var , rec , csum_work ); new_op[i_op] = addr_t( size_pair.i_op ); new_var[i_op] = addr_t( size_pair.i_var ); // abort rest of this case break; } case DivpvOp: case MulpvOp: case PowpvOp: case ZmulpvOp: if( previous == 0 ) { // size_pair = record_pv( play , random_itr , new_par , new_var , i_op , rec ); new_op[i_op] = addr_t( size_pair.i_op ); new_var[i_op] = addr_t( size_pair.i_var ); } break; // --------------------------------------------------- // Binary operator, left and right variables, one result case AddvvOp: case SubvvOp: // check if this is the top of a csum connection i_tmp = random_itr.var2op(size_t(arg[0])); top_csum = op_usage[i_tmp] == usage_t(csum_usage); i_tmp = random_itr.var2op(size_t(arg[1])); top_csum |= op_usage[i_tmp] == usage_t(csum_usage); if( top_csum ) { CPPAD_ASSERT_UNKNOWN( previous == 0 ); // // convert to a sequence of summation operators size_pair = record_csum( play , random_itr , op_usage , new_par , new_var , i_var , rec , csum_work ); new_op[i_op] = addr_t( size_pair.i_op ); new_var[i_op] = addr_t( size_pair.i_var ); // abort rest of this case break; } case DivvvOp: case MulvvOp: case PowvvOp: case ZmulvvOp: if( previous == 0 ) { // size_pair = record_vv( play , random_itr , new_var , i_op , rec ); new_op[i_op] = addr_t( size_pair.i_op ); new_var[i_op] = addr_t( size_pair.i_var ); } break; // --------------------------------------------------- // Conditional expression operators case CExpOp: CPPAD_ASSERT_UNKNOWN( previous == 0 ); CPPAD_ASSERT_NARG_NRES(op, 6, 1); new_arg[0] = arg[0]; new_arg[1] = arg[1]; mask = 1; for(size_t i = 2; i < 6; i++) { if( arg[1] & mask ) { new_arg[i] = new_var[ random_itr.var2op(size_t(arg[i])) ]; CPPAD_ASSERT_UNKNOWN( size_t(new_arg[i]) < num_var ); } else new_arg[i] = new_par[ arg[i] ]; mask = mask << 1; } rec->PutArg( new_arg[0] , new_arg[1] , new_arg[2] , new_arg[3] , new_arg[4] , new_arg[5] ); new_op[i_op] = addr_t( rec->num_var_op() ); new_var[i_op] = rec->PutOp(op); // // The new addresses for left and right are used during // fill in the arguments for the CSkip operations. This does not // affect max_left_right which is used during this sweep. if( conditional_skip ) { CPPAD_ASSERT_UNKNOWN( cexp_next < num_cexp ); CPPAD_ASSERT_UNKNOWN( size_t( cexp_info[cexp_next].i_op ) == i_op ); cskip_new[ cexp_next ].left = size_t( new_arg[2] ); cskip_new[ cexp_next ].right = size_t( new_arg[3] ); ++cexp_next; } break; // --------------------------------------------------- // Operations with no arguments and no results case EndOp: CPPAD_ASSERT_UNKNOWN( previous == 0 ); CPPAD_ASSERT_NARG_NRES(op, 0, 0); new_op[i_op] = addr_t( rec->num_var_op() ); rec->PutOp(op); break; // --------------------------------------------------- // Comparison operations: two arguments and no results // case LeppOp: case LtppOp: case EqppOp: case NeppOp: CPPAD_ASSERT_UNKNOWN( compare_op ); CPPAD_ASSERT_NARG_NRES(op, 2, 0); if( previous == 0 ) { new_arg[0] = new_par[ arg[0] ]; new_arg[1] = new_par[ arg[1] ]; rec->PutArg(new_arg[0], new_arg[1]); new_op[i_op] = addr_t( rec->num_var_op() ); rec->PutOp(op); } break; // case LepvOp: case LtpvOp: case EqpvOp: case NepvOp: CPPAD_ASSERT_UNKNOWN( compare_op ); CPPAD_ASSERT_NARG_NRES(op, 2, 0); if( previous == 0 ) { new_arg[0] = new_par[ arg[0] ]; new_arg[1] = new_var[ random_itr.var2op(size_t(arg[1])) ]; rec->PutArg(new_arg[0], new_arg[1]); new_op[i_op] = addr_t( rec->num_var_op() ); rec->PutOp(op); } break; // case LevpOp: case LtvpOp: CPPAD_ASSERT_UNKNOWN( compare_op ); CPPAD_ASSERT_NARG_NRES(op, 2, 0); if( previous == 0 ) { new_arg[0] = new_var[ random_itr.var2op(size_t(arg[0])) ]; new_arg[1] = new_par[ arg[1] ]; rec->PutArg(new_arg[0], new_arg[1]); new_op[i_op] = addr_t( rec->num_var_op() ); rec->PutOp(op); } break; // case LevvOp: case LtvvOp: case EqvvOp: case NevvOp: CPPAD_ASSERT_UNKNOWN( compare_op ); CPPAD_ASSERT_NARG_NRES(op, 2, 0); if( previous == 0 ) { new_arg[0] = new_var[ random_itr.var2op(size_t(arg[0])) ]; new_arg[1] = new_var[ random_itr.var2op(size_t(arg[1])) ]; rec->PutArg(new_arg[0], new_arg[1]); new_op[i_op] = addr_t( rec->num_var_op() ); rec->PutOp(op); } break; // --------------------------------------------------- // Operations with no arguments and one result case InvOp: CPPAD_ASSERT_UNKNOWN( previous == 0 ); CPPAD_ASSERT_NARG_NRES(op, 0, 1); new_op[i_op] = addr_t( rec->num_var_op() ); new_var[i_op] = rec->PutOp(op); break; // --------------------------------------------------- // Unary operators, argument a parameter, one result case ParOp: CPPAD_ASSERT_UNKNOWN( previous == 0 ); CPPAD_ASSERT_NARG_NRES(op, 1, 1); new_arg[0] = new_par[ arg[0] ]; rec->PutArg( new_arg[0] ); // new_op[i_op] = addr_t( rec->num_var_op() ); new_var[i_op] = rec->PutOp(op); break; // --------------------------------------------------- // print forward operator case PriOp: CPPAD_ASSERT_UNKNOWN( previous == 0 ); CPPAD_ASSERT_NARG_NRES(op, 5, 0); // arg[0] new_arg[0] = arg[0]; // // arg[1] if( arg[0] & 1 ) { new_arg[1] = new_var[ random_itr.var2op(size_t(arg[1])) ]; CPPAD_ASSERT_UNKNOWN( size_t(new_arg[1]) < num_var ); } else { new_arg[1] = new_par[ arg[1] ]; } // // arg[3] if( arg[0] & 2 ) { new_arg[3] = new_var[ random_itr.var2op(size_t(arg[3])) ]; CPPAD_ASSERT_UNKNOWN( size_t(new_arg[3]) < num_var ); } else { new_arg[3] = new_par[ arg[3] ]; } new_arg[2] = rec->PutTxt( play->GetTxt(size_t(arg[2])) ); new_arg[4] = rec->PutTxt( play->GetTxt(size_t(arg[4])) ); // rec->PutArg( new_arg[0] , new_arg[1] , new_arg[2] , new_arg[3] , new_arg[4] ); // new operator new_op[i_op] = addr_t( rec->num_var_op() ); // no new variable rec->PutOp(op); break; // --------------------------------------------------- // VecAD operators // Load using a parameter index case LdpOp: CPPAD_ASSERT_UNKNOWN( previous == 0 ); CPPAD_ASSERT_NARG_NRES(op, 3, 1); new_arg[0] = new_vecad_ind[ arg[0] ]; new_arg[1] = new_par[ arg[1] ]; CPPAD_ASSERT_UNKNOWN( size_t( (std::numeric_limits::max)() ) >= rec->num_var_load() ); new_arg[2] = addr_t( rec->num_var_load() ); CPPAD_ASSERT_UNKNOWN( size_t(new_arg[0]) < num_vecad_ind ); rec->PutArg( new_arg[0], new_arg[1], new_arg[2] ); new_op[i_op] = addr_t( rec->num_var_op() ); new_var[i_op] = rec->PutLoadOp(op); break; // Load using a variable index case LdvOp: CPPAD_ASSERT_UNKNOWN( previous == 0 ); CPPAD_ASSERT_NARG_NRES(op, 3, 1); new_arg[0] = new_vecad_ind[ arg[0] ]; new_arg[1] = new_var[ random_itr.var2op(size_t(arg[1])) ]; CPPAD_ASSERT_UNKNOWN( size_t( (std::numeric_limits::max)() ) >= rec->num_var_load() ); new_arg[2] = addr_t( rec->num_var_load() ); CPPAD_ASSERT_UNKNOWN( size_t(new_arg[0]) < num_vecad_ind ); CPPAD_ASSERT_UNKNOWN( size_t(new_arg[1]) < num_var ); rec->PutArg( new_arg[0], new_arg[1], new_arg[2] ); new_op[i_op] = addr_t( rec->num_var_op() ); new_var[i_op] = rec->PutLoadOp(op); break; // Store a parameter using a parameter index case StppOp: CPPAD_ASSERT_UNKNOWN( previous == 0 ); CPPAD_ASSERT_NARG_NRES(op, 3, 0); new_arg[0] = new_vecad_ind[ arg[0] ]; new_arg[1] = new_par[ arg[1] ]; new_arg[2] = new_par[ arg[2] ]; CPPAD_ASSERT_UNKNOWN( size_t(new_arg[0]) < num_vecad_ind ); rec->PutArg( new_arg[0], new_arg[1], new_arg[2] ); new_op[i_op] = addr_t( rec->num_var_op() ); rec->PutOp(op); break; // Store a parameter using a variable index case StvpOp: CPPAD_ASSERT_UNKNOWN( previous == 0 ); CPPAD_ASSERT_NARG_NRES(op, 3, 0); new_arg[0] = new_vecad_ind[ arg[0] ]; new_arg[1] = new_var[ random_itr.var2op(size_t(arg[1])) ]; new_arg[2] = new_par[ arg[2] ]; CPPAD_ASSERT_UNKNOWN( size_t(new_arg[0]) < num_vecad_ind ); CPPAD_ASSERT_UNKNOWN( size_t(new_arg[1]) < num_var ); rec->PutArg( new_arg[0], new_arg[1], new_arg[2] ); new_op[i_op] = addr_t( rec->num_var_op() ); rec->PutOp(op); break; // Store a variable using a parameter index case StpvOp: CPPAD_ASSERT_UNKNOWN( previous == 0 ); CPPAD_ASSERT_NARG_NRES(op, 3, 0); new_arg[0] = new_vecad_ind[ arg[0] ]; new_arg[1] = new_par[ arg[1] ]; new_arg[2] = new_var[ random_itr.var2op(size_t(arg[2])) ]; CPPAD_ASSERT_UNKNOWN( size_t(new_arg[0]) < num_vecad_ind ); CPPAD_ASSERT_UNKNOWN( size_t(new_arg[2]) < num_var ); rec->PutArg( new_arg[0], new_arg[1], new_arg[2] ); new_op[i_op] = addr_t( rec->num_var_op() ); rec->PutOp(op); break; // Store a variable using a variable index case StvvOp: CPPAD_ASSERT_UNKNOWN( previous == 0 ); CPPAD_ASSERT_NARG_NRES(op, 3, 0); new_arg[0] = new_vecad_ind[ arg[0] ]; new_arg[1] = new_var[ random_itr.var2op(size_t(arg[1])) ]; new_arg[2] = new_var[ random_itr.var2op(size_t(arg[2])) ]; CPPAD_ASSERT_UNKNOWN( size_t(new_arg[0]) < num_vecad_ind ); CPPAD_ASSERT_UNKNOWN( size_t(new_arg[1]) < num_var ); CPPAD_ASSERT_UNKNOWN( size_t(new_arg[2]) < num_var ); rec->PutArg( new_arg[0], new_arg[1], new_arg[2] ); new_op[i_op] = addr_t( rec->num_var_op() ); rec->PutOp(op); break; // ----------------------------------------------------------- // atomic function call operators case AFunOp: CPPAD_ASSERT_UNKNOWN( previous == 0 ); CPPAD_ASSERT_NARG_NRES(op, 4, 0); // atom_index, atom_old, atom_n, atom_m rec->PutArg(arg[0], arg[1], arg[2], arg[3]); new_op[i_op] = addr_t( rec->num_var_op() ); rec->PutOp(AFunOp); if( atom_state == start_atom ) { atom_state = arg_atom; atom_j = size_t( arg[2] ); // just for counting arguments atom_i = size_t( arg[3] ); // just for counting results CPPAD_ASSERT_UNKNOWN( atom_j > 0 ); CPPAD_ASSERT_UNKNOWN( atom_i > 0 ); } else { CPPAD_ASSERT_UNKNOWN( atom_state == end_atom ); atom_state = start_atom; } break; case FunapOp: CPPAD_ASSERT_UNKNOWN( previous == 0 ); CPPAD_ASSERT_NARG_NRES(op, 1, 0); new_arg[0] = new_par[ arg[0] ]; if( new_arg[0] == addr_t_max ) { // This parameter is not used, so we put zero here. If we // put nan here, atomic reverse mode would have to use azmul. new_arg[0] = zero_par_index; } rec->PutArg(new_arg[0]); new_op[i_op] = addr_t( rec->num_var_op() ); rec->PutOp(FunapOp); CPPAD_ASSERT_UNKNOWN( atom_state == arg_atom ); --atom_j; if( atom_j == 0 ) atom_state = ret_atom; break; case FunavOp: CPPAD_ASSERT_UNKNOWN( previous == 0 ); CPPAD_ASSERT_NARG_NRES(op, 1, 0); new_arg[0] = new_var[ random_itr.var2op(size_t(arg[0])) ]; CPPAD_ASSERT_UNKNOWN( size_t(new_arg[0]) < num_var ); if( new_arg[0] != 0 ) { rec->PutArg(new_arg[0]); new_op[i_op] = addr_t( rec->num_var_op() ); rec->PutOp(FunavOp); } else { // This argument does not affect the result. new_arg[0] = zero_par_index; rec->PutArg(new_arg[0]); new_op[i_op] = addr_t( rec->num_var_op() ); rec->PutOp(FunapOp); } CPPAD_ASSERT_UNKNOWN( atom_state == arg_atom ); --atom_j; if( atom_j == 0 ) atom_state = ret_atom; break; case FunrpOp: CPPAD_ASSERT_UNKNOWN( previous == 0 ); CPPAD_ASSERT_NARG_NRES(op, 1, 0); new_arg[0] = new_par[ arg[0] ]; if( new_arg[0] == addr_t_max ) { // This parameter is not used here or anywhere. CPPAD_ASSERT_UNKNOWN( op_usage[i_op] == usage_t(no_usage) ); new_arg[0] = zero_par_index; } rec->PutArg(new_arg[0]); // new_op[i_op] = addr_t( rec->num_var_op() ); rec->PutOp(FunrpOp); CPPAD_ASSERT_UNKNOWN( atom_state == ret_atom ); --atom_i; if( atom_i == 0 ) atom_state = end_atom; break; case FunrvOp: CPPAD_ASSERT_UNKNOWN( previous == 0 ); CPPAD_ASSERT_NARG_NRES(op, 0, 1); new_op[i_op] = addr_t( rec->num_var_op() ); if( op_usage[i_op] == usage_t(yes_usage) ) new_var[i_op] = rec->PutOp(FunrvOp); else { // This result is not used. CPPAD_ASSERT_UNKNOWN( op_usage[i_op] == usage_t(no_usage) ); CPPAD_ASSERT_NARG_NRES(FunrpOp, 1, 0); rec->PutArg( zero_par_index ); rec->PutOp(FunrpOp); } --atom_i; if( atom_i == 0 ) atom_state = end_atom; break; // --------------------------------------------------- case CSumOp: // --------------------------------------------------- CPPAD_ASSERT_UNKNOWN( previous == 0 ); // // check if more entries can be included in this summation size_pair = record_csum( play , random_itr , op_usage , new_par , new_var , i_var , rec , csum_work ); new_op[i_op] = addr_t( size_pair.i_op ); new_var[i_op] = addr_t( size_pair.i_var ); break; // --------------------------------------------------- // all cases should be handled above default: CPPAD_ASSERT_UNKNOWN(false); } } // modify the dependent variable vector to new indices for(size_t i = 0; i < dep_taddr.size(); i++ ) { dep_taddr[i] = size_t(new_var[ random_itr.var2op(dep_taddr[i]) ]); CPPAD_ASSERT_UNKNOWN( size_t(dep_taddr[i]) < num_var ); } # ifndef NDEBUG for(i_op = 0; i_op < num_op; i_op++) { random_itr.op_info(i_op, op, arg, i_var); if( NumRes(op) > 0 ) CPPAD_ASSERT_UNKNOWN( size_t(new_op[i_op]) < rec->num_var_op() ); } # endif // make sure that all the conditional expressions have been // checked to see if they are still present CPPAD_ASSERT_UNKNOWN( cskip_order_next == num_cexp ); // fill in the arguments for the CSkip operations for(size_t i = 0; i < num_cexp; i++) { // if cskip_new[i].i_arg == 0, this conditional expression was skipped if( cskip_new[i].i_arg > 0 ) { // size_t i_arg struct_cexp_info info = cexp_info[i]; addr_t n_true = addr_t( skip_op_true.number_elements(i) ); addr_t n_false = addr_t( skip_op_false.number_elements(i) ); i_arg = cskip_new[i].i_arg; addr_t left = addr_t( cskip_new[i].left ); addr_t right = addr_t( cskip_new[i].right ); rec->ReplaceArg(i_arg++, addr_t(info.cop) ); rec->ReplaceArg(i_arg++, addr_t(info.flag) ); rec->ReplaceArg(i_arg++, left ); rec->ReplaceArg(i_arg++, right ); rec->ReplaceArg(i_arg++, n_true ); rec->ReplaceArg(i_arg++, n_false ); sparse::list_setvec::const_iterator itr_true(skip_op_true, i); while( *itr_true != skip_op_true.end() ) { i_op = *itr_true; // op_usage[i_op] == usage_t(yes_usage) CPPAD_ASSERT_UNKNOWN( new_op[i_op] != 0 ); rec->ReplaceArg(i_arg++, new_op[i_op] ); // ++itr_true; } sparse::list_setvec::const_iterator itr_false(skip_op_false, i); while( *itr_false != skip_op_false.end() ) { i_op = *itr_false; // op_usage[i_op] == usage_t(yes_usage) CPPAD_ASSERT_UNKNOWN( new_op[i_op] != 0 ); rec->ReplaceArg(i_arg++, new_op[i_op] ); // ++itr_false; } rec->ReplaceArg(i_arg++, 7 + n_true + n_false); # ifndef NDEBUG size_t n_arg = 7 + size_t(n_true) + size_t(n_false); CPPAD_ASSERT_UNKNOWN( cskip_new[i].i_arg + n_arg == i_arg ); # endif } } return exceed_collision_limit; } } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE # endif ================================================ FILE: include/cppad/local/optimize/record_csum.hpp ================================================ # ifndef CPPAD_LOCAL_OPTIMIZE_RECORD_CSUM_HPP # define CPPAD_LOCAL_OPTIMIZE_RECORD_CSUM_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE namespace CppAD { namespace local { namespace optimize { /*! {xrst_begin optimize_record_csum dev} {xrst_spell addpv addvv subpv subvp subvv } Recording a Cumulative Summation Operator ######################################### Prototype ********* {xrst_literal // BEGIN_RECORD_CSUM // END_PROTOTYPE } play **** player object corresponding to the old recording. random_itr ********** is a random iterator corresponding to the old operation sequence. op_usage ******** mapping from old operator index to how it is used. new_par ******* mapping from old parameter index to parameter index in new recording. new_var ******* mapping from old operator index to variable index in new recording. current ******* is the index in the old operation sequence for the variable corresponding to the result for the current operator. We use the notation *i_op* = *random_itr* . ``var2op`` ( *current* ) . It follows that NumRes( random_itr.get_op[i_op] ) > 0. If 0 < j_op < i_op, either op_usage[j_op] == usage_t(csum_usage), op_usage[j_op] = usage_t(no_usage), or new_var[j_op] != 0. rec *** is the object that will record the new operations. return ****** is the operator and variable indices in the new operation sequence. stack ***** Is temporary work space. On input and output, stack.op_info, stack.add_var, and stack.sub_var, are all empty. These stacks are passed in so that they are created once and then be reused with calls to ``record_csum`` . Assumptions *********** #. random_itr.get_op[i_op] must be one of the following: CSumOp, AddpvOp, AddvvOp, SubpvOp, SubvpOp, SubvvOp. #. op_usage[i_op] == usage_t(yes_usage). #. Either this is a CSumOp, or op_usage[j_op] == usage_t(csum_usage) is true from some j_op that corresponds to a variable that is an argument to random_itr.get_op[i_op]. {xrst_end optimize_record_csum} */ // BEGIN_RECORD_CSUM template struct_size_pair record_csum( const player* play , const play::const_random_iterator& random_itr , const pod_vector& op_usage , const pod_vector& new_par , const pod_vector& new_var , size_t current , recorder* rec , // local information passed so stacks need not be allocated for every call struct_csum_stacks& stack ) // END_PROTOTYPE { # ifndef NDEBUG // number of parameters corresponding to the old operation sequence. size_t npar = play->num_par_all(); # endif // vector of length npar containing the parameters the old operation // sequence; i.e., given a parameter index i < npar, the corresponding // parameter value is par[i]. const Base* par = play->par_ptr(); // which parameters are dynamic const pod_vector& par_is_dyn( play->par_is_dyn() ); // check assumption about work space CPPAD_ASSERT_UNKNOWN( stack.op_info.empty() ); CPPAD_ASSERT_UNKNOWN( stack.add_var.empty() ); CPPAD_ASSERT_UNKNOWN( stack.sub_var.empty() ); // // this operator is not csum connected to some other result size_t i_op = random_itr.var2op(current); CPPAD_ASSERT_UNKNOWN( ! ( op_usage[i_op] == usage_t(csum_usage) ) ); // // information corresponding to the root node in the cumulative summation struct struct_csum_op_info info; size_t not_used; random_itr.op_info(i_op, info.op, info.arg, not_used); info.add = true; // was parent operator positive or negative // // initialize stack as containing this one operator stack.op_info.push( info ); // // initialize sum of parameter values as zero Base sum_par(0); // # ifndef NDEBUG // one argument of this operator must have been csum connected to it bool ok = info.op == CSumOp; if( (! ok) && (info.op != SubpvOp) && (info.op != AddpvOp) ) { // first argument is a variable being added i_op = random_itr.var2op(size_t(info.arg[0])); ok |= op_usage[i_op] == usage_t(csum_usage); } if( (! ok) && (info.op != SubvpOp) ) { // second argument is a variable being added or subtracted i_op = random_itr.var2op(size_t(info.arg[1])); ok |= op_usage[i_op] == usage_t(csum_usage); } CPPAD_ASSERT_UNKNOWN( ok ); # endif // // while there are operators left on the stack while( ! stack.op_info.empty() ) { // get this summation operator info = stack.op_info.top(); stack.op_info.pop(); op_code_var op = info.op; const addr_t* arg = info.arg; bool add = info.add; CPPAD_ASSERT_UNKNOWN( NumRes(op) == 1 ); // if( op == CSumOp ) { // --------------------------------------------------------------- // Begin op == CSumOp // // arg[0] is constant parameter that initializes the sum CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < npar ); CPPAD_ASSERT_UNKNOWN( ! par_is_dyn[ arg[0] ] ); if( add ) sum_par += par[arg[0]]; else sum_par -= par[arg[0]]; // // stack entries for addition variable size_t var_start = 5; // start addition variables size_t var_end = size_t( arg[1] ); // end addition variables bool add_var = add; // addition variables for(size_t j = 0; j < 2; ++j) { for(size_t i = var_start; i < var_end; ++i) { // // check if the i-th argument has csum usage i_op = random_itr.var2op(size_t(arg[i])); if( op_usage[i_op] == usage_t(csum_usage) ) { // there is no result corresponding to i-th argument CPPAD_ASSERT_UNKNOWN( size_t( new_var[i_op]) == 0 ); // push operator corresponding to the i-th argument random_itr.op_info(i_op, info.op, info.arg, not_used); info.add = add; stack.op_info.push( info ); } else { // there are no nodes below this one CPPAD_ASSERT_UNKNOWN( size_t(arg[i]) < current ); if( add_var ) stack.add_var.push(arg[i]); else stack.sub_var.push(arg[i]); } } var_start = var_end; // start subtraction variables var_end = size_t( arg[2] ); // end subtraction variables add_var = ! add; // subtraction variables } // // stack entries for addition dynamic parameters size_t dyn_start = var_end; // start addition dynamics size_t dyn_end = size_t( arg[3] ); // end addition dynamics bool dny_add = add; // addition dynamics for(size_t j = 0; j < 2; ++j) { for(size_t i = dyn_start; i < dyn_end; ++i) { // i-th argument is a dynamic parameter // (can't yet be a result, so no nodes below) CPPAD_ASSERT_UNKNOWN( par_is_dyn[ arg[i] ] ); if( dny_add ) stack.add_dyn.push(arg[i]); else stack.sub_dyn.push(arg[i]); } dyn_start = dyn_end; // start subtraction dynamics dyn_end = size_t( arg[4] ); // end subtraction dynamics dny_add = ! add; // subtraction dynamics } // End op == CSumOp // --------------------------------------------------------------- } else { // --------------------------------------------------------------- // Begin op != CSumOp // // is this a subtraction operator bool subtract = (op==SubpvOp) || (op==SubvpOp) || (op==SubvvOp); // // is the i-th argument a parameter CPPAD_ASSERT_UNKNOWN( NumArg(op) == 2 ); bool par_arg[2]; switch(op) { case SubpvOp: case AddpvOp: par_arg[0] = true; par_arg[1] = false; break; // case SubvpOp: par_arg[0] = false; par_arg[1] = true; break; // default: par_arg[0] = false; par_arg[1] = false; break; } // // loop over the arguments to this operator for(size_t i = 0; i < 2; ++i) { if( subtract & (i == 1) ) add = ! add; if( par_arg[i] ) { // case where i-th argument is a parameter CPPAD_ASSERT_UNKNOWN( size_t(arg[i]) < npar ); // if( par_is_dyn[ arg[i] ] ) { // i-th argument is a dynamic parameter // (can't yet be a result, so no nodes below) if( add ) stack.add_dyn.push(arg[i]); else stack.sub_dyn.push(arg[i]); } else { // i-th argument is constant parameter if( add ) sum_par += par[arg[i]]; else sum_par -= par[arg[i]]; } } else { // case where i-th argument is a variable // // check if the i-th argument has csum usage i_op = random_itr.var2op(size_t(arg[i])); if( op_usage[i_op] == usage_t(csum_usage) ) { // there is no result corresponding to i-th argument CPPAD_ASSERT_UNKNOWN( size_t( new_var[i_op]) == 0 ); // push operator corresponding to the i-th argument random_itr.op_info(i_op, info.op, info.arg, not_used); info.add = add; stack.op_info.push( info ); } else { // there are no nodes below this one CPPAD_ASSERT_UNKNOWN( size_t(arg[i]) < current ); if( add ) stack.add_var.push(arg[i]); else stack.sub_var.push(arg[i]); } } } // End op != CSumOp // --------------------------------------------------------------- } } // number of variables to add in this cumulative sum operator size_t n_add_var = stack.add_var.size(); // number of variables to subtract in this cumulative sum operator size_t n_sub_var = stack.sub_var.size(); // number of dynamics to add in this cumulative sum operator size_t n_add_dyn = stack.add_dyn.size(); // number of dynamics to subtract in this cumulative sum operator size_t n_sub_dyn = stack.sub_dyn.size(); // first five arguments to cumulative sum operator addr_t new_arg = rec->put_con_par(sum_par); rec->PutArg(new_arg); // arg[0]: initial sum size_t end = n_add_var + 5; rec->PutArg( addr_t(end) ); // arg[1]: end for add variables end += n_sub_var; rec->PutArg( addr_t(end) ); // arg[2]: end for sub variables end += n_add_dyn; rec->PutArg( addr_t(end) ); // arg[3]: end for add dynamics end += n_sub_dyn; rec->PutArg( addr_t(end) ); // arg[4]: end for sub dynamics // addition variable arguments for(size_t i = 0; i < n_add_var; i++) { CPPAD_ASSERT_UNKNOWN( ! stack.add_var.empty() ); addr_t old_arg = stack.add_var.top(); new_arg = new_var[ random_itr.var2op(size_t(old_arg)) ]; CPPAD_ASSERT_UNKNOWN( 0 < new_arg && size_t(new_arg) < current ); rec->PutArg(new_arg); // arg[5+i] stack.add_var.pop(); } // subtraction variable arguments for(size_t i = 0; i < n_sub_var; i++) { CPPAD_ASSERT_UNKNOWN( ! stack.sub_var.empty() ); addr_t old_arg = stack.sub_var.top(); new_arg = new_var[ random_itr.var2op(size_t(old_arg)) ]; CPPAD_ASSERT_UNKNOWN( 0 < new_arg && size_t(new_arg) < current ); rec->PutArg(new_arg); // arg[arg[1] + i] stack.sub_var.pop(); } // addition dynamic arguments for(size_t i = 0; i < n_add_dyn; ++i) { addr_t old_arg = stack.add_dyn.top(); new_arg = new_par[ old_arg ]; rec->PutArg(new_arg); // arg[arg[2] + i] stack.add_dyn.pop(); } // subtraction dynamic arguments for(size_t i = 0; i < n_sub_dyn; ++i) { addr_t old_arg = stack.sub_dyn.top(); new_arg = new_par[ old_arg ]; rec->PutArg(new_arg); // arg[arg[3] + i] stack.sub_dyn.pop(); } // number of arguments to this operator rec->PutArg( addr_t(end + 1) ); // arg[4] + 1 // // return value struct_size_pair ret; ret.i_op = rec->num_var_op(); ret.i_var = size_t(rec->PutOp(CSumOp)); // return ret; } } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE # endif ================================================ FILE: include/cppad/local/optimize/record_pv.hpp ================================================ # ifndef CPPAD_LOCAL_OPTIMIZE_RECORD_PV_HPP # define CPPAD_LOCAL_OPTIMIZE_RECORD_PV_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- /*! \file record_pv.hpp Record an operation of the form (parameter op variable). */ // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE namespace CppAD { namespace local { namespace optimize { /*! Record an operation of the form (parameter op variable). \param play player object corresponding to the old recroding. \param random_itr random iterator corresponding to old recording. \param new_par mapping from old parameter index to parameter index in new recording. \param new_var mapping from old operator index to variable index in new recording. \param i_op is the index in the old operation sequence for this operator. The operator must be one of the following: AddpvOp, DivpvOp, MulpvOp, PowpvOp, SubpvOp, ZmulpvOp. \param rec is the object that will record the new operations. \return is the operator and variable indices in the new operation sequence. */ template struct_size_pair record_pv( const player* play , const play::const_random_iterator& random_itr , const pod_vector& new_par , const pod_vector& new_var , size_t i_op , recorder* rec ) { // get_op_info op_code_var op; const addr_t* arg; size_t i_var; random_itr.op_info(i_op, op, arg, i_var); // # ifndef NDEBUG switch(op) { case AddpvOp: case DivpvOp: case MulpvOp: case PowpvOp: case SubpvOp: case ZmulpvOp: break; default: CPPAD_ASSERT_UNKNOWN(false); } // number of parameters corresponding to the old operation sequence. size_t npar = play->num_par_all(); # endif // // vector of length npar containing the parameters the old operation // sequence; i.e., given a parameter index i < npar, the corresponding // parameter value is par[i]. // CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 ); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < npar ); CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < i_var ); // DAG condition // addr_t new_arg[2]; new_arg[0] = new_par[ arg[0] ]; new_arg[1] = new_var[ random_itr.var2op(size_t(arg[1])) ]; rec->PutArg( new_arg[0], new_arg[1] ); // struct_size_pair ret; ret.i_op = rec->num_var_op(); ret.i_var = size_t(rec->PutOp(op)); CPPAD_ASSERT_UNKNOWN( 0 < new_arg[1] && size_t(new_arg[1]) < ret.i_var ); return ret; } } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE # endif ================================================ FILE: include/cppad/local/optimize/record_vp.hpp ================================================ # ifndef CPPAD_LOCAL_OPTIMIZE_RECORD_VP_HPP # define CPPAD_LOCAL_OPTIMIZE_RECORD_VP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- /*! \file record_vp.hpp Record an operation of the form (variable op parameter). */ // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE namespace CppAD { namespace local { namespace optimize { /*! Record an operation of the form (variable op parameter). \param play player object corresponding to the old recroding. \param random_itr is a random iterator corresponding to the old recording. \param new_par mapping from old parameter index to parameter index in new recording. \param new_var mapping from old operator index to variable index in new recording. \param i_op is the index in the old operation sequence for this operator. the must be one of the following: DivvpOp, PowvpOp, SubvpOp, ZmulvpOp. \param rec is the object that will record the new operations. \return is the operator and variable indices in the new operation sequence. */ template struct_size_pair record_vp( const player* play , const play::const_random_iterator& random_itr , const pod_vector& new_par , const pod_vector& new_var , size_t i_op , recorder* rec ) { // get_op_info op_code_var op; const addr_t* arg; size_t i_var; random_itr.op_info(i_op, op, arg, i_var); // # ifndef NDEBUG switch(op) { case DivvpOp: case PowvpOp: case SubvpOp: case ZmulvpOp: break; default: CPPAD_ASSERT_UNKNOWN(false); } // number of parameters corresponding to the old operation sequence. size_t npar = play->num_par_all(); # endif // vector of length npar containing the parameters the old operation // sequence; i.e., given a parameter index i < npar, the corresponding // parameter value is par[i]. // CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 ); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < i_var ); // DAG condition CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < npar ); // addr_t new_arg[2]; new_arg[0] = new_var[ random_itr.var2op(size_t(arg[0])) ]; new_arg[1] = new_par[ arg[1] ]; rec->PutArg( new_arg[0], new_arg[1] ); // struct_size_pair ret; ret.i_op = rec->num_var_op(); ret.i_var = size_t(rec->PutOp(op)); CPPAD_ASSERT_UNKNOWN( 0 < new_arg[0] && size_t(new_arg[0]) < ret.i_var ); return ret; } } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE # endif ================================================ FILE: include/cppad/local/optimize/record_vv.hpp ================================================ # ifndef CPPAD_LOCAL_OPTIMIZE_RECORD_VV_HPP # define CPPAD_LOCAL_OPTIMIZE_RECORD_VV_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- /*! \file record_vv.hpp Record an operation of the form (variable op variable). */ // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE namespace CppAD { namespace local { namespace optimize { /*! Record an operation of the form (variable op variable). \param play player object corresponding to the old recroding. \param random_itr random iterator corresponding to the old recording. \param new_var mapping from old operator index to variable index in new recording. \param i_op is the index in the old operation sequence for this operator. the must be one of the following: AddvvOp, DivvvOp, MulvvOp, PowvvOp, SubvvOp, ZmulvvOp. \param rec is the object that will record the new operations. \return is the operator and variable indices in the new operation sequence. */ template struct_size_pair record_vv( const player* play , const play::const_random_iterator& random_itr , const pod_vector& new_var , size_t i_op , recorder* rec ) { // get_op_info op_code_var op; const addr_t* arg; size_t i_var; random_itr.op_info(i_op, op, arg, i_var); // # ifndef NDEBUG switch(op) { case AddvvOp: case DivvvOp: case MulvvOp: case PowvvOp: case SubvvOp: case ZmulvvOp: break; default: CPPAD_ASSERT_UNKNOWN(false); } # endif CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 ); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < i_var ); // DAG condition CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < i_var ); // DAG condition // addr_t new_arg[2]; new_arg[0] = new_var[ random_itr.var2op(size_t(arg[0])) ]; new_arg[1] = new_var[ random_itr.var2op(size_t(arg[1])) ]; rec->PutArg( new_arg[0], new_arg[1] ); // struct_size_pair ret; ret.i_op = rec->num_var_op(); ret.i_var = size_t(rec->PutOp(op)); CPPAD_ASSERT_UNKNOWN( 0 < new_arg[0] && size_t(new_arg[0]) < ret.i_var ); CPPAD_ASSERT_UNKNOWN( 0 < new_arg[1] && size_t(new_arg[1]) < ret.i_var ); return ret; } } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE # endif ================================================ FILE: include/cppad/local/optimize/size_pair.hpp ================================================ # ifndef CPPAD_LOCAL_OPTIMIZE_SIZE_PAIR_HPP # define CPPAD_LOCAL_OPTIMIZE_SIZE_PAIR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /*! \file size_pair.hpp Information for one variable and one operation sequence. */ // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE namespace CppAD { namespace local { namespace optimize { /*! \file size_pair.hpp Information for one variable in one operation sequence. */ struct struct_size_pair { size_t i_op; /// operator index for this variable size_t i_var; /// variable index for this variable }; } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE # endif ================================================ FILE: include/cppad/local/optimize/usage.hpp ================================================ # ifndef CPPAD_LOCAL_OPTIMIZE_USAGE_HPP # define CPPAD_LOCAL_OPTIMIZE_USAGE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE namespace CppAD { namespace local { namespace optimize { typedef CPPAD_VEC_ENUM_TYPE usage_t; enum enum_usage { /// This operator is not used. no_usage, /// This operator is used one or more times. yes_usage, /*! This operator is only used once, it is a summation operator, and its parent is a summation operator. Furthermore, its result is not a dependent variable. Hence case it can be removed as part of a cumulative summation starting at its parent or above. */ csum_usage }; } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE # endif ================================================ FILE: include/cppad/local/play/addr_enum.hpp ================================================ # ifndef CPPAD_LOCAL_PLAY_ADDR_ENUM_HPP # define CPPAD_LOCAL_PLAY_ADDR_ENUM_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN_CPPAD_LOCAL_PLAY_NAMESPACE namespace CppAD { namespace local { namespace play { /*! \file addr_enum.hpp */ /// enum corresponding to type used for addressing iterators for a player enum addr_enum { unsigned_short_enum , addr_t_enum , size_t_enum }; } } } // BEGIN_CPPAD_LOCAL_PLAY_NAMESPACE # endif ================================================ FILE: include/cppad/local/play/atom_op_info.hpp ================================================ # ifndef CPPAD_LOCAL_PLAY_ATOM_OP_INFO_HPP # define CPPAD_LOCAL_PLAY_ATOM_OP_INFO_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN_CPPAD_LOCAL_PLAY_NAMESPACE namespace CppAD { namespace local { namespace play { /*! \file atom_op_info.hpp */ /*! \brief Unpack extra information corresponding to a AFunOp \param op [in] must be a AFunOp \param op_arg [in] is the arguments for this operator \param atom_index [out] is the index in local::atomic_index corresponding to this atomic functions. \param call_id [out] is the call_id for this atomic function call. \param atom_m [out] is the number of results for this user atomic function. \param atom_n [out] is the number of arguments for this user atomic function. \return Is a pointer to this atomic function. */ // MUSTDO: change return to void once all sweeps switch to use call_atomic. template atomic_base* atom_op_info( const op_code_var op , const addr_t* op_arg , size_t& atom_index , size_t& call_id , size_t& atom_m , size_t& atom_n ) { atomic_base* atom_fun; // CPPAD_ASSERT_UNKNOWN( op == AFunOp ); CPPAD_ASSERT_NARG_NRES(op, 4, 0); // atom_index = size_t(op_arg[0]); call_id = size_t(op_arg[1]); atom_n = size_t(op_arg[2]); atom_m = size_t(op_arg[3]); CPPAD_ASSERT_UNKNOWN( atom_n > 0 ); // bool set_null = false; size_t type = 0; // set to avoid warning std::string* name_ptr = nullptr; void* v_ptr = nullptr; // set to avoid warning local::atomic_index(set_null, atom_index, type, name_ptr, v_ptr); if( type == 3 ) return nullptr; # ifndef NDEBUG if( v_ptr == nullptr ) { // atom_fun is null so cannot use atom_fun->atomic_name() std::string msg = atomic_base::class_name(atom_index) + ": atomic_base function has been deleted"; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # endif // the atomic_base object corresponding to this atomic function atom_fun = reinterpret_cast< atomic_base* >( v_ptr ); return atom_fun; } } } } // END_CPPAD_LOCAL_PLAY_NAMESPACE # endif ================================================ FILE: include/cppad/local/play/dyn_player.hpp ================================================ # ifndef CPPAD_LOCAL_PLAY_DYN_PLAYER_HPP # define CPPAD_LOCAL_PLAY_DYN_PLAYER_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include /* ------------------------------------------------------------------------------ {xrst_begin dyn_player dev} Class That Records a Dynamic Parameter Operation Sequence ######################################################### dyn_play ******** We use dyn_play to denote a dynamic player create with one of these constructors. {xrst_literal , // BEGIN_CLASS , // END_CLASS // BEGIN_DYN_PLAY , // END_DYN_PLAY } check_dynamic_dag ***************** This checks that the dynamic parameter operation sequence is an acyclic graph. {xrst_literal // BEGIN_CHECK_DYNAMIC_DAG // END_CHECK_DYNAMIC_DAG } Assignment ********** {xrst_literal , // BEGIN_MOVE_ASSIGNMENT , // END_MOVE_ASSIGNMENT // BEGIN_COPY_ASSIGNMENT , // END_COPY_ASSIGNMENT } swap **** This switches the contents of the two players. {xrst_literal // BEGIN_SWAP // END_SWAP } base2ad ******* This returns a AD player corresponding with the same operation sequence. {xrst_literal // BEGIN_BASE2AD // END_BASE2AD } get_recording ************* This transfers a dynamic parameter recording to a dynamic parameter player. {xrst_literal // BEGIN_GET_RECORDING // END_GET_RECORDING } dyn_rec ======= The contents of *dyn_rec* are modified in an unspecified way by this operation. par_all ******* This vector holds all the parameter values (constant and dynamic parameters). {xrst_literal // BEGIN_PAR_ALL // END_PAR_ALL } par_is_dyn ********** This vector identifies which parameters are dynamic. {xrst_literal // BEGIN_PAR_IS_DYN // END_PAR_IS_DYN } dyn2par_index ************* This vector maps the dynamic parameter index to the corresponding index in the vector of all the parameters. {xrst_literal // BEGIN_DYN2PAR_INDEX // END_DYN2PAR_INDEX } par_ptr ******* {xrst_literal // BEGIN_PAR_PTR // END_PAR_PTR } par_one ******* {xrst_literal // BEGIN_PAR_ONE // END_PAR_ONE } par_value ========= is the value of one of the parameters. par_ptr ======= is a raw pointer to all of the parameters n_dyn_independent ***************** is the number of independent dynamic parameters {xrst_literal // BEGIN_N_DYN_INDEPENDENT // END_N_DYN_INDEPENDENT } num_dynamic_par *************** is the number of dynamic parameters {xrst_literal // BEGIN_NUM_DYNAMIC_PAR // END_NUM_DYNAMIC_PAR } num_dynamic_arg *************** is the length of the dynamic parameter argument vector {xrst_literal // BEGIN_NUM_DYNAMIC_ARG // END_NUM_DYNAMIC_ARG } size_op_seq *********** This is a measure of how may bytes are needed to store the operation sequence. Just lengths (not capacities) are used for this computation. This used to compute :ref:`fun_property@size_op_seq` for an ADFun object. {xrst_literal // BEGIN_SIZE_OP_SEQ // END_SIZE_OP_SEQ } {xrst_end dyn_player} */ // BEGIN_CPPAD_LOCAL_NAMESPACE // BEGIN_CLASS namespace CppAD { namespace local { template class dyn_player { // END_CLASS // // friend template friend class dyn_player; private: // // n_dyn_independent_ // Number of dynamic parameters in the recording size_t n_dyn_independent_; // // par_all_; // Vector containing all the parameters in the recording. // Use pod_vector_maybe because Base may not be plain old data. pod_vector_maybe par_all_; // // par_is_dyn_ // Which elements of par_all_ are dynamic parameters // (same size are par_all_) pod_vector par_is_dyn_; // // dyn2par_index_ // mapping from dynamic parameter index to parameter index // dyn2par_index_.size() equal to number of dynamic parameters // dyn2par_index_[j] < dyn2par_index_[j+1] pod_vector dyn2par_index_; // // dyn_par_op_ // operators for just the dynamic parameters in par_all_ pod_vector dyn_par_op_; // // dyn_par_arg_ // arguments for the dynamic parameter operators pod_vector dyn_par_arg_; // public: // set all scalars to zero to avoid valgraind warning when ani assignment // occurs before values get set. // BEGIN_DYN_PLAY // dyn_player dyn_play // dyn_player dyn_play(play) dyn_player(void) : n_dyn_independent_(0) { } dyn_player(dyn_player& dyn_play) { swap(dyn_play); } // END_DYN_PLAY // // destructor ~dyn_player(void) { } // // check_dynamic_dag // Check dynamic parameter graph to make sure arguments have value less // than or equal to the previously created dynamic parameter. // This is the directed acyclic graph condition (DAG). # ifdef NDEBUG void check_dynamic_dag(void) const { return; } # else // BEGIN_CHECK_DYNAMIC_DAG void check_dynamic_dag(void) const // END_CHECK_DYNAMIC_DAG { // number of dynamic parameters size_t num_dyn = dyn_par_op_.size(); // size_t i_arg = 0; // initialize dynamic parameter argument index for(size_t i_dyn = 0; i_dyn < num_dyn; ++i_dyn) { // i_par is parameter index addr_t i_par = dyn2par_index_[i_dyn]; CPPAD_ASSERT_UNKNOWN( par_is_dyn_[i_par] ); // // operator for this dynamic parameter op_code_dyn op = op_code_dyn( dyn_par_op_[i_dyn] ); // // number of arguments for this dynamic parameter size_t n_arg = num_arg_dyn(op); if( op == atom_dyn ) { size_t n = size_t( dyn_par_arg_[i_arg + 2] ); size_t m = size_t( dyn_par_arg_[i_arg + 3] ); n_arg = 6 + n + m; CPPAD_ASSERT_UNKNOWN( n_arg == size_t( dyn_par_arg_[i_arg + 5 + n + m] ) ); for(size_t i = 5; i < n - 1; ++i) CPPAD_ASSERT_UNKNOWN( dyn_par_arg_[i_arg + i] < i_par ); # ifndef NDEBUG for(size_t i = 5+n; i < 5+n+m; ++i) { addr_t j_par = dyn_par_arg_[i_arg + i]; CPPAD_ASSERT_UNKNOWN( (j_par == 0) || (j_par >= i_par) ); } # endif } else { size_t num_non_par = num_non_par_arg_dyn(op); for(size_t i = num_non_par; i < n_arg; ++i) CPPAD_ASSERT_UNKNOWN( dyn_par_arg_[i_arg + i] < i_par); } // // next dynamic parameter i_arg += n_arg; } return; } # endif // BEGIN_MOVE_ASSIGNMENT // dyn_play_1 = dyn_play_2 void operator=(dyn_player&& dyn_play) // END_MOVE_ASSIGNMENT { swap(dyn_play); } // // BEGIN_COPY_ASSIGNMENT // dyn_play_1 = dyn_play_2 void operator=(const dyn_player& dyn_play) // END_COPY_ASSIGNMENT { // size_t objects n_dyn_independent_ = dyn_play.n_dyn_independent_; // // pod_vectors par_is_dyn_ = dyn_play.par_is_dyn_; dyn2par_index_ = dyn_play.dyn2par_index_; dyn_par_op_ = dyn_play.dyn_par_op_; dyn_par_arg_ = dyn_play.dyn_par_arg_; // // pod_maybe_vectors par_all_ = dyn_play.par_all_; } // // BEGIN_BASE2AD // ad_dyn_play = dyn_play dyn_player< AD > base2ad(void) const // END_BASE2AD { // // dyn_play dyn_player< AD > dyn_play; // // size_t objects dyn_play.n_dyn_independent_ = n_dyn_independent_; // // pod_vectors dyn_play.par_is_dyn_ = par_is_dyn_; dyn_play.dyn2par_index_ = dyn2par_index_; dyn_play.dyn_par_op_ = dyn_par_op_; dyn_play.dyn_par_arg_ = dyn_par_arg_; // // pod_maybe_vector< AD > = pod_maybe_vector dyn_play.par_all_.resize( par_all_.size() ); for(size_t i = 0; i < par_all_.size(); ++i) dyn_play.par_all_[i] = par_all_[i]; // return dyn_play; } // BEGIN_SWAP // dyn_play_1.swap( dyn_play_2 ) void swap(dyn_player& other) // END_SWAP { // size_t objects std::swap(n_dyn_independent_, other.n_dyn_independent_); // // pod_vectors par_is_dyn_.swap( other.par_is_dyn_); dyn2par_index_.swap( other.dyn2par_index_); dyn_par_op_.swap( other.dyn_par_op_); dyn_par_arg_.swap( other.dyn_par_arg_); // // pod_maybe_vectors par_all_.swap( other.par_all_); } // // BEGIN_GET_RECORDING void get_recording(dyn_recorder& dyn_rec) // END_GET_RECORDING { // # ifndef NDEBUG size_t addr_t_max = size_t( std::numeric_limits::max() ); CPPAD_ASSERT_UNKNOWN( dyn_rec.par_is_dyn_.size() < addr_t_max ); CPPAD_ASSERT_UNKNOWN( dyn_rec.dyn_par_op_.size() < addr_t_max ); CPPAD_ASSERT_UNKNOWN( dyn_rec.dyn_par_arg_.size() < addr_t_max ); CPPAD_ASSERT_UNKNOWN( dyn_rec.par_all_.size() < addr_t_max ); # endif // size_t values n_dyn_independent_ = dyn_rec.n_dyn_independent_; // // pod_vectors par_is_dyn_.swap( dyn_rec.par_is_dyn_ ); dyn_par_op_.swap( dyn_rec.dyn_par_op_ ); dyn_par_arg_.swap( dyn_rec.dyn_par_arg_ ); // // pod_maybe par_all_.swap( dyn_rec.par_all_ ); // // dyn2par_index_ dyn2par_index_.resize( dyn_par_op_.size() ); size_t i_dyn = 0; for(size_t i_par = 0; i_par < par_all_.size(); ++i_par) { if( par_is_dyn_[i_par] ) { dyn2par_index_[i_dyn] = addr_t( i_par ); ++i_dyn; } } CPPAD_ASSERT_UNKNOWN( i_dyn == dyn2par_index_.size() ); // // check_dynamic_dag check_dynamic_dag(); } // // BEGIN_PAR_ALL // par_all = dyn_play.par_all() pod_vector_maybe& par_all(void) // END_PAR_ALL { return par_all_; } const pod_vector_maybe& par_all(void) const { return par_all_; } // // BEGIN_PAR_IS_DYN // par_is_dyn = dyn_play.par_is_dyn() const pod_vector& par_is_dyn(void) const // END_PAR_IS_DYN { return par_is_dyn_; } // // BEGIN_DYN2PAR_INDEX // dyn2par_index = dyn_play.dyn2par_index() const pod_vector& dyn2par_index(void) const // END_DYN2PAR_INDEX { return dyn2par_index_; } // // BEGIN_DYN_OP_ALL // dyn_par_op = dyn_play.dyn_par_op() const pod_vector& dyn_par_op(void) const // END_DYN_OP_ALL { return dyn_par_op_; } // // BEGIN_DYN_ARG_ALL // dyn_par_arg = dyn_play.dyn_par_arg() const pod_vector& dyn_par_arg(void) const { return dyn_par_arg_; } // END_DYN_ARG_ALL // // BEGIN_PAR_ONE // par_one = dyn_play.par_one(i) Base par_one(size_t i) const // END_PAR_ONE { return par_all_[i]; } // // BEGIN_PAR_PTR // par_ptr = dyn_play.par_ptr() const Base* par_ptr(void) const // END_PAR_PTR { return par_all_.data(); } // // BEGIN_N_DYN_INDEPENDENT // n_dyn_independent size_t n_dyn_independent(void) const // END_N_DYN_INDEPENDENT { return n_dyn_independent_; } // // BEGIN_NUM_DYNAMIC_PAR // num_dynamic_par = dy_play.num_dynamic_par() size_t num_dynamic_par(void) const // END_NUM_DYNAMIC_PAR { return dyn_par_op_.size(); } // // BEGIN_NUM_DYNAMIC_ARG // num_dynamic_arg = dyn_play.num_dynamic_arg() size_t num_dynamic_arg(void) const // END_NUM_DYNAMIC_ARG { return dyn_par_arg_.size(); } // // BEGIN_NUM_PAR_REC // num_par_all = dyn_play.rec() size_t num_par_all(void) const // END_NUM_PAR_REC { return par_all_.size(); } // // BEGIN_SIZE_OP_SEQ // size_op_seq = dyn_play.size_op_seq() size_t size_op_seq(void) const // END_SIZE_OP_SEQ { return 0 + par_all_.size() * sizeof(Base) + par_is_dyn_.size() * sizeof(bool) + dyn2par_index_.size() * sizeof(addr_t) + dyn_par_op_.size() * sizeof(opcode_t) + dyn_par_arg_.size() * sizeof(addr_t) ; } }; } } // END_CPPAD_lOCAL_NAMESPACE # endif ================================================ FILE: include/cppad/local/play/player.hpp ================================================ # ifndef CPPAD_LOCAL_PLAY_PLAYER_HPP # define CPPAD_LOCAL_PLAY_PLAYER_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include # include # include namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE /*! \file player.hpp File used to define the player class. */ /*! Class used to store and play back an operation sequence recording. \tparam Base These were AD< Base > operations when recorded. Operations during playback are done using the type Base . */ // random_itr_info struct random_itr_info_t { // // *_op2arg // index in var_arg_ corresponding to the first argument for each operator pod_vector short_op2arg; pod_vector addr_t_op2arg; pod_vector size_t_op2arg; /* *_op2var Index of the result variable for each operator. If the operator has no results, this is not defined. The invalid index num_var_ is used when NDEBUG is not defined. If the operator has more than one result, this is the primary result; i.e., the last result. Auxiliary results are only used by the current operator and not used by other operators. */ pod_vector short_op2var; pod_vector addr_t_op2var; pod_vector size_t_op2var; /* *_var2op Mapping from primary variable index to corresponding operator index. This is used to traverse sub-graphs of the operation sequence. This value is valid (invalid) for primary (auxiliary) variables. */ pod_vector short_var2op; pod_vector addr_t_var2op; pod_vector size_t_var2op; // // swap void swap( random_itr_info_t& other ) { // short_op2arg.swap( other.short_op2arg ); short_op2var.swap( other.short_op2var ); short_var2op.swap( other.short_var2op ); // addr_t_op2arg.swap( other.addr_t_op2arg ); addr_t_op2var.swap( other.addr_t_op2var ); addr_t_var2op.swap( other.addr_t_var2op ); // size_t_op2arg.swap( other.size_t_op2arg ); size_t_op2var.swap( other.size_t_op2var ); size_t_var2op.swap( other.size_t_var2op ); } // // clear void clear(void) { // short_op2arg.clear(); short_op2var.clear(); short_var2op.clear(); // addr_t_op2arg.clear(); addr_t_op2var.clear(); addr_t_var2op.clear(); // size_t_op2arg.clear(); size_t_op2var.clear(); size_t_var2op.clear(); } // // size size_t size(void) const { // CPPAD_ASSERT_UNKNOWN( short_op2arg.size() == short_op2var.size() ); CPPAD_ASSERT_UNKNOWN( addr_t_op2arg.size() == addr_t_op2var.size() ); CPPAD_ASSERT_UNKNOWN( size_t_op2arg.size() == size_t_op2var.size() ); // size_t short_num_arg = short_op2arg.size(); size_t int_num_arg = addr_t_op2arg.size(); size_t size_t_num_arg = size_t_op2arg.size(); // size_t short_num_var = short_var2op.size(); size_t int_num_var= addr_t_var2op.size(); size_t size_t_num_var = size_t_var2op.size(); // // result size_t result = 0; // // result if( short_num_arg != 0 ) { CPPAD_ASSERT_UNKNOWN( int_num_arg == 0 ); CPPAD_ASSERT_UNKNOWN( size_t_num_arg == 0 ); // result = 2 * short_num_arg + short_num_var; } else CPPAD_ASSERT_UNKNOWN( short_num_var == 0 ); // // result if( int_num_arg != 0 ) { CPPAD_ASSERT_UNKNOWN( short_num_arg == 0 ); CPPAD_ASSERT_UNKNOWN( size_t_num_arg == 0 ); // result = 2 * int_num_arg + int_num_var; } else CPPAD_ASSERT_UNKNOWN( int_num_var == 0 ); // // result if( size_t_num_arg != 0 ) { CPPAD_ASSERT_UNKNOWN( short_num_arg == 0 ); CPPAD_ASSERT_UNKNOWN( int_num_arg == 0 ); // result = 2 * size_t_num_arg + size_t_num_var; } else CPPAD_ASSERT_UNKNOWN( size_t_num_var == 0 ); // return result; } }; template class player { // // friend // player must be a friend of player< AD > for base2ad to work template friend class player; private: // ---------------------------------------------------------------------- // // dyn_play_ dyn_player dyn_play_; // // num_var_ // Number of variables in the recording. size_t num_var_; // num_var_load_ // number of vecad load operations in the reconding size_t num_var_load_; // num_var_vecad_ // Number of VecAD vectors in the recording size_t num_var_vecad_; // var_op_ // The operators in the recording. pod_vector var_op_; // var_arg_ // The operation argument indices in the recording pod_vector var_arg_; // var_text_ // Character strings ('\\0' terminated) in the recording. pod_vector var_text_; // var_vecad_ind_ // The VecAD indices in the recording. pod_vector var_vecad_ind_; // // random_itr_info_ // Information needed to use member functions that begin with random_ // and for using const_subgraph_iterator. random_itr_info_t random_itr_info_; // public: // /// default constructor // set all scalars to zero to avoid valgraind warning when ani assignment // occurs before values get set. player(void) : num_var_(0) , num_var_load_(0) , num_var_vecad_(0) { } // // move semantics constructor // (none of the default constructor values matter to the destructor) player(player& play) { swap(play); } // // destructor ~player(void) { } // // address_type // type used for addressing iterators for this player play::addr_enum address_type(void) const { // required size_t required = 0; required = std::max(required, num_var_ ); // number variables required = std::max(required, var_op_.size() ); // number operators required = std::max(required, var_arg_.size() ); // number arguments // // unsigned short if( required <= std::numeric_limits::max() ) return play::unsigned_short_enum; // // addr_t if( required <= std::numeric_limits::max() ) return play::addr_t_enum; // // unsigned size_t CPPAD_ASSERT_UNKNOWN( required <= std::numeric_limits::max() ); return play::size_t_enum; } // // get_recording /*! Moving an operation sequence from a recorder to this player \param rec the object that was used to record the operation sequence. After this operation, the state of the recording is no longer defined. For example, the pod_vector member variables in this have been swapped with rec. \param n_ind the number of independent variables (only used for error checking when NDEBUG is not defined). \par Use an assert to check that the length of the following vectors is less than the maximum possible value for addr_t; i.e., that an index in these vectors can be represented using the type addr_t: var_op_, var_vecad_ind_, var_arg_, test_vec_, par_all_, var_text_, dyn_par_arg_. */ void get_recording(recorder& rec, size_t n_ind) { # ifndef NDEBUG size_t addr_t_max = size_t( std::numeric_limits::max() ); # endif // // dyn_play_ dyn_play_.get_recording( rec.dyn_record_ ); // size_t values num_var_ = rec.num_var_; num_var_load_ = rec.num_var_load_; // var_op_ var_op_.swap(rec.var_op_); CPPAD_ASSERT_UNKNOWN(var_op_.size() < addr_t_max ); // var_arg_ var_arg_.swap(rec.var_arg_); CPPAD_ASSERT_UNKNOWN(var_arg_.size() < addr_t_max ); // var_text_ var_text_.swap(rec.var_text_); CPPAD_ASSERT_UNKNOWN(var_text_.size() < addr_t_max ); // var_vecad_ind_ var_vecad_ind_.swap(rec.var_vecad_ind_); CPPAD_ASSERT_UNKNOWN(var_vecad_ind_.size() < addr_t_max ); // num_var_vecad_ num_var_vecad_ = 0; { // var_vecad_ind_ contains size of each VecAD followed by // the parameter indices used to initialize it. size_t i = 0; while( i < var_vecad_ind_.size() ) { num_var_vecad_++; i += size_t( var_vecad_ind_[i] ) + 1; } CPPAD_ASSERT_UNKNOWN( i == var_vecad_ind_.size() ); } // random access information clear_random(); // some checks check_inv_op(n_ind); check_variable_dag(); } // // check_inv_op # ifdef NDEBUG void check_inv_op(size_t n_ind) const { return; } # else void check_inv_op(size_t n_ind) const { play::const_sequential_iterator itr = begin(); op_code_var op; const addr_t* op_arg; size_t var_index; itr.op_info(op, op_arg, var_index); CPPAD_ASSERT_UNKNOWN( op == BeginOp ); size_t i_op = 0; while( op != EndOp ) { // start at second operator (++itr).op_info(op, op_arg, var_index); ++i_op; CPPAD_ASSERT_UNKNOWN( (op == InvOp) == (i_op <= n_ind) ); } return; } # endif // // check_variable_dag # ifdef NDEBUG void check_variable_dag(void) const { return; } # else void check_variable_dag(void) const { play::const_sequential_iterator itr = begin(); op_code_var op; const addr_t* op_arg; size_t var_index; itr.op_info(op, op_arg, var_index); CPPAD_ASSERT_UNKNOWN( op == BeginOp ); // addr_t arg_var_bound = 0; while( op != EndOp ) { (++itr).op_info(op, op_arg, var_index); switch(op) { // cases where nothing to do case BeginOp: case EndOp: case EqppOp: case InvOp: case LdpOp: case LeppOp: case LtppOp: case NeppOp: case ParOp: case AFunOp: case FunapOp: case FunrpOp: case FunrvOp: case StppOp: break; // only first argument is a variable case AbsOp: case AcosOp: case AcoshOp: case AsinOp: case AsinhOp: case AtanOp: case AtanhOp: case CosOp: case CoshOp: case DivvpOp: case ErfOp: case ErfcOp: case ExpOp: case Expm1Op: case LevpOp: case LogOp: case Log1pOp: case LtvpOp: case NegOp: case PowvpOp: case SignOp: case SinOp: case SinhOp: case SqrtOp: case SubvpOp: case TanOp: case TanhOp: case FunavOp: case ZmulvpOp: CPPAD_ASSERT_UNKNOWN(op_arg[0] <= arg_var_bound ); break; // only second argument is a variable case AddpvOp: case DisOp: case DivpvOp: case EqpvOp: case LdvOp: case LepvOp: case LtpvOp: case MulpvOp: case NepvOp: case PowpvOp: case StvpOp: case SubpvOp: case ZmulpvOp: CPPAD_ASSERT_UNKNOWN(op_arg[1] <= arg_var_bound ); break; // only first and second arguments are variables case AddvvOp: case DivvvOp: case EqvvOp: case LevvOp: case LtvvOp: case MulvvOp: case NevvOp: case PowvvOp: case SubvvOp: case ZmulvvOp: CPPAD_ASSERT_UNKNOWN(op_arg[0] <= arg_var_bound ); CPPAD_ASSERT_UNKNOWN(op_arg[1] <= arg_var_bound ); break; // StpvOp case StpvOp: CPPAD_ASSERT_UNKNOWN(op_arg[2] <= arg_var_bound ); break; // StvvOp case StvvOp: CPPAD_ASSERT_UNKNOWN(op_arg[1] <= arg_var_bound ); CPPAD_ASSERT_UNKNOWN(op_arg[2] <= arg_var_bound ); break; // CSumOp case CSumOp: { CPPAD_ASSERT_UNKNOWN( 5 < op_arg[2] ); for(addr_t j = 5; j < op_arg[2]; j++) CPPAD_ASSERT_UNKNOWN(op_arg[j] <= arg_var_bound); } itr.correct_before_increment(); break; // CExpOp case CExpOp: if( op_arg[1] & 1 ) CPPAD_ASSERT_UNKNOWN( op_arg[2] <= arg_var_bound); if( op_arg[1] & 2 ) CPPAD_ASSERT_UNKNOWN( op_arg[3] <= arg_var_bound); if( op_arg[1] & 4 ) CPPAD_ASSERT_UNKNOWN( op_arg[4] <= arg_var_bound); if( op_arg[1] & 8 ) CPPAD_ASSERT_UNKNOWN( op_arg[5] <= arg_var_bound); break; // PriOp case PriOp: if( op_arg[0] & 1 ) CPPAD_ASSERT_UNKNOWN( op_arg[1] <= arg_var_bound); if( op_arg[0] & 2 ) CPPAD_ASSERT_UNKNOWN( op_arg[3] <= arg_var_bound); break; // CSkipOp case CSkipOp: if( op_arg[1] & 1 ) CPPAD_ASSERT_UNKNOWN( op_arg[2] <= arg_var_bound); if( op_arg[1] & 2 ) CPPAD_ASSERT_UNKNOWN( op_arg[3] <= arg_var_bound); itr.correct_before_increment(); break; default: CPPAD_ASSERT_UNKNOWN(false); break; } if( NumRes(op) > 0 ) { if( var_index > 0 ) { CPPAD_ASSERT_UNKNOWN(size_t(arg_var_bound) < var_index); } else { CPPAD_ASSERT_UNKNOWN(size_t(arg_var_bound) == var_index); } // arg_var_bound = addr_t(var_index); } } return; } # endif // // operator= // move semantics assignment void operator=(player&& play) { swap(play); } // // operator= void operator=(const player& play) { // // dyn_play_ dyn_play_ = play.dyn_play_; // // size_t objects num_var_ = play.num_var_; num_var_load_ = play.num_var_load_; num_var_vecad_ = play.num_var_vecad_; // // pod_vectors var_op_ = play.var_op_; var_arg_ = play.var_arg_; var_text_ = play.var_text_; var_vecad_ind_ = play.var_vecad_ind_; // // random_itr_info_ random_itr_info_ = play.random_itr_info_; } // // base2ad // Create a player< AD > from this player player< AD > base2ad(void) const { player< AD > play; // // dyn_play_ play.dyn_play_ = dyn_play_.base2ad(); // // size_t objects play.num_var_ = num_var_; play.num_var_load_ = num_var_load_; play.num_var_vecad_ = num_var_vecad_; // // pod_vectors play.var_op_ = var_op_; play.var_arg_ = var_arg_; play.var_text_ = var_text_; play.var_vecad_ind_ = var_vecad_ind_; // // random_itr_info_ play.random_itr_info_ = random_itr_info_; // return play; } // // swap /// used for move semantics version of ADFun assignment operation void swap(player& other) { // // dyn_play_ dyn_play_.swap( other.dyn_play_ ); // // size_t objects std::swap(num_var_, other.num_var_); std::swap(num_var_load_, other.num_var_load_); std::swap(num_var_vecad_, other.num_var_vecad_); // // pod_vectors var_op_.swap( other.var_op_); var_arg_.swap( other.var_arg_); var_text_.swap( other.var_text_); var_vecad_ind_.swap( other.var_vecad_ind_); // // random_itr_info_ random_itr_info_.swap( other.random_itr_info_); } // // setup_random /// Enable use of const_subgraph_iterator and member functions that begin // with random_(no work if already setup). void setup_random(unsigned short& not_used) { play::random_setup( num_var_ , var_op_ , var_arg_ , &random_itr_info_.short_op2arg , &random_itr_info_.short_op2var , &random_itr_info_.short_var2op ); } void setup_random(addr_t& not_used) { play::random_setup( num_var_ , var_op_ , var_arg_ , &random_itr_info_.addr_t_op2arg , &random_itr_info_.addr_t_op2var , &random_itr_info_.addr_t_var2op ); } # if ! CPPAD_IS_SAME_TAPE_ADDR_TYPE_SIZE_T void setup_random(size_t& not_used) { play::random_setup( num_var_ , var_op_ , var_arg_ , &random_itr_info_.size_t_op2arg , &random_itr_info_.size_t_op2var , &random_itr_info_.size_t_var2op ); } # endif // // clear_ /// Free memory used for functions that begin with random_ /// and random iterators and subgraph iterators void clear_random(void) { random_itr_info_.clear(); CPPAD_ASSERT_UNKNOWN( random_itr_info_.size() == 0 ); } // // par_all pod_vector_maybe& par_all(void) { return dyn_play_.par_all(); } const pod_vector_maybe& par_all(void) const { return dyn_play_.par_all(); } // // par_is_dyn const pod_vector& par_is_dyn(void) const { return dyn_play_.par_is_dyn(); } // // dyn2par_index const pod_vector& dyn2par_index(void) const { return dyn_play_.dyn2par_index(); } // // dyn_par_op const pod_vector& dyn_par_op(void) const { return dyn_play_.dyn_par_op(); } // // dyn_par_arg const pod_vector& dyn_par_arg(void) const { return dyn_play_.dyn_par_arg(); } // // GetOp /*! \brief fetch an operator from the recording. \return the i-th operator in the recording. \param i the index of the operator in recording */ op_code_var GetOp (size_t i) const { return op_code_var(var_op_[i]); } // // GetVecInd /*! \brief Fetch a VecAD index from the recording. \return the i-th VecAD index in the recording. \param i the index of the VecAD index in recording */ size_t GetVecInd (size_t i) const { return size_t( var_vecad_ind_[i] ); } // // par_one Base par_one(size_t i) const { return dyn_play_.par_one(i); } // // par_ptr const Base* par_ptr(void) const { return dyn_play_.par_ptr(); } // // GetTxt /*! \brief Fetch a '\\0' terminated string from the recording. \return the beginning of the string. \param i the index where the string begins. */ const char *GetTxt(size_t i) const { CPPAD_ASSERT_UNKNOWN(i < var_text_.size() ); return var_text_.data() + i; } // // n_dyn_independent size_t n_dyn_independent(void) const { return dyn_play_.n_dyn_independent(); } // // num_dynamic_par size_t num_dynamic_par(void) const { return dyn_play_.num_dynamic_par(); } // // num_dynamic_arg size_t num_dynamic_arg(void) const { return dyn_play_.num_dynamic_arg(); } // // num_var size_t num_var(void) const { return num_var_; } // // num_var_load size_t num_var_load(void) const { return num_var_load_; } // // num_var_op size_t num_var_op(void) const { return var_op_.size(); } // // num_var_vecad_ind size_t num_var_vec_ind(void) const { return var_vecad_ind_.size(); } // // num_var_vecad size_t num_var_vecad(void) const { return num_var_vecad_; } // // num_var_arg size_t num_var_arg(void) const { return var_arg_.size(); } // // num_par_all size_t num_par_all(void) const { return dyn_play_.num_par_all(); } // // num_var_text size_t num_var_text(void) const { return var_text_.size(); } // // size_op_seq // A measure of amount of memory used to store // the operation sequence, just lengths, not capacities. // In user api as f.size_op_seq(); see the file fun_property.omh. size_t size_op_seq(void) const { return 0 + dyn_play_.size_op_seq() + var_op_.size() * sizeof(opcode_t) + var_arg_.size() * sizeof(addr_t) + var_text_.size() * sizeof(char) + var_vecad_ind_.size() * sizeof(addr_t) ; } // size_random // A measure of amount of memory used for random access routine // In user api as f.size_random(); see the file fun_property.omh. size_t size_random(void) const { return random_itr_info_.size(); } // // begin /// const sequential iterator begin play::const_sequential_iterator begin(void) const { size_t op_index = 0; size_t num_var = num_var_; return play::const_sequential_iterator( num_var, &var_op_, &var_arg_, op_index ); } // // end play::const_sequential_iterator end(void) const { size_t op_index = var_op_.size() - 1; size_t num_var = num_var_; return play::const_sequential_iterator( num_var, &var_op_, &var_arg_, op_index ); } // // begin_subgraph play::const_subgraph_iterator begin_subgraph( const play::const_random_iterator& random_itr , const pod_vector* subgraph ) const { size_t subgraph_index = 0; return play::const_subgraph_iterator( random_itr, subgraph, subgraph_index ); } // // end_subgraph template play::const_subgraph_iterator end_subgraph( const play::const_random_iterator& random_itr , const pod_vector* subgraph ) const { size_t subgraph_index = subgraph->size() - 1; return play::const_subgraph_iterator( random_itr, subgraph, subgraph_index ); } // /// const random iterator play::const_random_iterator get_random(unsigned short& not_used) const { return play::const_random_iterator( var_op_ , var_arg_ , &random_itr_info_.short_op2arg , &random_itr_info_.short_op2var , &random_itr_info_.short_var2op ); } play::const_random_iterator get_random(addr_t& not_used) const { return play::const_random_iterator( var_op_ , var_arg_ , &random_itr_info_.addr_t_op2arg , &random_itr_info_.addr_t_op2var , &random_itr_info_.addr_t_var2op ); } # if ! CPPAD_IS_SAME_TAPE_ADDR_TYPE_SIZE_T play::const_random_iterator get_random(size_t& not_used) { return play::const_random_iterator( var_op_ , var_arg_ , &random_itr_info_.size_t_op2arg , &random_itr_info_.size_t_op2var , &random_itr_info_.size_t_var2op ); } # endif }; } } // END_CPPAD_lOCAL_NAMESPACE # endif ================================================ FILE: include/cppad/local/play/random_iterator.hpp ================================================ # ifndef CPPAD_LOCAL_PLAY_RANDOM_ITERATOR_HPP # define CPPAD_LOCAL_PLAY_RANDOM_ITERATOR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN_CPPAD_LOCAL_PLAY_NAMESPACE namespace CppAD { namespace local { namespace play { /*! \file random_iterator.hpp */ /*! Constant random iterator for a player object. \tparam Addr An integer type capable of representing the largest value in the vectors var_arg, op2arg_index, op2var_index, var2op_index. */ template class const_random_iterator { private: /// vector of operators on the tape const pod_vector* var_op_; /// vector of arguments for all the operators /// (note that this is same type as used in recorder; i.e., addr_t) const pod_vector* var_arg_; /// mapping from operator index to index of first argument in var_arg_ const pod_vector* op2arg_index_; /// mapping from operator index to index of primary (last) result const pod_vector* op2var_index_; /// mapping from primary variable index to operator index /// (only specified for primary variables) const pod_vector* var2op_index_; public: /// index_t typedef Addr index_t; // /// default constructor const_random_iterator(void) : var_op_(nullptr) , var_arg_(nullptr) , op2arg_index_(nullptr) , op2var_index_(nullptr) , var2op_index_(nullptr) { } /// default assignment operator void operator=(const const_random_iterator& rhs) { var_op_ = rhs.var_op_; op2arg_index_ = rhs.op2arg_index_; op2var_index_ = rhs.op2var_index_; var2op_index_ = rhs.var2op_index_; return; } /*! Create a random iterator \par var2op_index This variable is not needed and can be null if the var2op member function is not used. */ const_random_iterator( const pod_vector& var_op , ///< var_op_ const pod_vector& var_arg , ///< var_arg_ const pod_vector* op2arg_index , ///< op2ar_vec_ const pod_vector* op2var_index , ///< op2var_index_ const pod_vector* var2op_index ) ///< var2op_index_ : var_op_ ( &var_op ) , var_arg_ ( &var_arg ) , op2arg_index_ ( op2arg_index ) , op2var_index_ ( op2var_index ) , var2op_index_ ( var2op_index ) { } /*! \brief fetch the information corresponding to an operator \param op_index index for this operator [in] \param op [out] op code for this operator. \param op_arg [out] pointer to the first argument to this operator. \param var_index [out] index of the last variable (primary variable) for this operator. If there is no primary variable for this operator, i_var not specified and could have any value. */ void op_info( size_t op_index , op_code_var& op , const addr_t*& op_arg , size_t& var_index ) const { op = op_code_var( (*var_op_)[op_index] ); op_arg = (*op2arg_index_)[op_index] + var_arg_->data(); var_index = size_t( (*op2var_index_)[op_index] ); return; } /*! \brief map variable index to operator index. \param var_index must be the index of a primary variable. \return is the index of the operator corresponding to this primary variable. */ size_t var2op(size_t var_index) const { // check that var2op_index was not null in constructor CPPAD_ASSERT_UNKNOWN( var2op_index_ != nullptr ); // // operator index size_t op_index = size_t( (*var2op_index_)[var_index] ); // // check that var_index is a primary variable index (see random_setup) CPPAD_ASSERT_UNKNOWN( op_index < var_op_->size() ); // return op_index; } /// get operator corresponding to operator index op_code_var get_op(size_t op_index) const { return op_code_var( (*var_op_)[op_index] ); } /// number of operators size_t num_op(void) const { return var_op_->size(); } // /// number of variables size_t num_var(void) const { return var2op_index_->size(); } }; } } } // BEGIN_CPPAD_LOCAL_PLAY_NAMESPACE # endif ================================================ FILE: include/cppad/local/play/random_setup.hpp ================================================ # ifndef CPPAD_LOCAL_PLAY_RANDOM_SETUP_HPP # define CPPAD_LOCAL_PLAY_RANDOM_SETUP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN_CPPAD_LOCAL_PLAY_NAMESPACE namespace CppAD { namespace local { namespace play { /*! \file random_setup.hpp */ /*! Set up random access to a player object. \tparam Addr An integer type capable of representing the largest value in the vectors var_arg, op2arg_index, op2var_index, var2op_index. \param num_var num_var is the number of variables in this operation sequence. \param var_op The mapping op = op_code_var[ var_op[op_index] ] maps from operator index op_index to the operator op. \param var_arg is a vector of all the arguments for all the operators. The mapping op2arg_index will map from operator indices to index in this vector. \param op2arg_index On input, op2arg_index is either the empty vector (or contains the proper result from a previous call to random_setup). Upon return it maps each operator index to the index in op_var_arg of its first argument for the operator. \param op2var_index On input, op2var_index is either the empty vector (or contains the proper result from a previous call to random_setup). Upon return it maps each operator index to the primary (last) result for the operator. If there are no results for the operator, the return value map value is not specified. \param var2op_index On input, var2op_index is either the empty vector (or contains the proper result from a previous call to random_setup). Upon return it maps each primary variable index to the corresponding operator index. The value of the map is only specified for primary variable indices. */ template void random_setup( size_t num_var , const pod_vector& var_op , const pod_vector& var_arg , pod_vector* op2arg_index , pod_vector* op2var_index , pod_vector* var2op_index ) { if( op2arg_index->size() != 0 ) { CPPAD_ASSERT_UNKNOWN( op2arg_index->size() == var_op.size() ); CPPAD_ASSERT_UNKNOWN( op2var_index->size() == var_op.size() ); CPPAD_ASSERT_UNKNOWN( var2op_index->size() == num_var ); return; } CPPAD_ASSERT_UNKNOWN( op2var_index->size() == 0 ); CPPAD_ASSERT_UNKNOWN( op2var_index->size() == 0 ); CPPAD_ASSERT_UNKNOWN( var2op_index->size() == 0 ); CPPAD_ASSERT_UNKNOWN( op_code_var( var_op[0] ) == BeginOp ); CPPAD_ASSERT_NARG_NRES(BeginOp, 1, 1); // size_t num_op = var_op.size(); size_t var_index = 0; size_t arg_index = 0; // op2arg_index->resize( num_op ); op2var_index->resize( num_op ); var2op_index->resize( num_var ); # ifndef NDEBUG // value of var2op for auxiliary variables is num_op (invalid) for(size_t i_var = 0; i_var < num_var; ++i_var) (*var2op_index)[i_var] = Addr( num_op ); // value of op2var is num_var (invalid) when NumRes(op) = 0 for(size_t i_op = 0; i_op < num_op; ++i_op) (*op2var_index)[i_op] = Addr( num_var ); # endif for(size_t i_op = 0; i_op < num_op; ++i_op) { op_code_var op = op_code_var( var_op[i_op] ); // // index of first argument for this operator (*op2arg_index)[i_op] = Addr( arg_index ); arg_index += NumArg(op); // // index of first result for next operator var_index += NumRes(op); if( NumRes(op) > 0 ) { // index of last (primary) result for this operator (*op2var_index)[i_op] = Addr( var_index - 1 ); // // mapping from primary variable to its operator (*var2op_index)[var_index - 1] = Addr( i_op ); } // CSumOp if( op == CSumOp ) { CPPAD_ASSERT_UNKNOWN( NumArg(CSumOp) == 0 ); // // pointer to first argument for this operator const addr_t* op_arg = var_arg.data() + arg_index; // // The actual number of arguments for this operator is // op_arg[4] + 1 // Correct index of first argument for next operator arg_index += size_t(op_arg[4] + 1); } // // CSkip if( op == CSkipOp ) { CPPAD_ASSERT_UNKNOWN( NumArg(CSumOp) == 0 ); // // pointer to first argument for this operator const addr_t* op_arg = var_arg.data() + arg_index; // // The actual number of arguments for this operator is // 7 + op_arg[4] + op_arg[5]. // Correct index of first argument for next operator. arg_index += size_t(7 + op_arg[4] + op_arg[5]); } } } } } } // BEGIN_CPPAD_LOCAL_PLAY_NAMESPACE # endif ================================================ FILE: include/cppad/local/play/sequential_iterator.hpp ================================================ # ifndef CPPAD_LOCAL_PLAY_SEQUENTIAL_ITERATOR_HPP # define CPPAD_LOCAL_PLAY_SEQUENTIAL_ITERATOR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN_CPPAD_LOCAL_PLAY_NAMESPACE namespace CppAD { namespace local { namespace play { /*! \file sequential_iterator.hpp */ /*! Constant sequential iterator for a player object. \tparam Addr An integer type capable of representing the largest value in the vectors var_arg, op2arg_index, op2var_index, var2op_index. \par Except for constructor, the public API for this class is the same as for the subgraph_iterator class. */ class const_sequential_iterator { private: /// pointer to the first operator in the player, BeginOp = *op_begin_ const opcode_t* op_begin_; /// pointer one past last operator in the player, EndOp = *(op_end_ - 1) const opcode_t* op_end_; /// pointer to the first argument for the first operator const addr_t* arg_begin_; /// pointer on past last argument for last operator const addr_t* arg_end_; /// pointer to current operator const opcode_t* op_cur_; /// pointer to first argument for current operator const addr_t* arg_; /// number of variables in tape (not const for assignment operator) size_t num_var_; /// index of last result for current operator size_t var_index_; /// value of current operator; i.e. op_ = *op_cur_ op_code_var op_; public: /// default constructor const_sequential_iterator(void) : op_begin_(nullptr) , op_end_(nullptr) , arg_begin_(nullptr) , arg_end_(nullptr) , op_cur_(nullptr) , arg_(nullptr) , num_var_(0) , var_index_(0) , op_(NumberOp) { } /// assignment operator void operator=(const const_sequential_iterator& rhs) { op_begin_ = rhs.op_begin_; op_end_ = rhs.op_end_; arg_begin_ = rhs.arg_begin_; arg_end_ = rhs.arg_end_; op_cur_ = rhs.op_cur_; arg_ = rhs.arg_; num_var_ = rhs.num_var_; var_index_ = rhs.var_index_; op_ = rhs.op_; return; } /*! Create a sequential iterator starting either at beginning or end of tape \param num_var is the number of variables in the tape. \param var_op is the vector of operators on the tape. \param var_arg is the vector of arguments for all the operators \param op_index is the operator index that iterator will start at. It must be zero or var_op_->size() - 1. \par Assumptions - op_code_var(var_op_[0]) == BeginOp - op_code_var(var_op_[var_op_->size() - 1]) == EndOp */ const_sequential_iterator( size_t num_var , const pod_vector* var_op , const pod_vector* var_arg , size_t op_index ) : op_begin_ ( var_op->data() ) , op_end_ ( var_op->data() + var_op->size() ) , arg_begin_ ( var_arg->data() ) , arg_end_ ( var_arg->data() + var_arg->size() ), num_var_ ( num_var ) { if( op_index == 0 ) { // index of last result for BeginOp var_index_ = 0; // // first argument to BeginOp arg_ = var_arg->data(); // // BeginOp op_cur_ = op_begin_; op_ = op_code_var( *op_cur_ ); CPPAD_ASSERT_UNKNOWN( op_ == BeginOp ); CPPAD_ASSERT_NARG_NRES(op_, 1, 1); } else { CPPAD_ASSERT_UNKNOWN(op_index == var_op->size()-1); // // index of last result for EndOp var_index_ = num_var - 1; // // first argument to EndOp (has no arguments) arg_ = var_arg->data() + var_arg->size(); // // EndOp op_cur_ = op_end_ - 1; op_ = op_code_var( *op_cur_ ); CPPAD_ASSERT_UNKNOWN( op_ == EndOp ); CPPAD_ASSERT_NARG_NRES(op_, 0, 0); } } /*! Advance iterator to next operator */ const_sequential_iterator& operator++(void) { // first argument for next operator arg_ += NumArg(op_); // // next operator ++op_cur_; op_ = op_code_var( *op_cur_ ); // // last result for next operator var_index_ += NumRes(op_); // return *this; } /*! Correction applied before ++ operation when current operator is CSumOp, CSkipOp, or AFunOP. */ void correct_before_increment(void) { // // arg_ // actual number of arguments for this operator depends on argument data CPPAD_ASSERT_UNKNOWN( NumArg(op_) == 0 ); const addr_t* arg = arg_; // switch( op_ ) { // default: CPPAD_ASSERT_UNKNOWN( false ); break; // // CSumOp case CSumOp: CPPAD_ASSERT_UNKNOWN( arg + 4 < arg_end_ ); arg_ += arg[4] + 1; CPPAD_ASSERT_UNKNOWN( *(arg_ - 1) == arg[4] + 1 ); break; // // CSkipOp case CSkipOp: CPPAD_ASSERT_UNKNOWN( arg + 5 < arg_end_ ); arg_ += 7 + arg[4] + arg[5]; CPPAD_ASSERT_UNKNOWN( *(arg_ - 1) == 7 + arg[4] + arg[5] ); break; } CPPAD_ASSERT_UNKNOWN( arg_ <= arg_end_ ); return; } /*! Backup iterator to previous operator */ const_sequential_iterator& operator--(void) { // // last result for next operator var_index_ -= NumRes(op_); // // next operator --op_cur_; op_ = op_code_var( *op_cur_ ); // // first argument for next operator arg_ -= NumArg(op_); // return *this; } /*! Correction applied after -- operation when current operator is CSumOp or CSkipOp. \param arg [out] corrected point to arguments for this operation. */ void correct_after_decrement(const addr_t*& arg) { // // arg // actual number of arguments for this operator depends on argument data CPPAD_ASSERT_UNKNOWN( NumArg(op_) == 0 ); CPPAD_ASSERT_UNKNOWN( arg_begin_ < arg_ ); // switch( op_ ) { // default: CPPAD_ASSERT_UNKNOWN( false ); break; // // CSumOp case CSumOp: { // index of arg[4] addr_t n_arg = *(arg_ - 1); arg = arg_ - n_arg; CPPAD_ASSERT_UNKNOWN( arg[4] + 1 == n_arg ); } break; // // CSkipOp case CSkipOp: { addr_t n_arg = *(arg_ - 1); arg = arg_ - n_arg; CPPAD_ASSERT_UNKNOWN( 7 + arg[4] + arg[5] == n_arg ); } break; } // // arg_ CPPAD_ASSERT_UNKNOWN( arg_begin_ <= arg ); CPPAD_ASSERT_UNKNOWN( arg + NumArg(op_) <= arg_end_ ); arg_ = arg; } /*! \brief Get information corresponding to current operator. \param op [out] op code for this operator. \param arg [out] pointer to the first argument to this operator. \param var_index [out] index of the last variable (primary variable) for this operator. If there is no primary variable for this operator, var_index is not specified and could have any value. */ void op_info( op_code_var& op , const addr_t*& arg , size_t& var_index ) const { // op CPPAD_ASSERT_UNKNOWN( op_begin_ <= op_cur_ && op_cur_ < op_end_ ) op = op_; // // arg arg = arg_; CPPAD_ASSERT_UNKNOWN( arg_begin_ <= arg ); CPPAD_ASSERT_UNKNOWN( arg + NumArg(op) <= arg_end_ ); // // var_index CPPAD_ASSERT_UNKNOWN( var_index_ < num_var_ || NumRes(op) == 0 ); var_index = var_index_; } /// current operator index size_t op_index(void) { return size_t(op_cur_ - op_begin_); } }; } } } // BEGIN_CPPAD_LOCAL_PLAY_NAMESPACE # endif ================================================ FILE: include/cppad/local/play/subgraph_iterator.hpp ================================================ # ifndef CPPAD_LOCAL_PLAY_SUBGRAPH_ITERATOR_HPP # define CPPAD_LOCAL_PLAY_SUBGRAPH_ITERATOR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include // BEGIN_CPPAD_LOCAL_PLAY_NAMESPACE namespace CppAD { namespace local { namespace play { /*! \file random_iterator.hpp */ /*! Constant subgraph iterator for a player object. \tparam Addr An integer type capable of representing the largest value in the vectors var_arg, op2arg_index, op2var_index, var2op_index. Except for constructor, the public API for this class is the same as for the sequential iterator class. */ template class const_subgraph_iterator { private: /// a random iterator used to access player information const const_random_iterator* random_itr_; /// sorted subset of operator indices that we will include const pod_vector* subgraph_; /// index in subgraph of current operator /// The initial value for this index must be zero or subgraph.size()-1. size_t subgraph_index_; public: /// default constructor const_subgraph_iterator(void) : random_itr_(nullptr) , subgraph_(nullptr) , subgraph_index_(0) { } /// default assignment operator void operator=(const const_subgraph_iterator& rhs) { random_itr_ = rhs.random_itr_; subgraph_ = rhs.subgraph_; subgraph_index_ = rhs.subgraph_index_; return; } /*! Create a subgraph iterator starting either at beginning or end of subgraph */ const_subgraph_iterator( const const_random_iterator& random_itr , ///< random_itr_ const pod_vector* subgraph , ///< subgraph_ size_t subgraph_index ) ///< subgraph_index_ : random_itr_ ( &random_itr ) , subgraph_ ( subgraph ) , subgraph_index_ ( subgraph_index ) { CPPAD_ASSERT_UNKNOWN( subgraph_index == 0 || subgraph_index == subgraph->size() - 1 ); } /*! Advance iterator to next operator */ const_subgraph_iterator& operator++(void) { ++subgraph_index_; return *this; } /// No correction necessary when using random access to player void correct_before_increment(void) { return; } /*! Backup iterator to previous operator */ const_subgraph_iterator& operator--(void) { --subgraph_index_; return *this; } /*! No correction necessary when using random access to player. \param op_arg not used or modified. */ void correct_after_decrement(const addr_t*& op_arg) { return; } /*! \brief Get information corresponding to current operator. \param op [out] op code for this operator. \param op_arg [out] pointer to the first argument to this operator. \param var_index [out] index of the last variable (primary variable) for this operator. If there is no primary variable for this operator, var_index is not specified and could have any value. */ void op_info( op_code_var& op , const addr_t*& op_arg , size_t& var_index ) const { // op size_t op_index = size_t( (*subgraph_)[subgraph_index_] ); random_itr_->op_info(op_index, op, op_arg, var_index); } /// current operator index size_t op_index(void) { return size_t( (*subgraph_)[subgraph_index_] ); } }; } } } // BEGIN_CPPAD_LOCAL_PLAY_NAMESPACE # endif ================================================ FILE: include/cppad/local/pod_vector.hpp ================================================ # ifndef CPPAD_LOCAL_POD_VECTOR_HPP # define CPPAD_LOCAL_POD_VECTOR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # if CPPAD_CSTDINT_HAS_8_TO_64 # include # endif # include # include # include # include # include /* {xrst_begin_parent pod_vector dev} A Template Vector Class That does not construct or destruct Elements #################################################################### Prototype ********* {xrst_literal // BEGIN_POD_VECTOR_CLASS // END_POD_VECTOR_CLASS } Member Functions **************** All the member functions in this class are public. is_pod ****** The :ref:`is_pod-name` function must return true for this *Type* . {xrst_end pod_vector} */ // BEGIN_POD_VECTOR_CLASS namespace CppAD { namespace local { template class pod_vector // END_POD_VECTOR_CLASS { /* {xrst_begin pod_vector_private dev} pod_vector Private Data ####################### Prototype ********* {xrst_literal // BEGIN_PRIVATE // END_PRIVATE } size\_ ****** is the number of elements currently in this vector. capacity\_ ********** is the maximum number of elements that the current allocation can hold. It is always greater than or equal size\_ . The only operations that can decrease the capacity are :ref:`pod_vector_vector@swap` and :ref:`pod_vector_resize@clear` . data\_ ****** is a pointer to the first element of the vector. This is the null pointer when capacity\_ is zero. {xrst_end pod_vector_private} */ // BEGIN_PRIVATE private: size_t size_; size_t capacity_; Type *data_; // END_PRIVATE // --------------------------------------------------------------------------- public: // --------------------------------------------------------------------------- /* {xrst_begin pod_vector_ctor dev} pod_vector Constructors and Destructor ###################################### Copy **** {xrst_literal // BEGIN_COPY_CTOR // END_COPY_CTOR } This constructor cannot be used. Default ******* {xrst_literal // BEGIN_DEFAULT_CTOR // END_DEFAULT_CTOR } This constructor sets the size and capacity to zero. Size **** {xrst_literal // BEGIN_SIZE_CTOR // END_SIZE_CTOR } This constructor sets the size (capacity) equal *n* ( greater that or equal *n* ). Destructor ********** {xrst_literal // BEGIN_DESTRUCTOR // END_DESTRUCTOR } The memory is returned using :ref:`ta_return_memory-name` . {xrst_end pod_vector_ctor} */ // BEGIN_COPY_CTOR pod_vector(const pod_vector&) = delete; // END_COPY_CTOR // BEGIN_DEFAULT_CTOR pod_vector(void) // END_DEFAULT_CTOR : size_(0), capacity_(0), data_(nullptr) { CPPAD_ASSERT_UNKNOWN( is_pod() ); } // BEGIN_SIZE_CTOR pod_vector(size_t n) // END_SIZE_CTOR : size_(0), capacity_(0), data_(nullptr) { CPPAD_ASSERT_UNKNOWN( is_pod() ); extend(n); } // BEGIN_DESTRUCTOR ~pod_vector(void) // END_DESTRUCTOR { if( capacity_ > 0 ) { void* v_ptr = reinterpret_cast( data_ ); thread_alloc::return_memory(v_ptr); } } /* ------------------------------------------------------------------------------ {xrst_begin pod_vector_state dev} The Current State of a pod_vector ################################# size **** {xrst_literal // BEGIN_SIZE_STATE // END_SIZE_STATE } capacity ******** {xrst_literal // BEGIN_CAPACITY_STATE // END_CAPACITY_STATE } data **** {xrst_literal // BEGIN_DATA_STATE // END_DATA_STATE } This pointer is no longer valid after the following operations: extend, resize, clear, assignment, destructor. {xrst_end pod_vector_state} */ // BEGIN_SIZE_STATE size_t size(void) const { return size_; } // END_SIZE_STATE // BEGIN_CAPACITY_STATE size_t capacity(void) const { return capacity_; } // END_CAPACITY_STATE // BEGIN_DATA_STATE Type* data(void) { return data_; } const Type* data(void) const { return data_; } // END_DATA_STATE /* ------------------------------------------------------------------------------ {xrst_begin pod_vector_element dev} Access and Change an Element of a pod_vector ############################################ Not Constant ************ {xrst_literal // BEGIN_ELEMENT // END_ELEMENT } This accesses the element at the specified index (in a way that allows the element's value to be changed). An assert is generated the index is greater than or equal to `size_`` for this vector. Constant ******** {xrst_literal // BEGIN_CONST_ELEMENT // END_CONST_ELEMENT } This accesses the element at the specified index (in a way that does not allow the element's value to be changed). An assert is generated the index is greater than or equal to `size_`` for this vector. push_back ********* {xrst_literal // BEGIN_PUSH_BACK // END_PUSH_BACK } This increases the size by one and places the specified element at the end (highest valid index) of the vector. {xrst_end pod_vector_element} */ // BEGIN_ELEMENT template Type& operator[](Index index) // END_ELEMENT { CPPAD_ASSERT_UNKNOWN( size_t(index) < size_ ); return data_[index]; } // BEGIN_CONST_ELEMENT template const Type& operator[](Index index) const // END_CONST_ELEMENT { CPPAD_ASSERT_UNKNOWN( size_t(index) < size_ ); return data_[index]; } // BEGIN_PUSH_BACK void push_back(const Type& element) // END_PUSH_BACK { size_t i = extend(1); data_[i] = element; } /* ------------------------------------------------------------------------------- {xrst_begin pod_vector_vector dev} pod_vector Operations With Vector Arguments ########################################### swap **** {xrst_literal // BEGIN_SWAP // END_SWAP } This exchanges this all the information in this vector with another vector. It is faster that assignment because it does not allocate or free memory and does not do any element by element operations. Assignment ********** {xrst_literal // BEGIN_ASSIGNMENT // END_ASSIGNMENT } This copies all the information from the other vector to this vector. It copies each element from other and it may free and allocate memory. {xrst_end pod_vector_vector} */ // BEGIN_SWAP void swap(pod_vector& other) // END_SWAP { std::swap(capacity_, other.capacity_); std::swap(size_, other.size_); std::swap(data_, other.data_); } // BEGIN_ASSIGNMENT void operator=(const pod_vector& other) // END_ASSIGNMENT { resize( other.size_ ); if( size_ > 0 ) { size_t bytes = size_ * sizeof(Type); void* v_ptr = reinterpret_cast( data_ ); void* v_ptr_other = reinterpret_cast( other.data_ ); std::memcpy(v_ptr, v_ptr_other, bytes); } } /* ------------------------------------------------------------------------------- {xrst_begin pod_vector_resize dev} Changing the Size of a pod_vector ################################# extent ****** {xrst_literal // BEGIN_EXTEND // END_EXTEND } #. This increases the size of the vector by *n* . #. It returns the size before the increase which is the index of the first new element added to the vector. #. All of the elements in the vector, before the extension, are preserved by this operation. resize ****** {xrst_literal // BEGIN_RESIZE // END_RESIZE } #. This changes the size of the vector to *n* . #. If on input, *n* is less that or equal :ref:`pod_vector_private@capacity\_` , all the elements in the vector, before the extension, are preserved by this operation. #. If on input, *n* is greater than capacity\_ , the elements in the vector are lost. clear ***** {xrst_literal // BEGIN_CLEAR // END_CLEAR } This sets the size and capacity for the vector to zero and frees all the memory that it was using. {xrst_end pod_vector_resize} */ // BEGIN_EXTEND size_t extend(size_t n) // END_EXTEND { size_t old_size = size_; size_ += n; // check if we can use current memory if( size_ <= capacity_ ) return old_size; // save more old information size_t old_capacity = capacity_; void* old_v_ptr = reinterpret_cast(data_); // get new memory and set capacity size_t byte_capacity; size_t bytes = size_ * sizeof(Type); void* v_ptr = thread_alloc::get_memory(bytes, byte_capacity); capacity_ = byte_capacity / sizeof(Type); data_ = reinterpret_cast(v_ptr); // copy old data to new if( old_size > 0 ) std::memcpy(v_ptr, old_v_ptr, old_size * sizeof(Type)); // return old memory to available pool if( old_capacity > 0 ) thread_alloc::return_memory(old_v_ptr); // return value for extend(n) is the old length CPPAD_ASSERT_UNKNOWN( size_ <= capacity_ ); return old_size; } // BEGIN_RESIZE void resize(size_t n) // END_RESIZE { size_ = n; // check if we must allocate new memory if( capacity_ < size_ ) { void* v_ptr; // if( capacity_ > 0 ) { // return old memory to available pool v_ptr = reinterpret_cast( data_ ); thread_alloc::return_memory(v_ptr); } // // get new memory and set capacity size_t byte_capacity; size_t bytes = size_ * sizeof(Type); v_ptr = thread_alloc::get_memory(bytes, byte_capacity); capacity_ = byte_capacity / sizeof(Type); data_ = reinterpret_cast(v_ptr); // } CPPAD_ASSERT_UNKNOWN( size_ <= capacity_ ); } // BEGIN_CLEAR void clear(void) // END_CLEAR { if( capacity_ > 0 ) { void* v_ptr = reinterpret_cast( data_ ); thread_alloc::return_memory(v_ptr); } data_ = nullptr; capacity_ = 0; size_ = 0; } }; // --------------------------------------------------------------------------- /*! A vector class with that does not use element constructors or destructors when is_pod is true. */ template class pod_vector_maybe { private: /// maximum number of Type elements current allocation can hold size_t capacity_; /// number of elements currently in this vector size_t length_; /// pointer to the first type elements /// (not defined and should not be used when capacity_ = 0) Type *data_; /// do not use the copy constructor explicit pod_vector_maybe(const pod_vector_maybe& ) { CPPAD_ASSERT_UNKNOWN(false); } public: /// default constructor sets capacity_ = length_ = data_ = 0 pod_vector_maybe(void) : capacity_(0), length_(0), data_(nullptr) { CPPAD_ASSERT_UNKNOWN( is_pod() ); } /// sizing constructor pod_vector_maybe( /// number of elements in this vector size_t n ) : capacity_(0), length_(0), data_(nullptr) { extend(n); } /// Destructor: returns allocated memory to thread_alloc; /// see extend and resize. If this is not plain old data, /// the destructor for each element is called. ~pod_vector_maybe(void) { if( capacity_ > 0 ) { if( ! is_pod() ) { // call destructor for each element for(size_t i = 0; i < capacity_; i++) (data_ + i)->~Type(); } void* v_ptr = reinterpret_cast( data_ ); thread_alloc::return_memory(v_ptr); } } /// current number of elements in this vector. size_t size(void) const { return length_; } /// current capacity (amount of allocated storage) for this vector. size_t capacity(void) const { return capacity_; } /// current data pointer is no longer valid after any of the following: /// extend, resize, erase, clear, assignment, and destructor. Type* data(void) { return data_; } /// const version of data pointer (see non-const documentation) const Type* data(void) const { return data_; } // ---------------------------------------------------------------------- /// non-constant element access; i.e., we can change this element value Type& operator[]( /// element index, must be less than length size_t i ) { CPPAD_ASSERT_UNKNOWN( i < length_ ); return data_[i]; } /// non-constant element access; i.e., we can change this element value template Type& operator[]( /// element index, must be less than length and convertible to size_t Index i ) { return (*this)[size_t(i)]; } // ---------------------------------------------------------------------- /// constant element access; i.e., we cannot change this element value const Type& operator[]( /// element index, must be less than length size_t i ) const { CPPAD_ASSERT_UNKNOWN( i < length_ ); return data_[i]; } /// constant element access; i.e., we cannot change this element value template const Type& operator[]( /// element index, must be less than length and convertible to size_t Index i ) const { return (*this)[size_t(i)]; } // ---------------------------------------------------------------------- /*! Add an element to theh back of this vector \param e is the element we are adding to the back of the vector. */ void push_back(const Type& e) { size_t i = extend(1); data_[i] = e; } /*! Swap all properties of this vector with another. This is useful when moving a vector that grows after it has reached its final size (without copying every element). \param other is the other vector that we are swapping this vector with. */ void swap(pod_vector_maybe& other) { std::swap(capacity_, other.capacity_); std::swap(length_, other.length_); std::swap(data_, other.data_); } // ---------------------------------------------------------------------- /*! Increase the number of elements the end of this vector (existing elements are always preserved). \param n is the number of elements to add to end of this vector. \return is the number of elements in the vector before it was extended. This is the index of the first new element added to the vector. - If Type is plain old data, new elements are not initialized; i.e., their constructor is not called. Otherwise, the constructor is called for each new element. - This and resize are the only routine that allocate memory for pod_vector_maybe. They uses thread_alloc for this allocation. */ size_t extend(size_t n) { size_t old_length = length_; length_ += n; // check if we can use current memory if( length_ <= capacity_ ) return old_length; // save more old information size_t old_capacity = capacity_; Type* old_data = data_; // get new memory and set capacity size_t length_bytes = length_ * sizeof(Type); size_t capacity_bytes; void* v_ptr = thread_alloc::get_memory(length_bytes, capacity_bytes); capacity_ = capacity_bytes / sizeof(Type); data_ = reinterpret_cast(v_ptr); if( ! is_pod() ) { // call constructor for each new element for(size_t i = 0; i < capacity_; i++) new(data_ + i) Type(); } // copy old data to new for(size_t i = 0; i < old_length; i++) data_[i] = old_data[i]; // return old memory to available pool if( old_capacity > 0 ) { if( ! is_pod() ) { for(size_t i = 0; i < old_capacity; i++) (old_data + i)->~Type(); } v_ptr = reinterpret_cast( old_data ); thread_alloc::return_memory(v_ptr); } // return value for extend(n) is the old length CPPAD_ASSERT_UNKNOWN( length_ <= capacity_ ); return old_length; } // ---------------------------------------------------------------------- /*! resize the vector (existing elements preserved when n <= capacity_). \param n is the new size for this vector. \par if n <= capacity(), no memory is freed or allocated, the capacity is not changed, and existing elements are preserved. If n > capacity(), new memory is allocates and all the data in the vector is lost. - If Type is plain old data, new elements are not initialized; i.e., their constructor is not called. Otherwise, the constructor is called for each new element. - This and extend are the only routine that allocate memory for pod_vector_maybe. They uses thread_alloc for this allocation. */ void resize(size_t n) { length_ = n; // check if we must allocate new memory if( capacity_ < length_ ) { void* v_ptr; // // return old memory to available pool if( capacity_ > 0 ) { if( ! is_pod() ) { // call destructor for each old element for(size_t i = 0; i < capacity_; i++) (data_ + i)->~Type(); } v_ptr = reinterpret_cast( data_ ); thread_alloc::return_memory(v_ptr); } // // get new memory and set capacity size_t length_bytes = length_ * sizeof(Type); size_t capacity_bytes; v_ptr = thread_alloc::get_memory(length_bytes, capacity_bytes); capacity_ = capacity_bytes / sizeof(Type); data_ = reinterpret_cast(v_ptr); // CPPAD_ASSERT_UNKNOWN( length_ <= capacity_ ); // if( ! is_pod() ) { // call constructor for each new element for(size_t i = 0; i < capacity_; i++) new(data_ + i) Type(); } } } // ---------------------------------------------------------------------- /*! Remove all the elements from this vector and free its memory. */ void clear(void) { if( capacity_ > 0 ) { if( ! is_pod() ) { // call destructor for each element for(size_t i = 0; i < capacity_; i++) (data_ + i)->~Type(); } void* v_ptr = reinterpret_cast( data_ ); thread_alloc::return_memory(v_ptr); } data_ = nullptr; capacity_ = 0; length_ = 0; } // ----------------------------------------------------------------------- /// vector assignment operator void operator=( /// right hand size of the assignment operation const pod_vector_maybe& x ) { resize( x.length_ ); // CPPAD_ASSERT_UNKNOWN( length_ == x.length_ ); for(size_t i = 0; i < length_; i++) { data_[i] = x.data_[i]; } } }; } } // END_CPPAD_LOCAL_NAMESPACE # endif ================================================ FILE: include/cppad/local/record/comp_op.hpp ================================================ # ifndef CPPAD_LOCAL_RECORD_COMP_OP_HPP # define CPPAD_LOCAL_RECORD_COMP_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE /* {xrst_begin recorder_put_comp_op dev} {xrst_spell aleft dyn taddr } Put Compare Operators in Recording ################################## Syntax ****** | *rec* . ``put_comp_`` *rel* ( *aleft* , *aright* , *result* ) rel === The text *rel* in the function name above is ``eq`` (for equals), ``le`` (for less than or equal), or ``lt`` (for less than). Prototype ********* {xrst_literal // BEGIN_COMP_EQ // END_COMP_EQ } The other prototypes for the functions ``comp_le`` and ``comp_lt`` are same except for the function name. var_left ******** is true if the left operand is a variable. var_right ********* is true if the right operand is a variable. dyn_left ******** is true if the left operand is a dynamic parameter. dyn_right ********* is true if the right operand is a dynamic parameter. aleft ***** is the compare operator left operand. aright ****** is the compare operator right operand. taddr\_ ******* The values *aleft.taddr_* and *aright* . ``taddr_`` are the proper address for dynamic parameters and variables and does not matter for constants. value\_ ******* The values *aleft.value_* and *aright* . ``value_`` are the proper address for constants and does not matter for variables and dynamic parameters. result ****** This is the result for this comparison corresponding to this recording (sequence of operations). {xrst_end recorder_put_comp_op} */ // BEGIN_COMP_EQ template void recorder::comp_eq( bool var_left , bool var_right , bool dyn_left , bool dyn_right , const AD& aleft , const AD& aright , bool result ) // END_COMP_EQ { if( var_left ) { if( var_right ) { // variable == variable PutArg(aleft.taddr_, aright.taddr_); if( result ) PutOp(EqvvOp); else PutOp(NevvOp); } else { // variable == parameter addr_t p = aright.taddr_; if( ! dyn_right ) p = put_con_par(aright.value_); PutArg(p, aleft.taddr_); if( result ) PutOp(EqpvOp); else PutOp(NepvOp); } } else if ( var_right ) { // parameter == variable addr_t p = aleft.taddr_; if( ! dyn_left ) p = put_con_par(aleft.value_); PutArg(p, aright.taddr_); if( result ) PutOp(EqpvOp); else PutOp(NepvOp); } else if( dyn_left | dyn_right ) { // parameter == parameter addr_t arg0 = aleft.taddr_; addr_t arg1 = aright.taddr_; if( ! dyn_left ) arg0 = put_con_par(aleft.value_); if( ! dyn_right ) arg1 = put_con_par(aright.value_); // PutArg(arg0, arg1); if( result ) PutOp(EqppOp); else PutOp(NeppOp); } } // --------------------------------------------------------------------------- // comp_le template void recorder::comp_le( bool var_left , bool var_right , bool dyn_left , bool dyn_right , const AD& aleft , const AD& aright , bool result ) { if( var_left ) { if( var_right ) { // variable <= variable if( result ) { PutOp(LevvOp); PutArg(aleft.taddr_, aright.taddr_); } else { PutOp(LtvvOp); PutArg(aright.taddr_, aleft.taddr_); } } else { // variable <= parameter addr_t p = aright.taddr_; if( ! dyn_right ) p = put_con_par(aright.value_); if( result ) { PutOp(LevpOp); PutArg(aleft.taddr_, p); } else { PutOp(LtpvOp); PutArg(p, aleft.taddr_); } } } else if ( var_right ) { // parameter <= variable addr_t p = aleft.taddr_; if( ! dyn_left ) p = put_con_par(aleft.value_); if( result ) { PutOp(LepvOp); PutArg(p, aright.taddr_); } else { PutOp(LtvpOp); PutArg(aright.taddr_, p); } } else if( dyn_left | dyn_right ) { // parameter <= parameter addr_t arg0 = aleft.taddr_; addr_t arg1 = aright.taddr_; if( ! dyn_left ) arg0 = put_con_par(aleft.value_); if( ! dyn_right ) arg1 = put_con_par(aright.value_); // if( result ) { PutOp(LeppOp); PutArg(arg0, arg1); } else { PutOp(LtppOp); PutArg(arg1, arg0); } } } // -------------------------------------------------------------------------- // comp_lt template void recorder::comp_lt( bool var_left , bool var_right , bool dyn_left , bool dyn_right , const AD& aleft , const AD& aright , bool result ) { if( var_left ) { if( var_right ) { // variable < variable if( result ) { PutOp(LtvvOp); PutArg(aleft.taddr_, aright.taddr_); } else { PutOp(LevvOp); PutArg(aright.taddr_, aleft.taddr_); } } else { // variable < parameter addr_t p = aright.taddr_; if( ! dyn_right ) p = put_con_par(aright.value_); if( result ) { PutOp(LtvpOp); PutArg(aleft.taddr_, p); } else { PutOp(LepvOp); PutArg(p, aleft.taddr_); } } } else if ( var_right ) { // parameter < variable addr_t p = aleft.taddr_; if( ! dyn_left ) p = put_con_par(aleft.value_); if( result ) { PutOp(LtpvOp); PutArg(p, aright.taddr_); } else { PutOp(LevpOp); PutArg(aright.taddr_, p); } } else if( dyn_left | dyn_right ) { // parameter < parameter addr_t arg0 = aleft.taddr_; addr_t arg1 = aright.taddr_; if( ! dyn_left ) arg0 = put_con_par(aleft.value_); if( ! dyn_right ) arg1 = put_con_par(aright.value_); // if( result ) { PutOp(LtppOp); PutArg(arg0, arg1); } else { PutOp(LeppOp); PutArg(arg1, arg0); } } } } } // END_CPPAD_LOCAL_NAMESPACE # endif ================================================ FILE: include/cppad/local/record/cond_exp.hpp ================================================ # ifndef CPPAD_LOCAL_RECORD_COND_EXP_HPP # define CPPAD_LOCAL_RECORD_COND_EXP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE /* {xrst_begin recorder_cond_exp dev} Record a Variable or Dynamic Parameter Conditional Expression ############################################################# Syntax ****** | *rec* . ``cond_exp`` ( | |tab| *tape_id* , *cop* , *result* , *left* , *right* , *if_true* , *if_false* | ) Prototype ********* {xrst_literal // BEGIN_COND_EXP // END_COND_EXP } tape_id ******* identifier for the tape that this operation is being recorded on. Passing tape_id avoids having to call tape_ptr() in case where left, right, if_true, and if_false are all be constant at this AD level (but left and right are not identically constant). cop *** Which :ref:`comparison operator` ; i.e., <, <=, ==, >=, >, or !=. result ****** is the result for this operation conditional expression. On input, *result* . ``value_`` is the proper value and the other fields do not matter. Upon return, the other fields have been set to their proper values. It is an error to call this routine when all the arguments are constants; i.e., when the result is a constant. left **** value of the left operand in the comparison. If *left* . ``tape_id_`` is not zero it must equal *tape_id* . right ***** value of the right operand in the comparison. If *right* . ``tape_id_`` is not zero it must equal *tape_id* . if_true ******* value of the result if the comparison value is true. If *if_true* . ``tape_id_`` is not zero it must equal *tape_id* . if_false ******** value of the result if the comparison value is false. If *if_false* . ``tape_id_`` is not zero it must equal *tape_id* . {xrst_end recorder_cond_exp} */ // BEGIN_COND_EXP template void recorder::cond_exp( tape_id_t tape_id , enum CompareOp cop , AD &result , const AD &left , const AD &right , const AD &if_true , const AD &if_false ) // END_COND_EXP { // check for invalid tape_id CPPAD_ASSERT_UNKNOWN( tape_id != 0 ); // arg[0] = cop addr_t arg0 = addr_t( cop ); // arg[1] = base 2 representation of the value // [Var(left), Var(right), Var(if_true), Var(if_false)] addr_t arg1 = 0; // arg[2] = left address // set first bit in arg1 addr_t arg2 = left.taddr_; if( Constant(left) ) arg2 = put_con_par(left.value_); else { CPPAD_ASSERT_KNOWN( tape_id == left.tape_id_ , "CondExpRel: arguments are variables or dynamics for different thread" ); if(left.ad_type_ != dynamic_enum) arg1 += 1; } // arg[3] = right address // set second bit in arg1 addr_t arg3 = right.taddr_; if( Constant(right) ) arg3 = put_con_par(right.value_); else { CPPAD_ASSERT_KNOWN( tape_id == right.tape_id_ , "CondExpRel: arguments are variables or dynamics for different thread" ); if(right.ad_type_ != dynamic_enum) arg1 += 2; } // arg[4] = if_true address // set third bit in arg1 addr_t arg4 = if_true.taddr_; if( Constant(if_true) ) arg4 = put_con_par(if_true.value_); else { CPPAD_ASSERT_KNOWN( tape_id == if_true.tape_id_ , "CondExpRel: arguments are variables or dynamics for different thread" ); if(if_true.ad_type_ != dynamic_enum) arg1 += 4; } // arg[5] = if_false address // set fourth bit in arg1 addr_t arg5 = if_false.taddr_; if( Constant(if_false) ) arg5 = put_con_par(if_false.value_); else { CPPAD_ASSERT_KNOWN( tape_id == if_false.tape_id_ , "CondExpRel: arguments are variables or dynamics for different thread" ); if(if_false.ad_type_ != dynamic_enum) arg1 += 8; } if( arg1 == 0 ) { // none of the arguments are variables, record cond_exp_dyn // put the result at the end of the parameter vector as dynamic // put_dyn_cond_exp(par, cop, left, right, if_true, if_false) result.taddr_ = put_dyn_cond_exp( result.value_, CompareOp(arg0), arg2, arg3, arg4, arg5 ); result.ad_type_ = dynamic_enum; result.tape_id_ = tape_id; // check that result is a dynamic parameter CPPAD_ASSERT_UNKNOWN( Dynamic(result) ); } else { CPPAD_ASSERT_UNKNOWN( NumArg(CExpOp) == 6 ); CPPAD_ASSERT_UNKNOWN( NumRes(CExpOp) == 1 ); // put operator in tape result.taddr_ = PutOp(CExpOp); PutArg(arg0, arg1, arg2, arg3, arg4, arg5); // make result a variable CPPAD_ASSERT_UNKNOWN( result.ad_type_ == constant_enum ); result.ad_type_ = variable_enum; result.tape_id_ = tape_id; // check that result is a variable CPPAD_ASSERT_UNKNOWN( Variable(result) ); } } } } // END_CPPAD_LOCAL_NAMESPACE # endif ================================================ FILE: include/cppad/local/record/dyn_recorder.hpp ================================================ # ifndef CPPAD_LOCAL_RECORD_DYN_RECORDER_HPP # define CPPAD_LOCAL_RECORD_DYN_RECORDER_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include /* -- ---------------------------------------------------------------------------- {xrst_begin_parent dyn_recorder dev} Class That Records a Dynamic Parameter Operation Sequence ######################################################### dyn_record ********** {xrst_literal , // BEGIN_CLASS , // END_CLASS // BEGIN_DYN_RECORD , // END_DYN_RECORD } set_n_dyn_independent ********************* {xrst_literal // BEGIN_SET_NUM_DYNAMIC_IND // END_SET_NUM_DYNAMIC_IND } n_dyn_independent ***************** {xrst_literal // BEGIN_N_DYN_INDEPENDENT // END_N_DYN_INDEPENDENT } par_all ******* {xrst_literal // BEGIN_PAR_ALL // END_PAR_ALL } memory ****** {xrst_literal // BEGIN_MEMORY // END_MEMORY } {xrst_end dyn_recorder} */ // BEGIN_CPPAD_LOCAL_NAMESPACE // BEGIN_CLASS namespace CppAD { namespace local { template class dyn_recorder { // END_CLASS // // friend friend class dyn_player; // private: // // n_dyn_independent_ // Number of dynamic parameters in the recording size_t n_dyn_independent_; // // all_dyn_vec_ind_; // The VecAD indices in the recording. pod_vector dyn_vecad_ind_; // // par_hash_table_ // Hash table to reduced number of duplicate parameters in par_all_ pod_vector par_hash_table_; // // par_all_; // Vector containing all the parameters in the recording. // Use pod_vector_maybe because Base may not be plain old data. pod_vector_maybe par_all_; // // par_is_dyn_ // Which elements of par_all_ are dynamic parameters // (same size are par_all_) pod_vector par_is_dyn_; // // dyn_par_op_ // operators for just the dynamic parameters in par_all_ pod_vector dyn_par_op_; // // dyn_par_arg_ // arguments for the dynamic parameter operators pod_vector dyn_par_arg_; // public: // BEGIN_DYN_RECORD // dyn_recorder dyn_record dyn_recorder(void) // END_DYN_RECORD : n_dyn_independent_(0) , par_hash_table_( CPPAD_HASH_TABLE_SIZE ) { // // par_hash_table_ // It does not matter if uninitialized hash codes match but this // initilaization is here to avoid valgrind warnings. void* ptr = static_cast( par_hash_table_.data() ); int value = 0; size_t num = CPPAD_HASH_TABLE_SIZE * sizeof(addr_t); std::memset(ptr, value, num); } // // Destructor ~dyn_recorder(void) { } // // BEGIN_SET_NUM_DYNAMIC_IND // dyn_record.set_n_dyn_independent(n_dyn_independent) void set_n_dyn_independent(size_t n_dyn_independent) // END_SET_NUM_DYNAMIC_IND { n_dyn_independent_ = n_dyn_independent; } // // BEGIN_N_DYN_INDEPENDENT // n_dyn_independent = dyn_record.n_dyn_independent() size_t n_dyn_independent(void) const // END_N_DYN_INDEPENDENT { return n_dyn_independent_; } // // put_dyn_par addr_t put_dyn_par( const Base &par, op_code_dyn op ); addr_t put_dyn_par( const Base &par, op_code_dyn op, addr_t a0 ); addr_t put_dyn_par( const Base &par, op_code_dyn op, addr_t a0, addr_t a1 ); // // put_dyn_cond_exp addr_t put_dyn_cond_exp(const Base &par, CompareOp cop, addr_t left, addr_t right, addr_t if_true, addr_t if_false ); // // put_dyn_arg_vec void put_dyn_arg_vec(const pod_vector& arg); // // put_con_par addr_t put_con_par(const Base &par); // // put_dyn_atomic template void put_dyn_atomic( tape_id_t tape_id , size_t atom_index , size_t call_id , const vector& type_x , const vector& type_y , const VectorAD& ax , VectorAD& ay ); // // BEGIN_PAR_ALL // par_all = dyn_record.par_all() const pod_vector_maybe& par_all(void) const { return par_all_; } // END_PAR_ALL // // BEGIN_MEMORY /// memory = dyn_record.Memory() size_t Memory(void) const // END_MEMORY { return 0 + dyn_vecad_ind_.capacity() * sizeof(addr_t) + par_hash_table_.capacity() * sizeof(addr_t) + par_all_.capacity() * sizeof(Base) + par_is_dyn_.capacity() * sizeof(bool) + dyn_par_op_.capacity() * sizeof(opcode_t) + dyn_par_arg_.capacity() * sizeof(addr_t) ; } }; /* ------------------------------------------------------------------------------ {xrst_begin put_dyn_par dev} Put a Dynamic Parameter at End of Parameter Vector ################################################## Syntax ****** | *par_index* = *dyn_record* . ``put_dyn_par`` ( *par* , *op* ) | *par_index* = *dyn_record* . ``put_dyn_par`` ( *par* , *op* , *a0* ) | *par_index* = *dyn_record* . ``put_dyn_par`` ( *par* , *op* , *a0* , *a1* ) par *** is value of dynamic parameter to be placed at the end of the parameter vector. op ** is the operator for this dynamic parameter. a0 ** is the first argument for this operation. It must be present if :ref:`num_arg_dyn@n_arg` for this operator is greater than zero. a1 ** is the second argument for this operation. It must be present if :ref:`num_arg_dyn@n_arg` for this operator is greater than one. par_index ********* is the index of this dynamic parameter in the vector of all parameters. Prototype ********* {xrst_literal , // BEGIN_PUT_DYN_PAR_0 , // END_PUT_DYN_PAR_0 // BEGIN_PUT_DYN_PAR_1 , // END_PUT_DYN_PAR_1 // BEGIN_PUT_DYN_PAR_2 , // END_PUT_DYN_PAR_2 } {xrst_end put_dyn_par} */ // BEGIN_PUT_DYN_PAR_0 template addr_t dyn_recorder::put_dyn_par( const Base &par, op_code_dyn op ) // END_PUT_DYN_PAR_0 { CPPAD_ASSERT_UNKNOWN( op == ind_dyn || op == result_dyn || op == atom_dyn ); CPPAD_ASSERT_UNKNOWN( num_arg_dyn(op) == 0 ); par_all_.push_back( par ); par_is_dyn_.push_back(true); dyn_par_op_.push_back( opcode_t(op) ); return static_cast( par_all_.size() - 1 ); } // BEGIN_PUT_DYN_PAR_1 template addr_t dyn_recorder::put_dyn_par( const Base &par, op_code_dyn op, addr_t a0 ) // END_PUT_DYN_PAR_1 { CPPAD_ASSERT_UNKNOWN( num_arg_dyn(op) == 1 ); par_all_.push_back( par ); par_is_dyn_.push_back(true); dyn_par_op_.push_back( opcode_t(op) ); dyn_par_arg_.push_back(a0); return static_cast( par_all_.size() - 1 ); } // BEGIN_PUT_DYN_PAR_2 template addr_t dyn_recorder::put_dyn_par( const Base &par, op_code_dyn op, addr_t a0, addr_t a1 ) // END_PUT_DYN_PAR_2 { CPPAD_ASSERT_UNKNOWN( num_arg_dyn(op) == 2 ); par_all_.push_back( par ); par_is_dyn_.push_back(true); dyn_par_op_.push_back( opcode_t(op) ); dyn_par_arg_.push_back(a0); dyn_par_arg_.push_back(a1); return static_cast( par_all_.size() - 1 ); } /* ------------------------------------------------------------------------------ {xrst_begin put_dyn_cond_exp dev} Put a Conditional Expression Dynamic Parameter at End of Parameter Vector ######################################################################### Syntax ****** | *par_index* = *dyn_record* . ``put_dyn_cond_exp`` ( | *par* , *cop* , *left* , *right* , *if_true* , *if_false* | ) par *** is value of dynamic parameter to be placed at the end of the parameter vector. cop *** is the operator comparison operator; i.e., Lt, Le, Eq, Ge, Gt, or Ne. left **** is the left argument in conditional expression (which is a parameter). right ***** is the right argument in conditional expression (which is a parameter). if_true ******* is the if_true argument in conditional expression (which is a parameter). if_false ******** is the if_false argument in conditional expression (which is a parameter). par_index ********* is the index of this dynamic parameter in the vector of all parameters. Prototype ********* {xrst_literal // BEGIN_PUT_DYN_COND_EXP // END_PUT_DYN_COND_EXP } {xrst_end put_dyn_cond_exp} */ // BEGIN_PUT_DYN_COND_EXP template addr_t dyn_recorder::put_dyn_cond_exp( const Base &par, CompareOp cop, addr_t left, addr_t right, addr_t if_true, addr_t if_false ) // END_PUT_DYN_COND_EXP { CPPAD_ASSERT_UNKNOWN( num_arg_dyn(cond_exp_dyn) == 5 ); addr_t ret = addr_t( par_all_.size() ); par_all_.push_back( par ); par_is_dyn_.push_back(true); dyn_par_op_.push_back( opcode_t(cond_exp_dyn) ); dyn_par_arg_.push_back( addr_t(cop) ); dyn_par_arg_.push_back(left); dyn_par_arg_.push_back(right); dyn_par_arg_.push_back(if_true); dyn_par_arg_.push_back(if_false); return ret; } /* ------------------------------------------------------------------------------ {xrst_begin put_dyn_arg_vec dev} Put a Vector of Arguments at End of Dynamic Parameter Argument Vector ##################################################################### Prototype ********* {xrst_literal // BEGIN_PUT_DYN_ARG_VEC // END_PUT_DYN_ARG_VEC } arg_vec ******* is the vector of values to be added at the end of the dynamic parameter operator argument vector. {xrst_end put_dyn_arg_vec} */ // BEGIN_PUT_DYN_ARG_VEC // dyn_record.put_dyn_arg_vec(arg_vec) template void dyn_recorder::put_dyn_arg_vec(const pod_vector& arg_vec) // END_PUT_DYN_ARG_VEC { for(size_t i = 0; i < arg_vec.size(); ++i) dyn_par_arg_.push_back( arg_vec[i] ); } /* ------------------------------------------------------------------------------ {xrst_begin put_con_par dev} Find or Add a Constant Parameter to Current Parameter Vector ############################################################ Prototype ********* {xrst_literal // BEGIN_PUT_CON_PAR // END_PUT_CON_PAR } par *** is the parameter to be found or placed in the vector of parameters. par_index ********* is the index in the parameter vector corresponding to this parameter value. This value is not necessarily placed at the end of the vector (because values that are identically equal may be reused). {xrst_end put_con_par} */ // BEGIN_PUT_CON_PAR // par_index = dyn_record.put_con_par(par) template addr_t dyn_recorder::put_con_par(const Base &par) // END_PUT_CON_PAR { # ifndef NDEBUG // index zero is used to signify that a value is not a parameter; // i.e., it is a variable. if( par_all_.size() == 0 ) CPPAD_ASSERT_UNKNOWN( CppAD::isnan(par) ); # endif // --------------------------------------------------------------------- // check for a match with a previous parameter // // get hash code for this value size_t code = static_cast( hash_code(par) ); // current index in par_all_ corresponding to this hash code size_t index = static_cast( par_hash_table_[code] ); // check if the old parameter matches the new one if( (0 < index) && (index < par_all_.size()) ) { if( ! par_is_dyn_[index] ) if( IdenticalEqualCon(par_all_[index], par) ) return static_cast( index ); } // --------------------------------------------------------------------- // put parameter in par_all_ and replace hash entry for this codee // index = par_all_.size(); par_all_.push_back( par ); par_is_dyn_.push_back(false); // // change the hash table for this code to point to new value par_hash_table_[code] = static_cast( index ); // // return the parameter index CPPAD_ASSERT_KNOWN( static_cast( std::numeric_limits::max() ) >= index, "cppad_tape_addr_type maximum value has been exceeded" ) return static_cast( index ); } } } // END_CPPAD_LOCAL_NAMESPACE // ---------------------------------------------------------------------------- // member function implementations # include # endif ================================================ FILE: include/cppad/local/record/put_dyn_atomic.hpp ================================================ # ifndef CPPAD_LOCAL_RECORD_PUT_DYN_ATOMIC_HPP # define CPPAD_LOCAL_RECORD_PUT_DYN_ATOMIC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE /* {xrst_begin recorder_put_dyn_atomic dev} {xrst_spell taddr } Put a Dynamic Parameter Atomic Call Operator in Recording ######################################################### Syntax ****** | *dyn_record* . ``put_dyn_atomic`` ( | |tab| *tape_id* , *atomic_index* , *call_id* , *type_x* , *type_y* , *ax* , *ay* | ) Prototype ********* {xrst_literal // BEGIN_PUT_DYN_ATOMIC // END_PROTOTYPE } tape_id ******* identifies the tape that this recording corresponds to. This is zero if and only if there is no tape for this recording; i.e. ``AD`` < *Base* >. ``tape_ptr`` () is null. atomic_index ************ is the :ref:`atomic_index-name` for this atomic function. call_id ******* is the :ref:`atomic_four_call@call_id` for this atomic function. type_x ****** is the :ref:`ad_type_enum-name` for each of the atomic function arguments. type_y ****** is the ``ad_type_enum`` for each of the atomic function results. ax ** is the atomic function argument vector for this call. value\_ ======= The value *ax* [ *j* ]. ``value_`` is the proper value for parameters and does not matter for variables. taddr\_ ======= The value *ax* [ *j* ]. ``taddr_`` is the proper address for dynamic parameters and does not matter for constants or variables. ay ** is the atomic function result vector for this call. Input ===== On input, *ay* [ *j* ]. ``value_`` has the proper value for parameters (result for the atomic function). Output ====== Upon return, if the *i*-th result is a dynamic parameter, | |tab| *ay* [ *i* ]. ``ad_type_`` = ``dynamic_enum`` | |tab| *ay* [ *i* ]. ``tape_id_`` = *tape_id* | |tab| *ay* [ *i* ]. ``taddr_`` = *p_index* where *p_index* is the index of this dynamic parameter in the vector of all parameters. {xrst_end recorder_put_dyn_atomic} */ // BEGIN_PUT_DYN_ATOMIC template template void dyn_recorder::put_dyn_atomic( tape_id_t tape_id , size_t atomic_index , size_t call_id , const vector& type_x , const vector& type_y , const VectorAD& ax , VectorAD& ay ) // END_PROTOTYPE { CPPAD_ASSERT_UNKNOWN( (tape_id == 0) == (AD::tape_ptr() == nullptr) ); CPPAD_ASSERT_UNKNOWN( ax.size() == type_x.size() ); CPPAD_ASSERT_UNKNOWN( ay.size() == type_y.size() ); size_t n = ax.size(); size_t m = ay.size(); size_t num_dyn = 0; for(size_t i = 0; i < m; ++i) if( type_y[i] == dynamic_enum ) ++num_dyn; CPPAD_ASSERT_UNKNOWN( num_dyn > 0 ); // dyn_par_arg_.push_back( addr_t(atomic_index )); // arg[0] = atomic_index dyn_par_arg_.push_back( addr_t(call_id )); // arg[1] = call_id dyn_par_arg_.push_back( addr_t( n ) ); // arg[2] = n dyn_par_arg_.push_back( addr_t( m ) ); // arg[3] = m dyn_par_arg_.push_back( addr_t( num_dyn ) ); // arg[4] = num_dyn // arg[5 + j] for j = 0, ... , n-1 for(size_t j = 0; j < n; ++j) { addr_t arg = 0; switch( type_x[j] ) { case identical_zero_enum: case constant_enum: arg = put_con_par( ax[j].value_ ); break; case dynamic_enum: arg = ax[j].taddr_; break; case variable_enum: arg = 0; // phantom parameter index CPPAD_ASSERT_UNKNOWN( CppAD::isnan( par_all_[arg] ) ) break; default: arg = 0; CPPAD_ASSERT_UNKNOWN( false ); } dyn_par_arg_.push_back( arg ); // arg[5 + j] } // arg[5 + n + i] for i = 0, ... , m-1 bool first_dynamic_result = true; for(size_t i = 0; i < m; ++i) { addr_t arg; switch( type_y[i] ) { case identical_zero_enum: case constant_enum: arg = 0; // phantom parameter index break; case dynamic_enum: // one operator for each dynamic parameter result // so number of operators is equal number of dynamic parameters if( first_dynamic_result ) arg = put_dyn_par(ay[i].value_, atom_dyn ); // atom_dyn else arg = put_dyn_par(ay[i].value_, result_dyn ); // result_dyn ay[i].ad_type_ = dynamic_enum; ay[i].taddr_ = arg; ay[i].tape_id_ = tape_id; first_dynamic_result = false; break; case variable_enum: arg = 0; // phantom parameter (has value nan) break; default: arg = 0; CPPAD_ASSERT_UNKNOWN( false ); } dyn_par_arg_.push_back( arg ); // arg[5 + n + i] } dyn_par_arg_.push_back( addr_t(6 + n + m) ); // arg[5 + n + m] } } } // END_CPPAD_LOCAL_NAMESPACE # endif ================================================ FILE: include/cppad/local/record/put_var_atomic.hpp ================================================ # ifndef CPPAD_LOCAL_RECORD_PUT_VAR_ATOMIC_HPP # define CPPAD_LOCAL_RECORD_PUT_VAR_ATOMIC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE /* {xrst_begin recorder_put_var_atomic dev} {xrst_spell taddr } Put a Variable Atomic Call Operator in Recording ################################################ Syntax ****** | *rec* . ``put_var_atomic`` ( | |tab| *tape_id* , *atomic_index* , *call_id* , *type_x* , *type_y* , *ax* , *ay* | ) Prototype ********* {xrst_literal // BEGIN_PUT_VAR_ATOMIC // END_PROTOTYPE } tape_id ******* identifies the tape that this recording corresponds to. This is zero if and only if there is no tape for this recording; i.e. ``AD`` < *Base* >. ``tape_ptr`` () is null. atomic_index ************ is the :ref:`atomic_index-name` for this atomic function. call_id ******* Is the :ref:`atomic_four_call@call_id` for this atomic function call. type_x ****** is the :ref:`ad_type_enum-name` for each of the atomic function arguments. This is one of the rare cases where constants can have type ``identical_zero_enum`` . type_y ****** is the ``ad_type_enum`` for each of the atomic function results. This is one of the rare cases where constants can have type ``identical_zero_enum`` . ax ** is the atomic function argument vector for this call. value\_ ======= The value *ax* [ *j* ]. ``value_`` is the proper value for all arguments. taddr\_ ======= The value *ax* [ *j* ]. ``taddr_`` is the proper address for dynamic parameters and variables and does not matter for constants. ay ** is the atomic function result vector for this call. Input ===== On input, *ay* [ *i* ] has all the correct values for parameters and does not matter for variables. Output ====== Upon return, if the *i*-th result is a variable, | |tab| *ay* [ *i* ]. ``ad_type_`` = ``dynamic_enum`` | |tab| *ay* [ *i* ]. ``tape_id_`` = *tape_id* | |tab| *ay* [ *i* ]. ``taddr_`` = *v_index* where *v_index* is the index of this variable in the arrays containing all the variables. {xrst_end recorder_put_var_atomic} */ // BEGIN_PUT_VAR_ATOMIC template template void recorder::put_var_atomic( tape_id_t tape_id , size_t atomic_index , size_t call_id , const vector& type_x , const vector& type_y , const VectorAD& ax , VectorAD& ay ) // END_PROTOTYPE { CPPAD_ASSERT_KNOWN( size_t( std::numeric_limits::max() ) >= std::max( std::max(atomic_index, ax.size() ), ay.size() ), "atomic_three: cppad_tape_addr_type maximum not large enough" ); CPPAD_ASSERT_UNKNOWN( (tape_id == 0) == (AD::tape_ptr() == nullptr) ); // Operator that marks beginning of this atomic operation CPPAD_ASSERT_NARG_NRES(local::AFunOp, 4, 0 ); size_t n = ax.size(); size_t m = ay.size(); PutArg(addr_t(atomic_index), addr_t(call_id), addr_t(n), addr_t(m)); PutOp(local::AFunOp); // Now put n operators, one for each element of argument vector CPPAD_ASSERT_NARG_NRES(local::FunavOp, 1, 0 ); CPPAD_ASSERT_NARG_NRES(local::FunapOp, 1, 0 ); for(size_t j = 0; j < n; j++) { if( type_x[j] == variable_enum ) { // information for an argument that is a variable PutArg(ax[j].taddr_); PutOp(local::FunavOp); } else { // information for an argument that is parameter addr_t par = ax[j].taddr_; if( type_x[j] <= constant_enum ) par = put_con_par(ax[j].value_); PutArg(par); PutOp(local::FunapOp); } } // Now put m operators, one for each element of result vector CPPAD_ASSERT_NARG_NRES(local::FunrvOp, 0, 1); CPPAD_ASSERT_NARG_NRES(local::FunrpOp, 1, 0); for(size_t i = 0; i < m; i++) { if( type_y[i] == variable_enum ) { ay[i].taddr_ = PutOp(local::FunrvOp); ay[i].tape_id_ = tape_id; ay[i].ad_type_ = variable_enum; } else { addr_t par = ay[i].taddr_; if( type_y[i] <= constant_enum ) par = put_con_par( ay[i].value_ ); PutArg(par); PutOp(local::FunrpOp); } } // Put a duplicate AFunOp at end of AFunOp sequence PutArg(addr_t(atomic_index), addr_t(call_id), addr_t(n), addr_t(m)); PutOp(local::AFunOp); } } } // END_CPPAD_LOCAL_NAMESPACE # endif ================================================ FILE: include/cppad/local/record/put_var_vecad.hpp ================================================ # ifndef CPPAD_LOCAL_RECORD_PUT_VAR_VECAD_HPP # define CPPAD_LOCAL_RECORD_PUT_VAR_VECAD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE /* ------------------------------------------------------------------------------ {xrst_begin put_var_vecad_ind dev} Add One Index to End of Combined Variable VecAD Vector ###################################################### Syntax ****** | *offset* = *rec* . ``put_var_vecad_ind`` ( *vec_ind* ) Prototype ********* {xrst_literal // BEGIN_PUT_VAR_VECAD_IND // END_PUT_VAR_VECAD_IND } Purpose ******* For each variable VecAD vector, this routine is used to store the length of the vector followed by the parameter index corresponding to initial value in the vector; i.e., the values just before it changed from a parameter to a variable. vec_ind ******* is the index to be placed at the end of the combined vector of VecAD indices. offset ****** is the index in the combined variable VecAD vector where the value *vec_ind* is stored. This index starts at zero after the recorder default constructor and increments by one for each call to put_var_vecad_ind. {xrst_end put_var_vecad_ind} */ // BEGIN_PUT_VAR_VECAD_IND template addr_t recorder::put_var_vecad_ind(addr_t vec_ind) // END_PUT_VAR_VECAD_IND { size_t offset = var_vecad_ind_.size(); var_vecad_ind_.push_back( vec_ind ); CPPAD_ASSERT_KNOWN( size_t( addr_t( offset ) ) == offset, "cppad_tape_addr_type cannot support needed index range" ); return static_cast( offset ); } /* ------------------------------------------------------------------------------ {xrst_begin recorder_put_var_vecad dev} {xrst_spell taddr } Tape Initialization for a Variable VecAD Object ############################################### Syntax ****** *offset* = *rec* . ``put_var_vecad`` ( *length* , *taddr* ) Prototype ********* {xrst_literal // BEGIN_PUT_VAR_VECAD_VEC // END_PUT_VAR_VECAD_VEC } Usage ***** This routine should be called once for each variable VecAD object just before it changes from a parameter to a variable. length ****** is the size of the VecAD object. taddr ***** vector of parameter indices corresponding to the value of this VecAD vector just before it becomes a variable. offset ****** index of the start of this VecAD vector in the combined variable VecAD vector. The value corresponding to *offset* is the length of this VecAD vector. There are *length* more indices following the length. These values are the parameter indices. {xrst_end recorder_put_var_vecad} */ // BEGIN_PUT_VAR_VECAD_VEC template addr_t recorder::put_var_vecad( size_t length , const pod_vector& taddr ) // END_PUT_VAR_VECAD_VEC { CPPAD_ASSERT_UNKNOWN( length > 0 ); CPPAD_ASSERT_UNKNOWN( length == taddr.size() ); CPPAD_ASSERT_KNOWN( size_t( std::numeric_limits::max() ) >= length, "A VecAD vector length is too large fur cppad_tape_addr_type" ); // store the length in VecInd addr_t start = put_var_vecad_ind( addr_t(length) ); // store indices of the values in VecInd for(size_t i = 0; i < length; i++) put_var_vecad_ind( taddr[i] ); // return the taddr of the length (where the vector starts) return start; } } } // END_CPPAD_LOCAL_NAMESPACE # endif ================================================ FILE: include/cppad/local/record/recorder.hpp ================================================ # ifndef CPPAD_LOCAL_RECORD_RECORDER_HPP # define CPPAD_LOCAL_RECORD_RECORDER_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include /* ------------------------------------------------------------------------------- {xrst_begin_parent recorder dev} {xrst_spell getters } Class That Records Both Variable and Dynamic Parameter Operations ################################################################# Syntax ****** | CppAD::local::recorder *record* record ****** {xrst_literal // BEGIN_CLASS // END_CLASS } Base **** is the base type for this recording; i.e., we are recording ``AD`` < *Base* > operations. Dynamic Parameter Operations **************************** The :ref:`dyn_recorder-name` functions can be accessed using *record* ; e.g., | |tab| *record* . ``set_n_dyn_independent`` ( *n_dyn_independent* ) accesses the :ref:`dyn_recorder@set_n_dyn_independent` function. Setters ******* set_record_compare ================== {xrst_literal // BEGIN_SET_RECORD_COMPARE // END_SET_RECORD_COMPARE } set_abort_op_index ================== {xrst_literal // BEGIN_SET_ABORT_OP_INDEX // END_SET_ABORT_OP_INDEX } Getters ******* get_record_compare ================== {xrst_literal // BEGIN_GET_RECORD_COMPARE // END_GET_RECORD_COMPARE } get_abort_op_index ================== {xrst_literal // BEGIN_GET_ABORT_OP_INDEX // END_GET_ABORT_OP_INDEX } num_var ======= {xrst_literal // BEGIN_NUM_VAR_REC // END_NUM_VAR_REC } num_var_load ============ {xrst_literal // BEGIN_NUM_VAR_LOAD_REC // END_NUM_VAR_LOAD_REC } num_var_op ========== {xrst_literal // BEGIN_NUM_OP_REC // END_NUM_OP_REC } Memory ****** {xrst_literal // BEGIN_MEMORY // END_MEMORY } Contents ******** {xrst_toc_table after include/cppad/local/record/put_var_vecad.hpp include/cppad/local/record/put_dyn_atomic.hpp include/cppad/local/record/put_var_atomic.hpp include/cppad/local/record/cond_exp.hpp include/cppad/local/record/comp_op.hpp include/cppad/local/record/dyn_recorder.hpp } {xrst_end recorder} ------------------------------------------------------------------------------- */ // BEGIN_CPPAD_LOCAL_NAMESPACE // BEGIN_CLASS namespace CppAD { namespace local { template class recorder { // END_CLASS // friend class player; // private: // // dyn_record_ dyn_recorder dyn_record_; // // record_compare // are comparison operators being recorded bool record_compare_; // // abort_op_index_ // operator index at which to abort recording with an error // (do not abort when zero) size_t abort_op_index_; // // num_var_ // Number of variables in the recording. size_t num_var_; // // num_var_load_ // Number vecad load operations (LdpOp or LdvOp) currently in recording. size_t num_var_load_; // // var_op_ // The operators in the recording. pod_vector var_op_; // // var_vecad_ind_ // The VecAD indices in the recording. pod_vector var_vecad_ind_; // // var_arg_ // The argument indices in the recording pod_vector var_arg_; // // var_text_ // Character strings ('\\0' terminated) in the recording. pod_vector var_text_; // public: // // Constructor recorder(void) : record_compare_(true) , abort_op_index_(0) , num_var_(0) , num_var_load_(0) { } // // Destructor ~recorder(void) { } // ------------------------------------------------------------------------ // Parameter Operations // ------------------------------------------------------------------------ // set_n_dyn_independent void set_n_dyn_independent(size_t n_dyn_independent) { dyn_record_.set_n_dyn_independent(n_dyn_independent); } // // n_dyn_independent size_t n_dyn_independent(void) const { return dyn_record_.n_dyn_independent(); } // // put_dyn_atomic template void put_dyn_atomic( tape_id_t tape_id , size_t atom_index , size_t call_id , const vector& type_x , const vector& type_y , const VectorAD& ax , VectorAD& ay ) { dyn_record_.put_dyn_atomic( tape_id, atom_index, call_id, type_x, type_y, ax, ay ); } // // par_all const pod_vector_maybe& par_all(void) const { return dyn_record_.par_all(); } // // put_con_par addr_t put_con_par(const Base &par) { return dyn_record_.put_con_par(par); } // addr_t put_dyn_par(const Base &par, op_code_dyn op) { return dyn_record_.put_dyn_par(par, op); } addr_t put_dyn_par( const Base &par, op_code_dyn op, addr_t a0) { return dyn_record_.put_dyn_par(par, op, a0); } addr_t put_dyn_par( const Base &par, op_code_dyn op, addr_t a0, addr_t a1) { return dyn_record_.put_dyn_par(par, op, a0, a1); } // // put_dyn_cond_exp addr_t put_dyn_cond_exp(const Base &par, CompareOp cop, addr_t left, addr_t right, addr_t if_true, addr_t if_false ) { return dyn_record_.put_dyn_cond_exp( par, cop, left, right, if_true, if_false ); } // put_dyn_arg_vec void put_dyn_arg_vec(const pod_vector& arg_vec) { dyn_record_.put_dyn_arg_vec(arg_vec); } // ------------------------------------------------------------------------ // // BEGIN_SET_RECORD_COMPARE // recorder.set_record_compare(record_compare) void set_record_compare(bool record_compare) // END_SET_RECORD_COMPARE { record_compare_ = record_compare; } // // BEGIN_SET_ABORT_OP_INDEX // recorder.set_abort_op_index(abort_op_index) void set_abort_op_index(size_t abort_op_index) // END_SET_ABORT_OP_INDEX { abort_op_index_ = abort_op_index; } // // BEGIN_GET_RECORD_COMPARE // record_compare = recorder.get_record_compare() bool get_record_compare(void) const // END_GET_RECORD_COMPARE { return record_compare_; } // // BEGIN_GET_ABORT_OP_INDEX // abort_op_index = recorder.get_abort_op_index() size_t get_abort_op_index(void) const // END_GET_ABORT_OP_INDEX { return abort_op_index_; } // // BEGIN_NUM_VAR_REC /// num_var = recorder.num_var() size_t num_var(void) const // END_NUM_VAR_REC { return num_var_; } // // BEGIN_NUM_VAR_LOAD_REC // num_var_load = recorder.num_var_load() size_t num_var_load(void) const // END_NUM_VAR_LOAD_REC { return num_var_load_; } // // BEGIN_NUM_OP_REC // num_var_op = recorder.num_var_op() size_t num_var_op(void) const // END_NUM_OP_REC { return var_op_.size(); } // // BEGIN_MEMORY // memory = recorder.memory() size_t Memory(void) const // END_MEMORY { return 0 + dyn_record_.Memory() + var_op_.capacity() * sizeof(opcode_t) + var_vecad_ind_.capacity() * sizeof(addr_t) + var_arg_.capacity() * sizeof(addr_t) + var_text_.capacity() * sizeof(char) ; } // // PutOp addr_t PutOp(op_code_var op); // // PutArg void PutArg(addr_t a0); void PutArg(addr_t a0, addr_t a1); void PutArg(addr_t a0, addr_t a1, addr_t a2); void PutArg(addr_t a0, addr_t a1, addr_t a2, addr_t a3); void PutArg(addr_t a0, addr_t a1, addr_t a2, addr_t a3, addr_t a4); void PutArg( addr_t a0, addr_t a1, addr_t a2, addr_t a3, addr_t a4, addr_t a5 ); // // PutLoadOp addr_t PutLoadOp(op_code_var op); // // ReserveArg size_t ReserveArg(size_t n_arg); // // ReplaceArg void ReplaceArg(size_t i_arg, addr_t value); // // PutTxt addr_t PutTxt(const char *text); // // put_var_vecad_ind addr_t put_var_vecad_ind(addr_t vec_ind); // // put_var_vecad addr_t put_var_vecad(size_t length, const pod_vector& taddr); // // put_var_atomic template void put_var_atomic( tape_id_t tape_id , size_t atom_index , size_t call_id , const vector& type_x , const vector& type_y , const VectorAD& ax , VectorAD& ay ); /// record a variable or dynamic parameter conditional expression void cond_exp( tape_id_t tape_id , enum CompareOp cop , AD &result , const AD &left , const AD &right , const AD &if_true , const AD &if_false ); /// record a comparison operators for variables or just dynamic parameters void comp_eq( bool var_left , bool var_right , bool dyn_left , bool dyn_right , const AD& aleft , const AD& aright , bool result ); void comp_le( bool var_left , bool var_right , bool dyn_left , bool dyn_right , const AD& aleft , const AD& aright , bool result ); void comp_lt( bool var_left , bool var_right , bool dyn_left , bool dyn_right , const AD& aleft , const AD& aright , bool result ); }; /* ------------------------------------------------------------------------------ {xrst_begin var_put_op dev} {xrst_spell ldv ldp } Put Next Operator in the Variable Operation Sequence #################################################### Prototype ********* {xrst_literal // BEGIN_PUT_OP // END_PUT_OP } Description *********** This sets the op code for the next operation in this recording. This call must be followed by putting the corresponding argument in the recording. op ** is the op code corresponding to the operation that is being recorded. The LdpOp and LdvOp operators are special cases and must use the :ref:`var_put_load_op-name` function. var_index ********* is the index of the primary (last) variable corresponding to the result of this operation. The number of variables corresponding to the operation is given by ``NumRes`` ( *op* ) . With each call to PutOp or PutLoadOp, *var_index* increases by the number of variables corresponding to the call. This index starts at zero after the default constructor. {xrst_end var_put_op} */ // BEGIN_PUT_OP // var_index = record.PutOp(op) template addr_t recorder::PutOp(op_code_var op) // END_PUT_OP { size_t i = var_op_.extend(1); CPPAD_ASSERT_KNOWN( (abort_op_index_ == 0) || (abort_op_index_ != i), "Operator index equals abort_op_index in Independent" ); var_op_[i] = static_cast(op); CPPAD_ASSERT_UNKNOWN( var_op_.size() == i + 1 ); CPPAD_ASSERT_UNKNOWN( (op != LdpOp) && (op != LdvOp) ); // first operator should be a BeginOp and NumRes( BeginOp ) > 0 num_var_ += NumRes(op); CPPAD_ASSERT_UNKNOWN( num_var_ > 0 ); // index of last variable corresponding to this operation // (if NumRes(op) > 0) CPPAD_ASSERT_KNOWN( (size_t) std::numeric_limits::max() >= num_var_ - 1, "cppad_tape_addr_type maximum value has been exceeded" ) return static_cast( num_var_ - 1 ); } /* ------------------------------------------------------------------------------- {xrst_begin var_put_arg dev} {xrst_spell etc } Put Operator Arguments in the Variable Operation Sequence ######################################################### Syntax ****** | *recorder* . ``PutArg`` ( *a0* ) | *recorder* . ``PutArg`` ( *a0* , *a1* ) | ... | *recorder* . ``PutArg`` ( *a0* , *a1* , *a2* , *a3* , *a4* , *a5* ) Description *********** Places the values passed to ``PutArg`` at the end of the current operation argument indices for the recording; *a0* comes before *a1* etc. The number of the operation argument indices starts at zero after the default constructor. The proper number of operation arguments corresponding to the operation code *op* is given by ``NumArg`` ( *op* ) (except for the CSumOP and CSkipOp operators). a0 ** is the first argument to place at the end of the operator argument vector. a1 ** if present, is places after *a0* at the end of the operator argument vector. a2 ** if present, is places after *a1* at the end of the operator argument vector. a3 ** if present, is places after *a2* at the end of the operator argument vector. a4 ** if present, is places after *a3* at the end of the operator argument vector. a5 ** if present, is places after *a4* at the end of the operator argument vector. Prototype ********* {xrst_literal , // BEGIN_PUT_ARG_0 , END_PUT_ARG_0 // BEGIN_PUT_ARG_1 , END_PUT_ARG_1 // BEGIN_PUT_ARG_2 , END_PUT_ARG_2 // BEGIN_PUT_ARG_3 , END_PUT_ARG_3 // BEGIN_PUT_ARG_4 , END_PUT_ARG_4 // BEGIN_PUT_ARG_5 , END_PUT_ARG_5 } {xrst_end var_put_arg} */ // BEGIN_PUT_ARG_0 template void recorder::PutArg(addr_t a0) // END_PUT_ARG_0 { size_t i = var_arg_.extend(1); var_arg_[i] = a0; CPPAD_ASSERT_UNKNOWN( var_arg_.size() == i + 1 ); } // BEGIN_PUT_ARG_1 template void recorder::PutArg(addr_t a0, addr_t a1) // END_PUT_ARG_1 { size_t i = var_arg_.extend(2); var_arg_[i++] = a0; var_arg_[i] = a1; CPPAD_ASSERT_UNKNOWN( var_arg_.size() == i + 1 ); } // BEGIN_PUT_ARG_2 template void recorder::PutArg( addr_t a0, addr_t a1, addr_t a2 ) // END_PUT_ARG_2 { size_t i = var_arg_.extend(3); var_arg_[i++] = a0; var_arg_[i++] = a1; var_arg_[i] = a2; CPPAD_ASSERT_UNKNOWN( var_arg_.size() == i + 1 ); } // BEGIN_PUT_ARG_3 template void recorder::PutArg( addr_t a0, addr_t a1, addr_t a2, addr_t a3 ) // END_PUT_ARG_3 { size_t i = var_arg_.extend(4); var_arg_[i++] = a0; var_arg_[i++] = a1; var_arg_[i++] = a2; var_arg_[i] = a3; CPPAD_ASSERT_UNKNOWN( var_arg_.size() == i + 1 ); } // BEGIN_PUT_ARG_4 template void recorder::PutArg( addr_t a0, addr_t a1, addr_t a2, addr_t a3, addr_t a4 ) // END_PUT_ARG_4 { size_t i = var_arg_.extend(5); var_arg_[i++] = a0; var_arg_[i++] = a1; var_arg_[i++] = a2; var_arg_[i++] = a3; var_arg_[i] = a4; CPPAD_ASSERT_UNKNOWN( var_arg_.size() == i + 1 ); } // BEGIN_PUT_ARG_5 template void recorder::PutArg( addr_t a0, addr_t a1, addr_t a2, addr_t a3, addr_t a4, addr_t a5 ) // END_PUT_ARG_5 { size_t i = var_arg_.extend(6); var_arg_[i++] = a0; var_arg_[i++] = a1; var_arg_[i++] = a2; var_arg_[i++] = a3; var_arg_[i++] = a4; var_arg_[i] = a5; CPPAD_ASSERT_UNKNOWN( var_arg_.size() == i + 1 ); } /* ------------------------------------------------------------------------------- {xrst_begin var_put_load_op dev} {xrst_spell ldv ldp } Put Next LdpOp or LdvOp Operator in Operation Sequence ###################################################### Prototype ********* {xrst_literal // BEGIN_PUT_LOAD_OP // END_PUT_LOAD_OP } Description *********** This sets the op code for a load instruction. This call must be followed by putting the corresponding argument indices in the recording. op ** Is the op code corresponding to the operation that is being recorded (which must be LdpOp or LdvOp). var_index ********* The return value is the index of the variable corresponding to the result of this operation must be one. With each call to PutLoadOp or PutOp, the return index increases by the number of variables corresponding to this call to the call. This index starts at zero after the default constructor. num_var_load ************ The return value for ``num_var_load()`` increases by one after each call to this function. {xrst_end var_put_load_op} */ // BEGIN_PUT_LOAD_OP // var_index = recorder.PutLoadOp(op) template addr_t recorder::PutLoadOp(op_code_var op) // END_PUT_LOAD_OP { size_t i = var_op_.extend(1); CPPAD_ASSERT_KNOWN( (abort_op_index_ == 0) || (abort_op_index_ != i), "This is the abort operator index specified by " "Independent(x, abort_op_index)." ); var_op_[i] = op; CPPAD_ASSERT_UNKNOWN( var_op_.size() == i + 1 ); CPPAD_ASSERT_UNKNOWN( (op == LdpOp) || (op == LdvOp) ); // first operator should be a BeginOp and NumRes( BeginOp ) > 0 num_var_ += NumRes(op); CPPAD_ASSERT_UNKNOWN( num_var_ > 0 ); // count this vecad load operation num_var_load_++; // index of last variable corresponding to this operation // (if NumRes(op) > 0) CPPAD_ASSERT_KNOWN( (size_t) std::numeric_limits::max() >= num_var_ - 1, "cppad_tape_addr_type maximum value has been exceeded" ) return static_cast( num_var_ - 1 ); } /* ------------------------------------------------------------------------------- {xrst_begin var_reserve_arg dev} Reserve Space for Variable Recording Arguments, Delay Placing Values There ########################################################################## Prototype ********* {xrst_literal // BEGIN_RESERVE_ARG // END_RESERVE_ARG } n_arg ***** number of arguments to reserve space for arg_index ********* is the index in the argument vector corresponding to the first of the arguments being reserved. {xrst_end var_reserve_arg} */ // BEGIN_RESERVE_ARG // arg_index = recorder.ReserveArg(n_arg) template size_t recorder::ReserveArg(size_t n_arg) // END_RESERVE_ARG { size_t i = var_arg_.extend(n_arg); CPPAD_ASSERT_UNKNOWN( var_arg_.size() == i + n_arg ); return i; } /* ------------------------------------------------------------------------------- {xrst_begin var_replace_arg dev} Replace an Argument Value in the Variable Recording ################################################### Prototype ********* {xrst_literal // BEGIN_REPLACE_ARG // END_REPLACE_ARG } Purpose ******* This is intended to be used to replace reserved values. arg_index ********* is the index, in argument vector, for the value that is replaced. value ***** is the new value for the argument with the specified index. {xrst_end var_replace_arg} */ // BEGIN_REPLACE_ARG // recorder.ReplaceArg(arg_index, value) template void recorder::ReplaceArg( size_t arg_index, addr_t value ) // END_REPLACE_ARG { var_arg_[arg_index] = value; } // -------------------------------------------------------------------------- /* {xrst_begin var_put_txt dev} Put a Character String in the Text for This Variable Recording ############################################################## Prototype ********* {xrst_literal // BEGIN_PUT_TXT // END_PUT_TXT } text **** is a ``\\0`` terminated character string that is to be put in the vector of characters corresponding to this recording. The terminator ``\\0`` is included. txt_index ********* is the offset with in the text vector for this recording at which the character string starts. {xrst_end var_put_txt} */ // BEGIN_PUT_TXT // txt_index = recorder.PutTxt(text) template addr_t recorder::PutTxt(const char *text) // END_PUT_TXT { // determine length of the text including terminating '\0' size_t n = 0; while( text[n] != '\0' ) n++; CPPAD_ASSERT_UNKNOWN( n <= 1000 ); n++; CPPAD_ASSERT_UNKNOWN( text[n-1] == '\0' ); // copy text including terminating '\0' size_t i = var_text_.extend(n); size_t j; for(j = 0; j < n; j++) var_text_[i + j] = text[j]; CPPAD_ASSERT_UNKNOWN( var_text_.size() == i + n ); CPPAD_ASSERT_KNOWN( size_t( std::numeric_limits::max() ) >= i, "cppad_tape_addr_type maximum value has been exceeded" ); // return static_cast( i ); } } } // END_CPPAD_LOCAL_NAMESPACE // ---------------------------------------------------------------------------- // member function implementations # include # include # include # include # endif ================================================ FILE: include/cppad/local/set_get_in_parallel.hpp ================================================ # ifndef CPPAD_LOCAL_SET_GET_IN_PARALLEL_HPP # define CPPAD_LOCAL_SET_GET_IN_PARALLEL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE /* {xrst_begin set_get_in_parallel dev} {xrst_spell nullptr } Set in_parallel Routine or Get In Parallel Mode ############################################### Set and call the routine that determine if we are in parallel execution mode. Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } set === If *set* is true, set are setting the current in_parallel routine. In this case we must be in sequential execution mode; i.e., not parallel. If *set* is false, we are getting the result for the current in_parallel routine. in_parallel_new *************** #. If *set* is true, *in_parallel_new* becomes the most recent setting for the user's in_parallel routine. The nullptr in_parallel routine will always return false. #. If *set* is false, the value of *in_parallel_new* does not matter. flag **** #. If *set* is false, the return value *flag* is the current value for the current in_parallel routine. (It is false if the current in_parallel routine is the nullptr.) #. If *set* is true, the return value *flag* is unspecified. {xrst_end set_get_in_parallel} */ // BEGIN PROTOTYPE // flag = CppAD::local::set_get_inparallel( .. ) inline bool set_get_in_parallel( bool set = false , bool (*in_parallel_new)(void) = nullptr ) // END PROTOTYPE { typedef bool (*function_ptr)(void); static function_ptr in_parallel_user = nullptr; if( set ) { in_parallel_user = in_parallel_new; // Doing a raw assert in this case because set_get_in_parallel is used // by ErrorHandler and hence cannot use ErrorHandler. // CPPAD_ASSERT_UNKNOWN( in_parallel_user() == false ) assert(in_parallel_user == nullptr || in_parallel_user() == false); return false; } // if( in_parallel_user == nullptr ) return false; // return in_parallel_user(); } } } // END_CPPAD_LOCAL_NAMESPACE # endif ================================================ FILE: include/cppad/local/sparse/dev_sparse.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin dev_sparse dev} Developer Sparse Documentation ############################## Contents ******** {xrst_toc_table include/cppad/local/sparse/setvector.xrst } {xrst_end dev_sparse} ================================================ FILE: include/cppad/local/sparse/internal.hpp ================================================ # ifndef CPPAD_LOCAL_SPARSE_INTERNAL_HPP # define CPPAD_LOCAL_SPARSE_INTERNAL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // necessary definitions # include # include # include # include // BEGIN_CPPAD_LOCAL_SPARSE_NAMESPACE namespace CppAD { namespace local { namespace sparse { /*! \file sparse_internal.hpp Routines that enable code to be independent of which internal spasity pattern is used. */ // --------------------------------------------------------------------------- /*! Template structure used obtain the internal sparsity pattern type form the corresponding element type. The general form is not valid, must use a specialization. \tparam Element_type type of an element in the sparsity structure. \par internal_pattern::pattern_type is the type of the corresponding internal sparsity pattern. */ template struct internal_pattern; /// Specialization for bool elements. template <> struct internal_pattern { typedef sparse::pack_setvec pattern_type; }; /// Specialization for std::set elements. template <> struct internal_pattern< std::set > { typedef list_setvec pattern_type; }; // --------------------------------------------------------------------------- /*! Update the internal sparsity pattern for a sub-set of rows \tparam SizeVector The type used for index sparsity patterns. This is a simple vector with elements of type size_t. \tparam InternalSparsitiy The type used for internal sparsity patterns. This can be either sparse::pack_setvec or list_setvec. \param zero_empty If this is true, the internal sparstity pattern corresponds to row zero must be empty on input and will be empty output; i.e., any corresponding values in pattern_in will be ignored. \param input_empty If this is true, the initial sparsity pattern for row internal_index[i] is empty for all i. In this case, one is setting the sparsity patterns; i.e., the output pattern in row internal_index[i] is the corresponding entries in pattern. \param transpose If this is true, pattern_in is transposed. \param internal_index This specifies the sub-set of rows in internal_pattern that we are updating. If transpose is false (true), this is the mapping from row (column) index in pattern_in to the corresponding row index in the internal_pattern. \param internal_pattern On input, the number of sets internal_pattern.n_set(), and possible elements internal_pattern.end(), have been set. If input_empty is true, and all of the sets in internal_index are empty on input. On output, the entries in pattern_in are added to internal_pattern. To be specific, suppose transpose is false, and (i, j) is a possibly non-zero entry in pattern_in, the entry (internal_index[i], j) is added to internal_pattern. On the other hand, if transpose is true, the entry (internal_index[j], i) is added to internal_pattern. \param pattern_in This is the sparsity pattern for variables, or its transpose, depending on the value of transpose. */ template void set_internal_pattern( bool zero_empty , bool input_empty , bool transpose , const pod_vector& internal_index , InternalSparsity& internal_pattern , const sparse_rc& pattern_in ) { size_t nr = internal_index.size(); # ifndef NDEBUG size_t nc = internal_pattern.end(); if( transpose ) { CPPAD_ASSERT_UNKNOWN( pattern_in.nr() == nc ); CPPAD_ASSERT_UNKNOWN( pattern_in.nc() == nr ); } else { CPPAD_ASSERT_UNKNOWN( pattern_in.nr() == nr ); CPPAD_ASSERT_UNKNOWN( pattern_in.nc() == nc ); } if( input_empty ) for(size_t i = 0; i < nr; i++) { size_t i_var = internal_index[i]; CPPAD_ASSERT_UNKNOWN( internal_pattern.number_elements(i_var) == 0 ); } # endif const SizeVector& row( pattern_in.row() ); const SizeVector& col( pattern_in.col() ); size_t nnz = row.size(); for(size_t k = 0; k < nnz; k++) { size_t r = row[k]; size_t c = col[k]; if( transpose ) std::swap(r, c); // size_t i_var = internal_index[r]; CPPAD_ASSERT_UNKNOWN( i_var < internal_pattern.n_set() ); CPPAD_ASSERT_UNKNOWN( c < nc ); bool ignore = zero_empty && i_var == 0; if( ! ignore ) internal_pattern.post_element( internal_index[r], c ); } // process posts for(size_t i = 0; i < nr; ++i) internal_pattern.process_post( internal_index[i] ); } template void set_internal_pattern( bool zero_empty , bool input_empty , bool transpose , const pod_vector& internal_index , InternalSparsity& internal_pattern , const vectorBool& pattern_in ) { size_t nr = internal_index.size(); size_t nc = internal_pattern.end(); # ifndef NDEBUG CPPAD_ASSERT_UNKNOWN( pattern_in.size() == nr * nc ); if( input_empty ) for(size_t i = 0; i < nr; i++) { size_t i_var = internal_index[i]; CPPAD_ASSERT_UNKNOWN( internal_pattern.number_elements(i_var) == 0 ); } # endif for(size_t i = 0; i < nr; i++) { for(size_t j = 0; j < nc; j++) { bool flag = pattern_in[i * nc + j]; if( transpose ) flag = pattern_in[j * nr + i]; if( flag ) { size_t i_var = internal_index[i]; CPPAD_ASSERT_UNKNOWN( i_var < internal_pattern.n_set() ); CPPAD_ASSERT_UNKNOWN( j < nc ); bool ignore = zero_empty && i_var == 0; if( ! ignore ) internal_pattern.post_element( i_var, j); } } } // process posts for(size_t i = 0; i < nr; ++i) internal_pattern.process_post( internal_index[i] ); return; } template void set_internal_pattern( bool zero_empty , bool input_empty , bool transpose , const pod_vector& internal_index , InternalSparsity& internal_pattern , const vector& pattern_in ) { size_t nr = internal_index.size(); size_t nc = internal_pattern.end(); # ifndef NDEBUG CPPAD_ASSERT_UNKNOWN( pattern_in.size() == nr * nc ); if( input_empty ) for(size_t i = 0; i < nr; i++) { size_t i_var = internal_index[i]; CPPAD_ASSERT_UNKNOWN( internal_pattern.number_elements(i_var) == 0 ); } # endif for(size_t i = 0; i < nr; i++) { for(size_t j = 0; j < nc; j++) { bool flag = pattern_in[i * nc + j]; if( transpose ) flag = pattern_in[j * nr + i]; if( flag ) { size_t i_var = internal_index[i]; CPPAD_ASSERT_UNKNOWN( i_var < internal_pattern.n_set() ); CPPAD_ASSERT_UNKNOWN( j < nc ); bool ignore = zero_empty && i_var == 0; if( ! ignore ) internal_pattern.post_element( i_var, j); } } } // process posts for(size_t i = 0; i < nr; ++i) internal_pattern.process_post( internal_index[i] ); return; } template void set_internal_pattern( bool zero_empty , bool input_empty , bool transpose , const pod_vector& internal_index , InternalSparsity& internal_pattern , const vector< std::set >& pattern_in ) { size_t nr = internal_index.size(); size_t nc = internal_pattern.end(); # ifndef NDEBUG if( input_empty ) for(size_t i = 0; i < nr; i++) { size_t i_var = internal_index[i]; CPPAD_ASSERT_UNKNOWN( internal_pattern.number_elements(i_var) == 0 ); } # endif if( transpose ) { CPPAD_ASSERT_UNKNOWN( pattern_in.size() == nc ); for(size_t j = 0; j < nc; j++) { std::set::const_iterator itr( pattern_in[j].begin() ); while( itr != pattern_in[j].end() ) { size_t i = *itr; size_t i_var = internal_index[i]; CPPAD_ASSERT_UNKNOWN( i_var < internal_pattern.n_set() ); CPPAD_ASSERT_UNKNOWN( j < nc ); bool ignore = zero_empty && i_var == 0; if( ! ignore ) internal_pattern.post_element( i_var, j); ++itr; } } } else { CPPAD_ASSERT_UNKNOWN( pattern_in.size() == nr ); for(size_t i = 0; i < nr; i++) { std::set::const_iterator itr( pattern_in[i].begin() ); while( itr != pattern_in[i].end() ) { size_t j = *itr; size_t i_var = internal_index[i]; CPPAD_ASSERT_UNKNOWN( i_var < internal_pattern.n_set() ); CPPAD_ASSERT_UNKNOWN( j < nc ); bool ignore = zero_empty && i_var == 0; if( ! ignore ) internal_pattern.post_element( i_var, j); ++itr; } } } // process posts for(size_t i = 0; i < nr; ++i) internal_pattern.process_post( internal_index[i] ); return; } // --------------------------------------------------------------------------- /*! Get sparsity pattern for a sub-set of variables \tparam SizeVector The type used for index sparsity patterns. This is a simple vector with elements of type size_t. \tparam InternalSparsitiy The type used for internal sparsity patterns. This can be either sparse::pack_setvec or list_setvec. \param transpose If this is true, pattern_out is transposed. \param internal_index If transpose is false (true) this is the mapping from row (column) an index in pattern_out to the corresponding row index in internal_pattern. \param internal_pattern This is the internal sparsity pattern. \param pattern_out The input value of pattern_out does not matter. Upon return it is an index sparsity pattern for each of the variables in internal_index, or its transpose, depending on the value of transpose. */ template void get_internal_pattern( bool transpose , const pod_vector& internal_index , const InternalSparsity& internal_pattern , sparse_rc& pattern_out ) { typedef typename InternalSparsity::const_iterator iterator; // number variables size_t nr = internal_index.size(); // column size of internal sparstiy pattern size_t nc = internal_pattern.end(); // determine nnz, the number of possibly non-zero index pairs size_t nnz = 0; for(size_t i = 0; i < nr; i++) { CPPAD_ASSERT_UNKNOWN( internal_index[i] < internal_pattern.n_set() ); iterator itr(internal_pattern, internal_index[i]); size_t j = *itr; while( j < nc ) { ++nnz; j = *(++itr); } } // transposed if( transpose ) { pattern_out.resize(nc, nr, nnz); // size_t k = 0; for(size_t i = 0; i < nr; i++) { iterator itr(internal_pattern, internal_index[i]); size_t j = *itr; while( j < nc ) { pattern_out.set(k++, j, i); j = *(++itr); } } return; } // not transposed pattern_out.resize(nr, nc, nnz); // size_t k = 0; for(size_t i = 0; i < nr; i++) { iterator itr(internal_pattern, internal_index[i]); size_t j = *itr; while( j < nc ) { pattern_out.set(k++, i, j); j = *(++itr); } } return; } template void get_internal_pattern( bool transpose , const pod_vector& internal_index , const InternalSparsity& internal_pattern , vectorBool& pattern_out ) { typedef typename InternalSparsity::const_iterator iterator; // number variables size_t nr = internal_index.size(); // // column size of internal sparstiy pattern size_t nc = internal_pattern.end(); // pattern_out.resize(nr * nc); for(size_t ij = 0; ij < nr * nc; ij++) pattern_out[ij] = false; // for(size_t i = 0; i < nr; i++) { CPPAD_ASSERT_UNKNOWN( internal_index[i] < internal_pattern.n_set() ); iterator itr(internal_pattern, internal_index[i]); size_t j = *itr; while( j < nc ) { if( transpose ) pattern_out[j * nr + i] = true; else pattern_out[i * nc + j] = true; j = *(++itr); } } return; } template void get_internal_pattern( bool transpose , const pod_vector& internal_index , const InternalSparsity& internal_pattern , vector& pattern_out ) { typedef typename InternalSparsity::const_iterator iterator; // number variables size_t nr = internal_index.size(); // // column size of internal sparstiy pattern size_t nc = internal_pattern.end(); // pattern_out.resize(nr * nc); for(size_t ij = 0; ij < nr * nc; ij++) pattern_out[ij] = false; // for(size_t i = 0; i < nr; i++) { CPPAD_ASSERT_UNKNOWN( internal_index[i] < internal_pattern.n_set() ); iterator itr(internal_pattern, internal_index[i]); size_t j = *itr; while( j < nc ) { if( transpose ) pattern_out[j * nr + i] = true; else pattern_out[i * nc + j] = true; j = *(++itr); } } return; } template void get_internal_pattern( bool transpose , const pod_vector& internal_index , const InternalSparsity& internal_pattern , vector< std::set >& pattern_out ) { typedef typename InternalSparsity::const_iterator iterator; // number variables size_t nr = internal_index.size(); // // column size of internal sparstiy pattern size_t nc = internal_pattern.end(); // if( transpose ) pattern_out.resize(nc); else pattern_out.resize(nr); for(size_t k = 0; k < pattern_out.size(); k++) pattern_out[k].clear(); // for(size_t i = 0; i < nr; i++) { CPPAD_ASSERT_UNKNOWN( internal_index[i] < internal_pattern.n_set() ); iterator itr(internal_pattern, internal_index[i]); size_t j = *itr; while( j < nc ) { if( transpose ) pattern_out[j].insert(i); else pattern_out[i].insert(j); j = *(++itr); } } return; } } } } // END_CPPAD_LOCAL_SPARSE_NAMESPACE # endif ================================================ FILE: include/cppad/local/sparse/list_setvec.hpp ================================================ # ifndef CPPAD_LOCAL_SPARSE_LIST_SETVEC_HPP # define CPPAD_LOCAL_SPARSE_LIST_SETVEC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include // BEGIN_CPPAD_LOCAL_SPARSE_NAMESPACE namespace CppAD { namespace local { namespace sparse { /* {xrst_begin_parent list_setvec dev} {xrst_spell typedef } The list_setvec Class ##################### This class is a :ref:`SetVector-title` with elements of type ``size_t``. It is implemented using the template class :ref:`size_setvec-name` where the elements any positive integer type. {xrst_code cpp} */ typedef size_setvec list_setvec; typedef size_setvec_const_iterator list_setvec_const_iterator; /*{xrst_code} {xrst_toc_table include/cppad/local/sparse/size_setvec.hpp } {xrst_end list_setvec} // ---------------------------------------------------------------------------- {xrst_begin sparsity_user2internal_list_setvec dev} {xrst_spell msg } Copy A Vector of Standard Sets To A list_setvec Object ###################################################### SetVector ********* is a :ref:`simple vector` type with elements of type ``std::set`` . internal ******** The input value of this object does not matter. Upon return it contains the same sparsity pattern as *user* (or its transpose). user **** is the sparsity pattern we are copying to *internal* . n_set ***** is the number of sets in the output sparsity pattern *internal* . If *transpose* is false, *n_set* is equal to *user* . ``size`` () . end *** is the end value for the output sparsity pattern *internal* . ``list_setvec`` sparsity pattern *internal* . If *transpose* is true, *end* is equal to *user* . ``size`` () . transpose ********* If *transpose* is false, element *j* is in the *i*-th *internal* set if *j* is in the *user* [ *i* ] . Otherwise, element *j* is in the *i*-th *internal* set if *i* is in the *user* [ *j* ] . error_msg ********* is the error message to display if some values in the *user* sparsity pattern are not valid. Prototype ********* {xrst_spell_off} {xrst_code hpp} */ template void sparsity_user2internal( list_setvec& internal , const SetVector& user , size_t n_set , size_t end , bool transpose , const char* error_msg ) /* {xrst_code} {xrst_spell_on} {xrst_end sparsity_user2internal_list_setvec} **/ { # ifndef NDEBUG if( transpose ) CPPAD_ASSERT_KNOWN( end == size_t( user.size() ), error_msg); if( ! transpose ) CPPAD_ASSERT_KNOWN( n_set == size_t( user.size() ), error_msg); # endif // iterator for user set std::set::const_iterator itr; // size of internal sparsity pattern internal.resize(n_set, end); if( transpose ) { // transposed pattern case for(size_t j = 0; j < end; j++) { itr = user[j].begin(); while(itr != user[j].end()) { size_t i = *itr++; CPPAD_ASSERT_KNOWN(i < n_set, error_msg); internal.post_element(i, j); } } for(size_t i = 0; i < n_set; ++i) internal.process_post(i); } else { for(size_t i = 0; i < n_set; i++) { itr = user[i].begin(); while(itr != user[i].end()) { size_t j = *itr++; CPPAD_ASSERT_KNOWN( j < end, error_msg); internal.post_element(i, j); } internal.process_post(i); } } return; } } } } // END_CPPAD_LOCAL_SPARSE_NAMESPACE // ========================================================================= // Tell pod_vector class that each pair_size_t is plain old data and hence // the corresponding constructor need not be called. namespace CppAD { namespace local { template <> inline bool is_pod::pair_s_type>(void) { return true; } } } # endif ================================================ FILE: include/cppad/local/sparse/pack_setvec.hpp ================================================ # ifndef CPPAD_LOCAL_SPARSE_PACK_SETVEC_HPP # define CPPAD_LOCAL_SPARSE_PACK_SETVEC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include // BEGIN_CPPAD_LOCAL_SPARSE_NAMESPACE namespace CppAD { namespace local { namespace sparse { // forward declaration of iterator class class pack_setvec_const_iterator; // ============================================================================ class pack_setvec { // ============================================================================ /* {xrst_begin pack_setvec_member_data dev} class pack_setvec: Private Member Data ###################################### Pack **** Type used to pack multiple elements of a set (multiple bits) onto one *Pack* value. n_bit\_ ******* Number of bits (elements) per *Pack* value. zero\_ ****** The *Pack* value with all bits zero. one\_ ***** The *Pack* value with all bits zero, except for the lowest order bit. n_set\_ ******* Number of sets that we are representing. end\_ ***** The possible elements in each set are ``0`` , ``1`` , ..., ``end_-1`` . n_pack\_ ******** Number of Pack values used to represent one set in the vector; i.e., to represent ``end_`` bits. data\_ ****** Data for all of the sets. Source Code *********** {xrst_spell_off} {xrst_code hpp} */ private: typedef size_t Pack; const size_t n_bit_; const Pack zero_; const Pack one_; size_t n_set_; size_t end_; size_t n_pack_; pod_vector data_; /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_member_data} ----------------------------------------------------------------------------- {xrst_begin pack_setvec_vec_memory dev} class pack_setvec: Approximate Memory Used by Vector #################################################### Public ****** This function is declared public, but is not part of :ref:`SetVector-name` concept. Implementation ************** {xrst_spell_off} {xrst_code hpp} */ public: size_t memory(void) const { return data_.capacity() * sizeof(Pack); } /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_vec_memory} ------------------------------------------------------------------------------- {xrst_begin pack_setvec_vec_print dev} class pack_setvec: Print a Vector of Sets ######################################### Public ****** This function is declared public, but is not part of :ref:`SetVector-name` concept. Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void print(void) const; /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_vec_print} ------------------------------------------------------------------------------- {xrst_begin pack_setvec_iterators dev} {xrst_spell typedef } class pack_setvec: Iterators ############################ SetVector Concept ***************** :ref:`SetVector@const_iterator` typedef ******* {xrst_spell_off} {xrst_code hpp} */ public: /// declare a const iterator friend class pack_setvec_const_iterator; typedef pack_setvec_const_iterator const_iterator; /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_iterators} ------------------------------------------------------------------------------- {xrst_begin pack_setvec_default_ctor dev} class pack_setvec: Default Constructor ###################################### SetVector Concept ***************** :ref:`SetVector@Vector Operations@Constructor` n_bit\_ ******* This member variable is set to the number of bits in a *Pack* value. one\_ ***** This member variable has only its lowest order bit non-zero; data\_ ****** This member is initialized as the empty vector; i.e., size zero.. Other ***** All the other member data are ``size_t`` values that are initialized as zero. Implementation ************** {xrst_spell_off} {xrst_code hpp} */ public: pack_setvec(void) : n_bit_( std::numeric_limits::digits ), zero_(0), one_(1), n_set_(0), end_(0), n_pack_(0), data_(0) { } /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_default_ctor} ------------------------------------------------------------------------------- {xrst_begin pack_setvec_destructor dev} class pack_setvec: Destructor ############################# Implementation ************** {xrst_spell_off} {xrst_code hpp} */ public: ~pack_setvec(void) { } /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_destructor} ------------------------------------------------------------------------------- {xrst_begin pack_setvec_copy_ctor dev} class pack_setvec: Copy Constructor ################################### v * The vector of sets that we are attempting to make a copy of. Implementation ************** Using the copy constructor is probably due to a ``pack_setvec`` being passed by value instead of by reference. This is a CppAD programing error (not CppAD user error). {xrst_spell_off} {xrst_code hpp} */ public: pack_setvec(const pack_setvec& v) : n_bit_( std::numeric_limits::digits ), zero_(0), one_(1) { CPPAD_ASSERT_UNKNOWN(0); } /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_copy_ctor} ------------------------------------------------------------------------------- {xrst_begin pack_setvec_vec_resize dev} class pack_setvec: Vector resize ################################ SetVector Concept ***************** :ref:`vector resize` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void resize(size_t n_set, size_t end) /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_vec_resize} */ { n_set_ = n_set; end_ = end; if( n_set_ == 0 ) { CPPAD_ASSERT_UNKNOWN( end == 0 ); data_.clear(); return; } // now start a new vector with empty sets Pack zero(0); // n_pack_ = ( 1 + (end_ - 1) / n_bit_ ); size_t i = n_set_ * n_pack_; // data_.resize(i); while(i--) data_[i] = zero; } /* %$$ ------------------------------------------------------------------------------- {xrst_begin pack_setvec_vec_n_set dev} class pack_setvec: Number of Sets ################################# SetVector Concept ***************** :ref:`SetVector@Vector Operations@n_set` Implementation ************** {xrst_spell_off} {xrst_code hpp} */ public: size_t n_set(void) const { return n_set_; } /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_vec_n_set} ------------------------------------------------------------------------------- {xrst_begin pack_setvec_vec_end dev} class pack_setvec: End Value ############################ SetVector Concept ***************** :ref:`SetVector@Vector Operations@end` Implementation ************** {xrst_spell_off} {xrst_code hpp} */ public: size_t end(void) const { return end_; } /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_vec_end} ------------------------------------------------------------------------------- {xrst_begin pack_setvec_vec_assignment dev} class pack_setvec: Vector Assignment #################################### SetVector Concept ***************** :ref:`vector assignment` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void operator=(const pack_setvec& other) /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_vec_assignment} */ { CPPAD_ASSERT_UNKNOWN( n_bit_ == other.n_bit_); CPPAD_ASSERT_UNKNOWN( zero_ == other.zero_); CPPAD_ASSERT_UNKNOWN( one_ == other.one_); n_set_ = other.n_set_; end_ = other.end_; n_pack_ = other.n_pack_; data_ = other.data_; } /* ------------------------------------------------------------------------------- {xrst_begin pack_setvec_vec_swap dev} class pack_setvec: Vector Swap ############################## SetVector Concept ***************** :ref:`vector swap` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void swap(pack_setvec& other) /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_vec_swap} */ { // size_t objects CPPAD_ASSERT_UNKNOWN( n_bit_ == other.n_bit_); CPPAD_ASSERT_UNKNOWN( zero_ == other.zero_); CPPAD_ASSERT_UNKNOWN( one_ == other.one_); std::swap(n_set_ , other.n_set_); std::swap(end_ , other.end_); std::swap(n_pack_ , other.n_pack_); // // pod_vectors data_.swap(other.data_); } /* ------------------------------------------------------------------------------- {xrst_begin pack_setvec_number_elements dev} class pack_setvec: Number of Elements in a Set ############################################## SetVector Concept ***************** :ref:`SetVector@number_elements` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: size_t number_elements(size_t i) const /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_number_elements} */ { CPPAD_ASSERT_UNKNOWN( i < n_set_ ); // // special case where data_[i] is 0 or 1 if( end_ == 1 ) { CPPAD_ASSERT_UNKNOWN( n_pack_ == 1 ); return size_t( data_[i] ); } // // initialize count of non-zero bits in this set size_t count = 0; // // mask corresponding to first bit in Pack Pack mask = one_; // // number of bits in last Packing unit size_t n_last = (end_ - 1) % n_bit_ + 1; // // count bits in last unit Pack unit = data_[(i + 1) * n_pack_ - 1]; for(size_t bit = 0; bit < n_last; ++bit) { CPPAD_ASSERT_UNKNOWN( mask >= one_ ); if( mask & unit ) ++count; mask = mask << 1; } if( n_pack_ == 1 ) return count; // // count bits in other units for(size_t bit = 0; bit < n_bit_; ++bit) { CPPAD_ASSERT_UNKNOWN( mask >= one_ ); size_t k = n_pack_; while(--k) { if( data_[i * n_pack_ + k] & mask ) ++count; } mask = mask << 1; } return count; } /* ------------------------------------------------------------------------------- {xrst_begin pack_setvec_add_element dev} class pack_setvec: Add an Elements to a Set ########################################### SetVector Concept ***************** :ref:`SetVector@add_element` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void add_element(size_t i, size_t element) /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_add_element} */ { CPPAD_ASSERT_UNKNOWN( i < n_set_ ); CPPAD_ASSERT_UNKNOWN( element < end_ ); if( end_ == 1 ) data_[i] |= one_; else { size_t j = element / n_bit_; size_t k = element - j * n_bit_; Pack mask = one_ << k; data_[ i * n_pack_ + j] |= mask; } } /* ------------------------------------------------------------------------------- {xrst_begin pack_setvec_post_element dev} class pack_setvec: Add an Elements to a Set ########################################### SetVector Concept ***************** :ref:`SetVector@post_element` Implementation ************** {xrst_spell_off} {xrst_code hpp} */ public: void post_element(size_t i, size_t element) { add_element(i, element); } /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_post_element} */ /* ------------------------------------------------------------------------------- {xrst_begin pack_setvec_process_post dev} class pack_setvec: Add Posted Elements to a Set ############################################### SetVector Concept ***************** :ref:`SetVector@process_post` Implementation ************** {xrst_spell_off} {xrst_code hpp} */ public: void process_post(size_t i) { return; } /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_process_post} ------------------------------------------------------------------------------- {xrst_begin pack_setvec_is_element dev} class pack_setvec: Is an Element in a Set ######################################### SetVector Concept ***************** :ref:`SetVector@is_element` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: bool is_element(size_t i, size_t element) const /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_is_element} */ { CPPAD_ASSERT_UNKNOWN( i < n_set_ ); CPPAD_ASSERT_UNKNOWN( element < end_ ); if( end_ == 1 ) return data_[i] != zero_; // size_t j = element / n_bit_; size_t k = element - j * n_bit_; Pack mask = one_ << k; return (data_[i * n_pack_ + j] & mask) != zero_; } /* ------------------------------------------------------------------------------- {xrst_begin pack_setvec_clear dev} class pack_setvec: Assign a Set to be Empty ########################################### SetVector Concept ***************** :ref:`SetVector@clear` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void clear(size_t target) /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_clear} */ { CPPAD_ASSERT_UNKNOWN( target < n_set_ ); size_t t = target * n_pack_; size_t j = n_pack_; while(j--) data_[t++] = zero_; } /* ------------------------------------------------------------------------------- {xrst_begin pack_setvec_assignment dev} class pack_setvec: Assign a Set To Equal Another Set #################################################### SetVector Concept ***************** :ref:`SetVector@assignment` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void assignment( size_t this_target , size_t other_value , const pack_setvec& other ) /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_assignment} */ { CPPAD_ASSERT_UNKNOWN( this_target < n_set_ ); CPPAD_ASSERT_UNKNOWN( other_value < other.n_set_ ); CPPAD_ASSERT_UNKNOWN( n_pack_ == other.n_pack_ ); size_t t = this_target * n_pack_; size_t v = other_value * n_pack_; size_t j = n_pack_; while(j--) data_[t++] = other.data_[v++]; } /* ------------------------------------------------------------------------------- {xrst_begin pack_setvec_binary_union dev} class pack_setvec: Assign a Set To Equal Union of Two Sets ########################################################## SetVector Concept ***************** :ref:`SetVector@binary_union` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void binary_union( size_t this_target , size_t this_left , size_t other_right , const pack_setvec& other ) /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_binary_union} */ { CPPAD_ASSERT_UNKNOWN( this_target < n_set_ ); CPPAD_ASSERT_UNKNOWN( this_left < n_set_ ); CPPAD_ASSERT_UNKNOWN( other_right < other.n_set_ ); CPPAD_ASSERT_UNKNOWN( n_pack_ == other.n_pack_ ); size_t t = this_target * n_pack_; size_t l = this_left * n_pack_; size_t r = other_right * n_pack_; size_t j = n_pack_; while(j--) data_[t++] = ( data_[l++] | other.data_[r++] ); } /* ------------------------------------------------------------------------------- {xrst_begin pack_setvec_binary_intersection dev} class pack_setvec: Assign a Set To Intersection of Two Sets ########################################################### SetVector Concept ***************** :ref:`SetVector@binary_intersection` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void binary_intersection( size_t this_target , size_t this_left , size_t other_right , const pack_setvec& other ) /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_binary_intersection} */ { CPPAD_ASSERT_UNKNOWN( this_target < n_set_ ); CPPAD_ASSERT_UNKNOWN( this_left < n_set_ ); CPPAD_ASSERT_UNKNOWN( other_right < other.n_set_ ); CPPAD_ASSERT_UNKNOWN( n_pack_ == other.n_pack_ ); size_t t = this_target * n_pack_; size_t l = this_left * n_pack_; size_t r = other_right * n_pack_; size_t j = n_pack_; while(j--) data_[t++] = ( data_[l++] & other.data_[r++] ); } // ========================================================================== }; // END_CLASS_PACK_SETVEC // ========================================================================== // ========================================================================= class pack_setvec_const_iterator { // BEGIN_CLASS_PACK_SETVEC_CONST_ITERATOR // ========================================================================= /* {xrst_begin pack_setvec_const_iterator_member_data dev} class pack_setvec_const_iterator private: Member Data ##################################################### Pack **** This is the same type as :ref:`pack_setvec Pack` . n_bit\_ ******* This is a reference to :ref:`pack_setvec n_bit_` . one\_ ***** This is a reference to :ref:`pack_setvec one_` . n_pack\_ ******** This is a reference to :ref:`pack_setvec n_pack_` . end\_ ***** This is a reference to :ref:`pack_setvec end_` . data\_ ****** This is a reference to :ref:`pack_setvec data_` . data_index\_ ************ Index in ``data_`` where the next element is located. next_element ************ Value of the next element in this set If ``next_element_`` equals ``end_`` , no next element exists; i.e., past end of the set. Source Code *********** {xrst_spell_off} {xrst_code hpp} */ private: typedef pack_setvec::Pack Pack; const size_t& n_bit_; const Pack& one_; const size_t& n_pack_; const size_t& end_; const pod_vector& data_; size_t data_index_; size_t next_element_; public: /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_const_iterator_member_data} ------------------------------------------------------------------------------- {xrst_begin pack_setvec_const_iterator_ctor dev} class pack_setvec_const_iterator: Constructor ############################################# SetVector Concept ***************** :ref:`iterator constructor` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: pack_setvec_const_iterator (const pack_setvec& pack, size_t set_index) /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_const_iterator_ctor} */ : n_bit_ ( pack.n_bit_ ) , one_ ( pack.one_ ) , n_pack_ ( pack.n_pack_ ) , end_ ( pack.end_ ) , data_ ( pack.data_ ) , data_index_ ( set_index * n_pack_ ) { CPPAD_ASSERT_UNKNOWN( set_index < pack.n_set_ ); CPPAD_ASSERT_UNKNOWN( 0 < end_ ); // next_element_ = 0; if( data_[data_index_] & one_ ) return; // // element with index zero is not in this set of integers, // advance to first element or end ++(*this); } /* ------------------------------------------------------------------------------- {xrst_begin pack_setvec_const_iterator_dereference dev} class pack_setvec_const_iterator: Dereference ############################################# SetVector Concept ***************** :ref:`iterator deference` Implementation ************** {xrst_spell_off} {xrst_code hpp} */ size_t operator*(void) const { return next_element_; } /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_const_iterator_dereference} ------------------------------------------------------------------------------- {xrst_begin pack_setvec_const_iterator_increment dev} class pack_setvec_const_iterator: Increment ########################################### SetVector Concept ***************** :ref:`iterator increment` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: pack_setvec_const_iterator& operator++(void) /* {xrst_code} {xrst_spell_on} {xrst_end pack_setvec_const_iterator_increment} */ { CPPAD_ASSERT_UNKNOWN( next_element_ <= end_ ); if( next_element_ == end_ ) return *this; // ++next_element_; if( next_element_ == end_ ) return *this; // // bit index corresponding to next element size_t bit = next_element_ % n_bit_; // // check if we have advanced to the next data index if( bit == 0 ) ++data_index_; // // initialize mask size_t mask = one_ << bit; // while( next_element_ < end_ ) { // check if this element is in the set if( data_[data_index_] & mask ) return *this; // // try next larger element ++next_element_; ++bit; mask <<= 1; // // check if we must go to next packed data index CPPAD_ASSERT_UNKNOWN( bit <= n_bit_ ); if( bit == n_bit_ ) { // get next packed value bit = 0; mask = one_; ++data_index_; } } CPPAD_ASSERT_UNKNOWN( next_element_ == end_ ); return *this; } // ========================================================================= }; // END_CLASS_PACK_SETVEC_CONST_ITERATOR // ========================================================================= // Implemented after pack_setvec_const_iterator so can use it inline void pack_setvec::print(void) const { std::cout << "pack_setvec:\n"; for(size_t i = 0; i < n_set(); i++) { std::cout << "set[" << i << "] = {"; const_iterator itr(*this, i); while( *itr != end() ) { std::cout << *itr; if( *(++itr) != end() ) std::cout << ","; } std::cout << "}\n"; } return; } // ---------------------------------------------------------------------------- /* {xrst_begin sparsity_user2internal_pack_setvec dev} {xrst_spell msg } Copy A Boolean Sparsity Pattern To A pack_setvec Object ####################################################### SetVector ********* is a :ref:`simple vector` type with elements of type ``bool`` containing the input sparsity pattern. internal ******** The input value of this object does not matter. Upon return it contains the same sparsity pattern as *user* (or its transpose). user **** is the sparsity pattern we are copying to *internal* . n_set ***** is the number of sets in the output sparsity pattern *internal* . end *** is the end value for the output sparsity pattern *internal* . transpose ********* If *transpose* is false, element *j* is in the *i*-th *internal* set if *user* [ *i* * *end* + *j* ] Otherwise, element *j* is in the *i*-th *internal* set if *user* [ *i* * *n_set* + *j* ] error_msg ********* is the error message to display if *n_set* * *end* != *user* . ``size`` () Prototype ********* {xrst_spell_off} {xrst_code hpp} */ template void sparsity_user2internal( pack_setvec& internal , const SetVector& user , size_t n_set , size_t end , bool transpose , const char* error_msg ) /* {xrst_code} {xrst_spell_on} {xrst_end sparsity_user2internal_pack_setvec} */ { CPPAD_ASSERT_KNOWN(size_t( user.size() ) == n_set * end, error_msg ); // size of internal sparsity pattern internal.resize(n_set, end); if( transpose ) { // transposed pattern case for(size_t j = 0; j < end; j++) { for(size_t i = 0; i < n_set; i++) { // no advantage to using post_element for pack_setvec if( user[ j * n_set + i ] ) internal.add_element(i, j); } } return; } else { for(size_t i = 0; i < n_set; i++) { for(size_t j = 0; j < end; j++) { // no advantage to using post_element for pack_setvec if( user[ i * end + j ] ) internal.add_element(i, j); } } } return; } } } } // END_CPPAD_LOCAL_SPARSE_NAMESPACE # endif ================================================ FILE: include/cppad/local/sparse/pack_setvec.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin pack_setvec dev} Implement SetVector Using Packed Boolean Values ############################################### Namespace ********* This class is in the ``CppAD::local::sparse`` namespace. Public ****** The public member function for the ``list_setvec`` class implement the :ref:`SetVector-name` concept. Contents ******** {xrst_toc_table include/cppad/local/sparse/pack_setvec.hpp } {xrst_end pack_setvec} ================================================ FILE: include/cppad/local/sparse/setvector.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin SetVector dev} {xrst_spell dereference setvec } C++ Concept: Vector of Sets With size_t Elements ################################################ Purpose ******* The main CppAD use of this C++ Concept is to compute sparsity patterns as fast as possible. It is also used for conditional expression optimization. We refer to a type that supports this concept as *SetVector* below. Vector Operations ***************** Constructor =========== In the specifications below, *vec* and *other* are *SetVector* objects created using the default constructor; e.g., *SetVector* *vec* , *other* ; After this constructor the vectors are empty; i.e., there are no sets in either vector. The ``resize`` for *vec* and *other* can have different :ref:`SetVector@Vector Operations@n_set` values, but must have the same :ref:`SetVector@Vector Operations@end` value. resize ====== This operation has the following syntax: *vec* . ``resize`` ( *n_set* , *end* ) The argument *n_set* has type ``size_t`` and is the number of sets in *vec* . The argument *end* has type ``size_t`` and is greater than any element allowed in any set in *vec* . Any information in *vec* before this operation is lost. After this operation, all the sets in *vec* are empty. If *n_set* is zero, any allocated memory to keep track of this vector of sets is freed. n_set ===== The syntax *n_set* = *vec* . ``n_set`` () sets the ``size_t`` value *n_set* equal to the number of sets in *vec* . The *vec* object is ``const`` for this operation. end === The syntax *end* = *vec* . ``end`` () sets the ``size_t`` value *end* equal to the end value for the sets in *vec* . (This is one greater than the maximum value for any element in any set in *vec* .) The *vec* object is ``const`` for this operation. Assignment ========== The following makes *vec* into a separate copy of *other* : *vec* = *other* The *other* object is ``const`` for this operation. swap ==== The following exchanges to vector of sets in *vec* and *other* : *vec* . ``swap`` ( *other* ) number_elements *************** If *i* is a ``size_t`` value less than *n_set* , *count* = *vec* . ``number_elements`` ( *i* ) returns the ``size_t`` value *count* equal to the number of elements in the *i*-th set. The *vec* object is ``const`` for this operation. It is an error to have postings to *i*-th that have not been processed. add_element *********** If *i* is a ``size_t`` value less than *n_set* and *element* is a ``size_t`` value less than *end* , *vec* . ``add_element`` ( *i* , *element* ) adds the specified element to the *i*-th set. post_element ************ If *i* is a ``size_t`` value less than *n_set* and *element* is a ``size_t`` value less than *end* , *vec* . ``post_element`` ( *i* , *element* ) post the specified element for addition to the *i*-th set. Posting multiple elements to one set and then processing them may be faster than adding one element at a time. It is an error to use *vec* , in a way that depends on the values in the *i*-th set, between a ``post_element`` and the corresponding ``process_post`` . process_post ************ If *i* is a ``size_t`` value less than *n_set* , *vec* . ``process_post`` ( *i* ) Processes all of the posts that have been made for the *i*-th set; i.e., adds the posted elements to the set. is_element ********** If *i* is a ``size_t`` value less than *n_set* and *element* is a ``size_t`` value less than *end* , *find* = *vec* . ``is_element`` ( *i* , *element* ) returns the ``bool`` value *find* which is true (false) if the specified element is in (is not in) the *i*-th set. The *vec* object is ``const`` for this operation. clear ***** If *i* is a ``size_t`` value less than *n_set* , *vec* . ``clear`` ( *i* ) assigns the empty set to the *i*-th set. It is OK to have postings to *i*-th that have not been processed (they are removed). assignment ********** If *this_target* and *other_source* are ``size_t`` with value less than the end value, *vec* . ``assignment`` ( *this_target* , *other_source* , *other* ) sets the *this_target* set in *vec* equal to the *other_source* set in *other* . If *vec* and *other* are the same object, this operation may save memory and time using smart pointers. The *other* object is ``const`` for this operation. It is OK (is an error) to have postings to *this_target* ( *other_source* ) that have not been processed. binary_union ************ If *this_target* , *this_left* , and *other_right* are ``size_t`` with value less than the end value, | |tab| *vec* . ``binary_union`` ( | |tab| |tab| *this_target* , *this_left* , *other_right* , *other* | |tab| ) sets the *this_target* set in *vec* equal to the union of the *this_left* set in *vec* and the *other_right* set in *other* . If the resulting set is equal to the left set (right set), this operation may use save memory and time using smart pointers (provided *vec* and *other* are the same object), The *other* object is ``const`` for this operation. It is OK (is an error) to have postings to *this_target* ( *this_left* and ``other_right`` ) that have not been processed. binary_intersection ******************* If *this_target* , *this_left* , and *other_right* are ``size_t`` with value less than the end value, | |tab| *vec* . ``binary_intersection`` ( | |tab| |tab| *this_target* , *this_left* , *other_right* , *other* | |tab| ) sets the *this_target* set in *vec* equal to the intersection of the *this_left* set in *vec* and the *other_right* set in *other* . If the resulting set is equal to the left set (right set), this operation may use save memory and time using smart pointers (provided *vec* and *other* are the same object), The *other* object is ``const`` for this operation. It is OK (is an error) to have postings to *this_target* ( *this_left* and ``other_right`` ) that have not been processed. const_iterator ************** Constructor =========== Given a *SetVector* object *vec* , and a ``size_t`` index *i* , a constant iterator *itr* is constructed as follows: *SetVector* :: ``const_iterator`` *itr* ( *vec* , *i* ) After this constructor, *itr* points to the first (smallest) element in the *i*-th set. The *vec* object is ``const`` for this operation. It is an error to have postings to *i*-th that have not been processed. Dereference =========== The operation ``element`` = * ``itr`` sets the ``size_t`` value *element* to the current element value. If *element* is equal to value *vec* . ``end`` () , we have iterated through all the elements of the set ( *element* is not in the set). It is an error to have postings to *i*-th that have not been processed. Increment ========= The operation ++ *itr* points *itr* to the next larger element in the set. The increment operation is not defined when the value of * *itr* is equal to *vec* . ``end`` () . The operation *element* = * (++ *itr* ) increments the iterator *itr* and sets *element* to the deference after the increment (see dereference above). It is an error to have postings to *i*-th that have not been processed. Implementation ************** {xrst_toc_hidden include/cppad/local/sparse/list_setvec.hpp include/cppad/local/sparse/pack_setvec.xrst } .. csv-table:: :widths: auto list_setvec,:ref:`list_setvec-title` pack_setvec,:ref:`pack_setvec-title` {xrst_end SetVector} ================================================ FILE: include/cppad/local/sparse/size_setvec.hpp ================================================ # ifndef CPPAD_LOCAL_SPARSE_SIZE_SETVEC_HPP # define CPPAD_LOCAL_SPARSE_SIZE_SETVEC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include /* {xrst_begin_parent size_setvec dev} Implement SetVector Using Singly Linked Lists ############################################# Namespace ********* This class is in the ``CppAD::local::sparse`` namespace. Public ****** The public member functions for the ``size_setvec`` class implements the :ref:`SetVector-name` concept when *s_type* is ``size_t`` . {xrst_end size_setvec} */ // BEGIN_CPPAD_LOCAL_SPARSE_NAMESPACE namespace CppAD { namespace local { namespace sparse { // forward declaration of iterator class template class size_setvec_const_iterator; // ========================================================================= template class size_setvec { // BEGIN_CLASS_LIST_SETVEC // ========================================================================= /* ------------------------------------------------------------------------------- {xrst_begin size_setvec_member_data dev} {xrst_spell struct } class size_setvec: Private Member Data ###################################### pair_s_type *********** This ``struct`` is local to the ``size_setvec`` class. It is the type used for each entry in a singly linked list and has the following fields: value ===== The is the value of an entry in the list (for sets, the first entry in the list is a reference count). next ==== This is the index in ``data_`` for the next entry in the list. If there are no more entries in the list, this value is zero; i.e., ``data_[0]`` is used to represent the end of a list. end\_ ***** The possible elements in each set are ``0`` , ``1`` , ..., ``end_-1`` number_not_used\_ ***************** Number of elements of the ``data_`` vector that are not being used. data_not_used\_ *************** Index in ``data_`` for the start of the linked list of elements that are not being used. data\_ ****** The data for all the singly linked lists. If *n_set* > 0 , ``data`` [0]. ``value`` == ``end_`` and ``data`` [0]. ``next`` == 0 . start\_ ******* The size of this vector is the number of set; i.e., :ref:`SetVector@Vector Operations@n_set` . The starting index for *i*-th set is ``start_`` [ *i* ] . If ``start_`` [ *i* ] == 0 , the i-th set has no elements. Otherwise it is the index of the reference count for the list. Reference Count =============== If ``start_`` [ *i* ] != 0 , ``data_`` [ ``start_`` [ *i* ]]. ``value`` is the reference count for the *i*-th list (not the value of an element in the list). The reference count must be greater than zero. First Element ============= If ``start_`` [ *i* ] != 0 , *first_index* = ``data_`` [ ``start_`` [ *i* ]]. ``next`` is the index of the first element in the list. This must be non-zero because the list is empty. Next Element ============ Starting with *index* = *first_index* , while *index* != 0 , The value of the corresponding element in the list is ``data_`` [ *index* ]. ``value`` (which must be less than ``end_`` ). The *index* for the next element of the list is ``data_`` [ *index* ]. ``next`` . Last Element ============ An index *last* corresponds to the last element of a list if ``data_`` [ *last* ]. ``next`` == 0 . (Note that ``data_[0].value == end_`` ). post\_ ****** The size of this vector is the number of set; i.e., :ref:`SetVector@Vector Operations@n_set` . Starting with *index* = ``post_`` [ *i* ] , while *index* != 0 , The value of the next element posted to the *i*-th list is ``data_`` [ *index* ]. ``value`` (which must be less than ``end_`` ). The *index* for the next element posted to the *i*-th list is ``data_`` [ *index* ]. ``next`` . temporary\_ *********** A temporary vector, used by member functions, that keeps its capacity to avoid re-allocating memory. If a member function calls another, no conditions about ``temporary_`` should be assumed during that call. Source Code *********** {xrst_spell_off} {xrst_code hpp} */ private: struct pair_s_type {s_type value; s_type next; }; friend bool CppAD::local::is_pod(void); // s_type end_; s_type number_not_used_; s_type data_not_used_; // pod_vector data_; pod_vector start_; pod_vector post_; pod_vector temporary_; /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_member_data} ------------------------------------------------------------------------------ {xrst_begin size_setvec_reference_count dev} class size_setvec private: Number of References to a Set ######################################################## Syntax ****** | *count* = *vec* . ``reference_count`` ( *i* ) vec *** Is a ``size_setvec`` object and can be ``const`` . i * is the index of the set that we are retrieving the references for. count ***** is the reference count. Prototype ********* {xrst_spell_off} {xrst_code hpp} */ private: s_type reference_count(s_type i) const /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_reference_count} */ { // start data index s_type start = start_[i]; if( start == 0 ) return 0; // // reference count return data_[start].value; } /* ------------------------------------------------------------------------------ {xrst_begin size_setvec_drop dev} class size_setvec private: Drop a Set No Longer Being Used ########################################################## Syntax ****** *not_used* = *vec* . ``drop`` ( *i* ) i * is the index of the set that is dropped. reference_count *************** if the set is non-empty, the :ref:`size_setvec_member_data@start_@Reference Count` is decremented. start\_ ******* The value ``start_`` [ *i* ] is set to zero; i.e., the *i*-th set is empty after the call. post\_ ****** The value ``post_`` [ *i* ] is set to zero; i.e., any postings to the list are also dropped. data_not_used\_ *************** The elements of ``data_`` were information for the *i*-th set, and are no longer being used, are added to the linked list starting at ``data_not_used`` . This includes both set elements and postings. not_used ******** is the number of elements of ``data_`` that were begin used for the *i*-th set and are no longer being used; i.e., the number of elements moved to ``data_not_used`` . Prototype ********* {xrst_spell_off} {xrst_code hpp} */ private: s_type drop(s_type i) /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_drop} */ { // initialize count of addition elements not being used. s_type number_drop = 0; // the elements in the post list will no longer be used s_type post = post_[i]; if( post != 0 ) { // drop this posting post_[i] = 0; // // count elements in this posting ++number_drop; s_type previous = post; s_type next = data_[previous].next; while( next != 0 ) { previous = next; next = data_[previous].next; ++number_drop; } // // add the posting elements to data_not_used_ data_[previous].next = data_not_used_; data_not_used_ = post; } // check for empty set s_type start = start_[i]; if( start == 0 ) return number_drop; // decrement reference counter CPPAD_ASSERT_UNKNOWN( data_[start].value > 0 ); data_[start].value--; // set this set to empty start_[i] = 0; // If new reference count is positive, the list corresponding to // start is still being used. if( data_[start].value > 0 ) return number_drop; // // count elements representing this set ++number_drop; s_type previous = start; s_type next = data_[previous].next; while( next != 0 ) { previous = next; next = data_[previous].next; ++number_drop; } // // add representing this set to data_not_used_ data_[previous].next = data_not_used_; data_not_used_ = start; // return number_drop; } /* ------------------------------------------------------------------------------ {xrst_begin size_setvec_get_data_index dev} class size_setvec private: Get a New List Pair ############################################## Syntax ****** *index* = *vec* . ``get_data_index`` () vec *** Is a ``size_setvec`` object. data_not_used\_ *************** If the input value of ``data_not_used_`` is zero, it is not changed. Otherwise, the index for the element at the front of that list is returned. In this case, ``data_not_used`` is advanced to the next element in that list. number_not_used\_ ***************** If the input value of ``data_not_used_`` is zero, ``number_not_used_`` is not changed. Otherwise it is decremented by one. index ***** If the input value of ``data_not_used_`` is zero, the size of ``data_`` is increased by one and index corresponding to the end of ``data_`` is returned. Otherwise, the input value for ``data_not_used_`` is returned. Prototype ********* {xrst_spell_off} {xrst_code hpp} */ private: s_type get_data_index(void) /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_get_data_index} */ { s_type index; if( data_not_used_ > 0 ) { CPPAD_ASSERT_UNKNOWN( number_not_used_ > 0 ); --number_not_used_; index = data_not_used_; data_not_used_ = data_[index].next; } else { index = s_type( data_.extend(1) ); } return index; } /* ------------------------------------------------------------------------------- {xrst_begin size_setvec_check_data_structure dev} class size_setvec private: Check Data Structure ############################################### Syntax ****** *vec* . ``check_data_structure`` () vec *** Is a ``size_setvec`` object that is effectively const. It is not declared const because the data structure is modified and then restored. NDEBUG ****** If ``NDEBUG`` is defined, the routine does nothing. Otherwise, if an error is found in the data structure, a ``CPPAD_ASSERT_UNKNOWN`` is generated. Prototype ********* {xrst_spell_off} {xrst_code hpp} */ private: void check_data_structure(void) /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_check_data_structure} */ # ifdef NDEBUG { return; } # else { // number of sets CPPAD_ASSERT_UNKNOWN( s_type( post_.size() ) == s_type( start_.size() ) ); s_type n_set = s_type( start_.size() ); if( n_set == 0 ) { CPPAD_ASSERT_UNKNOWN( end_ == 0 ); CPPAD_ASSERT_UNKNOWN( number_not_used_ == 0 ); CPPAD_ASSERT_UNKNOWN( data_not_used_ == 0 ); CPPAD_ASSERT_UNKNOWN( s_type( data_.size() ) == 0 ); CPPAD_ASSERT_UNKNOWN( s_type( start_.size() ) == 0 ); return; } // check data index zero CPPAD_ASSERT_UNKNOWN( data_[0].value == end_ ); CPPAD_ASSERT_UNKNOWN( data_[0].next == 0 ); // ----------------------------------------------------------- // save a copy of the reference counters in temporary_ temporary_.resize( size_t(n_set) ); for(s_type i = 0; i < n_set; i++) temporary_[i] = reference_count(i); // ----------------------------------------------------------- // Initialize number of entries in data used by sets and posts. // Start with 1 for data_[0]. s_type number_used_by_sets = 1; // ----------------------------------------------------------- // count the number of entries in data_ that are used by sets for(s_type i = 0; i < n_set; i++) { s_type start = start_[i]; if( start > 0 ) { // check structure for this non-empty set s_type reference_count = data_[start].value; s_type next = data_[start].next; CPPAD_ASSERT_UNKNOWN( reference_count > 0 ); CPPAD_ASSERT_UNKNOWN( next != 0 ); CPPAD_ASSERT_UNKNOWN( data_[next].value < end_ ); // // decrement the reference counter data_[start].value--; // // count the entries when find last reference if( data_[start].value == 0 ) { // restore reference count data_[start].value = temporary_[i]; // number of data entries used for this set number_used_by_sets += number_elements(i) + 1; /* number of elements checks that value < end_ .resizeeach pair in the list except for the start pair and the pair with index zero. */ } } } // ------------------------------------------------------------------ // count the number of entries in data_ that are used by posts s_type number_used_by_posts = 0; for(s_type i = 0; i < n_set; i++) { s_type post = post_[i]; if( post > 0 ) { s_type value = data_[post].value; s_type next = data_[post].next; CPPAD_ASSERT_UNKNOWN( value < end_ ); // while( value < end_ ) { ++number_used_by_posts; value = data_[next].value; next = data_[next].next; } } } // ------------------------------------------------------------------ // count number of entries in data_not_used_ s_type count = 0; s_type next = data_not_used_; while( next != 0 ) { ++count; next = data_[next].next; } CPPAD_ASSERT_UNKNOWN( number_not_used_ == count ); // ------------------------------------------------------------------ s_type number_used = number_used_by_sets + number_used_by_posts; CPPAD_ASSERT_UNKNOWN( number_used + number_not_used_ == s_type( data_.size() ) ); return; } # endif /* ------------------------------------------------------------------------------- {xrst_begin size_setvec_vec_memory dev} class size_setvec: Approximate Memory Used by Vector #################################################### Public ****** This function is declared public, but is not part of :ref:`SetVector-name` concept. Implementation ************** {xrst_spell_off} {xrst_code hpp} */ public: s_type memory(void) const { return data_.capacity() * sizeof(pair_s_type); } /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_vec_memory} ------------------------------------------------------------------------------- {xrst_begin size_setvec_vec_print dev} class size_setvec: Print a Vector of Sets ######################################### Public ****** This function is declared public, but is not part of :ref:`SetVector-name` concept. Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void print(void) const; /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_vec_print} ------------------------------------------------------------------------------- {xrst_begin size_setvec_iterators dev} {xrst_spell typedef } class size_setvec: Iterators ############################ SetVector Concept ***************** :ref:`SetVector@const_iterator` typedef ******* {xrst_spell_off} {xrst_code hpp} */ public: friend class size_setvec_const_iterator; typedef size_setvec_const_iterator const_iterator; /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_iterators} ------------------------------------------------------------------------------- {xrst_begin size_setvec_default_ctor dev} class size_setvec: Default Constructor ###################################### SetVector Concept ***************** :ref:`SetVector@Vector Operations@Constructor` s_type Members ************** All of the ``s_type`` member variables are initialized as zero. pod_vector Members ****************** All of the ``pod_vector`` member variables are initialized using their default constructors. Implementation ************** {xrst_spell_off} {xrst_code hpp} */ public: size_setvec(void) : end_(0), number_not_used_(0), data_not_used_(0) { } /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_default_ctor} ------------------------------------------------------------------------------- {xrst_begin size_setvec_destructor dev} class size_setvec: Destructor ############################# Implementation ************** If ``NDEBUG`` is not defined, :ref:`check data structure` . {xrst_spell_off} {xrst_code hpp} */ public: ~size_setvec(void) { check_data_structure(); } /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_destructor} ------------------------------------------------------------------------------- {xrst_begin size_setvec_copy_ctor dev} class size_setvec: Copy Constructor ################################### v * The vector of sets that we are attempting to make a copy of. Implementation ************** Using the copy constructor is probably due to a ``size_setvec`` being passed by value instead of by reference. This is a CppAD programing error (not CppAD user error). {xrst_spell_off} {xrst_code hpp} */ public: size_setvec(const size_setvec& v) { CPPAD_ASSERT_UNKNOWN(false); } /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_copy_ctor} ------------------------------------------------------------------------------- {xrst_begin size_setvec_vec_resize dev} class size_setvec: Vector resize ################################ SetVector Concept ***************** :ref:`vector resize` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void resize(s_type n_set, s_type end) /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_vec_resize} */ { check_data_structure(); if( n_set == 0 ) { CPPAD_ASSERT_UNKNOWN( end == 0 ); // // restore object to start after constructor // (no memory allocated for this object) data_.clear(); start_.clear(); post_.clear(); number_not_used_ = 0; data_not_used_ = 0; end_ = 0; // return; } end_ = end; // start_.resize( size_t(n_set) ); post_.resize( size_t(n_set) ); // for(s_type i = 0; i < n_set; i++) { start_[i] = 0; post_[i] = 0; } // // last element, marks the end for all lists data_.resize(1); data_[0].value = end_; data_[0].next = 0; // number_not_used_ = 0; data_not_used_ = 0; } /* %$$ ------------------------------------------------------------------------------- {xrst_begin size_setvec_vec_n_set dev} class size_setvec: Number of Sets ################################# SetVector Concept ***************** :ref:`SetVector@Vector Operations@n_set` Implementation ************** {xrst_spell_off} {xrst_code hpp} */ public: s_type n_set(void) const { return s_type( start_.size() ); } /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_vec_n_set} ------------------------------------------------------------------------------- {xrst_begin size_setvec_vec_end dev} class size_setvec: End Value ############################ SetVector Concept ***************** :ref:`SetVector@Vector Operations@end` Implementation ************** {xrst_spell_off} {xrst_code hpp} */ public: s_type end(void) const { return end_; } /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_vec_end} ------------------------------------------------------------------------------- {xrst_begin size_setvec_vec_assignment dev} class size_setvec: Vector Assignment #################################### SetVector Concept ***************** :ref:`vector assignment` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void operator=(const size_setvec& other) /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_vec_assignment} */ { end_ = other.end_; number_not_used_ = other.number_not_used_; data_not_used_ = other.data_not_used_; data_ = other.data_; start_ = other.start_; post_ = other.post_; } /* ------------------------------------------------------------------------------- {xrst_begin size_setvec_vec_swap dev} class size_setvec: Vector Swap ############################## SetVector Concept ***************** :ref:`vector swap` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void swap(size_setvec& other) /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_vec_swap} */ { // s_type objects std::swap(end_ , other.end_); std::swap(number_not_used_ , other.number_not_used_); std::swap(data_not_used_ , other.data_not_used_); // pod_vectors data_.swap( other.data_); start_.swap( other.start_); post_.swap( other.post_); temporary_.swap( other.temporary_); } /* ------------------------------------------------------------------------------- {xrst_begin size_setvec_number_elements dev} class size_setvec: Number of Elements in a Set ############################################## SetVector Concept ***************** :ref:`SetVector@number_elements` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: s_type number_elements(s_type i) const /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_number_elements} */ { CPPAD_ASSERT_UNKNOWN( post_[i] == 0 ); // check if the set is empty s_type start = start_[i]; if( start == 0 ) return 0; // initialize counter s_type count = 0; // advance to the first element in the set s_type next = data_[start].next; while( next != 0 ) { CPPAD_ASSERT_UNKNOWN( data_[next].value < end_ ); count++; next = data_[next].next; } CPPAD_ASSERT_UNKNOWN( count > 0 ); return count; } /* ------------------------------------------------------------------------------- {xrst_begin size_setvec_add_element dev} class size_setvec: Add an Elements to a Set ########################################### SetVector Concept ***************** :ref:`SetVector@add_element` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void add_element(s_type i, s_type element) /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_add_element} */ { CPPAD_ASSERT_UNKNOWN( i < s_type( start_.size() ) ); CPPAD_ASSERT_UNKNOWN( element < end_ ); // check for case where starting set is empty s_type start = start_[i]; if( start == 0 ) { start = get_data_index(); start_[i] = start; data_[start].value = 1; // reference count // s_type next = get_data_index(); data_[start].next = next; // data_[next].value = element; data_[next].next = 0; return; } // // start of set with this index s_type previous = start_[i]; // // first entry in this set s_type next = data_[previous].next; s_type value = data_[next].value; // // locate place to insert this element while( value < element ) { previous = next; next = data_[next].next; value = data_[next].value; } // // check for case where element is in the set if( value == element ) return; // // // check for case where this is the only reference to this set CPPAD_ASSERT_UNKNOWN( element < value ); if( data_[start].value == 1 ) { s_type insert = get_data_index(); data_[insert].next = next; data_[insert].value = element; data_[previous].next = insert; // return; } // // must make a separate copy with new element inserted CPPAD_ASSERT_UNKNOWN( data_[start].value > 1 ); data_[start].value--; // reverence counter for old list // s_type start_new = get_data_index(); data_[start_new].value = 1; // reference counter for new list s_type previous_new = start_new; // // start of old set with this index previous = start_[i]; // // first entry in old set next = data_[previous].next; value = data_[next].value; // // locate place to insert this element while( value < element ) { // copy to new list s_type next_new = get_data_index(); data_[previous_new].next = next_new; data_[next_new].value = value; previous_new = next_new; // // get next value previous = next; next = data_[next].next; value = data_[next].value; } CPPAD_ASSERT_UNKNOWN( element < value ); // // insert the element s_type next_new = get_data_index(); data_[previous_new].next = next_new; data_[next_new].value = element; previous_new = next_new; // // copy rest of the old set while( value < end_ ) { // copy to new list next_new = get_data_index(); data_[previous_new].next = next_new; data_[next_new].value = value; previous_new = next_new; // // get next value previous = next; next = data_[next].next; value = data_[next].value; } CPPAD_ASSERT_UNKNOWN( next == 0 ); data_[previous_new].next = 0; // // hook up new list start_[i] = start_new; return; } /* ------------------------------------------------------------------------------- {xrst_begin size_setvec_post_element dev} class size_setvec: Post an Elements for Addition to a Set ######################################################### SetVector Concept ***************** :ref:`SetVector@post_element` post\_ ****** The element is added at the front of the linked list that starts at ``post_`` . Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void post_element(s_type i, s_type element) /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_post_element} */ { CPPAD_ASSERT_UNKNOWN( i < s_type( start_.size() ) ); CPPAD_ASSERT_UNKNOWN( element < end_ ); // put element at the front of this list s_type next = post_[i]; s_type post = get_data_index(); post_[i] = post; data_[post].value = element; data_[post].next = next; return; } /* ------------------------------------------------------------------------------- {xrst_begin size_setvec_process_post dev} class size_setvec: Add Posted Elements to a Set ############################################### SetVector Concept ***************** :ref:`SetVector@process_post` post\_ ****** Upon call, ``post_`` [ *i* ] is the linked list of elements to be added to the *i*-th set. Upon return, ``post_`` [ *i* ] is zero; i.e., the list is empty. Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void process_post(s_type i) /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_process_post} */ { // post s_type post = post_[i]; // // check if there are no elements to process if( post == 0 ) return; // // check if there is only one element to process s_type next = data_[post].next; if( next == 0 ) { // done with this posting s_type value = data_[post].value; post_[i] = 0; data_[post].next = data_not_used_; data_not_used_ = post; ++number_not_used_; // add_element(i, value); // return; } // // copy posting to temporary_ temporary_.resize(0); s_type previous = post; s_type value = data_[previous].value; CPPAD_ASSERT_UNKNOWN( value < end_ ); temporary_.push_back(value); while( next != 0 ) { previous = next; value = data_[previous].value; CPPAD_ASSERT_UNKNOWN( value < end_ ); temporary_.push_back(value); next = data_[previous].next; } s_type number_post = s_type( temporary_.size() ); // // done with this posting post_[i] = 0; data_[previous].next = data_not_used_; data_not_used_ = post; number_not_used_ += number_post;; // // sort temporary_ CPPAD_ASSERT_UNKNOWN( number_post > 1 ); std::sort( temporary_.data(), temporary_.data() + number_post); // posting is the set { temporary_[0], ... , [number_post-1] } // ------------------------------------------------------------------- // put union of posting and set i in // temporary_[number_post], ... , temporary_[ temporary_.size()-1 ] // s_type i_next = start_[i]; s_type i_value = end_; if( i_next > 0 ) { // skip reference count i_next = data_[i_next].next; i_value = data_[i_next].value; } bool post_is_subset = true; s_type previous_post = end_; for(s_type j =0; j < number_post; ++j) { s_type post_value = temporary_[j]; CPPAD_ASSERT_UNKNOWN( post_value < end_ ); while( i_value < post_value ) { // i_value is in union temporary_.push_back(i_value); i_next = data_[i_next].next; i_value = data_[i_next].value; } if( i_value == post_value ) { i_next = data_[i_next].next; i_value = data_[i_next].value; } else post_is_subset = false; // if( previous_post != post_value ) { // post_value is in union temporary_.push_back(post_value); } previous_post = post_value; } // check if posting is a subset of set i if( post_is_subset ) return; // // rest of elements in set i while( i_value < end_ ) { temporary_.push_back(i_value); i_next = data_[i_next].next; i_value = data_[i_next].value; } // adjust number_not_used_ s_type number_drop = drop(i); number_not_used_ += number_drop; // put new set in linked list for set i CPPAD_ASSERT_UNKNOWN( s_type( temporary_.size() ) >= number_post + 1 ); s_type index = get_data_index(); start_[i] = index; // start for the union data_[index].value = 1; // reference count for the union for(s_type j = number_post; j < s_type( temporary_.size() ); ++j) { next = get_data_index(); data_[index].next = next; data_[next].value = temporary_[j]; // next element in union index = next; } data_[index].next = 0; // end of union // return; } /* ------------------------------------------------------------------------------- {xrst_begin size_setvec_is_element dev} class size_setvec: Is an Element in a Set ######################################### SetVector Concept ***************** :ref:`SetVector@is_element` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: bool is_element(s_type i, s_type element) const /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_is_element} */ { CPPAD_ASSERT_UNKNOWN( post_[i] == 0 ); CPPAD_ASSERT_UNKNOWN( element < end_ ); // s_type start = start_[i]; if( start == 0 ) return false; // s_type next = data_[start].next; s_type value = data_[next].value; while( value < element ) { next = data_[next].next; value = data_[next].value; } return element == value; } /* ------------------------------------------------------------------------------- {xrst_begin size_setvec_clear dev} class size_setvec: Assign a Set to be Empty ########################################### SetVector Concept ***************** :ref:`SetVector@clear` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void clear(s_type target) /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_clear} */ { CPPAD_ASSERT_UNKNOWN( target < s_type( start_.size() ) ); // adjust number_not_used_ s_type number_drop = drop(target); number_not_used_ += number_drop; return; } /* ------------------------------------------------------------------------------- {xrst_begin size_setvec_assignment dev} class size_setvec: Assign a Set To Equal Another Set #################################################### SetVector Concept ***************** :ref:`SetVector@assignment` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void assignment( s_type this_target , s_type other_source , const size_setvec& other ) /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_assignment} */ { CPPAD_ASSERT_UNKNOWN( other.post_[ other_source ] == 0 ); // CPPAD_ASSERT_UNKNOWN( this_target < s_type( start_.size() ) ); CPPAD_ASSERT_UNKNOWN( other_source < s_type( other.start_.size() ) ); CPPAD_ASSERT_UNKNOWN( end_ == other.end_ ); // check if we are assigning a set to itself if( (this == &other) && (this_target == other_source) ) return; // set depending on cases below s_type this_start; // If this and other are the same, use another reference to same list s_type other_start = other.start_[other_source]; if( this == &other ) { this_start = other_start; if( other_start != 0 ) { data_[other_start].value++; // increment reference count CPPAD_ASSERT_UNKNOWN( data_[other_start].value > 1 ); } } else if( other_start == 0 ) { this_start = 0; } else { // make a copy of the other list in this size_setvec this_start = get_data_index(); s_type this_next = get_data_index(); data_[this_start].value = 1; // reference count data_[this_start].next = this_next; // s_type next = other.data_[other_start].next; CPPAD_ASSERT_UNKNOWN( next != 0 ); while( next != 0 ) { data_[this_next].value = other.data_[next].value; next = other.data_[next].next; if( next == 0 ) data_[this_next].next = 0; else { s_type tmp = get_data_index(); data_[this_next].next = tmp; this_next = tmp; } } } // adjust number_not_used_ s_type number_drop = drop(this_target); number_not_used_ += number_drop; // set the new start value for this_target start_[this_target] = this_start; return; } /* ------------------------------------------------------------------------------- {xrst_begin size_setvec_binary_union dev} class size_setvec: Assign a Set To Union of Two Sets #################################################### SetVector Concept ***************** :ref:`SetVector@binary_union` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void binary_union( s_type this_target , s_type this_left , s_type other_right , const size_setvec& other ) /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_binary_union} */ { CPPAD_ASSERT_UNKNOWN( post_[this_left] == 0 ); CPPAD_ASSERT_UNKNOWN( other.post_[ other_right ] == 0 ); // CPPAD_ASSERT_UNKNOWN( this_target < s_type( start_.size() ) ); CPPAD_ASSERT_UNKNOWN( this_left < s_type( start_.size() ) ); CPPAD_ASSERT_UNKNOWN( other_right < s_type( other.start_.size() ) ); CPPAD_ASSERT_UNKNOWN( end_ == other.end_ ); // start indices for left and right sets s_type start_left = start_[this_left]; s_type start_right = other.start_[other_right]; // if right is empty, the result is the left set if( start_right == 0 ) { assignment(this_target, this_left, *this); return; } // if left is empty, the result is the right set if( start_left == 0 ) { assignment(this_target, other_right, other); return; } // if neither case holds, then both left and right are non-empty CPPAD_ASSERT_UNKNOWN( reference_count(this_left) > 0 ); CPPAD_ASSERT_UNKNOWN( other.reference_count(other_right) > 0 ); // we will use temporary_ for temporary storage of the union temporary_.resize(0); // for left next and value s_type next_left = data_[start_left].next; s_type value_left = data_[next_left].value; // right next and value s_type next_right = other.data_[start_right].next; s_type value_right = other.data_[next_right].value; // both left and right set are non-empty CPPAD_ASSERT_UNKNOWN( value_left < end_ && value_right < end_ ); // flag that detects if left is or right is a subset of the other bool left_is_subset = true; bool right_is_subset = true; while( (value_left < end_) && (value_right < end_) ) { if( value_left == value_right ) { // value is in both sets temporary_.push_back(value_left); // // advance left next_left = data_[next_left].next; value_left = data_[next_left].value; // // advance right next_right = other.data_[next_right].next; value_right = other.data_[next_right].value; } else if( value_left < value_right ) { // need a value from left that is not in right left_is_subset = false; temporary_.push_back(value_left); // // advance left to its next element next_left = data_[next_left].next; value_left = data_[next_left].value; } else { CPPAD_ASSERT_UNKNOWN( value_right < value_left ) // need a value from right that is not in left right_is_subset = false; temporary_.push_back(value_right); // // advance right to its next element next_right = other.data_[next_right].next; value_right = other.data_[next_right].value; } } right_is_subset &= value_right == end_; left_is_subset &= value_left == end_; // // check right first in case they are equal will do this assignment if( right_is_subset ) { assignment(this_target, this_left, *this); return; } if( left_is_subset ) { assignment(this_target, other_right, other); return; } while( value_left < end_ ) { CPPAD_ASSERT_UNKNOWN( value_right == end_); temporary_.push_back(value_left); next_left = data_[next_left].next; value_left = data_[next_left].value; } while( value_right < end_ ) { CPPAD_ASSERT_UNKNOWN( value_left == end_); temporary_.push_back(value_right); next_right = other.data_[next_right].next; value_right = other.data_[next_right].value; } // adjust number_not_used_ s_type number_drop = drop(this_target); number_not_used_ += number_drop; // put new set in linked for this_target CPPAD_ASSERT_UNKNOWN( s_type( temporary_.size() ) >= 2 ); s_type index = get_data_index(); start_[this_target] = index; // start for the union data_[index].value = 1; // reference count for the union for(s_type i = 0; i < s_type( temporary_.size() ); ++i) { s_type next = get_data_index(); data_[index].next = next; data_[next].value = temporary_[i]; // next element in union index = next; } data_[index].next = 0; // end of union return; } /* ------------------------------------------------------------------------------- {xrst_begin size_setvec_binary_intersection dev} class size_setvec: Assign a Set To Equal Another Set #################################################### SetVector Concept ***************** :ref:`SetVector@binary_intersection` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: void binary_intersection( s_type this_target , s_type this_left , s_type other_right , const size_setvec& other ) /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_binary_intersection} */ { CPPAD_ASSERT_UNKNOWN( post_[this_left] == 0 ); CPPAD_ASSERT_UNKNOWN( other.post_[ other_right ] == 0 ); // CPPAD_ASSERT_UNKNOWN( this_target < s_type( start_.size() ) ); CPPAD_ASSERT_UNKNOWN( this_left < s_type( start_.size() ) ); CPPAD_ASSERT_UNKNOWN( other_right < s_type( other.start_.size() ) ); CPPAD_ASSERT_UNKNOWN( end_ == other.end_ ); // start indices for left and right sets s_type start_left = start_[this_left]; s_type start_right = other.start_[other_right]; // if left or right is empty, the result is empty if( (start_left == 0) || (start_right == 0) ) { clear(this_target); return; } // if neither case holds, then both left and right are non-empty CPPAD_ASSERT_UNKNOWN( reference_count(this_left) > 0 ); CPPAD_ASSERT_UNKNOWN( other.reference_count(other_right) > 0 ); // we will use temporary_ for temporary storage of the intersection temporary_.resize(0); // left next and value s_type next_left = data_[start_left].next; s_type value_left = data_[next_left].value; // right next and value s_type next_right = other.data_[start_right].next; s_type value_right = other.data_[next_right].value; // both left and right set are non-empty CPPAD_ASSERT_UNKNOWN( value_left < end_ && value_right < end_ ); // flag that detects if left is or right is a subset of the other bool left_is_subset = true; bool right_is_subset = true; while( (value_left < end_) && (value_right < end_) ) { if( value_left == value_right ) { // value is in both sets temporary_.push_back(value_left); // // advance left next_left = data_[next_left].next; value_left = data_[next_left].value; // // advance right next_right = other.data_[next_right].next; value_right = other.data_[next_right].value; } else if( value_left < value_right ) { // there is a value in left that is not in right left_is_subset = false; // // advance left to its next element next_left = data_[next_left].next; value_left = data_[next_left].value; } else { CPPAD_ASSERT_UNKNOWN( value_right < value_left ) // there is a value in right that is not in left right_is_subset = false; // // advance right to its next element next_right = other.data_[next_right].next; value_right = other.data_[next_right].value; } } right_is_subset &= value_right == end_; left_is_subset &= value_left == end_; // // check left first in case they are equal will do this assignment if( left_is_subset ) { assignment(this_target, this_left, *this); return; } if( right_is_subset ) { assignment(this_target, other_right, other); return; } // adjust number_not_used_ s_type number_drop = drop(this_target); number_not_used_ += number_drop; // check for empty result if( s_type( temporary_.size() ) == 0 ) return; // put new set in linked for this_target s_type index = get_data_index(); start_[this_target] = index; // start for the union data_[index].value = 1; // reference count for the union for(s_type i = 0; i < s_type( temporary_.size() ); ++i) { s_type next = get_data_index(); data_[index].next = next; data_[next].value = temporary_[i]; // next element in union index = next; } data_[index].next = 0; // end of union return; } // ========================================================================= }; // END_CLASS_LIST_SETVEC // ========================================================================= // ========================================================================= template class size_setvec_const_iterator { // BEGIN_CLASS_SIZE_SETVEC_CONST_ITERATOR // ========================================================================= /* {xrst_begin size_setvec_const_iterator_member_data dev} class size_setvec_const_iterator private: Member Data ##################################################### pair_s_type *********** This type is the same as :ref:`size_setvec pair_s_type` . end\_ ***** This is :ref:`size_setvec_member_data@end_` for the ``size_setvec`` object this iterator refers to. data\_ ****** This is :ref:`size_setvec_member_data@data_` for the ``size_setvec`` object this iterator refers to. next_pair\_ *********** Next element in the set that this iterator refers to. If ``next_pair_.value == end_`` there are no more elements in the set. Source Code *********** {xrst_spell_off} {xrst_code hpp} */ private: typedef typename size_setvec::pair_s_type pair_s_type; const s_type end_; const pod_vector& data_; pair_s_type next_pair_; /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_const_iterator_member_data} ------------------------------------------------------------------------------- {xrst_begin size_setvec_const_iterator_ctor dev} class size_setvec_const_iterator: Constructor ############################################# SetVector Concept ***************** :ref:`iterator constructor` Prototype ********* {xrst_spell_off} {xrst_code hpp} */ public: size_setvec_const_iterator (const size_setvec& list, s_type i) /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_const_iterator_ctor} */ : end_ ( list.end_ ), data_( list.data_ ) { CPPAD_ASSERT_UNKNOWN( list.post_[i] == 0 ); // s_type start = list.start_[i]; if( start == 0 ) { next_pair_.next = 0; next_pair_.value = end_; } else { // value for this entry is reference count for list CPPAD_ASSERT_UNKNOWN( data_[start].value > 0 ); // data index where list truly starts s_type next = data_[start].next; CPPAD_ASSERT_UNKNOWN( next != 0 ); // true first entry in the list next_pair_ = data_[next]; CPPAD_ASSERT_UNKNOWN( next_pair_.value < end_ ); } } /* ------------------------------------------------------------------------------- {xrst_begin size_setvec_const_iterator_dereference dev} class size_setvec_const_iterator: Dereference ############################################# SetVector Concept ***************** :ref:`iterator deference` Implementation ************** {xrst_spell_off} {xrst_code hpp} */ public: s_type operator*(void) { return next_pair_.value; } /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_const_iterator_dereference} ------------------------------------------------------------------------------- {xrst_begin size_setvec_const_iterator_increment dev} class size_setvec_const_iterator: Increment ########################################### SetVector Concept ***************** :ref:`iterator increment` Implementation ************** {xrst_spell_off} {xrst_code hpp} */ public: size_setvec_const_iterator& operator++(void) { next_pair_ = data_[next_pair_.next]; return *this; } /* {xrst_code} {xrst_spell_on} {xrst_end size_setvec_const_iterator_increment} */ // =========================================================================== }; // END_CLASS_SIZE_SETVEC_CONST_ITERATOR // =========================================================================== // Implemented after size_setvec_const_iterator so can use it template inline void size_setvec::print(void) const { std::cout << "size_setvec:\n"; for(s_type i = 0; i < n_set(); i++) { std::cout << "set[" << i << "] = {"; const_iterator itr(*this, i); while( *itr != end() ) { std::cout << *itr; if( *(++itr) != end() ) std::cout << ","; } std::cout << "}\n"; } return; } // ---------------------------------------------------------------------------- } } } // END_CPPAD_LOCAL_SPARSE_NAMESPACE # endif ================================================ FILE: include/cppad/local/sparse/svec_setvec.hpp ================================================ # ifndef CPPAD_LOCAL_SPARSE_SVEC_SETVEC_HPP # define CPPAD_LOCAL_SPARSE_SVEC_SETVEC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include // BEGIN_CPPAD_LOCAL_SPARSE_NAMESPACE namespace CppAD { namespace local { namespace sparse { /*! \file svec_setvec.hpp Vector of sets of positive integers stored as size_t vector with the element values strictly increasing. Testing indicates this does not work as well as using sparse::list_setvec (not currently being used except by test_more/general/local/vector_set.cpp). */ class svec_setvec_const_iterator; // ========================================================================= /*! Vector of sets of positive integers, each set stored as a size_t vector. All the public members for this class are also in the sparse::pack_setvec and sparse::list_setvec classes. This defines the CppAD vector_of_sets concept. */ class svec_setvec { friend class svec_setvec_const_iterator; private: /// Possible elements in each set are 0, 1, ..., end_ - 1; size_t end_; /// number of elements in data_ that have been allocated /// and are no longer being used. size_t data_not_used_; /// The data for all the singly linked lists. pod_vector data_; /*! Starting point for i-th set is start_[i]. \li If the i-th set has no elements, start_[i] is zero. Othersize the conditions below hold. \li data_[ start_[i] ] is the reference count for this set \li data_[ start_[i] + 1 ] is the first element in this set. \li data_[ start_[i] + 2 ] is the first element in this set. \li data_[ start_[i] + 2 + n ] = end_ where n is the number of elements in this set */ pod_vector start_; /*! Vectors of elements that have not yet been added to corresponding sets. \li If all the post_element calls for the i-th set have been added, post_[i] is zero. Otherwise the conditions below hold. \li data_[ post_[i] ] the number of elements that have been posted, but not yet added, to set i. \li data_[ post_[i] + 1 ] is the capacity for holding elements which is greater than or equal the number of elements. \li data_[ post_[i] + 2 ] is the first element that has been posted, but not yet added, to set i. \li data_[ post_[i] + 1 + n] is the last element that has been posted, but not yet added, to set i. Here n is the number of elements that are posted and not yet added to set i. */ pod_vector post_; // ----------------------------------------------------------------- /*! Counts references to a set. \param i is the index of the set that we are counting the references to. \return if the set is empty, the return value is zero. Otherwise it is the number of sets that share the same vector. */ size_t reference_count(size_t i) const { // start data index size_t start = start_[i]; if( start == 0 ) return 0; // // reference count return data_[start]; } // ----------------------------------------------------------------- /*! drop a set. \param i is the index of the set that will be dropped. \par reference_count if the set is non-empty, the reference count corresponding to index will be decremented. \return is the number of elements of data_ that will be lost when the set is dropped. This is non-zero when the initial reference count is one. */ size_t drop(size_t i) { // reference count size_t ref_count = reference_count(i); // empty set case if( ref_count == 0 ) return 0; // start size_t start = start_[i]; CPPAD_ASSERT_UNKNOWN( data_[start] == ref_count ); // decrement reference counter data_[start]--; // nothing lost unless new reference count is zero if( ref_count != 1 ) return 0; // number of elements in the set size_t length = data_[start + 1]; // reference count, length, end marker, plus elements in set size_t number_lost = 3 + length; // number_lost return number_lost; } // ----------------------------------------------------------------- /*! Checks data structure (effectively const, but modifies and restores values) */ # ifdef NDEBUG void check_data_structure(void) { return; } # else void check_data_structure(void) { // number of sets CPPAD_ASSERT_UNKNOWN( post_.size() == start_.size() ); size_t n_set = start_.size(); if( n_set == 0 ) { CPPAD_ASSERT_UNKNOWN( end_ == 0 ); CPPAD_ASSERT_UNKNOWN( data_not_used_ == 0 ); CPPAD_ASSERT_UNKNOWN( data_.size() == 0 ); CPPAD_ASSERT_UNKNOWN( start_.size() == 0 ); CPPAD_ASSERT_UNKNOWN( post_.size() == 0 ); return; } // ------------------------------------------------------------------ // save the reference counters pod_vector ref_count(n_set); for(size_t i = 0; i < n_set; i++) ref_count[i] = reference_count(i); // ------------------------------------------------------------------ // ------------------------------------------------------------------ // count the number of entries in data_ that are used by sets size_t data_used_by_sets = 0; for(size_t i = 0; i < n_set; i++) { size_t start = start_[i]; if( start > 0 ) { // check structure for this non-empty set size_t reference_count = data_[start + 0]; size_t length = data_[start + 1]; size_t first = data_[start + 2]; size_t last = data_[start + 2 + length]; CPPAD_ASSERT_UNKNOWN( reference_count > 0 ); CPPAD_ASSERT_UNKNOWN( length > 0 ); CPPAD_ASSERT_UNKNOWN( first < end_); CPPAD_ASSERT_UNKNOWN( last == end_); // // decrement the reference counter data_[start]--; // // count the entries when find last reference if( data_[start] == 0 ) { // restore reference count data_[start] = ref_count[i]; // number of data_ entries used for this set data_used_by_sets += number_elements(i) + 3; } } } // ------------------------------------------------------------------ // count the number of entries in data_ that are used by posts size_t data_used_by_posts = 0; for(size_t i = 0; i < n_set; i++) { size_t post = post_[i]; if( post > 0 ) { CPPAD_ASSERT_UNKNOWN( data_[post] > 0 ); CPPAD_ASSERT_UNKNOWN( data_[post + 1] > 0 ); CPPAD_ASSERT_UNKNOWN( data_[post + 2] < end_ ); // size_t capacity = data_[post + 1]; data_used_by_posts += capacity + 2; } } // ------------------------------------------------------------------ size_t data_used = data_used_by_sets + data_used_by_posts; CPPAD_ASSERT_UNKNOWN( data_used + data_not_used_ == data_.size() ); return; } # endif // ----------------------------------------------------------------- /*! Check if one of two sets is a subset of the other set \param one_this is the index in this svec_setvec object of the first set. \param two_other is the index in other svec_setvec object of the second set. \param other is the other svec_setvec object which may be the same as this object. \return If zero, neither set is a subset of the other. If one, then one is a subset of two and they are not equal. If two, then two is a subset of one and they are not equal. If three, then the sets are equal. */ size_t is_subset( size_t one_this , size_t two_other , const svec_setvec& other ) const { CPPAD_ASSERT_UNKNOWN( one_this < start_.size() ); CPPAD_ASSERT_UNKNOWN( two_other < other.start_.size() ); CPPAD_ASSERT_UNKNOWN( end_ == other.end_ ); // // start size_t start_one = start_[one_this]; size_t start_two = other.start_[two_other]; // if( start_one == 0 ) { // set one is empty if( start_two == 0 ) { // set two is empty return 3; } // set one is empty and two is not empty return 1; } if( start_two == 0 ) { // set two is empty and one is not empty return 2; } // // data index size_t index_one = start_one + 2; size_t index_two = start_two + 2; // // value size_t value_one = data_[index_one]; size_t value_two = other.data_[index_two]; // bool one_subset = true; bool two_subset = true; // size_t value_union = std::min(value_one, value_two); while( (one_subset | two_subset) && (value_union < end_) ) { // if( value_one > value_union ) two_subset = false; else { // value_one <= value_two and value_one < end_ value_one = data_[++index_one]; } // if( value_two > value_union ) one_subset = false; else { // value_two <= value_one and value_two < end_ value_two = other.data_[++index_two]; } value_union = std::min(value_one, value_two); } if( one_subset ) { if( two_subset ) { // sets are equal return 3; } // one is a subset of two return 1; } if( two_subset ) { // two is a subset of one return 2; } // // neither is a subset return 0; } // ----------------------------------------------------------------- /*! Does garbage collection when indicated. This routine should be called when more entries are not being used. If a significant proportion are not being used, the data structure will be compacted. The size of data_ should equal the number of entries used by the sets plus the number of entries that are not being used data_not_used_. Note that data_[0] never gets used. */ void collect_garbage(void) { if( data_not_used_ < data_.size() / 2 + 100) return; check_data_structure(); // // number of sets including empty ones size_t n_set = start_.size(); // // use temporary to hold copy of data_ and start_ pod_vector data_tmp(1); // data_tmp[0] will not be used pod_vector start_tmp(n_set); // for(size_t i = 0; i < n_set; i++) { size_t start = start_[i]; if( start == 0 ) start_tmp[i] = 0; else { // check if this set has already been copied if( data_[start] == 0 ) { // starting address in data_tmp has been stored here start_tmp[i] = data_[start + 1]; } else { size_t tmp_start = data_tmp.extend(2); start_tmp[i] = tmp_start; data_tmp[tmp_start + 0] = data_[start + 0]; data_tmp[tmp_start + 1] = data_[start + 1]; // for(size_t j = 2; data_[start + j] != end_; ++j) data_tmp.push_back( data_[start + j] ); data_tmp.push_back(end_); // // flag that indicates this set already copied data_[start] = 0; // // store the starting address here data_[start + 1] = tmp_start; } } } // swap the tmp and old data vectors start_.swap(start_tmp); data_.swap(data_tmp); // all of the elements, except the first, are used data_not_used_ = 1; } // ----------------------------------------------------------------- /*! Assign a set equal to the union of a set and a vector; \param target is the index in this svec_setvec object of the set being assigned. \param left is the index in this svec_setvec object of the left operand for the union operation. It is OK for target and left to be the same value. \param right is a vector of size_t, sorted in ascending order. right operand for the union operation. Elements can be repeated in right, but are not be repeated in the resulting set. All of the elements must have value less than end(); */ void binary_union( size_t target , size_t left , const pod_vector& right ) { CPPAD_ASSERT_UNKNOWN( post_[left] == 0 ); // CPPAD_ASSERT_UNKNOWN( target < start_.size() ); CPPAD_ASSERT_UNKNOWN( left < start_.size() ); size_t start_left = start_[left]; // ------------------------------------------------------------------- // Check if right is a subset of left so that we used reference count // and not make copies of identical sets. // // initialize index for left and right sets size_t current_left = start_left; size_t current_right = 0; // // initialize value_left size_t value_left = end_; if( current_left > 0 ) { // advance from reference counter to data current_left = current_left + 2; value_left = data_[current_left]; CPPAD_ASSERT_UNKNOWN( value_left < end_); } // // initialize value_right size_t value_right = end_; if( right.size() > 0 ) value_right = right[current_right]; // bool subset = true; while( subset & (value_right < end_) ) { while( value_left < value_right ) { // advance left value_left = data_[++current_left]; } if( value_right < value_left ) subset = false; else { // advance right ++current_right; if( current_right == right.size() ) value_right = end_; else value_right = right[current_right]; } } // if( subset ) { // target = left will use reference count for identical sets assignment(target, left, *this); return; } // ------------------------------------------------------------------- // number of elements that will be deleted by removing old version // of target size_t number_lost = drop(target); // drop any posting for the target set size_t post = post_[target]; if( post > 0 ) { CPPAD_ASSERT_UNKNOWN( target != left ); size_t capacity = data_[post + 1]; number_lost += capacity + 2; post_[target] = 0; } // // start new version of target size_t start = data_.extend(2); start_[target] = start; data_[start] = 1; // reference count // data_[start + 1] = length is not yet known // // initialize value_left current_left = start_left; value_left = end_; if( current_left > 0 ) { // advance from reference counter to data current_left = current_left + 2; value_left = data_[current_left]; CPPAD_ASSERT_UNKNOWN( value_left < end_); } // // initialize value_right value_right = end_; if( right.size() > 0 ) value_right = right[current_right]; // // merge while( (value_left < end_) || (value_right < end_) ) { if( value_left == value_right) { // advance left so left and right are no longer equal ++current_left; value_left = data_[current_left]; CPPAD_ASSERT_UNKNOWN( value_right < value_left ); } // if( value_left < value_right ) { // value_left case CPPAD_ASSERT_UNKNOWN( value_left < end_ ); data_.push_back( value_left ); // // advance left ++current_left; value_left = data_[current_left]; } else { CPPAD_ASSERT_UNKNOWN( value_right < value_left ) // value_right case CPPAD_ASSERT_UNKNOWN( value_right < end_); data_.push_back( value_right ); // // advance right (skip values equal to this one) size_t previous_value = value_right; while( value_right == previous_value ) { ++current_right; if( current_right == right.size() ) value_right = end_; else { value_right = right[current_right]; CPPAD_ASSERT_UNKNOWN( value_right < end_ ); } } } } // make end of target list data_.push_back( end_ ); // // reference count, length, and end_ are not elements of set CPPAD_ASSERT_UNKNOWN( data_.size() > start + 3 ); size_t length = data_.size() - (start + 3); data_[start + 1] = length; // // adjust data_not_used_ data_not_used_ += number_lost; collect_garbage(); } public: /// declare a const iterator typedef svec_setvec_const_iterator const_iterator; // ----------------------------------------------------------------- /*! Default constructor (no sets) */ svec_setvec(void) : end_(0) , data_not_used_(0) , data_(0) , start_(0) , post_(0) { } // ----------------------------------------------------------------- /// Destructor ~svec_setvec(void) { check_data_structure(); } // ----------------------------------------------------------------- /*! Using copy constructor is a programing (not user) error \param v vector of sets that we are attempting to make a copy of. */ svec_setvec(const svec_setvec& v) { // Error: Probably a svec_setvec argument has been passed by value CPPAD_ASSERT_UNKNOWN(false); } // ----------------------------------------------------------------- /*! Assignment operator. \param other this svec_setvec with be set to a deep copy of other. */ void operator=(const svec_setvec& other) { end_ = other.end_; data_not_used_ = other.data_not_used_; data_ = other.data_; start_ = other.start_; post_ = other.post_; } // ----------------------------------------------------------------- /*! swap (used by move semantics version of ADFun assignment operator) \param other this sparse::list_setvec with be swapped with other. \par vector_of_sets This public member function is not yet part of the vector_of_sets concept. */ void swap(svec_setvec& other) { // size_t objects std::swap(end_ , other.end_); std::swap(data_not_used_ , other.data_not_used_); // pod_vectors data_.swap( other.data_); start_.swap( other.start_); post_.swap( other.post_); } // ----------------------------------------------------------------- /*! Start a new vector of sets. \param n_set is the number of sets in this vector of sets. \li If n_set is zero, any memory currently allocated for this object is freed. \li If n_set is non-zero, a vector of n_set sets is created and all the sets are initialized as empty. \param end is the maximum element plus one (the minimum element is 0). If n_set is zero, end must also be zero. */ void resize(size_t n_set, size_t end) { check_data_structure(); if( n_set == 0 ) { CPPAD_ASSERT_UNKNOWN(end == 0 ); // // restore object to start after constructor // (no memory allocated for this object) data_.clear(); start_.clear(); post_.clear(); data_not_used_ = 0; end_ = 0; // return; } end_ = end; // start_.resize(n_set); post_.resize(n_set); for(size_t i = 0; i < n_set; i++) { start_[i] = 0; post_[i] = 0; } // data_.resize(1); // first element is not used data_not_used_ = 1; } // ----------------------------------------------------------------- /*! Return number of elements in a set. \param i is the index of the set we are checking number of the elements of. */ size_t number_elements(size_t i) const { CPPAD_ASSERT_UNKNOWN( post_[i] == 0 ); // size_t start = start_[i]; if( start == 0 ) return 0; return data_[start + 1]; } // ------------------------------------------------------------------ /*! Post an element for delayed addition to a set. \param i is the index for this set in the vector of sets. \param element is the value of the element that we are posting. The same element may be posted multiple times. \par It is faster to post multiple elements to set i and then call process_post(i) then to add each element individually. It is an error to call any member function, that depends on the value of set i, before processing the posts to set i. */ void post_element(size_t i, size_t element) { CPPAD_ASSERT_UNKNOWN( i < start_.size() ); CPPAD_ASSERT_UNKNOWN( element < end_ ); size_t post = post_[i]; if( post == 0 ) { // minimum capacity for an post vector size_t min_capacity = 10; size_t post_new = data_.extend(min_capacity + 2); data_[post_new] = 1; // length data_[post_new + 1] = min_capacity; // capacity data_[post_new + 2] = element; post_[i] = post_new; } else { size_t length = data_[post]; size_t capacity = data_[post + 1]; if( length == capacity ) { size_t post_new = data_.extend( 2 * capacity ); // data_[post_new] = length + 1; data_[post_new + 1] = 2 * capacity; // for(size_t j = 0; j < length; j++) data_[post_new + 2 + j] = data_[post + 2 + j]; data_[post_new + 2 + length] = element; // post_[i] = post_new; size_t number_lost = length + 2; data_not_used_ += number_lost; } else { data_[post] = length + 1; data_[post + 2 + length] = element; } } // check amount of data_not_used_ collect_garbage(); return; } // ----------------------------------------------------------------- /*! process post entries for a specific set. \param i index of the set for which we are processing the post entries. \par post_ Upon call, post_[i] is location in data_ of the elements that get added to the i-th set. Upon return, post_[i] is zero. */ void process_post(size_t i) { // start size_t start = start_[i]; // post size_t post = post_[i]; // // check if there are no elements to process if( post == 0 ) return; // // sort the elements that need to be processed size_t length_post = data_[post]; size_t capacity_post = data_[post + 1]; size_t* first_post = data_.data() + post + 2; size_t* last_post = first_post + length_post; std::sort(first_post, last_post); // ------------------------------------------------------------------- // check if posted elements are a subset of set // // first element of the set size_t current_set = start; size_t value_set = end_; if( start > 0 ) { current_set = start + 2; value_set = data_[current_set]; } // // first element to post size_t* current_post = first_post; size_t value_post = *current_post; // bool subset = true; while( subset & (value_post != end_) ) { while( value_set < value_post ) value_set = data_[++current_set]; if( value_post < value_set ) subset = false; else { ++current_post; if( current_post == last_post ) value_post = end_; else value_post = *current_post; } } // if( subset ) { // drop the post_ elements post_[i] = 0; // size_t number_lost = capacity_post + 2; data_not_used_ += number_lost; collect_garbage(); // // nothing else to do return; } // ------------------------------------------------------------------- // number of element that will be lost by removing old i-th set size_t number_lost = drop(i); // start new version of i-th set size_t start_new = data_.extend(2); start_[i] = start_new; data_[start_new] = 1; // reference count // data[start_new + 1] = length_new is not yet known // // first element of the set current_set = start; value_set = end_; if( start > 0 ) { current_set = start + 2; value_set = data_[current_set]; } // // first element to process current_post = first_post; value_post = *current_post; // // merge while( (value_set < end_) || (current_post != last_post ) ) { if( value_set == value_post ) { // advance left so left and right are no longer equal ++current_set; value_set = data_[current_set]; CPPAD_ASSERT_UNKNOWN( value_post < value_set ); } // if( value_set < value_post ) { // add value_set CPPAD_ASSERT_UNKNOWN( value_set < end_ ); data_.push_back( value_set ); // // advance set ++current_set; value_set = data_[current_set]; } else { CPPAD_ASSERT_UNKNOWN( value_post < value_set ) // add value_post CPPAD_ASSERT_UNKNOWN( value_post < end_); data_.push_back( value_post ); // // advance post (skip values equal to this one) size_t value_previous = value_post; while( value_post == value_previous ) { ++current_post; if( current_post == last_post ) value_post = end_; else value_post = *current_post; } } } // make end of target list data_.push_back( end_ ); // // reference count, length, and end_ are not elements of set CPPAD_ASSERT_UNKNOWN( data_.size() > start_new + 3 ); size_t length_new = data_.size() - (start_new + 3); data_[start_new + 1] = length_new; CPPAD_ASSERT_UNKNOWN( data_[start_new + length_new + 2] == end_ ); // // drop to post_ elements for this set post_[i] = 0; // number_lost += capacity_post + 2; data_not_used_ += number_lost; collect_garbage(); // return; } // ----------------------------------------------------------------- /*! Add one element to a set. \param i is the index for this set in the vector of sets. \param element is the element we are adding to the set. */ void add_element(size_t i, size_t element) { CPPAD_ASSERT_UNKNOWN( i < start_.size() ); CPPAD_ASSERT_UNKNOWN( element < end_ ); // check if element is already in the set if( is_element(i, element) ) return; // check for case where old set is empty size_t start = start_[i]; if( start == 0 ) { start = data_.extend(4); start_[i] = start; data_[start] = 1; // reference count data_[start + 1] = 1; // length data_[start + 2] = element; // the element data_[start + 3] = end_; // end marker return; } // size_t number_lost = drop(i); // // start of new set size_t length = data_[start + 1]; size_t new_start = data_.extend(2); data_[new_start] = 1; // reference count data_[new_start + 1] = length + 1; // new length // size_t count = 0; size_t value = data_[start + 2 + count]; // before new element while( value < element) { data_.push_back( value ); ++count; value = data_[start + 2 + count]; } CPPAD_ASSERT_UNKNOWN( element < value ) // new element data_.push_back( element ); // after new element while( value < end_ ) { data_.push_back( value ); ++count; value = data_[start + 2 + count]; } data_.push_back( end_ ); // // connect up new set start_[i] = new_start; // adjust data_not_used_ data_not_used_ += number_lost; collect_garbage(); // return; } // ----------------------------------------------------------------- /*! Check if an element is in a set. \param i is the index for this set in the vector of sets. \param element is the element we are checking to see if it is in the set. */ bool is_element(size_t i, size_t element) const { CPPAD_ASSERT_UNKNOWN( post_[i] == 0 ); CPPAD_ASSERT_UNKNOWN( element < end_ ); // size_t start = start_[i]; if( start == 0 ) return false; // size_t length = data_[start + 1]; const size_t* first = data_.data() + start + 2; const size_t* last = first + length; if( length < 10 ) { bool result = false; while( last > first ) result |= *(--last) == element; return result; } // return std::binary_search(first, last, element); } // ----------------------------------------------------------------- /*! Assign the empty set to one of the sets. \param target is the index of the set we are setting to the empty set. \par data_not_used_ increments this value by number of data_ elements that are lost (unlinked) by this operation. */ void clear(size_t target) { // number of data_ elements used for this set size_t number_lost = drop( target ); // set target to empty set start_[target] = 0; // drop the posted elements if( post_[target] != 0 ) { size_t capacity = post_[target + 1]; number_lost += capacity + 2; // post_[target] = 0; } // adjust data_not_used_ data_not_used_ += number_lost; collect_garbage(); } // ----------------------------------------------------------------- /*! Assign one set equal to another set. \param this_target is the index in this svec_setvec object of the set being assigned. \param other_source is the index in the other svec_setvec object of the set that we are using as the value to assign to the target set. \param other is the other svec_setvec object (which may be the same as this svec_setvec object). This must have the same value for end_. \par data_not_used_ increments this value by number of elements lost. */ void assignment( size_t this_target , size_t other_source , const svec_setvec& other ) { CPPAD_ASSERT_UNKNOWN( other.post_[ other_source ] == 0 ); // CPPAD_ASSERT_UNKNOWN( this_target < start_.size() ); CPPAD_ASSERT_UNKNOWN( other_source < other.start_.size() ); CPPAD_ASSERT_UNKNOWN( end_ == other.end_ ); // check if we are assigning a set to itself if( (this == &other) && (this_target == other_source) ) return; // number of elements that will be deleted by this operation size_t number_lost = drop(this_target); // drop any posting for the target set size_t post = post_[this_target]; if( post > 0 ) { // do not need to worry about target being same as source size_t capacity = data_[post + 1]; number_lost += capacity + 2; post_[this_target] = 0; } // If this and other are the same, use another reference to same list size_t other_start = other.start_[other_source]; if( this == &other ) { CPPAD_ASSERT_UNKNOWN( this_target != other_source ); start_[this_target] = other_start; if( other_start != 0 ) { // increment reference count data_[other_start]++; } } else if( other_start == 0 ) { // the target list is empty start_[this_target] = 0; } else { // make a copy of the other list in this svec_setvec size_t length = other.data_[other_start + 1]; size_t this_start = data_.extend(2); start_[this_target] = this_start; data_[this_start] = 1; // reference count data_[this_start + 1] = length; // length for(size_t j = 0; j < length; ++j) data_.push_back( other.data_[other_start + 2 + j] ); data_.push_back(end_); } // adjust data_not_used_ data_not_used_ += number_lost; collect_garbage(); } // ----------------------------------------------------------------- /*! Assign a set equal to the union of two other sets. \param this_target is the index in this svec_setvec object of the set being assigned. \param this_left is the index in this svec_setvec object of the left operand for the union operation. It is OK for this_target and this_left to be the same value. \param other_right is the index in the other svec_setvec object of the right operand for the union operation. It is OK for this_target and other_right to be the same value. \param other is the other svec_setvec object (which may be the same as this svec_setvec object). */ void binary_union( size_t this_target , size_t this_left , size_t other_right , const svec_setvec& other ) { CPPAD_ASSERT_UNKNOWN( post_[this_left] == 0 ); CPPAD_ASSERT_UNKNOWN( other.post_[ other_right ] == 0 ); // CPPAD_ASSERT_UNKNOWN( this_target < start_.size() ); CPPAD_ASSERT_UNKNOWN( this_left < start_.size() ); CPPAD_ASSERT_UNKNOWN( other_right < other.start_.size() ); CPPAD_ASSERT_UNKNOWN( end_ == other.end_ ); // check if one of the two operands is a subset of the the other size_t subset = is_subset(this_left, other_right, other); // case where right is a subset of left or right and left are equal if( subset == 2 || subset == 3 ) { assignment(this_target, this_left, *this); return; } // case where the left is a subset of right and they are not equal if( subset == 1 ) { assignment(this_target, other_right, other); return; } // if neither case holds, then both left and right are non-empty CPPAD_ASSERT_UNKNOWN( reference_count(this_left) > 0 ); CPPAD_ASSERT_UNKNOWN( other.reference_count(other_right) > 0 ); // must get all the start indices before modify start_this // (in case start_this is the same as start_left or start_right) size_t start_left = start_[this_left]; size_t start_right = other.start_[other_right]; // number of list elements that will be deleted by this operation size_t number_lost = drop(this_target); // drop any posting for the target set size_t post = post_[this_target]; if( post > 0 ) { // do not need to worry about target being same as left or right size_t capacity = data_[post + 1]; number_lost += capacity + 2; post_[this_target] = 0; } // start the new list size_t start = data_.extend(2); start_[this_target] = start; data_[start] = 1; // reference count // data_[start + 1] = length is not yet known // initialize left CPPAD_ASSERT_UNKNOWN( start_left != 0 ); size_t current_left = start_left + 2; size_t value_left = data_[current_left]; CPPAD_ASSERT_UNKNOWN( value_left < end_ ); // initialize right CPPAD_ASSERT_UNKNOWN( start_right != 0 ); size_t current_right = start_right + 2; size_t value_right = other.data_[current_right]; CPPAD_ASSERT_UNKNOWN( value_right < end_ ); CPPAD_ASSERT_UNKNOWN( value_left < end_ && value_right < end_ ); while( (value_left < end_) || (value_right < end_) ) { if( value_left == value_right ) { // advance right so left and right are no longer equal ++current_right; value_right = other.data_[current_right]; } if( value_left < value_right ) { data_.push_back( value_left ); // advance left to its next element ++current_left; value_left = data_[current_left]; } else { CPPAD_ASSERT_UNKNOWN( value_right < value_left ) data_.push_back( value_right ); // advance right to its next element ++current_right; value_right = other.data_[current_right]; } } // make end of target list data_.push_back( end_ ); // // reference count, length, and end_ are not elements of set CPPAD_ASSERT_UNKNOWN( data_.size() > start + 3 ); size_t length = data_.size() - (start + 3); data_[start + 1] = length; // adjust data_not_used_ data_not_used_ += number_lost; collect_garbage(); } // ----------------------------------------------------------------- /*! Assign a set equal to the intersection of two other sets. \param this_target is the index in this svec_setvec object of the set being assigned. \param this_left is the index in this svec_setvec object of the left operand for the intersection operation. It is OK for this_target and this_left to be the same value. \param other_right is the index in the other svec_setvec object of the right operand for the intersection operation. It is OK for this_target and other_right to be the same value. \param other is the other svec_setvec object (which may be the same as this svec_setvec object). */ void binary_intersection( size_t this_target , size_t this_left , size_t other_right , const svec_setvec& other ) { CPPAD_ASSERT_UNKNOWN( post_[this_left] == 0 ); CPPAD_ASSERT_UNKNOWN( other.post_[ other_right ] == 0 ); // CPPAD_ASSERT_UNKNOWN( this_target < start_.size() ); CPPAD_ASSERT_UNKNOWN( this_left < start_.size() ); CPPAD_ASSERT_UNKNOWN( other_right < other.start_.size() ); CPPAD_ASSERT_UNKNOWN( end_ == other.end_ ); // // check if one of the two operands is a subset of the the other size_t subset = is_subset(this_left, other_right, other); // case where left is a subset of right or left and right are equal if( subset == 1 || subset == 3 ) { assignment(this_target, this_left, *this); return; } // case where the right is a subset of left and they are not equal if( subset == 2 ) { assignment(this_target, other_right, other); return; } // if neither case holds, then both left and right are non-empty CPPAD_ASSERT_UNKNOWN( reference_count(this_left) > 0 ); CPPAD_ASSERT_UNKNOWN( other.reference_count(other_right) > 0 ); // must get all the start indices before modify start_this // (in case start_this is the same as start_left or start_right) size_t start_left = start_[this_left]; size_t start_right = other.start_[other_right]; // number of list elements that will be deleted by this operation size_t number_lost = drop(this_target); // drop any posting for the target set size_t post = post_[this_target]; if( post > 0 ) { // do not need to worry about target being same as left or right size_t capacity = data_[post + 1]; number_lost += capacity + 2; post_[this_target] = 0; } // initialize intersection as empty size_t start = 0; start_[this_target] = start; // initialize left CPPAD_ASSERT_UNKNOWN( start_left != 0 ); size_t current_left = start_left + 2; size_t value_left = data_[current_left]; CPPAD_ASSERT_UNKNOWN( value_left < end_ ); // initialize right CPPAD_ASSERT_UNKNOWN( start_right != 0 ); size_t current_right = start_right + 2; size_t value_right = other.data_[current_right]; CPPAD_ASSERT_UNKNOWN( value_right < end_ ); while( (value_left < end_) && (value_right < end_) ) { if( value_left == value_right ) { if( start == 0 ) { // this is the first element in the intersection start = data_.extend(2); start_[this_target] = start; data_[start] = 1; // reference count // data_[start + 1] = length is not yet known } data_.push_back( value_left ); // // advance left to its next element ++current_left; value_left = data_[current_left]; } if( value_left > value_right ) { // advance right ++current_right; value_right = other.data_[current_right]; } if( value_right > value_left ) { // advance left ++current_left; value_left = data_[current_left]; } } if( start != 0 ) { data_.push_back(end_); CPPAD_ASSERT_UNKNOWN( data_.size() > start + 3 ); size_t length = data_.size() - (start + 3); data_[start + 1] = length; } // adjust data_not_used_ data_not_used_ += number_lost; collect_garbage(); } // ----------------------------------------------------------------- /*! Fetch n_set for vector of sets object. \return Number of from sets for this vector of sets object */ size_t n_set(void) const { return start_.size(); } // ----------------------------------------------------------------- /*! Fetch end for this vector of sets object. \return is the maximum element value plus one (the minimum element value is 0). */ size_t end(void) const { return end_; } // ----------------------------------------------------------------- /*! Amount of memory used by this vector of sets \return The amount of memory in units of type unsigned char memory. */ size_t memory(void) const { return data_.capacity() * sizeof(size_t); } /*! Print the vector of sets (used for debugging) */ void print(void) const; }; // ========================================================================= /*! cons_iterator for one set of positive integers in a svec_setvec object. All the public member functions for this class are also in the sparse::pack_setvec_const_iterator and sparse::list_setvec_const_iterator classes. This defines the CppAD vector_of_sets iterator concept. */ class svec_setvec_const_iterator { private: /// data for the entire vector of sets const pod_vector& data_; /// Possible elements in a list are 0, 1, ..., end_ - 1; const size_t end_; /// data index of next entry, zero for no more entries size_t data_index_; public: /// construct a const_iterator for a set in a svec_setvec object svec_setvec_const_iterator (const svec_setvec& vec_set, size_t i) : data_( vec_set.data_ ) , end_ ( vec_set.end_ ) { CPPAD_ASSERT_UNKNOWN( vec_set.post_[i] == 0 ); // size_t start = vec_set.start_[i]; if( start == 0 ) { data_index_ = 0; } else { // data index of the first element in the set data_index_ = start + 2; CPPAD_ASSERT_UNKNOWN( data_[data_index_] < end_ ); } } /// advance to next element in this list svec_setvec_const_iterator& operator++(void) { if( data_index_ != 0 ) { ++data_index_; if( data_[data_index_] == end_ ) data_index_ = 0; } return *this; } /// obtain value of this element of the set of positive integers /// (end_ for no such element) size_t operator*(void) { if( data_index_ == 0 ) return end_; return data_[data_index_]; } }; // ========================================================================= /*! Print the vector of sets (used for debugging) */ inline void svec_setvec::print(void) const { std::cout << "svec_setvec:\n"; for(size_t i = 0; i < n_set(); i++) { std::cout << "set[" << i << "] = {"; const_iterator itr(*this, i); while( *itr != end() ) { std::cout << *itr; if( *(++itr) != end() ) std::cout << ","; } std::cout << "}\n"; } return; } /*! Copy a user vector of sets sparsity pattern to an internal svec_setvec object. \tparam SetVector is a simple vector with elements of type std::set. \param internal The input value of sparisty does not matter. Upon return it contains the same sparsity pattern as user (or the transposed sparsity pattern). \param user sparsity pattern that we are placing internal. \param n_set number of sets (rows) in the internal sparsity pattern. \param end end of set value (number of columns) in the internal sparsity pattern. \param transpose if true, the user sparsity patter is the transposed. \param error_msg is the error message to display if some values in the user sparstiy pattern are not valid. */ template void sparsity_user2internal( svec_setvec& internal , const SetVector& user , size_t n_set , size_t end , bool transpose , const char* error_msg ) { # ifndef NDEBUG if( transpose ) CPPAD_ASSERT_KNOWN( end == size_t( user.size() ), error_msg); if( ! transpose ) CPPAD_ASSERT_KNOWN( n_set == size_t( user.size() ), error_msg); # endif // iterator for user set std::set::const_iterator itr; // size of internal sparsity pattern internal.resize(n_set, end); if( transpose ) { // transposed pattern case for(size_t j = 0; j < end; j++) { itr = user[j].begin(); while(itr != user[j].end()) { size_t i = *itr++; CPPAD_ASSERT_KNOWN(i < n_set, error_msg); internal.add_element(i, j); } } } else { for(size_t i = 0; i < n_set; i++) { itr = user[i].begin(); while(itr != user[i].end()) { size_t j = *itr++; CPPAD_ASSERT_KNOWN( j < end, error_msg); internal.add_element(i, j); } } } return; } } } } // END_CPPAD_LOCAL_SPARSE_NAMESPACE # endif ================================================ FILE: include/cppad/local/std_set.hpp ================================================ # ifndef CPPAD_LOCAL_STD_SET_HPP # define CPPAD_LOCAL_STD_SET_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include // needed before one can use CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL # include namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE /*! \file std_set.hpp Two constant standard sets (currently used for concept checking). */ /*! A standard set with one element. */ template const std::set& one_element_std_set(void) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; static std::set one; if( one.empty() ) one.insert(1); return one; } /*! A standard set with a two elements. */ template const std::set& two_element_std_set(void) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; static std::set two; if( two.empty() ) { two.insert(1); two.insert(2); } return two; } } } // END_CPPAD_LOCAL_NAMESPACE # endif ================================================ FILE: include/cppad/local/subgraph/arg_variable.hpp ================================================ # ifndef CPPAD_LOCAL_SUBGRAPH_ARG_VARIABLE_HPP # define CPPAD_LOCAL_SUBGRAPH_ARG_VARIABLE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include // BEGIN_CPPAD_LOCAL_SUBGRAPH_NAMESPACE namespace CppAD { namespace local { namespace subgraph { /*! \file arg_variable.hpp Determine arguments that are variables. */ /*! Determine the set of arguments, for an operator, that are variables. \tparam Addr Type used for indices in random iterator. \param random_itr is a random iterator for this operation sequence. \param i_op is the operator index. If this operator is part of a atomic function call, it must be the first AFunOp in the call. (There is a AFunOp at the beginning and end of each call.) \param variable is the set of argument variables corresponding to this operator. If the operator is a AFunOp, the arguments are the variables that are passed into the function call. \param work this is work space used by arg_variable to make subsequent calls faster. It should not be used by the calling routine. In addition, it is better if work does not drop out of scope between calls. */ template void get_argument_variable( const play::const_random_iterator& random_itr , size_t i_op , pod_vector& variable , pod_vector& work ) { // reset to size zero, but keep allocated memory variable.resize(0); // // operator corresponding to i_op op_code_var op; const addr_t* op_arg; size_t i_var; random_itr.op_info(i_op, op, op_arg, i_var); // // partial check of assumptions on atomic function calls CPPAD_ASSERT_UNKNOWN( op != FunapOp && op != FunavOp && op != FunrpOp && op != FunrvOp ); // // we assume this is the first AFunOp of the call if( op == AFunOp ) { random_itr.op_info(++i_op, op, op_arg, i_var); while( op != AFunOp ) { switch(op) { case FunavOp: { CPPAD_ASSERT_NARG_NRES(op, 1, 0); size_t j_var = size_t( op_arg[0] ); variable.push_back(j_var); } break; case FunrvOp: case FunrpOp: case FunapOp: break; default: // cannot find second AFunOp in this call CPPAD_ASSERT_UNKNOWN(false); break; } random_itr.op_info(++i_op, op, op_arg, i_var); } CPPAD_ASSERT_UNKNOWN( variable.size() > 0 ); return; } // is_variable is a reference to work with a better name pod_vector& is_variable(work); arg_is_variable(op, op_arg, is_variable); size_t num_arg = is_variable.size(); for(size_t j = 0; j < num_arg; ++j) { if( is_variable[j] ) { size_t j_var = size_t( op_arg[j] ); variable.push_back(j_var); } } return; } } } } // END_CPPAD_LOCAL_SUBGRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/subgraph/entire_call.hpp ================================================ # ifndef CPPAD_LOCAL_SUBGRAPH_ENTIRE_CALL_HPP # define CPPAD_LOCAL_SUBGRAPH_ENTIRE_CALL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include // BEGIN_CPPAD_LOCAL_SUBGRAPH_NAMESPACE namespace CppAD { namespace local { namespace subgraph { /*! \file entire_call.hpp include entire function call in a subgraph */ // =========================================================================== /*! Convert from just first AFunOp to entire atomic function call in a subgraph. \tparam Addr Type used for indices in the random iterator. \param random_itr is a random iterator for this operation sequence. \param subgraph It a set of operator indices in this recording. If the corresponding operator is a AFunOp, it assumed to be the first one in the corresponding atomic function call. The other call operators are included in the subgraph. */ template void entire_call( const play::const_random_iterator& random_itr , pod_vector& subgraph ) { // add extra operators corresponding to rest of atomic function calls size_t n_sub = subgraph.size(); for(size_t k = 0; k < n_sub; ++k) { size_t i_op = size_t( subgraph[k] ); // if( random_itr.get_op(i_op) == AFunOp ) { // This is the first AFunOp of this atomic function call while( random_itr.get_op(++i_op) != AFunOp ) { switch(random_itr.get_op(i_op)) { case FunavOp: case FunrvOp: case FunrpOp: case FunapOp: subgraph.push_back( addr_t(i_op) ); break; default: // cannot find second AFunOp in this call CPPAD_ASSERT_UNKNOWN(false); break; } } // THis is the second AFunOp of this atomic function call subgraph.push_back( addr_t(i_op) ); } } } } } } // END_CPPAD_LOCAL_SUBGRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/subgraph/get_rev.hpp ================================================ # ifndef CPPAD_LOCAL_SUBGRAPH_GET_REV_HPP # define CPPAD_LOCAL_SUBGRAPH_GET_REV_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include // BEGIN_CPPAD_LOCAL_SUBGRAPH_NAMESPACE namespace CppAD { namespace local { namespace subgraph { /*! \file get_rev.hpp Get subgraph corresponding to a dependent variable. */ // =========================================================================== /*! Get the subgraph corresponding to a dependent variables (and a selected set of independent variables). \tparam Addr Type used for indices in the random iterator. \param random_itr is a random iterator for this operation sequence. \param dep_taddr is the vector mapping user dependent variable indices to the corresponding variable in the recording. \param i_dep is the user index for his dependent variable; that i_dep < n_dep_. \param subgraph the input size and contents of this vector do not matter. Repeated calls with the same subgraph vector should reduce the amount of memory allocation. Upon return it contains the operator indices for the subgraph corresponding to the dependent and the selected independent variables. Only selected independent variable operators InvOp are included in the subgraph. Furthermore the operator indices in subgraph are unique; i.e., if i_op != j_op then subgraph[i_op] != subgraph[j_op]. \par map_user_op_ This vector must be set. \par in_subgraph_ has size equal to the number of operators in play. If in_subgraph[i_op] <= n_dep_, the result for this operator depends on the selected independent variables. In addition, upon input, there is no i_op such that in_subgraph[i_op] == i_dep. Note that for atomic function call operators i_op, \code n_dep_ < in_subgraph[i_op] \endcode except for the first AFunOp in the atomic function call sequence. For the first AFunOp, \code in_subgraph[i_op] <= n_dep_ \endcode if any result for the atomic function call depends on the selected independent variables. Except for UserOP, only operators with NumRes(op) > 0 are included in the dependency; e.g., comparison operators are not included. Upon return, some of the i_op for which in_subgraph[i_op] <= n_dep_, will be changed to in_subgraph[i_op] = i_dep. \par process_range_ The value process_range_[i_dep] is checked to make sure it is false. It is then set to have value true. */ template void subgraph_info::get_rev( const play::const_random_iterator& random_itr , const pod_vector& dep_taddr , addr_t i_dep , pod_vector& subgraph ) { // check sizes CPPAD_ASSERT_UNKNOWN( map_user_op_.size() == n_op_ ); // process_range_ CPPAD_ASSERT_UNKNOWN( process_range_[i_dep] == false ); process_range_[i_dep] = true; // special value; see init_rev_in_subgraph addr_t depend_yes = addr_t( n_dep_ ); // assumption on i_dep CPPAD_ASSERT_UNKNOWN( i_dep < depend_yes ); // start with an empty subgraph for this dependent variable subgraph.resize(0); // tape index corresponding to this dependent variable size_t i_var = dep_taddr[i_dep]; // operator corresponding to this dependent variable size_t i_op = random_itr.var2op(i_var); i_op = size_t( map_user_op_[i_op] ); // if this variable depends on the selected independent variables // process its subgraph CPPAD_ASSERT_UNKNOWN( in_subgraph_[i_op] != i_dep ) if( in_subgraph_[i_op] <= depend_yes ) { subgraph.push_back( addr_t(i_op) ); in_subgraph_[i_op] = i_dep; } // space used to return set of arguments that are variables pod_vector argument_variable; // temporary space used by get_argument_variable pod_vector work; // scan all the operators in this subgraph size_t sub_index = 0; while(sub_index < subgraph.size() ) { // this operator connected to this dependent and selected independent i_op = size_t( subgraph[sub_index] ); CPPAD_ASSERT_UNKNOWN( in_subgraph_[i_op] == i_dep ); // // There must be a result for this operator # ifndef NDEBUG op_code_var op = random_itr.get_op(i_op); CPPAD_ASSERT_UNKNOWN(op == AFunOp || NumRes(op) > 0 ); # endif // // which variables are connected to this operator get_argument_variable(random_itr, i_op, argument_variable, work); for(size_t j = 0; j < argument_variable.size(); ++j) { // add the corresponding operators to the subgraph size_t j_var = argument_variable[j]; size_t j_op = random_itr.var2op(j_var); j_op = size_t( map_user_op_[j_op] ); bool add = in_subgraph_[j_op] <= depend_yes; add &= in_subgraph_[j_op] != i_dep; if( random_itr.get_op(j_op) == InvOp ) { CPPAD_ASSERT_UNKNOWN( j_op == j_var ); add &= select_domain_[j_var - 1]; } if( add ) { subgraph.push_back( addr_t(j_op) ); in_subgraph_[j_op] = i_dep; } } // we are done scanning this subgraph operator ++sub_index; } } } } } // END_CPPAD_LOCAL_SUBGRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/subgraph/info.hpp ================================================ # ifndef CPPAD_LOCAL_SUBGRAPH_INFO_HPP # define CPPAD_LOCAL_SUBGRAPH_INFO_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include // BEGIN_CPPAD_LOCAL_SUBGRAPH_NAMESPACE namespace CppAD { namespace local { namespace subgraph { /*! \file info.hpp subgraph information attached to a operation sequence */ /// class for maintaining subgraph information attached to on ADFun object. class subgraph_info { private: // ----------------------------------------------------------------------- // private member data set by constructor, resize, and assign // ----------------------------------------------------------------------- /// number of independent variables for this function size_t n_ind_; /// number of dependent variables for this function size_t n_dep_; /// number of operatros in operation sequence size_t n_op_; /// number of variables in operation sequence size_t n_var_; // ----------------------------------------------------------------------- // private member data set by set_map_user_op // ----------------------------------------------------------------------- /// Mapping atomic call operators to AFunOp that begins call sequence, /// other operators are not changed by the map. /// (size zero after constructor or resize) pod_vector map_user_op_; // ----------------------------------------------------------------------- // other private member data // ----------------------------------------------------------------------- /// flags which operators are in subgraph /// (size zero or n_op_). pod_vector in_subgraph_; /// flags which dependent variables are selected pod_vector select_domain_; /// flags which dependent variables have been processed since /// the previous init_rev pod_vector process_range_; public: // ----------------------------------------------------------------------- // const public functions // ----------------------------------------------------------------------- /// number of independent variables size_t n_ind(void) const { return n_ind_; } /// number of dependent variables size_t n_dep(void) const { return n_dep_; } /// number of operators size_t n_op(void) const { return n_op_; } // number of variables size_t n_var(void) const { return n_var_; } /// map atomic function calls to first operator in the call const pod_vector& map_user_op(void) const { return map_user_op_; } /// previous select_domain argument to init_rev const pod_vector& select_domain(void) const { return select_domain_; } /// dependent variables that have been processed since previous init_rev const pod_vector& process_range(void) const { return process_range_; } /// amount of memory corresponding to this object size_t memory(void) const { size_t sum = map_user_op_.size() * sizeof(addr_t); sum += in_subgraph_.size() * sizeof(addr_t); sum += select_domain_.size() * sizeof(bool); sum += process_range_.size() * sizeof(bool); return sum; } /// free memory used for calculating subgraph void clear(void) { map_user_op_.clear(); in_subgraph_.clear(); select_domain_.clear(); process_range_.clear(); } // ----------------------------------------------------------------------- /*! check that the value of map_user_op is OK for this operation sequence \param play is the player for this operation sequence. \return is true, if map_user_op has the correct value for this operation sequence (is the same as it would be after a set_map_user_op). */ template bool check_map_user_op(const player* play) const { if( map_user_op_.size() != n_op_ ) return false; bool ok = true; size_t i_op = 0; while( i_op < n_op_ ) { op_code_var op = play->GetOp(i_op); ok &= map_user_op_[i_op] == addr_t( i_op ); if( op == AFunOp ) { addr_t begin = addr_t( i_op ); op = play->GetOp(++i_op); while( op != AFunOp ) { CPPAD_ASSERT_UNKNOWN( op==FunapOp || op==FunavOp || op==FunrpOp || op==FunrvOp ); ok &= map_user_op_[i_op] == begin; op = play->GetOp(++i_op); } ok &= map_user_op_[i_op] == begin; } ++i_op; } return ok; } // ----------------------------------------------------------------------- // non const public functions // ----------------------------------------------------------------------- /// flag which operators that are in the subgraph pod_vector& in_subgraph(void) { return in_subgraph_; } /// default constructor (all sizes are zero) subgraph_info(void) : n_ind_(0), n_dep_(0), n_op_(0), n_var_(0) { CPPAD_ASSERT_UNKNOWN( map_user_op_.size() == 0 ); CPPAD_ASSERT_UNKNOWN( in_subgraph_.size() == 0 ); } // ----------------------------------------------------------------------- /// assignment operator void operator=(const subgraph_info& info) { n_ind_ = info.n_ind_; n_dep_ = info.n_dep_; n_op_ = info.n_op_; n_var_ = info.n_var_; map_user_op_ = info.map_user_op_; in_subgraph_ = info.in_subgraph_; select_domain_ = info.select_domain_; process_range_ = info.process_range_; return; } // ----------------------------------------------------------------------- /// swap /// (used for move semantics version of ADFun assignment) void swap(subgraph_info& info) { // size_t objects std::swap(n_ind_ , info.n_ind_); std::swap(n_dep_ , info.n_dep_); std::swap(n_op_ , info.n_op_); std::swap(n_var_ , info.n_var_); // // pod_vectors map_user_op_.swap( info.map_user_op_); in_subgraph_.swap( info.in_subgraph_); select_domain_.swap( info.select_domain_); process_range_.swap( info.process_range_); // return; } // ----------------------------------------------------------------------- /*! set sizes for this object (the default sizes are zero) \param n_ind number of independent variables. \param n_dep number of dependent variables. \param n_op number of operators. \param n_var number of variables. \par map_user_op_ is resized to zero. \par in_subgraph_ is resized to zero. */ void resize(size_t n_ind, size_t n_dep, size_t n_op, size_t n_var) { CPPAD_ASSERT_UNKNOWN( n_op <= size_t( std::numeric_limits::max() ) ); // n_ind_ n_ind_ = n_ind; // n_dep_ n_dep_ = n_dep; // n_op_ n_op_ = n_op; // n_var_ n_var_ = n_var; // // map_user_op_ map_user_op_.resize(0); // // in_subgraph_ in_subgraph_.resize(0); // return; } // ----------------------------------------------------------------------- /*! set the value of map_user_op for this operation sequence \param play is the player for this operation sequence. It must same number of operators and variables as this subgraph_info object. \par map_user_op_ This size of map_user_op_ must be zero when this function is called (which is true after a resize operation). This function sets its size to the number of operations in play. We use the term user OpCocde for the any one of the following: AFunOp, FunapOp, FunavOp, FunrpOp, or FunrvOp. Suppose \code OpCodce op_i = play->GetOp(i_op); size_t j_op = map_user_op[i_op]; op_code_var op_j = play->GetOP(j_op); \endcode If op is a user op_code_var, j_op is the index of the first operator in the corresponding atomic function call and op_j == AFunOp. Otherwise j_op == i_op; */ template void set_map_user_op(const player* play) { CPPAD_ASSERT_UNKNOWN( map_user_op_.size() == 0 ); // CPPAD_ASSERT_UNKNOWN( n_op_ == play->num_var_op() ); CPPAD_ASSERT_UNKNOWN( n_var_ == play->num_var() ); // // resize map_user_op_ map_user_op_.resize(n_op_); // // set map_user_op for each operator for(size_t i_op = 0; i_op < n_op_; ++i_op) { // this operator op_code_var op = play->GetOp(i_op); // // value of map_user_op when op is not in atomic function call) map_user_op_[i_op] = addr_t( i_op ); // if( op == AFunOp ) { // first AFunOp in an atomic function call sequence // // All operators in this atomic call sequence will be // mapped to the AFunOp that begins this call. addr_t begin = addr_t( i_op ); op = play->GetOp(++i_op); while( op != AFunOp ) { CPPAD_ASSERT_UNKNOWN( op==FunapOp || op==FunavOp || op==FunrpOp || op==FunrvOp ); // map this operator to the beginning of the call map_user_op_[i_op] = begin; op = play->GetOp(++i_op); } // map the second AFunOp to the beginning of the call map_user_op_[i_op] = begin; } } return; } // ----------------------------------------------------------------------- // see init_rev.hpp template void init_rev( const play::const_random_iterator& random_itr , const BoolVector& select_domain ); template void init_rev( player* play , const BoolVector& select_domain ); // ----------------------------------------------------------------------- // see get_rev.hpp template void get_rev( const play::const_random_iterator& random_itr , const pod_vector& dep_taddr , addr_t i_dep , pod_vector& subgraph ); }; } } } // END_CPPAD_LOCAL_SUBGRAPH_NAMESPACE // routines that operate on in_subgraph # include # include # endif ================================================ FILE: include/cppad/local/subgraph/init_rev.hpp ================================================ # ifndef CPPAD_LOCAL_SUBGRAPH_INIT_REV_HPP # define CPPAD_LOCAL_SUBGRAPH_INIT_REV_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include // BEGIN_CPPAD_LOCAL_SUBGRAPH_NAMESPACE namespace CppAD { namespace local { namespace subgraph { /*! \file init_rev.hpp initialize for a reverse mode subgraph calculation */ // ----------------------------------------------------------------------- /*! Initialize in_subgraph corresponding to a single dependent variable (and a selected set of independent variables). \tparam Addr is the type used for indices in the random iterator. \param random_itr Is a random iterator for this operation sequence. \param select_domain is a vector with, size equal to the number of independent variables in the recording. It determines the selected independent variables. \par in_subgraph_ We use depend_yes (depend_no) for the value n_dep_ (n_dep_ + 1). The important properties are that depend_yes < depend_no and for a valid independent variable index i_ind < depend_yes. The input size and elements of in_subgraph_ do not matter. If in_subgraph_[i_op] == depend_yes (depend_no), the result for this operator depends (does not depend) on the selected independent variables. Note that for atomic function call operators i_op, in_subgraph[i_op] is depend_no except for the first AFunOp in the atomic function call sequence. For the first AFunOp, it is depend_yes (depend_no) if any of the results for the call sequence depend (do not depend) on the selected independent variables. Except for UserOP, only operators with NumRes(op) > 0 have in_subgraph_ value depend_yes; e.g., comparison operators have in_subgraph_ value depend_no. \par select_domain_ This vector is is set equal to the select_domain argument. \par process_range_ This vector is to to size n_dep_ and its values are set to false */ template void subgraph_info::init_rev( const local::play::const_random_iterator& random_itr , const BoolVector& select_domain ) { // check sizes CPPAD_ASSERT_UNKNOWN( map_user_op_.size() == n_op_ ); CPPAD_ASSERT_UNKNOWN( random_itr.num_op() == n_op_ ); CPPAD_ASSERT_UNKNOWN( size_t( select_domain.size() ) == n_ind_ ); // depend_yes and depend_no addr_t depend_yes = addr_t( n_dep_ ); addr_t depend_no = addr_t( n_dep_ + 1 ); // select_domain_ select_domain_.resize(n_ind_); for(size_t j = 0; j < n_ind_; ++j) select_domain_[j] = select_domain[j]; // process_range_ process_range_.resize(n_dep_); for(size_t i = 0; i < n_dep_; ++i) process_range_[i] = false; // set in_subgraph to have proper size in_subgraph_.resize(n_op_); // space used to return set of arguments that are variables pod_vector argument_variable; // temporary space used by get_argument_variable pod_vector work; # ifndef NDEBUG size_t count_independent = 0; # endif bool begin_atomic_call = false; for(size_t i_op = 0; i_op < n_op_; ++i_op) { op_code_var op = random_itr.get_op(i_op); // // default value for this operator in_subgraph_[i_op] = depend_no; // switch(op) { case InvOp: CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 ); CPPAD_ASSERT_UNKNOWN( i_op > 0 ); { // get user index for this independent variable size_t j = i_op - 1; CPPAD_ASSERT_UNKNOWN( j < n_ind_ ); // // set in_subgraph_[i_op] if( select_domain[j] ) in_subgraph_[i_op] = depend_yes; } # ifndef NDEBUG ++count_independent; # endif break; // only mark both first AFunOp for each call as depending // on the selected independent variables case AFunOp: begin_atomic_call = ! begin_atomic_call; if( begin_atomic_call ) { get_argument_variable(random_itr, i_op, argument_variable, work); for(size_t j = 0; j < argument_variable.size(); ++j) { size_t j_var = argument_variable[j]; size_t j_op = random_itr.var2op(j_var); j_op = size_t( map_user_op_[j_op] ); CPPAD_ASSERT_UNKNOWN( j_op < i_op ); if( in_subgraph_[j_op] == depend_yes ) in_subgraph_[i_op] = depend_yes; } } break; // skip FunrvOp (gets mapped to first AFunOp in this call) case FunrvOp: CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 ); break; default: // Except for AFunOp, only include when NumRes(op) > 0. if( NumRes(op) > 0 ) { get_argument_variable(random_itr, i_op, argument_variable, work); for(size_t j = 0; j < argument_variable.size(); ++j) { size_t j_var = argument_variable[j]; size_t j_op = random_itr.var2op(j_var); j_op = size_t( map_user_op_[j_op] ); CPPAD_ASSERT_UNKNOWN( j_op < i_op ); if( in_subgraph_[j_op] == depend_yes ) in_subgraph_[i_op] = depend_yes; } } break; } } CPPAD_ASSERT_UNKNOWN( count_independent == size_t(select_domain.size()) ); // return; } // ----------------------------------------------------------------------- /*! Initialize in_subgraph corresponding to a single dependent variable (and a selected set of independent variables). \tparam Addr is the type used for indices in the random iterator. \tparam Base this recording was made using ADFun \param play is a player for this ADFun object. \param select_domain is a vector with, size equal to the number of independent variables in the recording. It determines the selected independent variables. \par in_subgraph_ We use depend_yes (depend_no) for the value n_dep_ (n_dep_ + 1). The important properties are that depend_yes < depend_no and for a valid independent variable index i_ind < depend_yes. The input size and elements of in_subgraph_ do not matter. If in_subgraph_[i_op] == depend_yes (depend_no), the result for this operator depends (does not depend) on the selected independent variables. Note that for atomic function call operators i_op, in_subgraph[i_op] is depend_no except for the first AFunOp in the atomic function call sequence. For the first AFunOp, it is depend_yes (depend_no) if any of the results for the call sequence depend (do not depend) on the selected independent variables. Except for UserOP, only operators with NumRes(op) > 0 have in_subgraph_ value depend_yes; e.g., comparison operators have in_subgraph_ value depend_no. \par select_domain_ This vector is is set equal to the select_domain argument. \par process_range_ This vector is to to size n_dep_ and its values are set to false */ template void subgraph_info::init_rev( player* play , const BoolVector& select_domain ) { // get random access iterator for this player Addr not_used; play->setup_random( not_used ); local::play::const_random_iterator random_itr = play->get_random( not_used ); // init_rev(random_itr, select_domain); // return; } } } } // END_CPPAD_LOCAL_SUBGRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/subgraph/sparsity.hpp ================================================ # ifndef CPPAD_LOCAL_SUBGRAPH_SPARSITY_HPP # define CPPAD_LOCAL_SUBGRAPH_SPARSITY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include // BEGIN_CPPAD_LOCAL_SUBGRAPH_NAMESPACE namespace CppAD { namespace local { namespace subgraph { /*! \file sparsity.hpp Compute dependency sparsity pattern using subgraph technique. */ // =========================================================================== /*! Compute dependency sparsity pattern for an ADFun function. \tparam Addr type used for indices in random iterator (must correspond to play->addr_type()) \tparam Base the operation sequence was recorded using AD. \tparam BoolVector a simple vector class with elements of type bool. \param play is the operation sequence corresponding to the ADFun function. It is effectively const except that play->setup_random() is called. \param sub_info is the subgraph information for this ADFun object. \param dep_taddr mapping from user dependent variable index to variable index in play (must have size sub_info.n_dep()). \param select_domain only the selected independent variables will be included in the sparsity pattern (must have size sub_info.n_ind()). \param select_range only the selected dependent variables will be included in the sparsity pattern (must have size sub_info.n_dep()). \param row_out The input size and elements of row_out do not matter. We use number of non-zeros (nnz) to denote the number of elements in row_out. For k = 0 , ... , nnz-1, row_out[k] is the row index of the k-th no-zero element of the dependency sparsitiy pattern for the function corresponding to the recording. \code 0 <= row_out[k] < dep_taddr.size() select_range[ row_out[k] ] == true \endcode \param col_out The input size and elements of col_out do not matter. Upon return is has the same size as row_out; i.e., nnz. For k = 0 , ... , nnz-1, col_out[k] is the column index of the k-th no-zero element of the dependency sparsitiy pattern for the function corresponding to the recording. \code 0 <= col_out[k] < sub_info.n_ind() select_domain[ col_out[k] ] == true \endcode \par AFunOp All of the inputs and outputs for an atomic function call are considered to be connected. 2DO: It would be good to use the sparsity patters for atomic function calls to to make the sparsity pattern more efficient. */ template void subgraph_sparsity( player* play , subgraph_info& sub_info , const pod_vector& dep_taddr , const BoolVector& select_domain , const BoolVector& select_range , pod_vector& row_out , pod_vector& col_out ) { // get random access iterator for this player Addr not_used; play->setup_random( not_used ); local::play::const_random_iterator random_itr = play->get_random( not_used ); // check dimension assumptions CPPAD_ASSERT_UNKNOWN( dep_taddr.size() == sub_info.n_dep() ); CPPAD_ASSERT_UNKNOWN( size_t(select_domain.size()) == sub_info.n_ind() ); CPPAD_ASSERT_UNKNOWN( size_t(select_range.size()) == sub_info.n_dep() ); // number of dependent variables size_t n_dep = dep_taddr.size(); CPPAD_ASSERT_UNKNOWN( size_t(select_range.size()) == n_dep ); // start with an empty sparsity pattern row_out.resize(0); col_out.resize(0); // map_user_op if( sub_info.map_user_op().size() == 0 ) sub_info.set_map_user_op(play); else { CPPAD_ASSERT_UNKNOWN( sub_info.check_map_user_op(play) ); } CPPAD_ASSERT_UNKNOWN( sub_info.map_user_op().size() == play->num_var_op() ); // subgraph of operators that are are connected to one of the selected // dependent variables and depend on the selected independent variables pod_vector subgraph; // initialize a reverse mode subgraph calculation sub_info.init_rev(random_itr, select_domain); CPPAD_ASSERT_UNKNOWN( sub_info.in_subgraph().size() == play->num_var_op() ); // # ifndef NDEBUG addr_t depend_yes = addr_t( n_dep ); # endif // for each of the selected dependent variables # ifndef NDEBUG addr_t depend_no = addr_t( n_dep + 1 ); # endif CPPAD_ASSERT_UNKNOWN( depend_yes < depend_no ); CPPAD_ASSERT_UNKNOWN( NumRes(BeginOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(InvOp) == 1 ); for(size_t i_dep = 0; i_dep < n_dep; ++i_dep) if( select_range[i_dep] ) { CPPAD_ASSERT_UNKNOWN( i_dep < size_t( depend_yes ) ); // // subgraph of operators connected to i_dep sub_info.get_rev( random_itr, dep_taddr, addr_t(i_dep), subgraph ); // for(size_t k = 0; k < subgraph.size(); k++) { size_t i_op = size_t( subgraph[k] ); // // operator corresponding to this index op_code_var op = play->GetOp(i_op); // // This version of the subgraph only has first AFunOp // for each atomic functionc all. CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 || op == AFunOp ); // // independent variable entries correspond to sparsity pattern if( op == InvOp ) { CPPAD_ASSERT_NARG_NRES(op, 0, 1); // i_var is equal i_op because BeginOp and InvOp have 1 result size_t i_var = i_op; // tape index for this variable size_t i_ind = i_var - 1; // user index for this variable CPPAD_ASSERT_UNKNOWN( random_itr.var2op(i_var) == i_op ); CPPAD_ASSERT_UNKNOWN( select_domain[i_ind] ); // // put this pair in the sparsity pattern row_out.push_back(i_dep); col_out.push_back(i_ind); } } } } } } } // END_CPPAD_LOCAL_SUBGRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/sweep/call_atomic.hpp ================================================ # ifndef CPPAD_LOCAL_SWEEP_CALL_ATOMIC_HPP # define CPPAD_LOCAL_SWEEP_CALL_ATOMIC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include // BEGIN_CPAPD_LOCAL_SWEEP_NAMESPACE namespace CppAD { namespace local { namespace sweep { /* // ---------------------------------------------------------------------------- {xrst_begin_parent call_atomic dev} Atomic Function Callbacks ######################### {xrst_end call_atomic} // ---------------------------------------------------------------------------- {xrst_begin atomic_forward_callback dev} Forward Mode Callback to Atomic Functions ######################################### Prototype ********* {xrst_literal // BEGIN_FORWARD // END_FORWARD } Base **** Is the base type corresponding to the atomic function call. RecBase ******* Is the base type corresponding to this atomic function call. vector ****** is the CppAD::vector template class. parameter_x *********** contains the values, in afun(ax, ay), for arguments that are parameters. type_x ****** what is the type, in the call, for each component of x. need_y ****** specifies which components of taylor_y are necessary. Only the components of y with the type equal to need_y need to be computed. If need_y is greater than variable_enum, then all the components of y must be computed. select_y ******** specifies which components of taylor_y are necessary. order_low ********* lowest order for this forward mode calculation. order_up ******** highest order for this forward mode calculation. atom_index ********** is the index, in local::atomic_index, corresponding to this atomic function. call_id ******* see the atomic_four :ref:`atomic_four_call@call_id` and the atomic_one :ref:`atomic_one@id` . taylor_x ******** Taylor coefficients corresponding to x. taylor_y ******** Taylor coefficient corresponding to y. {xrst_end atomic_forward_callback} */ // BEGIN_FORWARD template void call_atomic_forward( const vector& parameter_x , const vector& type_x , size_t need_y , const vector& select_y , size_t order_low , size_t order_up , size_t atom_index , size_t call_id , const vector& taylor_x , vector& taylor_y ) // END_FORWARD { CPPAD_ASSERT_UNKNOWN( 0 < atom_index ); bool set_null = false; size_t type = 0; // set to avoid warning std::string* name_ptr = nullptr; void* v_ptr = nullptr; // set to avoid warning local::atomic_index(set_null, atom_index, type, name_ptr, v_ptr); # ifndef NDEBUG bool ok = v_ptr != nullptr; if( ok ) { if( type == 2 ) { atomic_base* afun = reinterpret_cast< atomic_base* >(v_ptr); afun->set_old(call_id); vector empty; ok = afun->forward( order_low, order_up, empty, empty, taylor_x, taylor_y ); } else if( type == 3 ) { CPPAD_ASSERT_UNKNOWN( type == 3 ); atomic_three* afun = reinterpret_cast< atomic_three* >(v_ptr); ok = afun->forward( parameter_x, type_x, need_y, order_low, order_up, taylor_x, taylor_y ); } else { CPPAD_ASSERT_UNKNOWN( type == 4 ); atomic_four* afun = reinterpret_cast< atomic_four* >(v_ptr); ok = afun->forward( call_id, select_y, order_low, order_up, taylor_x, taylor_y ); } } if( ! ok ) { // now take the extra time to copy the name std::string name; local::atomic_index(set_null, atom_index, type, &name, v_ptr); std::string msg = name; if( v_ptr == nullptr ) msg += ": this atomic function has been deleted"; else msg += ": atomic forward returned false"; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # else if( type == 2 ) { atomic_base* afun = reinterpret_cast< atomic_base* >(v_ptr); vector empty; afun->set_old(call_id); afun->forward( order_low, order_up, empty, empty, taylor_x, taylor_y ); } else if( type == 3 ) { atomic_three* afun = reinterpret_cast< atomic_three* >(v_ptr); afun->forward( parameter_x, type_x, need_y, order_low, order_up, taylor_x, taylor_y ); } else { atomic_four* afun = reinterpret_cast< atomic_four* >(v_ptr); afun->forward( call_id, select_y, order_low, order_up, taylor_x, taylor_y ); } # endif } // ---------------------------------------------------------------------------- /* {xrst_begin atomic_reverse_callback dev} Reverse Mode callback to Atomic Functions ######################################### Prototype ********* {xrst_literal // BEGIN_REVERSE // END_REVERSE } Base **** Is the base type corresponding to the atomic function call. RecBase ******* Is the base type corresponding to this atomic function call. vector ****** is the CppAD::vector template class. parameter_x *********** value of the parameter arguments to the atomic function (other arguments have the value nan). type_x ****** type for each component of x (not used by atomic_two interface). select_x ******** specifies which components of partial_x are necessary. order_up ******** highest order for this reverse mode calculation. atom_index ********** is the index, in local::atomic_index, corresponding to this atomic function. call_id ******* see the atomic_four :ref:`atomic_four_call@call_id` and the atomic_one :ref:`atomic_one@id` . taylor_x ******** Taylor coefficients corresponding to x. taylor_y ******** Taylor coefficient corresponding to y. partial_x ********* Partials w.r.t the x Taylor coefficients. partial_y ********* Partials w.r.t the y Taylor coefficients. {xrst_end atomic_reverse_callback} */ // BEGIN_REVERSE template void call_atomic_reverse( const vector& parameter_x , const vector& type_x , const vector& select_x , size_t order_up , size_t atom_index , size_t call_id , const vector& taylor_x , const vector& taylor_y , vector& partial_x , const vector& partial_y ) // END_REVERSE { CPPAD_ASSERT_UNKNOWN( 0 < atom_index ); bool set_null = false; size_t type = 0; // set to avoid warning std::string* name_ptr = nullptr; void* v_ptr = nullptr; // set to avoid warning local::atomic_index(set_null, atom_index, type, name_ptr, v_ptr); # ifndef NDEBUG bool ok = v_ptr != nullptr; if( ok ) { if( type == 2 ) { atomic_base* afun = reinterpret_cast< atomic_base* >(v_ptr); afun->set_old(call_id); ok = afun->reverse( order_up, taylor_x, taylor_y, partial_x, partial_y ); } else if( type == 3 ) { atomic_three* afun = reinterpret_cast< atomic_three* >(v_ptr); ok = afun->reverse( parameter_x, type_x, order_up, taylor_x, taylor_y, partial_x, partial_y ); } else { CPPAD_ASSERT_UNKNOWN( type == 4 ); atomic_four* afun = reinterpret_cast< atomic_four* >(v_ptr); ok = afun->reverse( call_id, select_x, order_up, taylor_x, taylor_y, partial_x, partial_y ); } } if( ! ok ) { // now take the extra time to copy the name std::string name; local::atomic_index(set_null, atom_index, type, &name, v_ptr); std::string msg = name; if( v_ptr == nullptr ) msg += ": this atomic function has been deleted"; else msg += ": atomic reverse returned false"; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # else if( type == 2 ) { atomic_base* afun = reinterpret_cast< atomic_base* >(v_ptr); afun->set_old(call_id); afun->reverse( order_up, taylor_x, taylor_y, partial_x, partial_y ); } else if( type == 3 ) { atomic_three* afun = reinterpret_cast< atomic_three* >(v_ptr); afun->reverse( parameter_x, type_x, order_up, taylor_x, taylor_y, partial_x, partial_y ); } else { atomic_four* afun = reinterpret_cast< atomic_four* >(v_ptr); afun->reverse( call_id, select_x, order_up, taylor_x, taylor_y, partial_x, partial_y ); } # endif } // ---------------------------------------------------------------------------- /* {xrst_begin atomic_for_jac_sparsity_callback dev} {xrst_spell setvec } Forward Jacobian Sparsity Callback to Atomic Functions ###################################################### Prototype ********* {xrst_literal // BEGIN_FOR_JAC_SPARSITY // END_FOR_JAC_SPARSITY } Base **** Is the base type corresponding to the atomic function call. vector ****** is the CppAD::vector template class. InternalSparsity **************** is the internal type used to represent sparsity; i.e., sparse::pack_setvec or sparse::list_setvec. atom_index ********** is the index, in local::atomic_index, corresponding to this atomic function. call_id ******* see the atomic_four :ref:`atomic_four_call@call_id` and the atomic_one :ref:`atomic_one@id` . dependency ********** is this a dependency or sparsity calculation. parameter_x *********** value of the parameter arguments to the atomic function (other arguments have the value nan). type_x ****** type for each component of x (not used by atomic_two interface). x_index ******* is a mapping from the index of an atomic function argument to the corresponding variable on the tape. y_index ******* is a mapping from the index of an atomic function result to the corresponding variable on the tape. var_sparsity ************ On input, for j = 0, ... , n-1, the sparsity pattern with index x_index[j], is the sparsity for the j-th argument to this atomic function. On output, for i = 0, ... , m-1, the sparsity pattern with index y_index[i], is the sparsity for the i-th result for this atomic function. {xrst_end atomic_for_jac_sparsity_callback} */ // BEGIN_FOR_JAC_SPARSITY template void call_atomic_for_jac_sparsity( size_t atom_index , size_t call_id , bool dependency , const vector& parameter_x , const vector& type_x , const vector& x_index , const vector& y_index , InternalSparsity& var_sparsity ) // END_FOR_JAC_SPARSITY { CPPAD_ASSERT_UNKNOWN( 0 < atom_index ); bool set_null = false; size_t type = 0; // set to avoid warning std::string* name_ptr = nullptr; void* v_ptr = nullptr; // set to avoid warning local::atomic_index(set_null, atom_index, type, name_ptr, v_ptr); // // ident_zero_x vector ident_zero_x; if( type == 4 ) { size_t n = x_index.size(); ident_zero_x.resize(n); for(size_t j = 0; j < n; ++j) { if( type_x[j] > constant_enum ) ident_zero_x[j] = false; else ident_zero_x[j] = IdenticalZero( parameter_x[j] ); } } # ifndef NDEBUG bool ok = v_ptr != nullptr; if ( ok ) { if( type == 2 ) { atomic_base* afun = reinterpret_cast< atomic_base* >(v_ptr); afun->set_old(call_id); ok = afun->for_sparse_jac( parameter_x, x_index, y_index, var_sparsity ); } else if( type == 3 ) { atomic_three* afun = reinterpret_cast< atomic_three* >(v_ptr); ok = afun->for_jac_sparsity( dependency, parameter_x, type_x, x_index, y_index, var_sparsity ); } else { CPPAD_ASSERT_UNKNOWN( type == 4 ); atomic_four* afun = reinterpret_cast< atomic_four* >(v_ptr); ok = afun->for_jac_sparsity( call_id, dependency, ident_zero_x, x_index, y_index, var_sparsity ); } } if( ! ok ) { // now take the extra time to copy the name std::string name; local::atomic_index( set_null, atom_index, type, &name, v_ptr ); std::string msg = name; if( v_ptr == nullptr ) msg += ": this atomic function has been deleted"; else msg += ": atomic jac_sparsity returned false"; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # else if( type == 2 ) { atomic_base* afun = reinterpret_cast< atomic_base* >(v_ptr); afun->set_old(call_id); afun->for_sparse_jac( parameter_x, x_index, y_index, var_sparsity ); } else if( type == 3 ) { atomic_three* afun = reinterpret_cast< atomic_three* >(v_ptr); afun->for_jac_sparsity( dependency, parameter_x, type_x, x_index, y_index, var_sparsity ); } else { atomic_four* afun = reinterpret_cast< atomic_four* >(v_ptr); afun->for_jac_sparsity( call_id, dependency, ident_zero_x, x_index, y_index, var_sparsity ); } # endif } // ---------------------------------------------------------------------------- /* {xrst_begin atomic_rev_jac_sparsity_callback dev} {xrst_spell setvec } Reverse Jacobian sparsity Callback to Atomic Functions ###################################################### Prototype ********* {xrst_literal // BEGIN_REV_JAC_SPARSITY // END_REV_JAC_SPARSITY } Base **** is the type corresponding to parameter_x and to this atomic function. InternalSparsity **************** is the internal type used to represent sparsity; i.e., sparse::pack_setvec or sparse::list_setvec. atom_index ********** is the index, in local::atomic_index, corresponding to this atomic function. call_id ******* see the atomic_four :ref:`atomic_four_call@call_id` and the atomic_one :ref:`atomic_one@id` . dependency ********** is this a dependency or sparsity calculation. parameter_x *********** value of the parameter arguments to the atomic function (other arguments have the value nan). type_x ****** type for each component of x (not used by atomic_two interface). x_index ******* is a mapping from the index of an atomic function argument to the corresponding variable on the tape. y_index ******* is a mapping from the index of an atomic function result to the corresponding variable on the tape. var_sparsity [in/out] ********************* On input, for i = 0, ... , m-1, the sparsity pattern with index y_index[i], is the sparsity for the i-th argument to this atomic function. On output, for j = 0, ... , n-1, the sparsity pattern with index x_index[j], the sparsity has been updated to remove y as a function of x. {xrst_end atomic_rev_jac_sparsity_callback} */ // BEGIN_REV_JAC_SPARSITY template void call_atomic_rev_jac_sparsity( size_t atom_index , size_t call_id , bool dependency , const vector& parameter_x , const vector& type_x , const vector& x_index , const vector& y_index , InternalSparsity& var_sparsity ) // END_REV_JAC_SPARSITY { CPPAD_ASSERT_UNKNOWN( 0 < atom_index ); bool set_null = false; size_t type = 0; // set to avoid warning std::string* name_ptr = nullptr; void* v_ptr = nullptr; // set to avoid warning local::atomic_index(set_null, atom_index, type, name_ptr, v_ptr); // // ident_zero_x vector ident_zero_x; if( type == 4 ) { size_t n = x_index.size(); ident_zero_x.resize(n); for(size_t j = 0; j < n; ++j) { if( type_x[j] > constant_enum ) ident_zero_x[j] = false; else ident_zero_x[j] = IdenticalZero( parameter_x[j] ); } } # ifndef NDEBUG bool ok = v_ptr != nullptr; if( ok ) { if( type == 2 ) { atomic_base* afun = reinterpret_cast< atomic_base* >(v_ptr); afun->set_old(call_id); ok = afun->rev_sparse_jac( parameter_x, x_index, y_index, var_sparsity ); } else if( type == 3 ) { atomic_three* afun = reinterpret_cast< atomic_three* >(v_ptr); ok = afun->rev_jac_sparsity( dependency, parameter_x, type_x, x_index, y_index, var_sparsity ); } else { CPPAD_ASSERT_UNKNOWN( type == 4 ); atomic_four* afun = reinterpret_cast< atomic_four* >(v_ptr); ok = afun->rev_jac_sparsity(call_id, dependency, ident_zero_x, x_index, y_index, var_sparsity ); } } if( ! ok ) { // now take the extra time to copy the name std::string name; local::atomic_index( set_null, atom_index, type, &name, v_ptr ); std::string msg = name; if( v_ptr == nullptr ) msg += ": this atomic function has been deleted"; else msg += ": atomic jac_sparsity returned false"; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # else if( type == 2 ) { atomic_base* afun = reinterpret_cast< atomic_base* >(v_ptr); afun->set_old(call_id); afun->rev_sparse_jac( parameter_x, x_index, y_index, var_sparsity ); } else if( type == 3 ) { atomic_three* afun = reinterpret_cast< atomic_three* >(v_ptr); afun->rev_jac_sparsity( dependency, parameter_x, type_x, x_index, y_index, var_sparsity ); } else { atomic_four* afun = reinterpret_cast< atomic_four* >(v_ptr); afun->rev_jac_sparsity(call_id, dependency, ident_zero_x, x_index, y_index, var_sparsity ); } # endif } // ---------------------------------------------------------------------------- /* {xrst_begin atomic_for_hes_sparsity_callback dev} {xrst_spell setvec } Forward Hessian Sparsity Callback to Atomic Functions ##################################################### Prototype ********* {xrst_literal // BEGIN_FOR_HES_SPARSITY // END_FOR_HES_SPARSITY } Base **** is the type corresponding to *parameter_x* and to this atomic function. InternalSparsity **************** is the internal type used to represent sparsity; i.e., ``sparse::pack_setvec`` or ``sparse::list_setvec`` . atom_index ********** is the index, in local::atomic_index, corresponding to this atomic function. call_id ******* see the atomic_four :ref:`atomic_four_call@call_id` and the atomic_one :ref:`atomic_one@id` . parameter_x *********** value of the parameter arguments to the atomic function (other arguments have the value nan). type_x ****** type for each component of x (not used by atomic_two interface). x_index ******* is a mapping from the index of an atomic function argument to the corresponding variable on the tape. We use *m_x* to denote the maximum value w.r.t *i* of *x_index* [ *i* ] . y_index ******* is a mapping from the index of an atomic function result to the corresponding variable on the tape. It should hold that *m_i* < ``y_index`` [ ``i`` ] for all *i* . np1 *** This is the number of independent variables plus one; i.e. size of *x* plus one. num_var ******* This is the total number of variables in the tape. rev_jac_sparsity **************** For i = 0, ... , m-1, the sparsity pattern with index y_index[i], is the reverse Jacobian sparsity for the i-th result to this atomic function. This shows which components of the result affect the function we are computing the Hessian of. for_sparsity ************ We have the conditions *np1* = *for_sparsity* . ``end`` () and *for_sparsity* . ``n_set`` () = *np1* + *num_var* . Input Jacobian Sparsity ======================= For *i* = 0, ..., *m_x* , the *np1* + *i* row of *for_sparsity* is the Jacobian sparsity for the *i*-th variable. These values do not change. Note that *i* =0 corresponds to a parameter and the corresponding Jacobian sparsity is empty. Input Hessian Sparsity ====================== For *i* =1, ..., *n* , the *i*-th row of *for_sparsity* is the Hessian sparsity before including the function :math:`y = f(x)`. Output Jacobian Sparsity ======================== For *i* =0, ..., *y_index* . ``size`` () , row *np1* + *y_index* [ *i* ] of *for_sparsity* is the Jacobian sparsity for the variable with index *y_index* [ *i* ] . Output Hessian Sparsity ======================= For *i* =1, ..., *n* , the *i*-th row of *for_sparsity* is the Hessian sparsity after including the function :math:`y = f(x)`. {xrst_end atomic_for_hes_sparsity_callback} */ // BEGIN_FOR_HES_SPARSITY template void call_atomic_for_hes_sparsity( size_t atom_index , size_t call_id , const vector& parameter_x , const vector& type_x , const vector& x_index , const vector& y_index , size_t np1 , size_t num_var , const InternalSparsity& rev_jac_sparsity , InternalSparsity& for_sparsity ) // END_FOR_HES_SPARSITY { CPPAD_ASSERT_UNKNOWN( 0 < atom_index ); CPPAD_ASSERT_UNKNOWN( for_sparsity.end() == np1 ); CPPAD_ASSERT_UNKNOWN( for_sparsity.n_set() == np1 + num_var ); bool set_null = false; size_t type = 0; // set to avoid warning std::string* name_ptr = nullptr; void* v_ptr = nullptr; // set to avoid warning local::atomic_index(set_null, atom_index, type, name_ptr, v_ptr); // // ident_zero_x vector ident_zero_x; if( type == 4 ) { size_t n = x_index.size(); ident_zero_x.resize(n); for(size_t j = 0; j < n; ++j) { if( type_x[j] > constant_enum ) ident_zero_x[j] = false; else ident_zero_x[j] = IdenticalZero( parameter_x[j] ); } } # ifndef NDEBUG bool ok = v_ptr != nullptr; if( ok ) { if( type == 2 ) { atomic_base* afun = reinterpret_cast< atomic_base* >(v_ptr); afun->set_old(call_id); ok = afun->for_sparse_hes( parameter_x, x_index, y_index, np1, num_var, rev_jac_sparsity, for_sparsity ); } else if( type == 3 ) { atomic_three* afun = reinterpret_cast< atomic_three* >(v_ptr); ok = afun->for_hes_sparsity( parameter_x, type_x, x_index, y_index, np1, num_var, rev_jac_sparsity, for_sparsity ); } else { CPPAD_ASSERT_UNKNOWN( type == 4 ); atomic_four* afun = reinterpret_cast< atomic_four* >(v_ptr); ok = afun->for_hes_sparsity( call_id, ident_zero_x, x_index, y_index, np1, num_var, rev_jac_sparsity, for_sparsity ); } } if( ! ok ) { // now take the extra time to copy the name std::string name; local::atomic_index( set_null, atom_index, type, &name, v_ptr ); std::string msg = name; if( v_ptr == nullptr ) msg += ": this atomic function has been deleted"; else msg += ": atomic hes_sparsity returned false"; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # else if( type == 2 ) { atomic_base* afun = reinterpret_cast< atomic_base* >(v_ptr); afun->set_old(call_id); afun->for_sparse_hes( parameter_x, x_index, y_index, np1, num_var, rev_jac_sparsity, for_sparsity ); } else if( type == 3 ) { atomic_three* afun = reinterpret_cast< atomic_three* >(v_ptr); afun->for_hes_sparsity( parameter_x, type_x, x_index, y_index, np1, num_var, rev_jac_sparsity, for_sparsity ); } else { atomic_four* afun = reinterpret_cast< atomic_four* >(v_ptr); afun->for_hes_sparsity( call_id, ident_zero_x, x_index, y_index, np1, num_var, rev_jac_sparsity, for_sparsity ); } # endif } // ---------------------------------------------------------------------------- /* {xrst_begin atomic_rev_hes_sparsity_callback dev} {xrst_spell setvec } Reverse Hessian Sparsity Callback to Atomic Functions ##################################################### Prototype ********* {xrst_literal // BEGIN_REV_HES_SPARSITY // END_REV_HES_SPARSITY } Base **** is the type corresponding to parameter_x and to this atomic function. InternalSparsity **************** is the internal type used to represent sparsity; i.e., sparse::pack_setvec or sparse::list_setvec. call_id ******* see the atomic_four :ref:`atomic_four_call@call_id` and the atomic_one :ref:`atomic_one@id` . parameter_x *********** value of the parameter arguments to the atomic function (other arguments have the value nan). type_x ****** type for each component of x (not used by atomic_two interface). x_index ******* is a mapping from the index of an atomic function argument to the corresponding variable on the tape. y_index ******* is a mapping from the index of an atomic function result to the corresponding variable on the tape. for_jac_sparsity **************** For j = 0, ... , n-1, the sparsity pattern with index x_index[j], is the forward Jacobian sparsity for the j-th argument to this atomic function. rev_jac_flag ************ On input, for i = 0, ... , m-1, rev_jac_flag[ y_index[i] ] is true if the function (we are computing the sparsity for) depends on the variable y_index[i]. Upon return, for j = 0, ..., n-1, rev_jac_flag[ x_index[j] ] has been set to true any of the y_index variables are flagged depend on x_index[j]. Otherwise, rev_jac_flag[ x_index[j] ] is not modified. rev_hes_sparsity **************** This is the sparsity pattern for the Hessian. On input, for i = 0, ... , m-1, row y_index[i] is the reverse Hessian sparsity with one of the partials with respect to y_index[i]. Upon return, for j = 0, ..., n-1, the row x_index[j] has been modified to include components that have a non-zero hessian through the atomic function with one of the partials w.r.t. x_index[j]. {xrst_end atomic_rev_hes_sparsity_callback} */ // BEGIN_REV_HES_SPARSITY template void call_atomic_rev_hes_sparsity( size_t atom_index , size_t call_id , const vector& parameter_x , const vector& type_x , const vector& x_index , const vector& y_index , const InternalSparsity& for_jac_sparsity , bool* rev_jac_flag , InternalSparsity& rev_hes_sparsity ) // END_REV_HES_SPARSITY { CPPAD_ASSERT_UNKNOWN( 0 < atom_index ); bool set_null = false; size_t type = 0; // set to avoid warning std::string* name_ptr = nullptr; void* v_ptr = nullptr; // set to avoid warning local::atomic_index(set_null, atom_index, type, name_ptr, v_ptr); // // ident_zero_x vector ident_zero_x; if( type == 4 ) { size_t n = x_index.size(); ident_zero_x.resize(n); for(size_t j = 0; j < n; ++j) { if( type_x[j] > constant_enum ) ident_zero_x[j] = false; else ident_zero_x[j] = IdenticalZero( parameter_x[j] ); } } # ifndef NDEBUG bool ok = v_ptr != nullptr; if( ok ) { if( type == 2 ) { atomic_base* afun = reinterpret_cast< atomic_base* >(v_ptr); afun->set_old(call_id); ok = afun->rev_sparse_hes( parameter_x, x_index, y_index, for_jac_sparsity, rev_jac_flag, rev_hes_sparsity ); } else if( type == 3 ) { atomic_three* afun = reinterpret_cast< atomic_three* >(v_ptr); ok = afun->rev_hes_sparsity( parameter_x, type_x, x_index, y_index, for_jac_sparsity, rev_jac_flag, rev_hes_sparsity ); } else { CPPAD_ASSERT_UNKNOWN( type == 4 ); atomic_four* afun = reinterpret_cast< atomic_four* >(v_ptr); ok = afun->rev_hes_sparsity( call_id, ident_zero_x, x_index, y_index, for_jac_sparsity, rev_jac_flag, rev_hes_sparsity ); } } if( ! ok ) { // now take the extra time to copy the name std::string name; local::atomic_index( set_null, atom_index, type, &name, v_ptr ); std::string msg = name; if( v_ptr == nullptr ) msg += ": this atomic function has been deleted"; else msg += ": atomic hes_sparsity returned false"; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # else if( type == 2 ) { atomic_base* afun = reinterpret_cast< atomic_base* >(v_ptr); afun->set_old(call_id); afun->rev_sparse_hes( parameter_x, x_index, y_index, for_jac_sparsity, rev_jac_flag, rev_hes_sparsity ); } else if( type == 3 ) { atomic_three* afun = reinterpret_cast< atomic_three* >(v_ptr); afun->rev_hes_sparsity( parameter_x, type_x, x_index, y_index, for_jac_sparsity, rev_jac_flag, rev_hes_sparsity ); } else { atomic_four* afun = reinterpret_cast< atomic_four* >(v_ptr); afun->rev_hes_sparsity( call_id, ident_zero_x, x_index, y_index, for_jac_sparsity, rev_jac_flag, rev_hes_sparsity ); } # endif } // ---------------------------------------------------------------------------- /* {xrst_begin atomic_rev_depend_callback dev} Reverse Dependency Callback to Atomic Functions ############################################### Prototype ********* {xrst_literal // BEGIN_REV_DEPEND // END_REV_DEPEND } atom_index ********** is the index, in local::atomic_index, corresponding to this atomic function. call_id ******* see the atomic_four :ref:`atomic_four_call@call_id` and the atomic_one :ref:`atomic_one@id` . parameter_x *********** is the value of the parameters in the corresponding atomic function call. type_x ****** is the type for each x component in the corresponding atomic function call. depend_x ******** which components of x affect values we are interested in. This is the only output for this routine. depend_y ******** which components of y affect values we are interested in. {xrst_end atomic_rev_depend_callback} */ // BEGIN_REV_DEPEND template void call_atomic_rev_depend( size_t atom_index , size_t call_id , const vector& parameter_x , const vector& type_x , vector& depend_x , const vector& depend_y ) // END_REV_DEPEND { CPPAD_ASSERT_UNKNOWN( 0 < atom_index ); bool set_null = false; size_t type = 0; // set to avoid warning std::string* name_ptr = nullptr; void* v_ptr = nullptr; // set to avoid warning local::atomic_index(set_null, atom_index, type, name_ptr, v_ptr); // // ident_zero_x vector ident_zero_x; if( type == 4 ) { size_t n = parameter_x.size(); ident_zero_x.resize(n); for(size_t j = 0; j < n; ++j) { if( type_x[j] > constant_enum ) ident_zero_x[j] = false; else ident_zero_x[j] = IdenticalZero( parameter_x[j] ); } } # ifndef NDEBUG bool ok = v_ptr != nullptr; if( ok ) { if( type == 2 ) { atomic_base* afun = reinterpret_cast< atomic_base* >(v_ptr); afun->set_old(call_id); vector empty; ok = afun->rev_depend(parameter_x, type_x, depend_x, depend_y); } else if( type == 3 ) { atomic_three* afun = reinterpret_cast< atomic_three* >(v_ptr); ok = afun->rev_depend(parameter_x, type_x, depend_x, depend_y); } else { CPPAD_ASSERT_UNKNOWN( type == 4 ); atomic_four* afun = reinterpret_cast< atomic_four* >(v_ptr); // rev_depend does not have a wrapper that drops back to // deprecated version of this function. ok = afun->rev_depend(call_id, ident_zero_x, depend_x, depend_y); if( ! ok ) ok = afun->rev_depend(call_id, depend_x, depend_y); } } if( ! ok ) { // now take the extra time to copy the name std::string name; local::atomic_index(set_null, atom_index, type, &name, v_ptr); std::string msg = name; if( v_ptr == nullptr ) msg += ": this atomic function has been deleted"; else msg += ": atomic rev_depend returned false"; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # else if( type == 2 ) { atomic_base* afun = reinterpret_cast< atomic_base* >(v_ptr); vector empty; afun->set_old(call_id); afun->rev_depend(parameter_x, type_x, depend_x, depend_y); } else if( type == 3 ) { atomic_three* afun = reinterpret_cast< atomic_three* >(v_ptr); afun->rev_depend(parameter_x, type_x, depend_x, depend_y); } else { atomic_four* afun = reinterpret_cast< atomic_four* >(v_ptr); // rev_depend does not have a wrapper that drops back to // deprecated version of this function. bool ok = afun->rev_depend(call_id, ident_zero_x, depend_x, depend_y); if( ! ok ) afun->rev_depend(call_id, depend_x, depend_y); } # endif } } } } // END_CPAPD_LOCAL_SWEEP_NAMESPACE # endif ================================================ FILE: include/cppad/local/sweep/dev_sweep.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin dev_sweep dev} Developer Sweep Documentation ############################# Contents ******** {xrst_toc_table include/cppad/local/sweep/forward_0.hpp include/cppad/local/sweep/forward_any.hpp include/cppad/local/sweep/forward_dir.hpp include/cppad/local/sweep/for_hes.hpp include/cppad/local/sweep/rev_jac.hpp include/cppad/local/sweep/call_atomic.hpp } {xrst_end dev_sweep} ================================================ FILE: include/cppad/local/sweep/dynamic.hpp ================================================ # ifndef CPPAD_LOCAL_SWEEP_DYNAMIC_HPP # define CPPAD_LOCAL_SWEEP_DYNAMIC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include // BEGIN_CPPAD_LOCAL_SWEEP_NAMESPACE namespace CppAD { namespace local { namespace sweep { /*! \file sweep/dynamic.hpp Under Construction */ /*! \def CPPAD_DYNAMIC_TRACE This value is either zero or one. Zero is the normal operational value. If it is one, a trace for each dynamic parameter is computation is printed. Sometimes it is useful to trace f.new_dynamic with the same dynamic parameter values as during the recording (to debug the recording process). */ # define CPPAD_DYNAMIC_TRACE 0 /*! Compute dynamic parameters. \tparam Base The type of the parameters. \tparam BaseVector is a simple vector class with elements of type Base. \param ind_dynamic new value for the independent dynamic parameter vector. \param par_all is the vector of all the parameters. The constant parameters are inputs and the dynamic parameters are outputs. \param par_is_dyn is a vector with the same length as par_vec. The i-th parameter is dynamic if and only if par_is_dyn[i] is true. \param dyn2par_index is a vector with length equal to the number of dynamic parameters. The element dyn2par_index[j] is the index in par_all corresponding to the j-th dynamic parameter. Note that if par_is_dyn[i] is false, the i-th parameter does not appear in this vector. \param dyn_par_op is a vector with length equal to the number of dynamic parameters. The element dyn_par_op_[j] is the operator for the j-th dynamic parameter. Note that if par_is_dyn[i] is false, the i-th parameter does not have a parameter in this list. \param dyn_par_arg is a vector containing the arguments for the dynamic parameters. The first argument for the j-th dynamic parameter is dyn_par_arg[k] where \code k = NumArg( dyn_par_op[0] ) + ... + NumArg( dyn_par_op[j-1] ) \endcode The arguments for each dynamic parameter have index value lower than the index value for the parameter. \param not_used_rec_base Specifies RecBase for this call. */ template RecBase discrete_eval(size_t index, const RecBase& x, const RecBase& not_used) { return discrete::eval(index, x); } template AD discrete_eval( size_t index, const AD& ax, const RecBase& not_used ) { return discrete::ad_eval(index, ax); } template void dynamic( pod_vector_maybe& par_all , const BaseVector& ind_dynamic , const pod_vector& par_is_dyn , const pod_vector& dyn2par_index , const pod_vector& dyn_par_op , const pod_vector& dyn_par_arg , const RecBase& not_used_rec_base ) { // number of dynamic parameters size_t num_dynamic_par = dyn2par_index.size(); // vectors used in call to atomic functions vector type_x; vector taylor_x, taylor_y; vector select_y; # ifndef NDEBUG for(size_t j = 0; j < ind_dynamic.size(); ++j) CPPAD_ASSERT_UNKNOWN( par_is_dyn[j+1] && op_code_dyn( dyn_par_op[j] ) == ind_dyn ); # endif # if CPPAD_DYNAMIC_TRACE const char* cond_exp_name[] = { "CondExpLt", "CondExpLe", "CondExpEq", "CondExpGe", "CondExpGt", "CondExpNe" }; std::cout << std::endl << std::setw(10) << std::left << "index" << std::setw(10) << std::left << "old" << std::setw(10) << std::left << "new" << std::setw(11) << std::left << "op" << std::setw(26) << std::right << "dynamic i=, constant v=" << std::endl; # endif // used to hold the first two parameter arguments const Base* par[2]; for(size_t j = 0; j < 2; ++j) par[j] = nullptr; // // Initialize index in dyn_par_arg size_t i_arg = 0; // // Loop throubh the dynamic parameters size_t i_dyn = 0; while(i_dyn < num_dynamic_par) { // number of dynamic parameters created by this operator size_t n_dyn = 1; // // parameter index for this dynamic parameter size_t i_par = size_t( dyn2par_index[i_dyn] ); // # if CPPAD_DYNAMIC_TRACE Base old_value = par_all[i_par]; # endif // // operator for this dynamic parameter op_code_dyn op = op_code_dyn( dyn_par_op[i_dyn] ); // // number of arguments for this operator size_t n_arg = num_arg_dyn(op); // // for unary or binary operators bool unary_or_binary = true; unary_or_binary &= op != atom_dyn; unary_or_binary &= op != cond_exp_dyn; unary_or_binary &= op != dis_dyn; unary_or_binary &= op != ind_dyn; unary_or_binary &= op != result_dyn; if( unary_or_binary ) { CPPAD_ASSERT_UNKNOWN( n_arg == 1 || n_arg == 2 ); for(size_t j = 0; j < n_arg; ++j) par[j] = & par_all[ dyn_par_arg[i_arg + j] ]; } // switch(op) { // --------------------------------------------------------------- // standard_math_98 // acos case acos_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = acos( *par[0] ); break; // asin case asin_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = asin( *par[0] ); break; // atan case atan_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = atan( *par[0] ); break; // cos case cos_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = cos( *par[0] ); break; // cosh case cosh_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = cosh( *par[0] ); break; // ind case ind_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 0 ); CPPAD_ASSERT_UNKNOWN( i_par == i_dyn + 1 ); par_all[i_par] = ind_dynamic[i_dyn]; break; // exp case exp_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = exp( *par[0] ); break; // fabs case fabs_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = fabs( *par[0] ); break; // log case log_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = log( *par[0] ); break; // sin case sin_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = sin( *par[0] ); break; // sinh case sinh_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = sinh( *par[0] ); break; // sqrt case sqrt_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = sqrt( *par[0] ); break; // tan case tan_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = tan( *par[0] ); break; // tanh case tanh_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = tanh( *par[0] ); break; // --------------------------------------------------------------- // asinh case asinh_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = asinh( *par[0] ); break; // acosh case acosh_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = acosh( *par[0] ); break; // atanh case atanh_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = atanh( *par[0] ); break; // expm1 case expm1_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = expm1( *par[0] ); break; // erf case erf_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = erf( *par[0] ); break; // erfc case erfc_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = erfc( *par[0] ); break; // log1p case log1p_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = log1p( *par[0] ); break; // --------------------------------------------------------------- // abs case abs_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = fabs( *par[0] ); break; // add case add_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 2 ); par_all[i_par] = *par[0] + *par[1]; break; // div case div_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 2 ); par_all[i_par] = *par[0] / *par[1]; break; // mul case mul_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 2 ); par_all[i_par] = *par[0] * *par[1]; break; // neg case neg_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = - *par[0]; break; // pow case pow_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 2 ); par_all[i_par] = pow( *par[0], *par[1] ); break; // sign case sign_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); par_all[i_par] = sign( *par[0] ); break; // sub case sub_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 2 ); par_all[i_par] = *par[0] - *par[1]; break; // zmul case zmul_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 2 ); par_all[i_par] = azmul( *par[0], *par[1] ); break; // --------------------------------------------------------------- // discrete(index, argument) case dis_dyn: par_all[i_par] = discrete_eval( size_t( dyn_par_arg[i_arg + 0] ) , // index par_all[ dyn_par_arg[i_arg + 1] ] , // argument RecBase(0) // not_used ); # if CPPAD_DYNAMIC_TRACE std::cout << std::setw(10) << std::left << i_par << std::setw(10) << std::left << old_value << std::setw(10) << std::left << par_all[i_par] << "=" << std::setw(10) << std::right << op_name_dyn(op) << "(" << std::setw(12) << std::right << discrete::name( size_t( dyn_par_arg[i_arg + 0] ) ); if( par_is_dyn[ dyn_par_arg[i_arg + 1] ] ) { std::cout << ", i=" << std::setw(10) << std::right << dyn_par_arg[i_arg + 1]; } else { std::cout << ", v=" << std::setw(10) << std::right << par_all[ dyn_par_arg[i_arg + 1] ]; } std::cout << ")" << std::endl; # endif break; // --------------------------------------------------------------- // cond_exp(cop, left, right, if_true, if_false) // (not yet implemented) case cond_exp_dyn: CPPAD_ASSERT_UNKNOWN( n_arg == 5 ); par_all[i_par] = CondExpOp( CompareOp( dyn_par_arg[i_arg + 0] ) , // cop par_all[ dyn_par_arg[i_arg + 1] ] , // left par_all[ dyn_par_arg[i_arg + 2] ] , // right par_all[ dyn_par_arg[i_arg + 3] ] , // if_true par_all[ dyn_par_arg[i_arg + 4] ] // if_false ); # if CPPAD_DYNAMIC_TRACE std::cout << std::setw(10) << std::left << i_par << std::setw(10) << std::left << old_value << std::setw(10) << std::left << par_all[i_par] << "=" << std::setw(10) << std::right << cond_exp_name[ dyn_par_arg[i_arg + 0] ] << "("; for(size_t i = 1; i < 5; ++i) { if( par_is_dyn[ dyn_par_arg[i_arg + i] ] ) { std::cout << "i=" << std::setw(10) << std::right << dyn_par_arg[i_arg + i]; } else { std::cout << "v=" << std::setw(10) << std::right << par_all[ dyn_par_arg[i_arg + i] ]; } if( i < 4 ) std::cout << ","; } std::cout << ")" << std::endl; # endif break; // --------------------------------------------------------------- // atomic function results case result_dyn: break; // atomic function call case atom_dyn: { size_t atom_index = size_t( dyn_par_arg[i_arg + 0] ); size_t call_id = size_t( dyn_par_arg[i_arg + 1] ); size_t n = size_t( dyn_par_arg[i_arg + 2] ); size_t m = size_t( dyn_par_arg[i_arg + 3] ); n_dyn = size_t( dyn_par_arg[i_arg + 4] ); n_arg = 6 + n + m; CPPAD_ASSERT_UNKNOWN( size_t( dyn_par_arg[i_arg + 5 + n + m] ) == n_arg ); // size_t need_y = size_t(dynamic_enum); size_t order_low = 0; size_t order_up = 0; type_x.resize(n); taylor_x.resize(n); taylor_y.resize(m); select_y.resize(m); // // taylor_x, type_x for(size_t j = 0; j < n; ++j) { addr_t arg_j = dyn_par_arg[i_arg + 5 + j]; taylor_x[j] = par_all[ arg_j ]; if( arg_j == 0 ) type_x[j] = variable_enum; else if ( par_is_dyn[arg_j] ) type_x[j] = dynamic_enum; else type_x[j] = constant_enum; } // select_y for(size_t i = 0; i < m; ++i) { i_par = size_t( dyn_par_arg[i_arg + 5 + n + i] ); select_y[i] = par_is_dyn[i_par]; } call_atomic_forward( taylor_x, type_x, need_y, select_y, order_low, order_up, atom_index, call_id, taylor_x, taylor_y ); # if CPPAD_DYNAMIC_TRACE // get the name of this atomic function bool set_null = false; size_t type = 0; // set to avoid warning std::string name; void* v_ptr = nullptr; // set to avoid warning atomic_index( set_null, atom_index, type, &name, v_ptr ); std::cout << "atom_dyn " << name << " arguments\n"; for(size_t j = 0; j < n; ++j) { std::cout << "index = " << j << ", value = " << taylor_x[j] << std::endl; } std::cout << "atom_dyn " << name << " results\n"; # endif # ifndef NDEBUG size_t count_dyn = 0; # endif for(size_t i = 0; i < m; ++i) { i_par = size_t( dyn_par_arg[i_arg + 5 + n + i] ); if( par_is_dyn[i_par] ) { CPPAD_ASSERT_UNKNOWN( i_par != 0 ); par_all[i_par] = taylor_y[i]; # ifndef NDEBUG ++count_dyn; # endif # if CPPAD_DYNAMIC_TRACE std::cout << std::setw(10) << std::left << i_par << std::setw(10) << std::left << old_value << std::setw(10) << std::left << par_all[i_par] << "= " << name << "_" << i << std::endl; # endif } } CPPAD_ASSERT_UNKNOWN( count_dyn == n_dyn ); # if CPPAD_DYNAMIC_TRACE std::cout << "end atomic dynamic parameter results\n"; # endif } break; // --------------------------------------------------------------- default: std::cerr << "op_code_dyn = " << op_name_dyn(op) << std::endl; CPPAD_ASSERT_UNKNOWN(false); break; } # if CPPAD_DYNAMIC_TRACE if( (op != cond_exp_dyn) & (op != dis_dyn ) & (op != atom_dyn ) & (op != result_dyn ) ) { std::cout << std::setw(10) << std::left << i_par << std::setw(10) << std::left << old_value << std::setw(10) << std::left << par_all[i_par] << "=" << std::setw(10) << std::right << op_name_dyn(op) << "("; if( 0 < n_arg ) { if( par_is_dyn[ dyn_par_arg[i_arg + 0] ] ) { std::cout << "i=" << std::setw(10) << std::right << dyn_par_arg[i_arg + 0]; } else { std::cout << "v=" << std::setw(10) << std::right << par_all[ dyn_par_arg[i_arg + 0] ]; } } if( 1 < n_arg ) { if( par_is_dyn[ dyn_par_arg[i_arg + 1] ] ) { std::cout << ", i=" << std::setw(10) << std::right << dyn_par_arg[i_arg + 1]; } else { std::cout << ", v=" << std::setw(10) << std::right << par_all[ dyn_par_arg[i_arg + 1] ]; } } std::cout << ")" << std::endl; } # endif i_arg += n_arg; i_dyn += n_dyn; } CPPAD_ASSERT_UNKNOWN( i_arg == dyn_par_arg.size() ) return; } // preprocessor symbols that are local to this file # undef CPPAD_DYNAMIC_TRACE } } } // END_CPPAD_LOCAL_SWEEP_NAMESPACE # endif ================================================ FILE: include/cppad/local/sweep/for_hes.hpp ================================================ # ifndef CPPAD_LOCAL_SWEEP_FOR_HES_HPP # define CPPAD_LOCAL_SWEEP_FOR_HES_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include /* {xrst_begin local_sweep_for_hes dev} {xrst_spell inv } Forward Mode Hessian Sparsity Patterns ###################################### Syntax ****** | ``local::sweep::for_hes`` ( | |tab| *play* , | |tab| *n* , | |tab| *num_var* , | |tab| *select_domain* , | |tab| *rev_jac_sparse* , | |tab| *for_hes_sparse* , | |tab| ``not_used_rec_base`` | ) Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Purpose ******* Given the forward Jacobian sparsity pattern for all the variables, and the reverse Jacobian sparsity pattern for the dependent variables, ``for_hes`` computes the Hessian sparsity pattern for all the independent variables. CPPAD_FOR_HES_TRACE ******************* This value is either zero or one. Zero is the normal operational value. If it is one, a trace of Jacobian and Hessian sparsity result for every operation for every ``for_hes`` sweep is printed. The sparsity patterns are printed as binary numbers with 1 (0) meaning that the corresponding index is (is not) in the set. {xrst_code hpp} */ # define CPPAD_FOR_HES_TRACE 0 /* {xrst_code} Base **** The operation sequence in *play* was recorded using ``AD`` < *Base* > . RecBase ******* Is the base type when this function was recorded. This is different from *Base* if this function object was created by :ref:`base2ad-name` . SetVector ********* This is a :ref:`SetVector-name` type. play **** The information stored in play is a recording of the operations corresponding to a function :math:`F : \B{R}^n \rightarrow \B{R}^m` where *m* is the number of dependent variables. n * is the number of independent variables in the tape. num_var ******* is the total number of variables in the tape; i.e., *play* ``->num_var`` () . This is also the number of sets in all the sparsity patterns. select_domain ************* is a vector with size *n* that specifies which components of the domain to include in the Hessian sparsity pattern. For *j* = 0, ..., *n* ``-1`` , the *j*-th independent variable will be included if and only if *select_domain* [ *j* ] is true. This assumes that the order of the independent variables is the same as the order of the InvOp operators. rev_jac_sparse ************** Is a sparsity pattern with size *num_var* by one. For *i* =1, ..., *num_var* ``-1`` , the if the scalar valued function we are computing the Hessian sparsity for has a non-zero derivative w.r.t. variable with index *i* , the set with index *i* has the element zero. Otherwise it has no elements. for_hes_sparse ************** This is a sparsity pattern with *n* + 1 + *num_var* sets and end value *n* + 1 . On input, all of the sets are empty. On output, it contains the two sparsity patterns described below: Hessian Sparsity ================ For *j* equal 1 to *n* , if *i* is in set with index *j* , the Hessian may have a non-zero partial with respect to the independent variables with indices ( *i* - 1, *j* - 1 ) . Note that the index zero is not used because it corresponds to the phantom variable on the tape. Jacobian Sparsity ================= For *k* equal 1 to *num_var* - 1 , if *i* is in the set with index *n* + 1 + *k* , the variable with index *k* may have a non-zero partial with resect to the independent variable with index *i* - 1 . Method ====== For *k* equal 1 to *num_var* - 1, the Jacobian sparsity pattern for variable with index *k* is computed using the previous Jacobian sparsity patterns. The Hessian sparsity pattern is updated using linear and non-linear interactions for the variable with index *k* and the previous Jacobian sparsity patterns. not_used_rec_base ***************** This argument is only used to specify the type *RecBase* for this call. {xrst_end local_sweep_for_hes} */ // BEGIN_CPPAD_LOCAL_SWEEP_NAMESPACE namespace CppAD { namespace local { namespace sweep { // BEGIN PROTOTYPE template void for_hes( const local::player* play , size_t n , size_t num_var , const pod_vector& select_domain , const SetVector& rev_jac_sparse , SetVector& for_hes_sparse , const RecBase& not_used_rec_base ) // END PROTOTYPE { // length of the parameter vector (used by CppAD assert macros) # ifndef NDEBUG const size_t num_par = play->num_par_all(); # endif // check arguments size_t np1 = n+1; CPPAD_ASSERT_UNKNOWN( select_domain.size() == n ); CPPAD_ASSERT_UNKNOWN( play->num_var() == num_var ); CPPAD_ASSERT_UNKNOWN( rev_jac_sparse.n_set() == num_var ); CPPAD_ASSERT_UNKNOWN( for_hes_sparse.n_set() == np1+num_var ); // CPPAD_ASSERT_UNKNOWN( rev_jac_sparse.end() == 1 ); CPPAD_ASSERT_UNKNOWN( for_hes_sparse.end() == np1 ); // CPPAD_ASSERT_UNKNOWN( num_var > 0 ); // // vecad_sparsity: forward Jacobian sparsity pattern for each VecAD object. // vecad_ind: maps the VecAD index at beginning of the VecAD object // to the index for the corresponding set in vecad_sparsity. size_t num_vecad_ind = play->num_var_vec_ind(); size_t num_vecad_vec = play->num_var_vecad(); SetVector vecad_sparsity; pod_vector vecad_ind; if( num_vecad_vec > 0 ) { size_t length; vecad_sparsity.resize(num_vecad_vec, np1); vecad_ind.extend(num_vecad_ind); size_t j = 0; for(size_t i = 0; i < num_vecad_vec; i++) { // length of this VecAD length = play->GetVecInd(j); // set vecad_ind to proper index for this VecAD vecad_ind[j] = i; // make all other values for this vector invalid for(size_t k = 1; k <= length; k++) vecad_ind[j+k] = num_vecad_vec; // start of next VecAD j += length + 1; } CPPAD_ASSERT_UNKNOWN( j == play->num_var_vec_ind() ); } // ------------------------------------------------------------------------ // work space used by atomic functions var_op::atomic_op_work atom_work; // // // pointer to the beginning of the parameter vector // (used by atomic functions) CPPAD_ASSERT_UNKNOWN( num_par > 0 ) const Base* parameter = play->par_ptr(); // // skip the BeginOp at the beginning of the recording play::const_sequential_iterator itr = play->begin(); // op_info op_code_var op; size_t i_var; const addr_t* arg; itr.op_info(op, arg, i_var); CPPAD_ASSERT_UNKNOWN( op == BeginOp ); # if CPPAD_FOR_HES_TRACE std::cout << std::endl; bool atom_trace = true; # else bool atom_trace = false; # endif bool more_operators = true; size_t count_independent = 0; while(more_operators) { bool linear[3]; // next op (++itr).op_info(op, arg, i_var); // does the Hessian in question have a non-zero derivative // with respect to this variable bool include = NumRes(op) > 0; if( include ) include = rev_jac_sparse.is_element(i_var, 0); switch( op ) { // include // operators that must always be included case EndOp: case CSkipOp: case AFunOp: case FunapOp: case FunavOp: case FunrpOp: case FunrvOp: case StppOp: case StpvOp: case StvpOp: case StvvOp: include = true; break; // count_independent case InvOp: if( ! include ) ++count_independent; break; // itr case CSumOp: if( ! include ) itr.correct_before_increment(); break; // default default: break; } // if( include ) switch( op ) { // operators that should not occur // case BeginOp // operators that do not affect Jacobian or Hessian // and where with a fixed number of arguments and results case CExpOp: case DisOp: case ParOp: case PriOp: case SignOp: break; // ------------------------------------------------- // independent variable operator: set J(i_var) = { i_var } case InvOp: CPPAD_ASSERT_UNKNOWN( for_hes_sparse.number_elements(i_var) == 0 ); if( select_domain[count_independent] ) { // Not using post_element because only adding one element // per set for_hes_sparse.add_element(np1 + i_var, i_var); } ++count_independent; break; // ------------------------------------------------- // linear operators where arg[0] is the only variable case AbsOp: case DivvpOp: case SubvpOp: case ZmulvpOp: linear[0] = true; var_op::one_var_for_hes( np1, num_var, i_var, size_t(arg[0]), linear, for_hes_sparse ); break; // ------------------------------------------------- // linear operators where arg[1] is the only variable case AddpvOp: case MulpvOp: case SubpvOp: linear[0] = true; var_op::one_var_for_hes( np1, num_var, i_var, size_t(arg[1]), linear, for_hes_sparse ); break; // ------------------------------------------------- // linear operators where arg[0] and arg[1] are variables case AddvvOp: case SubvvOp: linear[0] = true; linear[1] = true; linear[2] = true; var_op::two_var_for_hes( np1, num_var, i_var, arg, linear, for_hes_sparse ); break; // ------------------------------------------------------ // VecAD load operators case LdvOp: case LdpOp: var_op::load_for_hes( op, arg, num_vecad_ind, i_var, n, vecad_ind, vecad_sparsity, for_hes_sparse ); break; // // VecAD store operators case StppOp: case StpvOp: case StvpOp: case StvvOp: var_op::store_for_hes(op, arg, num_vecad_ind, n, vecad_ind, vecad_sparsity, for_hes_sparse ); break; // ------------------------------------------------------ // nonlinear operators where arg[0] is the only variable case AcosOp: case AsinOp: case AtanOp: case CosOp: case CoshOp: case ExpOp: case LogOp: case NegOp: case SinOp: case SinhOp: case SqrtOp: case TanOp: case TanhOp: case AcoshOp: case AsinhOp: case AtanhOp: case Expm1Op: case Log1pOp: case ErfOp: case ErfcOp: CPPAD_ASSERT_UNKNOWN( 0 < NumArg(op) ) linear[0] = false; var_op::one_var_for_hes( np1, num_var, i_var, size_t(arg[0]), linear, for_hes_sparse ); break; // ------------------------------------------------- case CSkipOp: itr.correct_before_increment(); break; // ------------------------------------------------- case CSumOp: var_op::csum_for_hes(arg, i_var, n, for_hes_sparse); itr.correct_before_increment(); break; // ------------------------------------------------- case DivvvOp: linear[0] = true; linear[1] = false; linear[2] = false; CPPAD_ASSERT_NARG_NRES(op, 2, 1) var_op::two_var_for_hes( np1, num_var, i_var, arg, linear, for_hes_sparse ); break; // ------------------------------------------------- // nonlinear operators where arg[1] is the only variable case DivpvOp: CPPAD_ASSERT_NARG_NRES(op, 2, 1) linear[0] = false; var_op::one_var_for_hes( np1, num_var, i_var, size_t(arg[1]), linear, for_hes_sparse ); break; // ------------------------------------------------- case EndOp: CPPAD_ASSERT_NARG_NRES(op, 0, 0); more_operators = false; break; // ------------------------------------------------- // logical comparison operators case EqppOp: case EqpvOp: case EqvvOp: case LtppOp: case LtpvOp: case LtvpOp: case LtvvOp: case LeppOp: case LepvOp: case LevpOp: case LevvOp: case NepvOp: case NeppOp: case NevvOp: CPPAD_ASSERT_NARG_NRES(op, 2, 0); break; // ------------------------------------------------- case MulvvOp: case ZmulvvOp: linear[0] = true; linear[1] = true; linear[2] = false; CPPAD_ASSERT_NARG_NRES(op, 2, 1) var_op::two_var_for_hes( np1, num_var, i_var, arg, linear, for_hes_sparse ); break; // ------------------------------------------------- case PowpvOp: CPPAD_ASSERT_NARG_NRES(op, 2, 3) linear[0] = false; var_op::one_var_for_hes( np1, num_var, i_var, size_t(arg[1]), linear, for_hes_sparse ); break; // ------------------------------------------------- case PowvpOp: CPPAD_ASSERT_NARG_NRES(op, 2, 1) linear[0] = false; var_op::one_var_for_hes( np1, num_var, i_var, size_t(arg[0]), linear, for_hes_sparse ); break; // ------------------------------------------------- case PowvvOp: linear[0] = false; linear[1] = false; linear[2] = false; CPPAD_ASSERT_NARG_NRES(op, 2, 3) var_op::two_var_for_hes( np1, num_var, i_var, arg, linear, for_hes_sparse ); break; // ------------------------------------------------- case AFunOp: var_op::atomic_for_hes( itr, play, parameter, atom_trace, atom_work, np1, rev_jac_sparse, for_hes_sparse ); break; case FunapOp: case FunavOp: case FunrpOp: case FunrvOp: CPPAD_ASSERT_UNKNOWN( false ); break; // ------------------------------------------------- default: CPPAD_ASSERT_UNKNOWN(0); } # if CPPAD_FOR_HES_TRACE if( op != AFunOp ) { // printOp( std::cout, play, itr.op_index(), i_var, op, arg ); // if( NumRes(op) > 0 ) { typedef typename SetVector::const_iterator itr_sparse_t; CPPAD_ASSERT_UNKNOWN( np1 == for_hes_sparse.end() ); CppAD::vectorBool jac_row(np1); for(size_t j = 0; j < np1; ++j) jac_row[j] = false; itr_sparse_t itr_jac(for_hes_sparse, np1 + i_var); { size_t j = *itr_jac; while( j < np1 ) { jac_row[j] = true; j = *(++itr_jac); } } printOpResult( std::cout, 1, &jac_row, 0, (CppAD::vectorBool *) nullptr ); std::cout << std::endl; // CppAD::vector< CppAD::vectorBool > hes(np1); for(size_t i = 0; i < np1; ++i) { hes[i].resize(np1); for(size_t j = 0; j < np1; ++j) hes[i][j] = false; itr_sparse_t itr_hes(for_hes_sparse, i); size_t j = *itr_hes; while( j < np1 ) { hes[i][j] = true; j = *(++itr_hes); } } printOpResult( std::cout, np1, hes.data(), 0, (CppAD::vectorBool *) nullptr ); std::cout << std::endl; } } } std::cout << std::endl; # else } # endif return; } } } } // END_CPPAD_LOCAL_SWEEP_NAMESPACE // preprocessor symbols that are local to this file # undef CPPAD_FOR_HES_TRACE # endif ================================================ FILE: include/cppad/local/sweep/for_jac.hpp ================================================ # ifndef CPPAD_LOCAL_SWEEP_FOR_JAC_HPP # define CPPAD_LOCAL_SWEEP_FOR_JAC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include // BEGIN_CPPAD_LOCAL_SWEEP_NAMESPACE namespace CppAD { namespace local { namespace sweep { /*! \file sweep/for_jac.hpp Compute Forward mode Jacobian sparsity patterns. */ /*! \def CPPAD_FOR_JAC_TRACE This value is either zero or one. Zero is the normal operational value. If it is one, a trace of every for_jac_sweep computation is printed. */ # define CPPAD_FOR_JAC_TRACE 0 /*! Given the sparsity pattern for the independent variables, ForJacSweep computes the sparsity pattern for all the other variables. \tparam Base this operation sequence was recorded using AD. \tparam Vector_set is the type used for vectors of sets. It can be either sparse::pack_setvec or sparse::list_setvec. \param dependency Are the derivatives with respect to left and right of the expression below considered to be non-zero: \code CondExpRel(left, right, if_true, if_false) \endcode This is used by the optimizer to obtain the correct dependency relations. \param n is the number of independent variables on the tape. \param num_var is the total number of variables on the tape; i.e., play->num_var(). \param play The information stored in play is a recording of the operations corresponding to a function \f[ F : {\bf R}^n \rightarrow {\bf R}^m \f] where \f$ n \f$ is the number of independent variables and \f$ m \f$ is the number of dependent variables. \param var_sparsity \b Input: For j = 1 , ... , n, the sparsity pattern for the independent variable with index (j-1) corresponds to the set with index j in var_sparsity. \n \n \b Output: For i = n + 1 , ... , num_var - 1, the sparsity pattern for the variable with index i on the tape corresponds to the set with index i in var_sparsity. \par Checked Assertions: \li num_var == var_sparsity.n_set() \li num_var == play->num_var() \param not_used_rec_base Specifies RecBase for this call. */ template void for_jac( const local::player* play, bool dependency , size_t n , size_t num_var , Vector_set& var_sparsity, const RecBase& not_used_rec_base ) { size_t i, j, k; // check num_var argument CPPAD_ASSERT_UNKNOWN( play->num_var() == num_var ); CPPAD_ASSERT_UNKNOWN( var_sparsity.n_set() == num_var ); // length of the parameter vector (used by CppAD assert macros) const size_t num_par = play->num_par_all(); // cum_sparsity accumulates sparsity pattern a cumulative sum size_t limit = var_sparsity.end(); // vecad_sparsity contains a sparsity pattern from each VecAD object // to all the other variables. // vecad_ind maps a VecAD index (the beginning of the // VecAD object) to its from index in vecad_sparsity size_t num_vecad_ind = play->num_var_vec_ind(); size_t num_vecad_vec = play->num_var_vecad(); Vector_set vecad_sparsity; pod_vector vecad_ind; if( num_vecad_vec > 0 ) { size_t length; vecad_sparsity.resize(num_vecad_vec, limit); vecad_ind.extend(num_vecad_ind); j = 0; for(i = 0; i < num_vecad_vec; i++) { // length of this VecAD length = play->GetVecInd(j); // set to proper index for this VecAD vecad_ind[j] = i; for(k = 1; k <= length; k++) vecad_ind[j+k] = num_vecad_vec; // invalid index // start of next VecAD j += length + 1; } CPPAD_ASSERT_UNKNOWN( j == play->num_var_vec_ind() ); } // work space used by atomic functions var_op::atomic_op_work atom_work; // // // pointer to the beginning of the parameter vector // (used by atomic functions) CPPAD_ASSERT_UNKNOWN( num_par > 0 ) const Base* parameter = play->par_ptr(); // # if CPPAD_FOR_JAC_TRACE std::cout << std::endl; CppAD::vectorBool z_value(limit); bool atom_trace = true; # else bool atom_trace = false; # endif // skip the BeginOp at the beginning of the recording play::const_sequential_iterator itr = play->begin(); // op_info op_code_var op; size_t i_var; const addr_t* arg; itr.op_info(op, arg, i_var); CPPAD_ASSERT_UNKNOWN( op == BeginOp ); // bool more_operators = true; while(more_operators) { // // this op (++itr).op_info(op, arg, i_var); // // rest of information depends on the case switch( op ) { // // operators with one primary result and // where the first argument is the only variable case AbsOp: case AcosOp: case AcoshOp: case AsinOp: case AsinhOp: case AtanOp: case AtanhOp: case CosOp: case CoshOp: case DivvpOp: case ErfOp: case ErfcOp: case ExpOp: case Expm1Op: case LogOp: case NegOp: case Log1pOp: case PowvpOp: case SinOp: case SinhOp: case SqrtOp: case SubvpOp: case TanOp: case TanhOp: case ZmulvpOp: CPPAD_ASSERT_UNKNOWN( 0 < NumArg(op) ); var_op::one_var_for_jac( i_var, size_t(arg[0]), var_sparsity ); break; // ------------------------------------------------- // operators with one primary result and // where the second argument is the only variable case AddpvOp: case DivpvOp: case MulpvOp: case PowpvOp: case SubpvOp: case ZmulpvOp: CPPAD_ASSERT_UNKNOWN( 1 < NumArg(op) ); var_op::one_var_for_jac( i_var, size_t(arg[1]), var_sparsity ); break; // ------------------------------------------------- case AddvvOp: CPPAD_ASSERT_NARG_NRES(op, 2, 1); var_op::two_var_for_jac( i_var, arg, var_sparsity ); break; // ------------------------------------------------- case CSkipOp: itr.correct_before_increment(); break; // ------------------------------------------------- case CSumOp: var_op::csum_for_jac( i_var, arg, var_sparsity ); itr.correct_before_increment(); break; // ------------------------------------------------- case CExpOp: var_op::cexp_for_jac( dependency, i_var, arg, num_par, var_sparsity ); break; // -------------------------------------------------- case DisOp: CPPAD_ASSERT_NARG_NRES(op, 2, 1); // derivative is identically zero but dependency is not if( dependency ) var_op::one_var_for_jac( i_var, size_t(arg[1]), var_sparsity ); else var_sparsity.clear(i_var); break; // ------------------------------------------------- case DivvvOp: CPPAD_ASSERT_NARG_NRES(op, 2, 1); var_op::two_var_for_jac( i_var, arg, var_sparsity ); break; // ------------------------------------------------- case EndOp: CPPAD_ASSERT_NARG_NRES(op, 0, 0); more_operators = false; break; // ------------------------------------------------- case InvOp: CPPAD_ASSERT_NARG_NRES(op, 0, 1); // sparsity pattern is already defined break; // ------------------------------------------------- case LdpOp: case LdvOp: var_op::load_for_jac( op, num_vecad_ind, i_var, arg, dependency, vecad_ind, var_sparsity, vecad_sparsity ); break; // ------------------------------------------------- case EqppOp: case EqpvOp: case EqvvOp: case LtppOp: case LtpvOp: case LtvpOp: case LtvvOp: case LeppOp: case LepvOp: case LevpOp: case LevvOp: case NeppOp: case NepvOp: case NevvOp: CPPAD_ASSERT_NARG_NRES(op, 2, 0); break; // ------------------------------------------------- case MulvvOp: CPPAD_ASSERT_NARG_NRES(op, 2, 1); var_op::two_var_for_jac( i_var, arg, var_sparsity ); break; // ------------------------------------------------- case ParOp: CPPAD_ASSERT_NARG_NRES(op, 1, 1); var_sparsity.clear(i_var); break; // ------------------------------------------------- case PowvvOp: CPPAD_ASSERT_NARG_NRES(op, 2, 3); var_op::two_var_for_jac( i_var, arg, var_sparsity ); break; // ------------------------------------------------- case PriOp: CPPAD_ASSERT_NARG_NRES(op, 5, 0); break; // ------------------------------------------------- case SignOp: CPPAD_ASSERT_NARG_NRES(op, 1, 1); // derivative is identically zero but dependency is not if( dependency ) var_op::one_var_for_jac( i_var, size_t(arg[0]), var_sparsity ); else var_sparsity.clear(i_var); break; // ------------------------------------------------- case StppOp: case StpvOp: case StvpOp: case StvvOp: var_op::store_for_jac( op, num_vecad_ind, arg, dependency, vecad_ind, var_sparsity, vecad_sparsity ); break; // ------------------------------------------------- case SubvvOp: CPPAD_ASSERT_NARG_NRES(op, 2, 1); var_op::two_var_for_jac( i_var, arg, var_sparsity ); break; // ------------------------------------------------- case AFunOp: var_op:: atomic_for_jac( itr, play, parameter, atom_trace, atom_work, dependency, var_sparsity ); break; case FunapOp: case FunavOp: case FunrpOp: case FunrvOp: CPPAD_ASSERT_UNKNOWN( false ); break; // ------------------------------------------------- case ZmulvvOp: CPPAD_ASSERT_NARG_NRES(op, 2, 1); var_op::two_var_for_jac( i_var, arg, var_sparsity ); break; // ------------------------------------------------- default: CPPAD_ASSERT_UNKNOWN(0); } # if CPPAD_FOR_JAC_TRACE if( op != AFunOp ) { // value for this variable for(j = 0; j < limit; j++) z_value[j] = false; typename Vector_set::const_iterator sparse_itr(var_sparsity, i_var); j = *sparse_itr; while( j < limit ) { z_value[j] = true; j = *(++sparse_itr); } printOp( std::cout, play, itr.op_index(), i_var, op, arg ); if( NumRes(op) > 0 ) printOpResult( std::cout, 1, &z_value, 0, (CppAD::vectorBool *) nullptr ); std::cout << std::endl; } # endif } return; } } } } // END_CPPAD_LOCAL_SWEEP_NAMESPACE // preprocessor symbols that are local to this file # undef CPPAD_FOR_JAC_TRACE # endif ================================================ FILE: include/cppad/local/sweep/forward_0.hpp ================================================ # ifndef CPPAD_LOCAL_SWEEP_FORWARD_0_HPP # define CPPAD_LOCAL_SWEEP_FORWARD_0_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # define CPPAD_FORWARD_0_TRACE 0 // BEGIN_CPPAD_LOCAL_SWEEP_NAMESPACE namespace CppAD { namespace local { namespace sweep { /* ------------------------------------------------------------------------------ {xrst_begin sweep_forward_0 dev} {xrst_spell cskip } {xrst_template ; include/cppad/local/sweep/template/forward_sweep.xrst headers all: CPPAD_@NAME@_TRACE, Portotype, Base, RecBase, play, num_var headers all: cap_order, cskip, load_op2var, taylor headers 0 & any: change_count, change_number, change_op_index, sout, print @title@ The title for this forward sweep @#####@ Underlining for the tilte @NAME@ is one of FORWARD_0, FORWARD_ANY, FORWARD_DIR @****@ Underlining for @NAME@ @title@ ; Compute Zero Order Forward Mode Taylor Coefficients @#####@ ; ################################################### @NAME@ ; FORWARD_0 @****@ ; ********* } {xrst_end sweep_forward_0} */ // BEGIN_FORWARD_0 template void forward_0( const RecBase& not_used_rec_base, const local::player* play, size_t num_var, size_t cap_order, bool* cskip_op, pod_vector& load_op2var, size_t change_count, size_t& change_number, size_t& change_op_index, std::ostream& s_out, bool print, Base* taylor ) // END_FORWARD_0 { CPPAD_ASSERT_UNKNOWN( cap_order >= 1 ); CPPAD_ASSERT_UNKNOWN( play->num_var() == num_var ); // use p, q, r so other forward sweeps can use code defined here size_t order_low = 0; size_t order_up = 0; size_t r = 1; // initialize the comparison operator counter if( order_low == 0 ) { change_number = 0; change_op_index = 0; } // If this includes a zero calculation, initialize this information pod_vector vec_ad2isvar; pod_vector vec_ad2index; if( order_low == 0 ) { size_t i; // this includes order zero calculation, initialize vector indices size_t num = play->num_var_vec_ind(); if( num > 0 ) { vec_ad2isvar.extend(num); vec_ad2index.extend(num); for(i = 0; i < num; i++) { vec_ad2index[i] = play->GetVecInd(i); vec_ad2isvar[i] = false; } } // includes zero order, so initialize conditional skip flags num = play->num_var_op(); for(i = 0; i < num; i++) cskip_op[i] = false; } // information used by atomic function operators // work space used by atomic functions var_op::atomic_op_work atom_work; // length of the parameter vector (used by CppAD assert macros) const size_t num_par = play->num_par_all(); // pointer to the beginning of the parameter vector CPPAD_ASSERT_UNKNOWN( num_par > 0 ) const Base* parameter = play->par_ptr(); // length of the text vector (used by CppAD assert macros) const size_t num_text = play->num_var_text(); // pointer to the beginning of the text vector const char* text = nullptr; if( num_text > 0 ) text = play->GetTxt(0); # if CPPAD_FORWARD_0_TRACE // flag as to when to trace atomic function values bool atom_trace = true; # else bool atom_trace = false; # endif // skip the BeginOp at the beginning of the recording play::const_sequential_iterator itr = play->begin(); // op_info op_code_var op; size_t i_var; const addr_t* arg; itr.op_info(op, arg, i_var); CPPAD_ASSERT_UNKNOWN( op == BeginOp ); // # if CPPAD_FORWARD_0_TRACE std::cout << std::endl; # endif bool more_operators = true; while(more_operators) { // next op (++itr).op_info(op, arg, i_var); CPPAD_ASSERT_UNKNOWN( itr.op_index() < play->num_var_op() ); // check if we are skipping this operation while( cskip_op[itr.op_index()] ) { switch(op) { case AFunOp: { // get information for this atomic function call size_t atom_index, atom_id, atom_m, atom_n; play::atom_op_info( op, arg, atom_index, atom_id, atom_m, atom_n ); // // skip to the second AFunOp for(size_t i = 0; i < atom_m + atom_n + 1; ++i) ++itr; # ifndef NDEBUG itr.op_info(op, arg, i_var); CPPAD_ASSERT_UNKNOWN( op == AFunOp ); # endif } break; case CSkipOp: case CSumOp: itr.correct_before_increment(); break; default: break; } (++itr).op_info(op, arg, i_var); } // action to take depends on the case switch( op ) { case EqppOp: case EqpvOp: case EqvvOp: case LeppOp: case LepvOp: case LevpOp: case LevvOp: case LtppOp: case LtpvOp: case LtvpOp: case LtvvOp: case NeppOp: case NepvOp: case NevvOp: var_op::compare_forward_any(op, arg, parameter, cap_order, taylor, itr.op_index(), change_count, change_number, change_op_index ); break; // ------------------------------------------------- case AbsOp: var_op::abs_forward_0(i_var, arg, cap_order, taylor); break; // ------------------------------------------------- case AddvvOp: var_op::addvv_forward_0(i_var, arg, parameter, cap_order, taylor); break; // ------------------------------------------------- case AddpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::addpv_forward_0(i_var, arg, parameter, cap_order, taylor); break; // ------------------------------------------------- case AcosOp: // sqrt(1 - x * x), acos(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::acos_forward_0(i_var, arg, cap_order, taylor); break; // ------------------------------------------------- case AcoshOp: // sqrt(x * x - 1), acosh(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::acosh_forward_0(i_var, arg, cap_order, taylor); break; // ------------------------------------------------- case AsinOp: // sqrt(1 - x * x), asin(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::asin_forward_0(i_var, arg, cap_order, taylor); break; // ------------------------------------------------- case AsinhOp: // sqrt(1 + x * x), asinh(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::asinh_forward_0(i_var, arg, cap_order, taylor); break; // ------------------------------------------------- case AtanOp: // 1 + x * x, atan(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::atan_forward_0(i_var, arg, cap_order, taylor); break; // ------------------------------------------------- case AtanhOp: // 1 - x * x, atanh(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::atanh_forward_0(i_var, arg, cap_order, taylor); break; // ------------------------------------------------- case CExpOp: // Use the general case with d == 0 // (could create an optimized version for this case) var_op::cexp_forward_0( i_var, arg, num_par, parameter, cap_order, taylor ); break; // --------------------------------------------------- case CosOp: // sin(x), cos(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::cos_forward_0(i_var, arg, cap_order, taylor); break; // --------------------------------------------------- case CoshOp: // sinh(x), cosh(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::cosh_forward_0(i_var, arg, cap_order, taylor); break; // ------------------------------------------------- case CSkipOp: var_op::cskip_forward_0( i_var, arg, num_par, parameter, cap_order, taylor, cskip_op ); itr.correct_before_increment(); break; // ------------------------------------------------- case CSumOp: var_op::csum_forward_any( 0, 0, i_var, arg, num_par, parameter, cap_order, taylor ); itr.correct_before_increment(); break; // ------------------------------------------------- case DisOp: var_op::dis_forward_dir( order_low, order_up, r, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case DivvvOp: var_op::divvv_forward_0(i_var, arg, parameter, cap_order, taylor); break; // ------------------------------------------------- case DivpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::divpv_forward_0(i_var, arg, parameter, cap_order, taylor); break; // ------------------------------------------------- case DivvpOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par ); var_op::divvp_forward_0(i_var, arg, parameter, cap_order, taylor); break; // ------------------------------------------------- case EndOp: CPPAD_ASSERT_NARG_NRES(op, 0, 0); more_operators = false; break; // ------------------------------------------------- case ErfOp: case ErfcOp: var_op::erf_forward_0(op, i_var, arg, parameter, cap_order, taylor); break; // ------------------------------------------------- case ExpOp: var_op::exp_forward_0(i_var, arg, cap_order, taylor); break; // ------------------------------------------------- case Expm1Op: var_op::expm1_forward_0(i_var, arg, cap_order, taylor); break; // ------------------------------------------------- case InvOp: CPPAD_ASSERT_NARG_NRES(op, 0, 1); break; // --------------------------------------------------- case LdpOp: case LdvOp: var_op::load_forward_0( op, i_var, play->num_var_vec_ind(), arg, num_var, num_par, parameter, cap_order, taylor, vec_ad2isvar, vec_ad2index, load_op2var ); break; // ------------------------------------------------- case LogOp: var_op::log_forward_0(i_var, arg, cap_order, taylor); break; // ------------------------------------------------- case Log1pOp: var_op::log1p_forward_0(i_var, arg, cap_order, taylor); break; // ------------------------------------------------- case MulpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::mulpv_forward_0(i_var, arg, parameter, cap_order, taylor); break; // ------------------------------------------------- case MulvvOp: var_op::mulvv_forward_0(i_var, arg, parameter, cap_order, taylor); break; // ------------------------------------------------- case NegOp: var_op::neg_forward_0(i_var, arg, cap_order, taylor); break; // ------------------------------------------------- case ParOp: var_op::par_forward_0( i_var, arg, num_par, parameter, cap_order, taylor ); break; // ------------------------------------------------- case PowvpOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par ); var_op::powvp_forward_0(i_var, arg, parameter, cap_order, taylor); break; // ------------------------------------------------- case PowpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::powpv_forward_0(i_var, arg, parameter, cap_order, taylor); break; // ------------------------------------------------- case PowvvOp: var_op::powvv_forward_0(i_var, arg, parameter, cap_order, taylor); break; // ------------------------------------------------- case PriOp: if( print ) var_op::pri_forward_0(s_out, arg, num_text, text, num_par, parameter, cap_order, taylor ); break; // ------------------------------------------------- case SignOp: // cos(x), sin(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::sign_forward_0(i_var, arg, cap_order, taylor); break; // ------------------------------------------------- case SinOp: // cos(x), sin(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::sin_forward_0(i_var, arg, cap_order, taylor); break; // ------------------------------------------------- case SinhOp: // cosh(x), sinh(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::sinh_forward_0(i_var, arg, cap_order, taylor); break; // ------------------------------------------------- case SqrtOp: var_op::sqrt_forward_0(i_var, arg, cap_order, taylor); break; // ------------------------------------------------- case StppOp: case StpvOp: case StvpOp: case StvvOp: var_op::store_forward_0( op, arg, num_var, num_par, parameter, cap_order, taylor, vec_ad2isvar, vec_ad2index ); break; // ------------------------------------------------- case SubvvOp: var_op::subvv_forward_0(i_var, arg, parameter, cap_order, taylor); break; // ------------------------------------------------- case SubpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::subpv_forward_0(i_var, arg, parameter, cap_order, taylor); break; // ------------------------------------------------- case SubvpOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par ); var_op::subvp_forward_0(i_var, arg, parameter, cap_order, taylor); break; // ------------------------------------------------- case TanOp: // tan(x)^2, tan(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::tan_forward_0(i_var, arg, cap_order, taylor); break; // ------------------------------------------------- case TanhOp: // tanh(x)^2, tanh(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::tanh_forward_0(i_var, arg, cap_order, taylor); break; // ------------------------------------------------- case AFunOp: // start of an atomic function call var_op::atomic_forward_any( itr, play, parameter, atom_trace, atom_work, cap_order, order_low, order_up, taylor ); break; case FunapOp: case FunavOp: case FunrpOp: case FunrvOp: CPPAD_ASSERT_UNKNOWN( false ); break; // ------------------------------------------------- case ZmulpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::zmulpv_forward_0(i_var, arg, parameter, cap_order, taylor); break; // ------------------------------------------------- case ZmulvpOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par ); var_op::zmulvp_forward_0(i_var, arg, parameter, cap_order, taylor); break; // ------------------------------------------------- case ZmulvvOp: var_op::zmulvv_forward_0(i_var, arg, parameter, cap_order, taylor); break; // ------------------------------------------------- default: CPPAD_ASSERT_UNKNOWN(false); } # if CPPAD_FORWARD_0_TRACE size_t d = 0; Base* Z_tmp = taylor + i_var * cap_order; if( op != AFunOp ) { printOp( std::cout, play, itr.op_index(), i_var, op, arg ); if( NumRes(op) > 0 ) printOpResult( std::cout, d + 1, Z_tmp, 0, (Base *) nullptr ); std::cout << std::endl; } } std::cout << std::endl; # else } # endif return; } } } } // END_CPPAD_LOCAL_SWEEP_NAMESPACE // preprocessor symbols that are local to this file # undef CPPAD_FORWARD_0_TRACE # endif ================================================ FILE: include/cppad/local/sweep/forward_any.hpp ================================================ # ifndef CPPAD_LOCAL_SWEEP_FORWARD_ANY_HPP # define CPPAD_LOCAL_SWEEP_FORWARD_ANY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # define CPPAD_FORWARD_ANY_TRACE 0 // BEGIN_CPPAD_LOCAL_SWEEP_NAMESPACE namespace CppAD { namespace local { namespace sweep { /* ------------------------------------------------------------------------------ {xrst_begin sweep_forward_any dev} {xrst_spell cskip } {xrst_template ; include/cppad/local/sweep/template/forward_sweep.xrst headers all: CPPAD_@NAME@_TRACE, Portotype, Base, RecBase, play, num_var headers all: cap_order, cskip, load_op2var, taylor headers 0 & any: change_count, change_number, change_op_index, sout, print headers any: order_low, order_up @title@ The title for this forward sweep @#####@ Underlining for the tilte @NAME@ is one of FORWARD_0, FORWARD_ANY, FORWARD_DIR @****@ Underlining for @NAME@ @title@ ; Compute Any Order Forward Mode Taylor Coefficients @#####@ ; ################################################## @NAME@ ; FORWARD_ANY @****@ ; *********** } {xrst_end sweep_forward_any} */ // BEGIN_FORWARD_ANY template void forward_any( const RecBase& not_used_rec_base, const local::player* play, const size_t num_var, const size_t cap_order, bool* cskip_op, pod_vector& load_op2var, size_t change_count, size_t& change_number, size_t& change_op_index, std::ostream& s_out, const bool print, const size_t order_low, const size_t order_up, Base* taylor ) // END_FORWARD_ANY { // number of directions const size_t r = 1; CPPAD_ASSERT_UNKNOWN(order_low <= order_up ); CPPAD_ASSERT_UNKNOWN( cap_order >= order_up + 1 ); CPPAD_ASSERT_UNKNOWN( play->num_var() == num_var ); /* */ // initialize the comparison operator counter if( order_low == 0 ) { change_number = 0; change_op_index = 0; } // If this includes a zero calculation, initialize this information pod_vector vec_ad2isvar; pod_vector vec_ad2index; if( order_low == 0 ) { size_t i; // this includes order zero calculation, initialize vector indices size_t num = play->num_var_vec_ind(); if( num > 0 ) { vec_ad2isvar.extend(num); vec_ad2index.extend(num); for(i = 0; i < num; i++) { vec_ad2index[i] = play->GetVecInd(i); vec_ad2isvar[i] = false; } } // includes zero order, so initialize conditional skip flags num = play->num_var_op(); for(i = 0; i < num; i++) cskip_op[i] = false; } // information used by atomic function operators // work space used by atomic functions var_op::atomic_op_work atom_work; // information defined by atomic function operators size_t atom_index, atom_id, atom_m, atom_n; // length of the parameter vector (used by CppAD assert macros) const size_t num_par = play->num_par_all(); // pointer to the beginning of the parameter vector CPPAD_ASSERT_UNKNOWN( num_par > 0 ) const Base* parameter = play->par_ptr(); // length of the text vector (used by CppAD assert macros) const size_t num_text = play->num_var_text(); // pointer to the beginning of the text vector const char* text = nullptr; if( num_text > 0 ) text = play->GetTxt(0); /* */ // temporary indices size_t i; // skip the BeginOp at the beginning of the recording play::const_sequential_iterator itr = play->begin(); // op_info op_code_var op; size_t i_var; const addr_t* arg; itr.op_info(op, arg, i_var); CPPAD_ASSERT_UNKNOWN( op == BeginOp ); // # if CPPAD_FORWARD_ANY_TRACE // flag as to when to trace atomic function values bool atom_trace = true; # else bool atom_trace = false; # endif // bool more_operators = true; while(more_operators) { // next op (++itr).op_info(op, arg, i_var); CPPAD_ASSERT_UNKNOWN( itr.op_index() < play->num_var_op() ); // check if we are skipping this operation while( cskip_op[itr.op_index()] ) { switch(op) { case AFunOp: { // get information for this atomic function call play::atom_op_info( op, arg, atom_index, atom_id, atom_m, atom_n ); // // skip to the second AFunOp for(i = 0; i < atom_m + atom_n + 1; ++i) ++itr; # ifndef NDEBUG itr.op_info(op, arg, i_var); CPPAD_ASSERT_UNKNOWN( op == AFunOp ); # endif } break; case CSkipOp: case CSumOp: itr.correct_before_increment(); break; default: break; } (++itr).op_info(op, arg, i_var); } // action depends on the operator switch( op ) { case EqppOp: case EqpvOp: case EqvvOp: case LeppOp: case LepvOp: case LevpOp: case LevvOp: case LtppOp: case LtpvOp: case LtvpOp: case LtvvOp: case NeppOp: case NepvOp: case NevvOp: var_op::compare_forward_any(op, arg, parameter, cap_order, taylor, itr.op_index(), change_count, change_number, change_op_index ); break; // ------------------------------------------------- case AbsOp: var_op::abs_forward_any( order_low, order_up, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case AddvvOp: var_op::addvv_forward_any( order_low, order_up, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case AddpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::addpv_forward_any( order_low, order_up, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case AcosOp: // sqrt(1 - x * x), acos(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::acos_forward_any( order_low, order_up, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case AcoshOp: // sqrt(x * x - 1), acosh(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::acosh_forward_any( order_low, order_up, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case AsinOp: // sqrt(1 - x * x), asin(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::asin_forward_any( order_low, order_up, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case AsinhOp: // sqrt(1 + x * x), asinh(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::asinh_forward_any( order_low, order_up, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case AtanOp: // 1 + x * x, atan(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::atan_forward_any( order_low, order_up, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case AtanhOp: // 1 - x * x, atanh(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::atanh_forward_any( order_low, order_up, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case CExpOp: var_op::cexp_forward_any( order_low, order_up, i_var, arg, num_par, parameter, cap_order, taylor ); break; // --------------------------------------------------- case CosOp: // sin(x), cos(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::cos_forward_any( order_low, order_up, i_var, arg, cap_order, taylor ); break; // --------------------------------------------------- case CoshOp: // sinh(x), cosh(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::cosh_forward_any( order_low, order_up, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case CSkipOp: if( order_low == 0 ) { var_op::cskip_forward_0( i_var, arg, num_par, parameter, cap_order, taylor, cskip_op ); } itr.correct_before_increment(); break; // ------------------------------------------------- case CSumOp: var_op::csum_forward_any( order_low, order_up, i_var, arg, num_par, parameter, cap_order, taylor ); itr.correct_before_increment(); break; // ------------------------------------------------- case DisOp: var_op::dis_forward_dir( order_low, order_up, r, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case DivvvOp: var_op::divvv_forward_any( order_low, order_up, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case DivpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::divpv_forward_any( order_low, order_up, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case DivvpOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par ); var_op::divvp_forward_any( order_low, order_up, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case EndOp: CPPAD_ASSERT_NARG_NRES(op, 0, 0); more_operators = false; break; // ------------------------------------------------- case ErfOp: case ErfcOp: var_op::erf_forward_any( op, order_low, order_up, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case ExpOp: var_op::exp_forward_any( order_low, order_up, i_var, arg, cap_order, taylor ); break; // --------------------------------------------------- case Expm1Op: var_op::expm1_forward_any( order_low, order_up, i_var, arg, cap_order, taylor ); break; // --------------------------------------------------- case InvOp: CPPAD_ASSERT_NARG_NRES(op, 0, 1); break; // ------------------------------------------------- case LdpOp: case LdvOp: if( order_low == 0 ) { var_op::load_forward_0( op, i_var, play->num_var_vec_ind(), arg, num_var, num_par, parameter, cap_order, taylor, vec_ad2isvar, vec_ad2index, load_op2var ); if(order_low < order_up ) var_op::load_forward_nonzero( op, i_var, arg, order_low + 1, order_up, r, cap_order, load_op2var, taylor ); } else var_op::load_forward_nonzero( op, i_var, arg, order_low, order_up, r, cap_order, load_op2var, taylor ); break; // ------------------------------------------------- case LogOp: var_op::log_forward_any( order_low, order_up, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case Log1pOp: var_op::log1p_forward_any( order_low, order_up, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case MulpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::mulpv_forward_any( order_low, order_up, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case MulvvOp: var_op::mulvv_forward_any( order_low, order_up, i_var, arg, parameter, cap_order, taylor ); break; // -------------------------------------------------- case NegOp: var_op::neg_forward_any( order_low, order_up, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case ParOp: var_op::par_forward_any( order_low, order_up, i_var, arg, num_par, parameter, cap_order, taylor ); break; // ------------------------------------------------- case PowvpOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par ); var_op::powvp_forward_any( order_low, order_up, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case PowpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::powpv_forward_any( order_low, order_up, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case PowvvOp: var_op::powvv_forward_any( order_low, order_up, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case PriOp: if( ( order_low == 0 ) & print ) var_op::pri_forward_0(s_out, arg, num_text, text, num_par, parameter, cap_order, taylor ); break; // ------------------------------------------------- case SignOp: // sign(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::sign_forward_any( order_low, order_up, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case SinOp: // cos(x), sin(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::sin_forward_any( order_low, order_up, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case SinhOp: // cosh(x), sinh(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::sinh_forward_any( order_low, order_up, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case SqrtOp: var_op::sqrt_forward_any( order_low, order_up, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case StppOp: case StpvOp: case StvpOp: case StvvOp: if( order_low == 0 ) { var_op::store_forward_0( op, arg, num_var, num_par, parameter, cap_order, taylor, vec_ad2isvar, vec_ad2index ); } break; // ------------------------------------------------- case SubvvOp: var_op::subvv_forward_any( order_low, order_up, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case SubpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::subpv_forward_any( order_low, order_up, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case SubvpOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par ); var_op::subvp_forward_any( order_low, order_up, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case TanOp: // tan(x)^2, tan(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::tan_forward_any( order_low, order_up, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case TanhOp: // tanh(x)^2, tanh(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::tanh_forward_any( order_low, order_up, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case AFunOp: // start of an atomic function call var_op::atomic_forward_any( itr, play, parameter, atom_trace, atom_work, cap_order, order_low, order_up, taylor ); break; case FunapOp: case FunavOp: case FunrpOp: case FunrvOp: CPPAD_ASSERT_UNKNOWN( false ); break; // ------------------------------------------------- case ZmulpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::zmulpv_forward_any( order_low, order_up, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case ZmulvpOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par ); var_op::zmulvp_forward_any( order_low, order_up, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case ZmulvvOp: var_op::zmulvv_forward_any( order_low, order_up, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- default: CPPAD_ASSERT_UNKNOWN(false); } # if CPPAD_FORWARD_ANY_TRACE Base* Z_tmp = taylor + cap_order * i_var; if( op != AFunOp ) { printOp( std::cout, play, itr.op_index(), i_var, op, arg ); if( NumRes(op) > 0 ) printOpResult( std::cout, order_up + 1, Z_tmp, 0, (Base *) nullptr ); std::cout << std::endl; } } std::cout << std::endl; # else } # endif if( ( order_low == 0 ) && (change_count == 0) ) change_number = 0; return; } // preprocessor symbols that are local to this file # undef CPPAD_FORWARD_ANY_TRACE } } } // END_CPPAD_LOCAL_SWEEP_NAMESPACE # endif ================================================ FILE: include/cppad/local/sweep/forward_dir.hpp ================================================ # ifndef CPPAD_LOCAL_SWEEP_FORWARD_DIR_HPP # define CPPAD_LOCAL_SWEEP_FORWARD_DIR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # define CPPAD_FORWARD_DIR_TRACE 0 // BEGIN_CPPAD_LOCAL_SWEEP_NAMESPACE namespace CppAD { namespace local { namespace sweep { /* ------------------------------------------------------------------------------ {xrst_begin sweep_forward_dir dev} {xrst_spell cskip } {xrst_template ; include/cppad/local/sweep/template/forward_sweep.xrst headers all: CPPAD_@NAME@_TRACE, Portotype, Base, RecBase, play, num_var headers all: cap_order, cskip, load_op2var, taylor headers dir: order_up, n_dir @title@ The title for this forward sweep @#####@ Underlining for the tilte @NAME@ is one of FORWARD_0, FORWARD_ANY, FORWARD_DIR @****@ Underlining for @NAME@ @title@ ; Compute One Order Multiple Directions Forward Taylor Coefficients @#####@ ; ################################################################# @NAME@ ; FORWARD_DIR @****@ ; *********** } {xrst_end sweep_forward_dir} */ // BEGIN_FORWARD_DIR template void forward_dir( const RecBase& not_used_rec_base, const local::player* play, const size_t num_var, const size_t cap_order, const bool* cskip_op, const pod_vector& load_op2var, const size_t order_up, const size_t n_dir, Base* taylor ) // END_FORWARD_DIR { CPPAD_ASSERT_UNKNOWN( order_up > 0 ); CPPAD_ASSERT_UNKNOWN( cap_order >= order_up + 1 ); CPPAD_ASSERT_UNKNOWN( play->num_var() == num_var ); // only compute one order at a time when using multi-direction forward size_t order_low = order_up; // information used by atomic function operators // work space used by atomic functions var_op::atomic_op_work atom_work; // information defined by atomic function operators size_t atom_index=0, atom_id=0, atom_m=0, atom_n=0; // // length of the parameter vector (used by CppAD assert macros) const size_t num_par = play->num_par_all(); // pointer to the beginning of the parameter vector CPPAD_ASSERT_UNKNOWN( num_par > 0 ) const Base* parameter = play->par_ptr(); // temporary indices size_t i; // skip the BeginOp at the beginning of the recording play::const_sequential_iterator itr = play->begin(); // op_info op_code_var op; size_t i_var; const addr_t* arg; itr.op_info(op, arg, i_var); CPPAD_ASSERT_UNKNOWN( op == BeginOp ); # if CPPAD_FORWARD_DIR_TRACE bool atom_trace = true; std::cout << std::endl; CppAD::vector Z_vec(order_up + 1); # else bool atom_trace = false; # endif bool more_operators = true; while(more_operators) { // next op (++itr).op_info(op, arg, i_var); CPPAD_ASSERT_UNKNOWN( itr.op_index() < play->num_var_op() ); // check if we are skipping this operation while( cskip_op[itr.op_index()] ) { switch(op) { case AFunOp: { // get information for this atomic function call play::atom_op_info( op, arg, atom_index, atom_id, atom_m, atom_n ); // // skip to the second AFunOp for(i = 0; i < atom_m + atom_n + 1; ++i) ++itr; # ifndef NDEBUG itr.op_info(op, arg, i_var); CPPAD_ASSERT_UNKNOWN( op == AFunOp ); # endif } break; case CSkipOp: case CSumOp: itr.correct_before_increment(); break; default: break; } (++itr).op_info(op, arg, i_var); } // action depends on the operator switch( op ) { case AbsOp: var_op::abs_forward_dir( order_up, n_dir, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case AddvvOp: var_op::addvv_forward_dir( order_up, n_dir, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case AddpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::addpv_forward_dir( order_up, n_dir, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case AcosOp: // sqrt(1 - x * x), acos(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::acos_forward_dir( order_up, n_dir, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case AcoshOp: // sqrt(x * x - 1), acosh(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::acosh_forward_dir( order_up, n_dir, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case AsinOp: // sqrt(1 - x * x), asin(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::asin_forward_dir( order_up, n_dir, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case AsinhOp: // sqrt(1 + x * x), asinh(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::asinh_forward_dir( order_up, n_dir, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case AtanOp: // 1 + x * x, atan(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::atan_forward_dir( order_up, n_dir, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case AtanhOp: // 1 - x * x, atanh(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::atanh_forward_dir( order_up, n_dir, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case CExpOp: var_op::cexp_forward_dir( order_up, n_dir, i_var, arg, num_par, parameter, cap_order, taylor ); break; // --------------------------------------------------- case CosOp: // sin(x), cos(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::cos_forward_dir( order_up, n_dir, i_var, arg, cap_order, taylor ); break; // --------------------------------------------------- case CoshOp: // sinh(x), cosh(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::cosh_forward_dir( order_up, n_dir, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case CSkipOp: // CSkipOp only does something on order zero. CPPAD_ASSERT_UNKNOWN( order_low > 0 ); itr.correct_before_increment(); break; // ------------------------------------------------- case CSumOp: var_op::csum_forward_dir( order_up, i_var, arg, num_par, parameter, n_dir, cap_order, taylor ); itr.correct_before_increment(); break; // ------------------------------------------------- case DisOp: var_op::dis_forward_dir( order_low, order_up, n_dir, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case DivvvOp: var_op::divvv_forward_dir( order_up, n_dir, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case DivpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::divpv_forward_dir( order_up, n_dir, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case DivvpOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par ); var_op::divvp_forward_dir( order_up, n_dir, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case EndOp: // needed for sparse_jacobian test CPPAD_ASSERT_NARG_NRES(op, 0, 0); more_operators = false; break; // ------------------------------------------------- case ErfOp: case ErfcOp: var_op::erf_forward_dir( op, order_up, n_dir, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case ExpOp: var_op::exp_forward_dir( order_up, n_dir, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case Expm1Op: var_op::expm1_forward_dir( order_up, n_dir, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case InvOp: CPPAD_ASSERT_NARG_NRES(op, 0, 1); break; // ------------------------------------------------- case LdpOp: case LdvOp: var_op::load_forward_nonzero( op, i_var, arg, order_low, order_up, n_dir, cap_order, load_op2var, taylor ); break; // --------------------------------------------------- case EqppOp: case EqpvOp: case EqvvOp: case LtppOp: case LtpvOp: case LtvpOp: case LtvvOp: case LeppOp: case LepvOp: case LevpOp: case LevvOp: case NeppOp: case NepvOp: case NevvOp: CPPAD_ASSERT_UNKNOWN( order_up > 0 ); break; // ------------------------------------------------- case LogOp: var_op::log_forward_dir( order_up, n_dir, i_var, arg, cap_order, taylor ); break; // --------------------------------------------------- case Log1pOp: var_op::log1p_forward_dir( order_up, n_dir, i_var, arg, cap_order, taylor ); break; // --------------------------------------------------- case MulpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::mulpv_forward_dir( order_up, n_dir, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case MulvvOp: var_op::mulvv_forward_dir( order_up, n_dir, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case NegOp: var_op::neg_forward_dir( order_up, n_dir, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case ParOp: var_op::par_forward_dir( order_up, n_dir, i_var, arg, num_par, parameter, cap_order, taylor ); break; // ------------------------------------------------- case PowpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::powpv_forward_dir( order_up, n_dir, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case PowvpOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par ); var_op::powvp_forward_dir( order_up, n_dir, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case PowvvOp: var_op::powvv_forward_dir( order_up, n_dir, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case PriOp: CPPAD_ASSERT_UNKNOWN( order_up > 0 ); break; // ------------------------------------------------- case SignOp: // sign(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::sign_forward_dir( order_up, n_dir, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case SinOp: // cos(x), sin(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::sin_forward_dir( order_up, n_dir, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case SinhOp: // cosh(x), sinh(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::sinh_forward_dir( order_up, n_dir, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case SqrtOp: var_op::sqrt_forward_dir( order_up, n_dir, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case StppOp: case StpvOp: case StvpOp: case StvvOp: CPPAD_ASSERT_UNKNOWN( order_up > 0 ); break; // ------------------------------------------------- case SubvvOp: var_op::subvv_forward_dir( order_up, n_dir, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case SubpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::subpv_forward_dir( order_up, n_dir, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case SubvpOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par ); var_op::subvp_forward_dir( order_up, n_dir, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case TanOp: // tan(x)^2, tan(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::tan_forward_dir( order_up, n_dir, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case TanhOp: // tanh(x)^2, tanh(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::tanh_forward_dir( order_up, n_dir, i_var, arg, cap_order, taylor ); break; // ------------------------------------------------- case AFunOp: var_op::atomic_forward_dir( itr, play, parameter, atom_trace, atom_work, cap_order, order_up, n_dir, taylor ); break; case FunapOp: case FunavOp: case FunrpOp: case FunrvOp: break; // ------------------------------------------------- case ZmulpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::zmulpv_forward_dir( order_up, n_dir, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case ZmulvpOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par ); var_op::zmulvp_forward_dir( order_up, n_dir, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- case ZmulvvOp: var_op::zmulvv_forward_dir( order_up, n_dir, i_var, arg, parameter, cap_order, taylor ); break; // ------------------------------------------------- default: CPPAD_ASSERT_UNKNOWN(0); } # if CPPAD_FORWARD_DIR_TRACE if( op != AFunOp ) { printOp( std::cout, play, itr.op_index(), i_var, op, arg ); Base* Z_tmp = nullptr; if( op == FunavOp ) Z_tmp = taylor + size_t(arg[0])*((cap_order - 1) * n_dir + 1); else if( NumRes(op) > 0 ) Z_tmp = taylor + i_var*((cap_order - 1)*n_dir + 1); if( Z_tmp != nullptr ) { Z_vec[0] = Z_tmp[0]; for(size_t ell = 0; ell < n_dir; ell++) { std::cout << std::endl << " "; for(size_t p_tmp = 1; p_tmp <= order_up; p_tmp++) Z_vec[p_tmp] = Z_tmp[ (p_tmp-1)*n_dir + ell + 1]; printOpResult( std::cout, order_up + 1, Z_vec.data(), 0, (Base *) nullptr ); } } std::cout << std::endl; } } std::cout << std::endl; # else } # endif return; } // preprocessor symbols that are local to this file # undef CPPAD_FORWARD_DIR_TRACE } } } // END_CPPAD_LOCAL_SWEEP_NAMESPACE # endif ================================================ FILE: include/cppad/local/sweep/rev_hes.hpp ================================================ # ifndef CPPAD_LOCAL_SWEEP_REV_HES_HPP # define CPPAD_LOCAL_SWEEP_REV_HES_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include // BEGIN_CPPAD_LOCAL_SWEEP_NAMESPACE namespace CppAD { namespace local { namespace sweep { /*! \file sweep/rev_hes.hpp Compute Reverse mode Hessian sparsity patterns. */ /*! \def CPPAD_REV_HES_TRACE This value is either zero or one. Zero is the normal operational value. If it is one, a trace of every rev_hes_sweep computation is printed. */ # define CPPAD_REV_HES_TRACE 0 /*! Given the forward Jacobian sparsity pattern for all the variables, and the reverse Jacobian sparsity pattern for the dependent variables, RevHesSweep computes the Hessian sparsity pattern for all the independent variables. \tparam Base this operation sequence was recorded using AD. \tparam Vector_set is the type used for vectors of sets. It can be either sparse::pack_setvec or sparse::list_setvec. \param n is the number of independent variables on the tape. \param num_var is the total number of variables on the tape; i.e., play->num_var(). This is also the number of rows in the entire sparsity pattern rev_hes_sparse. \param play The information stored in play is a recording of the operations corresponding to a function \f[ F : {\bf R}^n \rightarrow {\bf R}^m \f] where \f$ n \f$ is the number of independent variables and \f$ m \f$ is the number of dependent variables. \param for_jac_sparse For i = 0 , ... , num_var - 1, (for all the variables on the tape), the forward Jacobian sparsity pattern for the variable with index i corresponds to the set with index i in for_jac_sparse. \param RevJac \b Input: For i = 0, ... , num_var - 1 the if the variable with index i on the tape is an dependent variable and included in the Hessian, RevJac[ i ] is equal to true, otherwise it is equal to false. \n \n \b Output: The values in RevJac upon return are not specified; i.e., it is used for temporary work space. \param rev_hes_sparse The reverse Hessian sparsity pattern for the variable with index i corresponds to the set with index i in rev_hes_sparse. \n \n \b Input: For i = 0 , ... , num_var - 1 the reverse Hessian sparsity pattern for the variable with index i is empty. \n \n \b Output: For j = 1 , ... , n, the reverse Hessian sparsity pattern for the independent dependent variable with index (j-1) is given by the set with index j in rev_hes_sparse. The values in the rest of rev_hes_sparse are not specified; i.e., they are used for temporary work space. \param not_used_rec_base Specifies RecBase for this call. */ template void rev_hes( const local::player* play, size_t num_var, const Vector_set& for_jac_sparse, bool* RevJac, Vector_set& rev_hes_sparse, const RecBase& not_used_rec_base ) { // length of the parameter vector (used by CppAD assert macros) const size_t num_par = play->num_par_all(); size_t i, j, k; // check num_var argument CPPAD_ASSERT_UNKNOWN( play->num_var() == num_var ); CPPAD_ASSERT_UNKNOWN( for_jac_sparse.n_set() == num_var ); CPPAD_ASSERT_UNKNOWN( rev_hes_sparse.n_set() == num_var ); CPPAD_ASSERT_UNKNOWN( num_var > 0 ); // upper limit exclusive for set elements size_t limit = rev_hes_sparse.end(); CPPAD_ASSERT_UNKNOWN( for_jac_sparse.end() == limit ); // check number of sets match CPPAD_ASSERT_UNKNOWN( for_jac_sparse.n_set() == rev_hes_sparse.n_set() ); // vecad_sparsity contains a sparsity pattern for each VecAD object. // vecad_ind maps a VecAD index (beginning of the VecAD object) // to the index for the corresponding set in vecad_sparsity. size_t num_vecad_ind = play->num_var_vec_ind(); size_t num_vecad_vec = play->num_var_vecad(); Vector_set vecad_sparse; pod_vector vecad_ind; pod_vector vecad_jac; if( num_vecad_vec > 0 ) { size_t length; vecad_sparse.resize(num_vecad_vec, limit); vecad_ind.extend(num_vecad_ind); vecad_jac.extend(num_vecad_vec); j = 0; for(i = 0; i < num_vecad_vec; i++) { // length of this VecAD length = play->GetVecInd(j); // set vecad_ind to proper index for this VecAD vecad_ind[j] = i; // make all other values for this vector invalid for(k = 1; k <= length; k++) vecad_ind[j+k] = num_vecad_vec; // start of next VecAD j += length + 1; // initialize this vector's reverse jacobian value vecad_jac[i] = false; } CPPAD_ASSERT_UNKNOWN( j == play->num_var_vec_ind() ); } // ---------------------------------------------------------------------- // // work space used by atomic functions var_op::atomic_op_work atom_work; // // pointer to the beginning of the parameter vector // (used by atomic functions CPPAD_ASSERT_UNKNOWN( num_par > 0 ) const Base* parameter = play->par_ptr(); // // skip the EndOp at the end of the recording play::const_sequential_iterator itr = play->end(); // op_info op_code_var op; size_t i_var; const addr_t* arg; itr.op_info(op, arg, i_var); CPPAD_ASSERT_UNKNOWN( op == EndOp ); # if CPPAD_REV_HES_TRACE std::cout << std::endl; CppAD::vectorBool zf_value(limit); CppAD::vectorBool zh_value(limit); bool atom_trace = true; # else bool atom_trace = false; # endif bool more_operators = true; while(more_operators) { bool linear[3]; // // next op (--itr).op_info(op, arg, i_var); // rest of information depends on the case switch( op ) { // // linear operators with one primary result // and where the first argument is the only variable case AbsOp: case DivvpOp: case NegOp: case SubvpOp: case ZmulvpOp: CPPAD_ASSERT_UNKNOWN( 0 < NumArg(op) ); linear[0] = true; var_op::one_var_rev_hes( i_var, size_t(arg[0]), linear, RevJac, for_jac_sparse, rev_hes_sparse ); break; // ------------------------------------------------- // non-linear operators with one primary result // and where the first argument is the only variable case AcosOp: case AcoshOp: case AsinOp: case AsinhOp: case AtanOp: case AtanhOp: case CosOp: case CoshOp: case ErfOp: case ErfcOp: case ExpOp: case Expm1Op: case LogOp: case Log1pOp: case PowvpOp: case SinOp: case SinhOp: case SqrtOp: case TanOp: case TanhOp: CPPAD_ASSERT_UNKNOWN( 0 < NumArg(op) ); linear[0] = false; var_op::one_var_rev_hes( i_var, size_t(arg[0]), linear, RevJac, for_jac_sparse, rev_hes_sparse ); break; // ------------------------------------------------- // linear and non-liner operators with one primary result // and where the second argument is the only variable case AddpvOp: case MulpvOp: case SubpvOp: case ZmulpvOp: CPPAD_ASSERT_UNKNOWN( 1 < NumArg(op) ); linear[0] = true; var_op::one_var_rev_hes( i_var, size_t(arg[1]), linear, RevJac, for_jac_sparse, rev_hes_sparse ); break; case PowpvOp: case DivpvOp: CPPAD_ASSERT_UNKNOWN( 1 < NumArg(op) ); linear[0] = false; var_op::one_var_rev_hes( i_var, size_t(arg[1]), linear, RevJac, for_jac_sparse, rev_hes_sparse ); break; // ------------------------------------------------- case AddvvOp: case SubvvOp: linear[0] = true; linear[1] = true; linear[2] = true; var_op::two_var_rev_hes( i_var, arg, linear, RevJac, for_jac_sparse, rev_hes_sparse ); break; // ------------------------------------------------- case BeginOp: CPPAD_ASSERT_NARG_NRES(op, 1, 1) more_operators = false; break; // ------------------------------------------------- case CSkipOp: itr.correct_after_decrement(arg); break; // ------------------------------------------------- case CSumOp: itr.correct_after_decrement(arg); var_op::csum_rev_hes( i_var, arg, RevJac, rev_hes_sparse ); break; // ------------------------------------------------- case CExpOp: var_op::cexp_rev_hes( i_var, arg, num_par, RevJac, rev_hes_sparse ); break; // ------------------------------------------------- case DisOp: CPPAD_ASSERT_NARG_NRES(op, 2, 1) // derivativve is identically zero break; // ------------------------------------------------- case DivvvOp: linear[0] = true; linear[1] = false; linear[2] = false; CPPAD_ASSERT_NARG_NRES(op, 2, 1) var_op::two_var_rev_hes( i_var, arg, linear, RevJac, for_jac_sparse, rev_hes_sparse ); break; // ------------------------------------------------- case InvOp: CPPAD_ASSERT_NARG_NRES(op, 0, 1) // Z is already defined break; // ------------------------------------------------- case LdpOp: case LdvOp: var_op::load_rev_hes( op, arg, num_vecad_ind, i_var, vecad_ind, rev_hes_sparse, vecad_sparse, RevJac, vecad_jac ); break; // ------------------------------------------------- case EqppOp: case EqpvOp: case EqvvOp: case LtppOp: case LtpvOp: case LtvpOp: case LtvvOp: case LeppOp: case LepvOp: case LevpOp: case LevvOp: case NeppOp: case NepvOp: case NevvOp: CPPAD_ASSERT_NARG_NRES(op, 2, 0); break; // ------------------------------------------------- case MulvvOp: case ZmulvvOp: linear[0] = true; linear[1] = true; linear[2] = false; CPPAD_ASSERT_NARG_NRES(op, 2, 1) var_op::two_var_rev_hes( i_var, arg, linear, RevJac, for_jac_sparse, rev_hes_sparse ); break; // ------------------------------------------------- case ParOp: CPPAD_ASSERT_NARG_NRES(op, 1, 1) break; // ------------------------------------------------- case PowvvOp: linear[0] = false; linear[1] = false; linear[2] = false; CPPAD_ASSERT_NARG_NRES(op, 2, 3) var_op::two_var_rev_hes( i_var, arg, linear, RevJac, for_jac_sparse, rev_hes_sparse ); break; // ------------------------------------------------- case PriOp: CPPAD_ASSERT_NARG_NRES(op, 5, 0); break; // ------------------------------------------------- case SignOp: CPPAD_ASSERT_NARG_NRES(op, 1, 1); // Derivative is identiaclly zero break; // ------------------------------------------------- case StppOp: case StpvOp: case StvpOp: case StvvOp: var_op::store_rev_hes( op, arg, num_vecad_ind, vecad_ind, rev_hes_sparse, vecad_sparse, RevJac, vecad_jac ); break; // ------------------------------------------------- case AFunOp: var_op::atomic_rev_hes( itr, play, parameter, atom_trace, atom_work, for_jac_sparse, RevJac, rev_hes_sparse ); break; case FunapOp: case FunavOp: case FunrpOp: case FunrvOp: CPPAD_ASSERT_UNKNOWN(false); break; // ------------------------------------------------- default: CPPAD_ASSERT_UNKNOWN(0); } # if CPPAD_REV_HES_TRACE if( op != AFunOp ) { for(j = 0; j < limit; j++) { zf_value[j] = false; zh_value[j] = false; } typename Vector_set::const_iterator itr_jac(for_jac_sparse, i_var); j = *itr_jac; while( j < limit ) { zf_value[j] = true; j = *(++itr_jac); } typename Vector_set::const_iterator itr_hes(rev_hes_sparse, i_var); j = *itr_hes; while( j < limit ) { zh_value[j] = true; j = *(++itr_hes); } printOp( std::cout, play, itr.op_index(), i_var, op, arg ); // should also print RevJac[i_var], but printOpResult does not // yet allow for this if( NumRes(op) > 0 && op != BeginOp ) printOpResult( std::cout, 1, &zf_value, 1, &zh_value ); std::cout << std::endl; } } std::cout << std::endl; # else } # endif // values corresponding to BeginOp CPPAD_ASSERT_UNKNOWN( itr.op_index() == 0 ); CPPAD_ASSERT_UNKNOWN( i_var == 0 ); return; } } } } // END_CPPAD_LOCAL_SWEEP_NAMESPACE // preprocessor symbols that are local to this file # undef CPPAD_REV_HES_TRACE # endif ================================================ FILE: include/cppad/local/sweep/rev_jac.hpp ================================================ # ifndef CPPAD_LOCAL_SWEEP_REV_JAC_HPP # define CPPAD_LOCAL_SWEEP_REV_JAC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include // This value is either zero or one. Zero is the normal operational value. // If it is one, a trace of every rev_jac_sweep computation is printed. # define CPPAD_REV_JAC_TRACE 0 /* {xrst_begin local_sweep_rev_jac dev} {xrst_spell setvec } Reverse Mode Jacobian Sparsity Patterns ####################################### Syntax ****** | ``local::sweep::rev_jac`` ( | |tab| *play* , | |tab| *dependency* , | |tab| *n* , | |tab| *num_var* , | |tab| *var_sparsity* , | |tab| ``not_used_rec_base`` | ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } Base **** this operation sequence was recorded using ``AD`` < *Base* > . Vector_set ********** is the type used for vectors of sets. It can be either ``sparse::pack_setvec`` or ``sparse::list_setvec`` . {xrst_comment 2DO: in previous line change code to cref} RecBase ******* Is the base type when this function was recorded. This is different from *Base* if this function object was created by :ref:`base2ad-name` . play **** The information stored in play is a recording of the operations corresponding to a function :math:`F : \B{R}^n \rightarrow \B{R}^m` where *m* is the number of dependent variables. dependency ********** Are we computing dependency relations, or only concerned with possibly non-zero derivatives. For example, are the derivatives with respect to *left* and *right* of the expression below considered to be non-zero: ``CondExpRel`` ( *left* , *right* , *if_true* , *if_false* ) This is used by the optimizer to obtain the correct dependency relations. n * is the number of independent variables in the tape. num_var ******* is the total number of variables in the tape; i.e., *play* ``->num_var`` () . This is also the number of rows in all the sparsity patterns. var_sparsity ************ On Input ======== For *i* = 0 , ... , *num_var* ``-1`` , if *i* corresponds to a dependent variables, the set with index *i* is an input. Otherwise the set with index *i* is empty. On Output ========= For *i* = 0 , ... , *num_var* ``-1`` , the sparsity pattern for the variable with index *j* ``-1`` is given by the set with index *j* in *var_sparsity* . Note that one dependent variable may depend on the value of another, in which case its output sparsity pattern may be different than its input pattern. not_used_rec_base ***************** Specifies *RecBase* for this call. {xrst_end local_sweep_rev_jac} */ // BEGIN_CPPAD_LOCAL_SWEEP_NAMESPACE namespace CppAD { namespace local { namespace sweep { // BEGIN_PROTOTYPE template void rev_jac( const local::player* play , bool dependency , size_t n , size_t num_var , Vector_set& var_sparsity , const RecBase& not_used_rec_base ) // END_PROTOTYPE { size_t i, j, k; // length of the parameter vector (used by CppAD assert macros) const size_t num_par = play->num_par_all(); // check num_var argument CPPAD_ASSERT_UNKNOWN( num_var > 0 ); CPPAD_ASSERT_UNKNOWN( play->num_var() == num_var ); CPPAD_ASSERT_UNKNOWN( var_sparsity.n_set() == num_var ); // upper limit (exclusive) for elements in the set size_t limit = var_sparsity.end(); // vecad_sparsity contains a sparsity pattern for each VecAD object. // vecad_ind maps a VecAD index (beginning of the VecAD object) // to the index of the corresponding set in vecad_sparsity. size_t num_vecad_ind = play->num_var_vec_ind(); size_t num_vecad_vec = play->num_var_vecad(); Vector_set vecad_sparsity; pod_vector vecad_ind; if( num_vecad_vec > 0 ) { size_t length; vecad_sparsity.resize(num_vecad_vec, limit); vecad_ind.extend(num_vecad_ind); j = 0; for(i = 0; i < num_vecad_vec; i++) { // length of this VecAD length = play->GetVecInd(j); // set to proper index for this VecAD vecad_ind[j] = i; for(k = 1; k <= length; k++) vecad_ind[j+k] = num_vecad_vec; // invalid index // start of next VecAD j += length + 1; } CPPAD_ASSERT_UNKNOWN( j == play->num_var_vec_ind() ); } // ---------------------------------------------------------------------- // // work space used by atomic functions var_op::atomic_op_work atom_work; // // pointer to the beginning of the parameter vector // (used by atomic functions CPPAD_ASSERT_UNKNOWN( num_par > 0 ) const Base* parameter = play->par_ptr(); // // skip the EndOp at the end of the recording play::const_sequential_iterator itr = play->end(); // op_info op_code_var op; size_t i_var; const addr_t* arg; itr.op_info(op, arg, i_var); CPPAD_ASSERT_UNKNOWN( op == EndOp ); # if CPPAD_REV_JAC_TRACE std::cout << std::endl; CppAD::vectorBool z_value(limit); bool atom_trace = true; # else bool atom_trace = false; # endif bool more_operators = true; while(more_operators) { // // next op (--itr).op_info(op, arg, i_var); // rest of information depends on the case switch( op ) { // // operators with one primary result and // where the first argument is the only variable case AbsOp: case AcosOp: case AcoshOp: case AsinOp: case AsinhOp: case AtanOp: case AtanhOp: case CosOp: case CoshOp: case DivvpOp: case ErfOp: case ErfcOp: case ExpOp: case Expm1Op: case LogOp: case NegOp: case Log1pOp: case PowvpOp: case SinOp: case SinhOp: case SqrtOp: case SubvpOp: case TanOp: case TanhOp: case ZmulvpOp: CPPAD_ASSERT_UNKNOWN( 0 < NumArg(op) ); var_op::one_var_rev_jac( i_var, size_t(arg[0]), var_sparsity ); break; // ------------------------------------------------- // operators with one primary result and // where the second argument is the only variable case AddpvOp: case DivpvOp: case MulpvOp: case PowpvOp: case SubpvOp: case ZmulpvOp: CPPAD_ASSERT_UNKNOWN( 1 < NumArg(op) ); var_op::one_var_rev_jac( i_var, size_t(arg[1]), var_sparsity ); break; // ------------------------------------------------- case AddvvOp: CPPAD_ASSERT_NARG_NRES(op, 2, 1); var_op::two_var_rev_jac( i_var, arg, var_sparsity ); break; // ------------------------------------------------- case BeginOp: CPPAD_ASSERT_NARG_NRES(op, 1, 1); more_operators = false; break; // ------------------------------------------------- case CSkipOp: itr.correct_after_decrement(arg); break; // ------------------------------------------------- case CSumOp: itr.correct_after_decrement(arg); var_op::csum_rev_jac( i_var, arg, var_sparsity ); break; // ------------------------------------------------- case CExpOp: var_op::cexp_rev_jac( dependency, i_var, arg, num_par, var_sparsity ); break; // --------------------------------------------------- case DisOp: CPPAD_ASSERT_NARG_NRES(op, 2, 1); // derivative is identically zero but dependency is not if( dependency ) var_op::one_var_rev_jac( i_var, size_t(arg[1]), var_sparsity ); break; // ------------------------------------------------- case DivvvOp: CPPAD_ASSERT_NARG_NRES(op, 2, 1); var_op::two_var_rev_jac( i_var, arg, var_sparsity ); break; // ------------------------------------------------- case InvOp: CPPAD_ASSERT_NARG_NRES(op, 0, 1); break; // ------------------------------------------------- case LdpOp: case LdvOp: var_op::load_rev_jac( op, num_vecad_ind, i_var, arg, dependency, vecad_ind, var_sparsity, vecad_sparsity ); break; // ------------------------------------------------- case EqppOp: case EqpvOp: case EqvvOp: case LtppOp: case LtpvOp: case LtvpOp: case LtvvOp: case LeppOp: case LepvOp: case LevpOp: case LevvOp: case NeppOp: case NepvOp: case NevvOp: CPPAD_ASSERT_NARG_NRES(op, 2, 0); break; // ------------------------------------------------- case MulvvOp: CPPAD_ASSERT_NARG_NRES(op, 2, 1); var_op::two_var_rev_jac( i_var, arg, var_sparsity ); break; // ------------------------------------------------- case ParOp: CPPAD_ASSERT_NARG_NRES(op, 1, 1); break; // ------------------------------------------------- case PowvvOp: CPPAD_ASSERT_NARG_NRES(op, 2, 3); var_op::two_var_rev_jac( i_var, arg, var_sparsity ); break; // ------------------------------------------------- case PriOp: CPPAD_ASSERT_NARG_NRES(op, 5, 0); break; // ------------------------------------------------- case SignOp: CPPAD_ASSERT_NARG_NRES(op, 1, 1); // derivative is identically zero but dependency is not if( dependency ) var_op::one_var_rev_jac( i_var, size_t(arg[0]), var_sparsity ); break; // ------------------------------------------------- case StppOp: case StpvOp: case StvpOp: case StvvOp: var_op::store_rev_jac( op, num_vecad_ind, arg, dependency, vecad_ind, var_sparsity, vecad_sparsity ); break; // ------------------------------------------------- case SubvvOp: CPPAD_ASSERT_NARG_NRES(op, 2, 1); var_op::two_var_rev_jac( i_var, arg, var_sparsity ); break; // ------------------------------------------------- case AFunOp: var_op::atomic_rev_jac( itr, play, parameter, atom_trace, atom_work, dependency, var_sparsity ); break; case FunapOp: case FunavOp: case FunrpOp: case FunrvOp: CPPAD_ASSERT_UNKNOWN( false ); break; // ------------------------------------------------- case ZmulvvOp: CPPAD_ASSERT_NARG_NRES(op, 2, 1); var_op::two_var_rev_jac( i_var, arg, var_sparsity ); break; // ------------------------------------------------- default: CPPAD_ASSERT_UNKNOWN(0); } # if CPPAD_REV_JAC_TRACE if( op != AFunOp ) { for(j = 0; j < limit; j++) z_value[j] = false; typename Vector_set::const_iterator itr_sparse(var_sparsity, i_var); j = *itr_sparse; while( j < limit ) { z_value[j] = true; j = *(++itr_sparse); } printOp( std::cout, play, itr.op_index(), i_var, op, arg ); // Note that sparsity for FunrvOp are computed before call to // atomic function so no need to delay printing (as in forward mode) if( NumRes(op) > 0 && op != BeginOp ) printOpResult( std::cout, 0, (CppAD::vectorBool *) nullptr, 1, &z_value ); std::cout << std::endl; } } std::cout << std::endl; # else } # endif // values corresponding to BeginOp CPPAD_ASSERT_UNKNOWN( itr.op_index() == 0 ); CPPAD_ASSERT_UNKNOWN( i_var == 0 ); return; } // preprocessor symbols that are local to this file # undef CPPAD_REV_JAC_TRACE } } } // END_CPPAD_LOCAL_SWEEP_NAMESPACE # endif ================================================ FILE: include/cppad/local/sweep/reverse.hpp ================================================ # ifndef CPPAD_LOCAL_SWEEP_REVERSE_HPP # define CPPAD_LOCAL_SWEEP_REVERSE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include // BEGIN_CPPAD_LOCAL_SWEEP_NAMESPACE namespace CppAD { namespace local { namespace sweep { /*! \file sweep/reverse.hpp Compute derivatives of arbitrary order Taylor coefficients. */ /*! \def CPPAD_REVERSE_TRACE This value is either zero or one. Zero is the normal operational value. If it is one, a trace of every reverse_sweep computation is printed. */ # define CPPAD_REVERSE_TRACE 0 /*! Compute derivative of arbitrary order forward mode Taylor coefficients. \tparam Base this operation sequence was recorded using AD and computations by this routine are done using type Base. \param n is the number of independent variables on the tape. \param num_var is the total number of variables on the tape. This is also equal to the number of rows in the matrix Taylor; i.e., play->num_var(). \param play The information stored in play is a recording of the operations corresponding to the function \f[ F : {\bf R}^n \rightarrow {\bf R}^m \f] where \f$ n \f$ is the number of independent variables and \f$ m \f$ is the number of dependent variables. We define \f$ u^{(k)} \f$ as the value of x_k in the previous call of the form f.Forward(k, x_k) We define \f$ X : {\bf R}^{n \times d} \rightarrow {\bf R}^n \f$ by \f[ X(t, u) = u^{(0)} + u^{(1)} t + \cdots + u^{(d)} t^d \f] We define \f$ Y : {\bf R}^{n \times d} \rightarrow {\bf R}^m \f$ by \f[ Y(t, u) = F[ X(t, u) ] \f] We define the function \f$ W : {\bf R}^{n \times d} \rightarrow {\bf R} \f$ by \f[ W(u) = \sum_{k=0}^{d} ( w^{(k)} )^{\rm T} \frac{1}{k !} \frac{\partial^k}{\partial t^k} Y(0, u) \f] (The matrix \f$ w \in {\bf R}^m \f$, is defined below under the heading Partial.) Note that the scale factor 1 / k converts the k-th partial derivative to the k-th order Taylor coefficient. This routine computes the derivative of \f$ W(u) \f$ with respect to all the Taylor coefficients \f$ u^{(k)} \f$ for \f$ k = 0 , ... , d \f$. \param J Is the number of columns in the coefficient matrix Taylor. This must be greater than or equal d + 1. \param Taylor For i = 1 , ... , num_var, and for k = 0 , ... , d, Taylor [ i * J + k ] is the k-th order Taylor coefficient corresponding to variable with index i on the tape. The value \f$ u \in {\bf R}^{n \times d} \f$, at which the derivative is computed, is defined by \f$ u_j^{(k)} \f$ = Taylor [ j * J + k ] for j = 1 , ... , n, and for k = 0 , ... , d. \param K Is the number of columns in the partial derivative matrix Partial. It must be greater than or equal d + 1. \param Partial \b Input: The last \f$ m \f$ rows of Partial are inputs. The matrix \f$ w \f$, used to define \f$ W(u) \f$, is specified by these rows. For i = 0 , ... , m - 1, for k = 0 , ... , d, Partial [ (num_var - m + i ) * K + k ] = w[i,k]. \n \n \b Temporary: For i = n+1 , ... , num_var - 1 and for k = 0 , ... , d, the value of Partial [ i * K + k ] is used for temporary work space and its output value is not defined. \n \n \b Output: For j = 1 , ... , n and for k = 0 , ... , d, Partial [ j * K + k ] is the partial derivative of \f$ W( u ) \f$ with respect to \f$ u_j^{(k)} \f$. \param cskip_op Is a vector with size play->num_var_op(). If cskip_op[i] is true, the operator index i in the recording does not affect any of the dependent variable (given the value of the independent variables). Note that all the operators in an atomic function call are skipped as a block, so only the last AFunOp fore each call needs to have cskip_op[i] true. \param load_op2var is a vector with size play->num_var_load(). It contains the variable index corresponding to each load instruction. In the case where the index is zero, the instruction corresponds to a parameter (not variable). \tparam Iterator This is either player::const_iteratoror player::const_subgraph_iterator. \param play_itr On input this is either play->end(), for the entire graph, or play->end(subgraph), for a subgraph. This routine mode will use --play_itr to iterate over the graph or subgraph. It is assumes that the iterator starts just past the EndOp and it will continue until it reaches the BeginOp. If i_var is a variable index, and the corresponding operator is not in the subgraph, then the partials with respect to i_var are not modified and need to be initialized as zero. Note that this means the partial for the independent variables, that are not in the subgraph are not calculated. If part of an atomic function call is in the subgraph, the entire atomic function call must be in the subgraph. \param not_used_rec_base Specifies RecBase for this call. \par Assumptions The first operator on the tape is a BeginOp, and the next n operators are InvOp operations for the corresponding independent variables; see play->check_inv_op(n_ind). */ template void reverse( size_t num_var, const local::player* play, size_t cap_order, const Base* Taylor, size_t K, Base* Partial, bool* cskip_op, const pod_vector& load_op2var, Iterator& play_itr, const RecBase& not_used_rec_base ) { // check num_var argument CPPAD_ASSERT_UNKNOWN( play->num_var() == num_var ); CPPAD_ASSERT_UNKNOWN( num_var > 0 ); // length of the parameter vector (used by CppAD assert macros) const size_t num_par = play->num_par_all(); // pointer to the beginning of the parameter vector CPPAD_ASSERT_UNKNOWN( num_par > 0 ) const Base* parameter = play->par_ptr(); // work space used by atomic functions var_op::atomic_op_work atom_work; // // information defined by atomic forward size_t atom_index=0, atom_old=0, atom_m=0, atom_n=0; // A vector with unspecified contents declared here so that operator // routines do not need to re-allocate it vector work; // Initialize # if CPPAD_REVERSE_TRACE std::cout << std::endl; bool atom_trace = true; # else bool atom_trace = false; # endif op_code_var op; const addr_t* arg; size_t i_var; play_itr.op_info(op, arg, i_var); CPPAD_ASSERT_UNKNOWN( op == EndOp ); while(op != BeginOp ) { // // next op (--play_itr).op_info(op, arg, i_var); // check if we are skipping this operation size_t i_op = play_itr.op_index(); while( cskip_op[i_op] ) { switch(op) { case CSumOp: case CSkipOp: // cases that never happen but the code reads better with them play_itr.correct_after_decrement(arg); break; case AFunOp: { // get information for this atomic function call play::atom_op_info( op, arg, atom_index, atom_old, atom_m, atom_n ); // // skip to the first AFunOp for(size_t i = 0; i < atom_m + atom_n + 1; ++i) --play_itr; play_itr.op_info(op, arg, i_var); CPPAD_ASSERT_UNKNOWN( op == AFunOp ); } break; default: break; } (--play_itr).op_info(op, arg, i_var); i_op = play_itr.op_index(); } # if CPPAD_REVERSE_TRACE if( op != AFunOp ) { size_t i_tmp = i_var; const Base* Z_tmp = Taylor + i_var * cap_order; const Base* pZ_tmp = Partial + i_var * K; printOp( std::cout, play, i_op, i_tmp, op, arg ); if( NumRes(op) > 0 && op != BeginOp ) printOpResult( std::cout, K, Z_tmp, K, pZ_tmp ); std::cout << std::endl; } # endif switch( op ) { case AbsOp: var_op::abs_reverse( i_var, arg, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case AcosOp: // sqrt(1 - x * x), acos(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::acos_reverse( i_var, arg, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case AcoshOp: // sqrt(x * x - 1), acosh(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::acosh_reverse( i_var, arg, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case AddvvOp: var_op::addvv_reverse( i_var, arg, parameter, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case AddpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::addpv_reverse( i_var, arg, parameter, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case AsinOp: // sqrt(1 - x * x), asin(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::asin_reverse( i_var, arg, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case AsinhOp: // sqrt(1 + x * x), asinh(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::asinh_reverse( i_var, arg, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case AtanOp: // 1 + x * x, atan(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::atan_reverse( i_var, arg, cap_order, Taylor, K, Partial ); break; // ------------------------------------------------- case AtanhOp: // 1 - x * x, atanh(x) CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::atanh_reverse( i_var, arg, cap_order, Taylor, K, Partial ); break; // ------------------------------------------------- case BeginOp: CPPAD_ASSERT_NARG_NRES(op, 1, 1); CPPAD_ASSERT_UNKNOWN( i_op == 0 ); break; // -------------------------------------------------- case CSkipOp: // CSkipOp has a zero order forward action. play_itr.correct_after_decrement(arg); break; // ------------------------------------------------- case CSumOp: play_itr.correct_after_decrement(arg); var_op::csum_reverse( i_var, arg, K, Partial ); // end of a cumulative summation break; // ------------------------------------------------- case CExpOp: var_op::cexp_reverse( i_var, arg, num_par, parameter, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case CosOp: CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::cos_reverse( i_var, arg, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case CoshOp: CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::cosh_reverse( i_var, arg, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case DisOp: // Derivative of discrete operation is zero so no // contribution passes through this operation. break; // -------------------------------------------------- case DivvvOp: var_op::divvv_reverse( i_var, arg, parameter, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case DivpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::divpv_reverse( i_var, arg, parameter, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case DivvpOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par ); var_op::divvp_reverse( i_var, arg, parameter, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case EndOp: CPPAD_ASSERT_UNKNOWN( i_op == play->num_var_op() - 1 ); break; // -------------------------------------------------- case ErfOp: case ErfcOp: var_op::erf_reverse( op, i_var, arg, parameter, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case ExpOp: var_op::exp_reverse( i_var, arg, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case Expm1Op: var_op::expm1_reverse( i_var, arg, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case InvOp: break; // -------------------------------------------------- case LdpOp: case LdvOp: var_op::load_reverse( op, i_var, arg, load_op2var, cap_order, K, Partial ); break; // ------------------------------------------------- case EqppOp: case EqpvOp: case EqvvOp: case LtppOp: case LtpvOp: case LtvpOp: case LtvvOp: case LeppOp: case LepvOp: case LevpOp: case LevvOp: case NeppOp: case NepvOp: case NevvOp: break; // ------------------------------------------------- case LogOp: var_op::log_reverse( i_var, arg, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case Log1pOp: var_op::log1p_reverse( i_var, arg, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case MulpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::mulpv_reverse( i_var, arg, parameter, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case MulvvOp: var_op::mulvv_reverse( i_var, arg, parameter, cap_order, Taylor, K, Partial ); break; // ------------------------------------------------- case NegOp: var_op::neg_reverse( i_var, arg, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case ParOp: break; // -------------------------------------------------- case PowvpOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par ); var_op::powvp_reverse( i_var, arg, parameter, cap_order, Taylor, K, Partial, work ); break; // ------------------------------------------------- case PowpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::powpv_reverse( i_var, arg, parameter, cap_order, Taylor, K, Partial ); break; // ------------------------------------------------- case PowvvOp: var_op::powvv_reverse( i_var, arg, parameter, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case PriOp: // no result so nothing to do break; // -------------------------------------------------- case SignOp: CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::sign_reverse( i_var, arg, cap_order, Taylor, K, Partial ); break; // ------------------------------------------------- case SinOp: CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::sin_reverse( i_var, arg, cap_order, Taylor, K, Partial ); break; // ------------------------------------------------- case SinhOp: CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::sinh_reverse( i_var, arg, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case SqrtOp: var_op::sqrt_reverse( i_var, arg, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case StppOp: break; // -------------------------------------------------- case StpvOp: break; // ------------------------------------------------- case StvpOp: break; // ------------------------------------------------- case StvvOp: break; // -------------------------------------------------- case SubvvOp: var_op::subvv_reverse( i_var, arg, parameter, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case SubpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::subpv_reverse( i_var, arg, parameter, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case SubvpOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par ); var_op::subvp_reverse( i_var, arg, parameter, cap_order, Taylor, K, Partial ); break; // ------------------------------------------------- case TanOp: CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::tan_reverse( i_var, arg, cap_order, Taylor, K, Partial ); break; // ------------------------------------------------- case TanhOp: CPPAD_ASSERT_UNKNOWN( i_var < num_var ); var_op::tanh_reverse( i_var, arg, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case AFunOp: var_op::atomic_reverse( play_itr, play, parameter, atom_trace, atom_work, cap_order, K, Taylor, Partial ); break; case FunapOp: case FunavOp: case FunrpOp: case FunrvOp: CPPAD_ASSERT_UNKNOWN(false); break; // ------------------------------------------------------------ case ZmulpvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); var_op::zmulpv_reverse( i_var, arg, parameter, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case ZmulvpOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par ); var_op::zmulvp_reverse( i_var, arg, parameter, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- case ZmulvvOp: var_op::zmulvv_reverse( i_var, arg, parameter, cap_order, Taylor, K, Partial ); break; // -------------------------------------------------- default: CPPAD_ASSERT_UNKNOWN(false); } } # if CPPAD_REVERSE_TRACE std::cout << std::endl; # endif } } } } // END_CPPAD_LOCAL_SWEEP_NAMESPACE // preprocessor symbols that are local to this file # undef CPPAD_REVERSE_TRACE # endif ================================================ FILE: include/cppad/local/sweep/template/forward_sweep.xrst ================================================ {xrst_comment SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later SPDX-FileCopyrightText: Bradley M. Bell SPDX-FileContributor: 2024-25 Bradley M. Bell Under Construction: This file is intended to help document the forward_* sweep functions. headers all: CPPAD_@NAME@_TRACE, Portotype, Base, RecBase, play, num_var headers all: cap_order, cskip, load_op2var, taylor headers 0 & any: change_count, change_number, change_op_index, sout, print headers any: order_low, order_up headers dir: order_up, n_dir It has the following xrst template replacements: @title@ The title for this forward sweep @#####@ Underlining for the tilte @NAME@ is one of FORWARD_0, FORWARD_ANY, FORWARD_DIR Prototype is surrounded by // BEGIN_@NAME@ and // END_@NAME@ @****@ Underlining for @NAME@ } @title@ @#####@ CPPAD_@NAME@_TRACE ******@****@****** This value is defined in the source code for this function and is either zero or one. Zero is the normal operational value. If it is one, a trace of every zero order forward mode computation is printed. {xrst_comment ----------------------------------------------------------------} {xrst_suspend @NAME@ != FORWARD_0} order_low ********* We use *order_low* for the lowest order Taylor coefficient that is computed which is zero for this case. {xrst_resume} {xrst_comment ----------------------------------------------------------------} {xrst_suspend @NAME@ != FORWARD_DIR} order_low ********* We use *order_low* for the lowest order Taylor coefficient that is computed which is equal to *order_up* and non-zero for this case. {xrst_resume} {xrst_comment ----------------------------------------------------------------} Prototype ********* {xrst_literal // BEGIN_@NAME@ // END_@NAME@ } Base **** The type used during the forward mode computations. RecBase ******* The type use to record these operations. This is the same as Base except for the :ref:`base2ad-name` case where Base = AD< RecBase > . not_used_rec_base ***************** This only present so that *RecBase* can be determined without it having to be specified as a template parameter in a call. play **** The information stored in play is a recording of the operations corresponding to a function .. math:: f : \B{R}^n \rightarrow \B{R}^m where *n* is the number of independent variables and *m* is the number of dependent variables. num_var ******* is the total number of variables on the tape; i.e., *play* ``->num_var()`` . cap_order ********* Is the maximum number of Taylor coefficients, for each variable, that can fit in *taylor*. This must be greater than or equal one. cskip_op ******** Is a vector with size *play* ``->num_op_rec()`` . order_low is zero ================= If *order_low* is zero, the input value of the elements of *cskip_op* does not matter. Upon return, if *cskip_op* [ *i* ] is true, the operator with index *i* does not affect any of the dependent variable (given the value of the independent variables). order_low is non-zero ===================== If *order_low* is non-zero, *cskip_op* is not modified. If the value *cskip_op* [ *i* ] is true, the operator with index *i* does not affect any of the dependent variable (given the value of the independent variables). load_op2var *********** Is a vector with size *play* ``->num_var_load()`` . order_low is zero ================= If *order_low* is zero, the input value of the elements of *load_op2var* does not matter. Upon return, *load_op2var* [ *i* ] is the variable index corresponding to the *i*-th variable VecAD load operator. Note that even though the VecAD vector is a variable, the load can correspond to an element that is a parameter in which case *load_op2var* [ *i* ] is zero. order_low is non-zero ===================== If *order_low* is non-zero, *load_op2var* is not modified. The value *load_op2var* [ *i* ] is the variable index corresponding to the *i*-th variable VecAD load operator. {xrst_comment ----------------------------------------------------------------} {xrst_suspend @NAME@ == FORWARD_DIR} change_count ************ If *change_count* is non-zero (zero), the comparison changes are counted (not counted). Furthermore, if *change_count* is non-zero, it is the compare change count value for which *change_op_index* is returned. change_number ************* If *change_count* is zero, this value is set to zero. Otherwise, the return value is the number of comparison operations that have a different result from when the information in *play* was recorded. change_op_index *************** If *change_count* is zero, this value is set to zero. Otherwise it is the operator index (see forward_next) for the comparison operation that has a different result from when the information in play was recorded. This is not the first comparison that is different, but rather the *change_count* comparison. s_out ***** Is the stream where output corresponding to {xrst_spell_off} PriOp {xrst_spell_on} operations is written. print ***** If print is false, suppress the output that is otherwise generated by the {xrst_spell_off} PriOp {xrst_spell_on} instructions. {xrst_resume} {xrst_comment ----------------------------------------------------------------} {xrst_suspend @NAME@ != FORWARD_0} taylor ****** The vector *taylor* has size *num_var* * *cap_order* . We use *n* to denote the number of independent variables. Input ===== :: for j = 1 , ... , n taylor [ j * cap_order + 0 ] is an input Output ====== :: for j = n + 1 , ... , num_var - 1 taylor [ j * cap_order + 0 ] is an output {xrst_resume} {xrst_comment ----------------------------------------------------------------} {xrst_suspend @NAME@ != FORWARD_ANY} order_low ********* is the lowest order of the Taylor coefficients that are computed by this call. order_up ******** is the highest order of the Taylor coefficients that are computed by this call. taylor ****** The vector *taylor* has size *num_var* * *cap_order* . We use *n* to denote the number of independent variables. Input ===== :: for j = 1 , ... , num_var - 1 for k = 0 , ... , order_low - 1 taylor [ j * cap_order + k ] is an input for j = 1 , ... , n for k = order_low , ... , order_up taylor [ j * cap_order + k ] is an input Output ====== :: for j = n + 1 , ... , num_var - 1 for k = order_low , ... , order_up taylor [ j * cap_order + k ] is an output {xrst_resume} {xrst_comment ----------------------------------------------------------------} {xrst_suspend @NAME@ != FORWARD_DIR} order_up ******** is the order of the Taylor coefficients that are computed by this call. n_dir ***** number of directions that we are computing the Taylor coefficient for. taylor ****** We use *n* to denote the number of independent variables. per_variable ============ For each variable there is one Taylor coefficient of order zero and *n_dir* coefficients for orders greater than zero. The taylor coefficients capacity per variable is:: per_variable = (cap_order - 1) * n_dir + 1 The vector *taylor* has size *num_var* * *per_variable* . (j, k, ell) =========== For variable index j, order k, and direction index ell:: if k == 0 (j, k, ell) = j * per_variable else (j, k, ell) = j * per_variable + (k-1) * n_dir + 1 + ell The value taylor[ (j, k, ell) ] is the Taylor coefficient corresponding to the variable with index j, the order k, and the direction with index ell. n_dir = 1 ========= If *n_dir* is equal to one then *ell* is zero and:: (j, k, ell) = j * cap_order + k Input ===== :: for j = 1, ..., n for k = 0 , ... , order_up for ell = 0 , ... , n_dir - 1 taylor [ (j, k, ell) ] is an input for j = n + 1, ..., num_var - 1 for k = 0 , ... , order_up - 1 for ell = 0 , ... , n_dir - 1 taylor [ (j, k, ell) ] is an input Output ====== :: for j = n + 1, ..., num_var - 1 for ell = 0 , ... , n_dir - 1 taylor [ (j, order_up, ell) ] is an output {xrst_resume} {xrst_comment ----------------------------------------------------------------} ================================================ FILE: include/cppad/local/temp_file.hpp ================================================ # ifndef CPPAD_LOCAL_TEMP_FILE_HPP # define CPPAD_LOCAL_TEMP_FILE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include namespace CppAD { namespace local { std::string temp_file(void); } } # endif ================================================ FILE: include/cppad/local/utility/cppad_vector_itr.hpp ================================================ # ifndef CPPAD_LOCAL_UTILITY_CPPAD_VECTOR_ITR_HPP # define CPPAD_LOCAL_UTILITY_CPPAD_VECTOR_ITR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include /* ------------------------------------------------------------------------------ {xrst_begin cppad_vector_itr_define dev} {xrst_spell undef } Vector Class Iterator Preprocessor Definitions ############################################## Syntax ****** {xrst_code cpp} # define CPPAD_CONST 0 # include # undef CPPAD_LOCAL_UTILITY_CPPAD_VECTOR_ITR_HPP # define CPPAD_CONST 1 # include {xrst_code} Beginning of cppad_vector_itr.hpp ********************************* The following preprocessor definition appears at the beginning of ``cppad_vector_itr.hpp`` and is used for the class definition in this file: :: # if CPPAD_CONST # define CPPAD_VECTOR_ITR const_cppad_vector_itr # else # define CPPAD_VECTOR_ITR cppad_vector_itr # endif End of cppad_vector_itr.hpp *************************** The following preprocessor definition appears at the end of ``cppad_vector_itr.hpp`` so that it can be included with a different value for ``CPPAD_CONST`` : :: # undef CPPAD_CONST # undef CPPAD_VECTOR_ITR {xrst_end cppad_vector_itr_define} */ # if CPPAD_CONST # define CPPAD_VECTOR_ITR const_cppad_vector_itr # else # define CPPAD_VECTOR_ITR cppad_vector_itr # endif // BEGIN_CPPAD_LOCAL_UTILITY_NAMESPACE namespace CppAD { namespace local { namespace utility { // const_cppad_vector_itr // declare here can be make it a friend in cppad_vector_itr template class const_cppad_vector_itr; // ========================================================================== template class CPPAD_VECTOR_ITR { // ========================================================================== /* ----------------------------------------------------------------------------- {xrst_begin cppad_vector_itr_traits dev} Vector Class Iterator Traits and Friends ######################################## {xrst_spell_off} {xrst_code hpp} */ # if ! CPPAD_CONST friend class const_cppad_vector_itr; # endif public: typedef std::random_access_iterator_tag iterator_category; typedef Type value_type; typedef std::ptrdiff_t difference_type; typedef Type* pointer; typedef Type& reference; /* {xrst_code} {xrst_spell_on} {xrst_end cppad_vector_itr_traits} ------------------------------------------------------------------------------- {xrst_begin cppad_vector_itr_ctor dev} Vector Class Iterator Member Data and Constructors ################################################## Constructors ************ Constant ======== | ``const_cppad_vector_itr`` *itr* () | ``const_cppad_vector_itr`` *itr* ( *data* , *length* , *index* ) | ``const_cppad_vector_itr`` *itr* ( *other* ) | ``const_cppad_vector_itr`` *itr* ( *non_const_other* ) Not Constant ============ | ``cppad_vector_itr`` *itr* () | ``cppad_vector_itr`` *itr* ( *data* , *length* , *index* ) | ``cppad_vector_itr`` *itr* ( *other* ) Namespace ********* These definitions are in the ``CppAD::local::utility`` namespace. Indirection *********** We use an extra level of indirection in this routine so that the iterator has the same values as the vector even if the vector changes. data\_ ****** is a pointer to a constant pointer to data for this vector (used by operations that are not supported by constant iterators). length\_ ******** is a pointer to the length of the corresponding vector. index\_ ******* is the current vector index corresponding to this iterator. check_element ************* generates an assert with a known cause when the ``index_`` does not correspond go a valid element and ``NDEBUG`` is not defined. check_cop ********* Generates an assert with a known cause when the ``data_`` for this vector is different from the other vector and ``NDEBUG`` is not defined. This should be used by operators that compare iterators. Source ****** {xrst_spell_off} {xrst_code hpp} */ private: # if CPPAD_CONST const Type* const* data_; # else Type* const* data_; # endif const size_t* length_; difference_type index_; void check_element(void) const CPPAD_NDEBUG_NOEXCEPT { CPPAD_ASSERT_KNOWN( 0 <= index_ && size_t(index_) < *length_, "CppAD vector iterator: accessing element out of range" ); } void check_cop(const CPPAD_VECTOR_ITR& other) const CPPAD_NDEBUG_NOEXCEPT { CPPAD_ASSERT_KNOWN( data_ == other.data_, "CppAD vector iterator: comparing indices from different vectors" ); } public: CPPAD_VECTOR_ITR(void) noexcept : data_(nullptr), length_(nullptr), index_(0) { } # if CPPAD_CONST // ctor const_cppad_vector_itr( const Type* const* data, const size_t* length, difference_type index ) noexcept : data_(data), length_(length), index_( difference_type(index) ) { } // ctor a const_iterator from an iterator. Here is were we need // const_cppad_vector_itr to be friend of cppad_vector_itr const_cppad_vector_itr( const cppad_vector_itr& non_const_other ) noexcept { data_ = non_const_other.data_; length_ = non_const_other.length_; index_ = non_const_other.index_; } # else // ctor cppad_vector_itr( Type* const* data, const size_t* length, difference_type index ) noexcept : data_(data), length_(length), index_( difference_type(index) ) { } # endif void operator=(const CPPAD_VECTOR_ITR& other) noexcept { data_ = other.data_; length_ = other.length_; index_ = other.index_; } CPPAD_VECTOR_ITR(const CPPAD_VECTOR_ITR& other) noexcept { *this = other; } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_vector_itr_ctor} ------------------------------------------------------------------------------- {xrst_begin cppad_vector_itr_inc dev} Vector Class Iterator Increment Operators ######################################### Syntax ****** | ++ *itr* | ``--`` *itr* | *itr* ++ | *itr* ``--`` Source ****** {xrst_spell_off} {xrst_code hpp} */ public: CPPAD_VECTOR_ITR& operator++(void) noexcept { ++index_; return *this; } CPPAD_VECTOR_ITR& operator--(void) noexcept { --index_; return *this; } CPPAD_VECTOR_ITR operator++(int) noexcept { CPPAD_VECTOR_ITR ret(*this); ++index_; return ret; } CPPAD_VECTOR_ITR operator--(int) noexcept { CPPAD_VECTOR_ITR ret(*this); --index_; return ret; } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_vector_itr_inc} ------------------------------------------------------------------------------- {xrst_begin cppad_vector_itr_equal dev} Vector Class Iterator Equality Operators ######################################## Syntax ****** | *itr* == *other* | *itr* != *other* Restrictions ************ It is an error to compare iterators corresponding to different ``data_`` vectors Source ****** {xrst_spell_off} {xrst_code hpp} */ public: bool operator==(const CPPAD_VECTOR_ITR& other) const CPPAD_NDEBUG_NOEXCEPT { check_cop(other); return index_ == other.index_; } bool operator!=(const CPPAD_VECTOR_ITR& other) const CPPAD_NDEBUG_NOEXCEPT { check_cop(other); return index_ != other.index_; } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_vector_itr_equal} ------------------------------------------------------------------------------- {xrst_begin cppad_vector_itr_element dev} Vector Class Iterator Access Elements ##################################### Syntax ****** | *element* = * *itr* | * *itr* = *element* | *element* = *itr* [ *n* ] | *itr* [ *n* ] = *element* Source ****** {xrst_spell_off} {xrst_code hpp} */ public: # if CPPAD_CONST const Type& operator*(void) const { check_element(); return (*data_)[index_]; } const Type& operator[](difference_type n) const { return *(*this + n); } const Type& operator[](size_t n) const { return *( *this + difference_type(n) ); } # else Type& operator*(void) const { check_element(); return (*data_)[index_]; } Type& operator[](difference_type n) const { return *(*this + n); } Type& operator[](size_t n) const { return *( *this + difference_type(n) ); } # endif /* {xrst_code} {xrst_spell_on} {xrst_end cppad_vector_itr_element} ------------------------------------------------------------------------------- {xrst_begin cppad_vector_itr_random dev} Vector Class Iterator Random Access ################################### Syntax ****** | *itr* + *-* = *n* | *itr* = *other* + *-* *n* | *itr* = *n* + *-* *other* | *n* = *itr* ``-`` *other* | *b* = *itr* *cop* *other* Plus or Minus ************* The notation + *-* above is either ``+`` or ``-`` . cop *** is one of the following: ``<`` , ``<=`` , ``>`` , ``>=`` . itr, other ********** are iterators of the same type. n * is a ``difference_type`` object. b * is a ``bool`` . Restrictions ************ It is an error to use a *cop* with iterators corresponding to different ``data_`` vectors Source ****** {xrst_spell_off} {xrst_code hpp} */ public: // sum and difference operators CPPAD_VECTOR_ITR& operator+=(difference_type n) noexcept { index_ += n; return *this; } CPPAD_VECTOR_ITR& operator-=(difference_type n) noexcept { index_ -= n; return *this; } CPPAD_VECTOR_ITR operator+(difference_type n) const noexcept { return CPPAD_VECTOR_ITR(data_, length_, index_ + n); } CPPAD_VECTOR_ITR operator-(difference_type n) const noexcept { return CPPAD_VECTOR_ITR(data_, length_, index_ - n); } difference_type operator-(const CPPAD_VECTOR_ITR& other) const noexcept { return index_ - other.index_; } // comparison operators bool operator<(const CPPAD_VECTOR_ITR& other) const CPPAD_NDEBUG_NOEXCEPT { check_cop(other); return index_ < other.index_; } bool operator<=(const CPPAD_VECTOR_ITR& other) const CPPAD_NDEBUG_NOEXCEPT { check_cop(other); return index_ <= other.index_; } bool operator>(const CPPAD_VECTOR_ITR& other) const CPPAD_NDEBUG_NOEXCEPT { check_cop(other); return index_ > other.index_; } bool operator>=(const CPPAD_VECTOR_ITR& other) const CPPAD_NDEBUG_NOEXCEPT { check_cop(other); return index_ >= other.index_; } /* {xrst_code} {xrst_spell_on} {xrst_literal // BEGIN_BINARY_OP // END_BINARY_OP } {xrst_end cppad_vector_itr_random} */ // ========================================================================== }; // END_TEMPLATE_CLASS_CPPAD_VECTOR_ITR // ========================================================================== // BEGIN_BINARY_OP template CPPAD_VECTOR_ITR operator+( typename CPPAD_VECTOR_ITR::difference_type n , const CPPAD_VECTOR_ITR& other ) noexcept { return other + n; } // END_BINARY_OP } } } // END_CPPAD_LOCAL_UTILITY_NAMESPACE # undef CPPAD_CONST # undef CPPAD_VECTOR_ITR # endif ================================================ FILE: include/cppad/local/utility/vector_bool.hpp ================================================ # ifndef CPPAD_LOCAL_UTILITY_VECTOR_BOOL_HPP # define CPPAD_LOCAL_UTILITY_VECTOR_BOOL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include // BEGIN_CPPAD_LOCAL_UTILITY_NAMESPACE namespace CppAD { namespace local { namespace utility { /* {xrst_begin vector_bool_element dev} vectorBoolElement Class ####################### Syntax ****** | ``local::utility::vectorBoolElement`` *element* ( *unit* , *mask* ) | ``local::utility::vectorBoolElement`` *element* ( *other* ) | *value* = *element* | *element* = *value* | *element* = *element* unit_t ****** Type used to pack multiple boolean (bit) values into one unit. Logical operations are preformed one unit at a time. unit\_ ****** pointer to the unit that holds the value for this element. mask\_ ****** mask for the bit corresponding to this element; i.e., all the bits are zero except for bit that corresponds to this element which is one. value ***** is a ``bool`` . Source ****** {xrst_spell_off} {xrst_code hpp} */ class vectorBoolElement { private: typedef size_t unit_t; unit_t* unit_; unit_t mask_; public: vectorBoolElement(unit_t* unit, unit_t mask ) : unit_(unit) , mask_(mask) { } vectorBoolElement(const vectorBoolElement& other) : unit_(other.unit_) , mask_(other.mask_) { } operator bool() const { return (*unit_ & mask_) != 0; } vectorBoolElement& operator=(bool value) { if(value) *unit_ |= mask_; else *unit_ &= ~mask_; return *this; } vectorBoolElement& operator=(const vectorBoolElement& element) { if( *(element.unit_) & element.mask_ ) *unit_ |= mask_; else *unit_ &= ~mask_; return *this; } }; /* {xrst_code} {xrst_spell_on} {xrst_end vector_bool_element} */ } } } // END_CPPAD_LOCAL_UTILITY_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/base_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_BASE_OP_HPP # define CPPAD_LOCAL_VAL_GRAPH_BASE_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include namespace CppAD { namespace local { namespace val_graph { /* {xrst_begin val_base_op dev} The Value Operator Base Class ############################# Prototype ********* {xrst_literal // BEGIN_BASE_OP_T // END_BASE_OP_T } Purpose ******* This section describes the value operator base class (all the value operators are derived from this class). Member Variables **************** None of the value operators have any member variables, so their functions are like static functions but cannot be declared as such because they are virtual or override functions. Value ***** This template parameter is the type used for each element of the :ref:`val_graph@Value Vector` . base_op ******* This is the base class used for operators. All of its functions are public and pure virtual; i.e., they all must be defined in order for a derived class object to be used. op_enum ******* This member function returns the enum value corresponding to this operator; see :ref:`val_graph_type@op_enum_t` . {xrst_literal // BEGIN_OP_ENUM // END_OP_ENUM } n_before ******** This member function returns the number of auxiliary arguments that come before the arguments that are value vector indices. {xrst_literal // BEGIN_N_BEFORE // END_N_BEFORE } n_after ******* This member function returns the number of auxiliary arguments that come after the arguments that are value vector indices. This can only be zero or one. {xrst_literal // BEGIN_N_AFTER // END_N_AFTER } n_arg ***** This member function returns the number of arguments for this operator. {xrst_literal // BEGIN_N_ARG // END_N_ARG } Fixed ===== If *n_after* returns zero, this operator has a fixed number of arguments. In this case, the *arg_index* and *arg_vec* arguments to this function. You can set *arg_index* to *arg_vec*.size() (an invalid value) if you know this is not a call operator. Variable ======== If *n_after* returns one, this operator has a variable number of arguments. In this case the last argument is an auxiliary argument and is equal to the number of arguments for this use of this operator. n_res ***** This member function returns the number of results for this operator. {xrst_literal // BEGIN_N_RES // END_N_RES } eval **** The values in *val_vec* with index less than *res_index* are inputs to this member function. The *n_res* values starting at *res_index* in *val_vec* are computed by this function. {xrst_literal // BEGIN_EVAL // END_EVAL } tape ==== This is a pointer to the tape that the operator is being evaluated for. We use arg_vec to denote tape->arg_vec() below. trace ===== if this is true (false) the operator is (is not) printed at the end of the eval operation. ind_vec_vec =========== The size of this vector is equal to the number of dynamic vectors. The size of each element of this vector is equal to the size of the corresponding dynamic vector. If there are no dynamic vectors in the tape this argument must be empty (or not present). This argument is only used by the :ref:`val_vector_op-name` operations. compare_false ============= This is the number of :ref:`val_comp_op-name` that had a false result for their comparisons. This is both an input and output; i.e., each false comparison will add one to this value. name **** is a short name, 5 or less characters, for this operator. arg_index ********* is the index in *arg_vec* where the *n_arg* arguments to this function start. res_index ********* is the index in *val_vec* where the results for this operator start. val_vec ******* is the entire :ref:`val_graph@Value Vector` . arg_vec ******* During the eval function, arg_vec below denotes tape->arg_vec() . Unary Operators =============== If this operator is a unary operator #. arg_vec[arg_index + 0] < res_index #. val_vec[ arg_vec[ arg_index + 0 ] ] is the operand #. val_vec[ res_index] is the result computed by eval Binary Operators ================ If this operator is a binary operator #. arg_vec[arg_index + 0] < res_index #. arg_vec[arg_index + 1] < res_index #. val_vec[ arg_vec[ arg_index + 1 ] ] is the left operand #. val_vec[ arg_vec[ arg_index + 2 ] ] is the right operand #. val_vec[ res_index] is the result computed by eval is_unary ******** is true (false) if this is (is not) a unary operator; see :ref:`val_unary_op-name`. {xrst_literal // BEGIN_IS_UNARY // END_IS_UNARY } is_binary ********* is true (false) if this is (is not) a binary operator; see :ref:`val_binary_op-name`. {xrst_literal // BEGIN_IS_BINARY // END_IS_BINARY } Operator Classes **************** {xrst_comment BEGIN_SORT_THIS_LINE_PLUS_2} {xrst_toc_table include/cppad/local/val_graph/binary_op.hpp include/cppad/local/val_graph/call_op.hpp include/cppad/local/val_graph/cexp_op.hpp include/cppad/local/val_graph/comp_op.hpp include/cppad/local/val_graph/con_op.hpp include/cppad/local/val_graph/csum_op.hpp include/cppad/local/val_graph/dis_op.hpp include/cppad/local/val_graph/pri_op.hpp include/cppad/local/val_graph/unary_op.hpp include/cppad/local/val_graph/vector_op.hpp } {xrst_comment END_SORT_THIS_LINE_MINUS_2} {xrst_end val_base_op} */ // // tape_t template class tape_t; // BEGIN_BASE_OP_T template class base_op_t { public: // END_BASE_OP_T // BEGIN_OP_ENUM virtual op_enum_t op_enum(void) const = 0; // END_OP_ENUM // // BEGIN_N_BEFORE virtual addr_t n_before(void) const = 0; // END_N_BEFORE // // BEGIN_N_AFTER virtual addr_t n_after(void) const = 0; // END_N_AFTER // // BEGIN_N_ARG virtual addr_t n_arg( addr_t arg_index , const Vector& arg_vec ) const = 0; // END_N_ARG // // BEGIN_N_RES virtual addr_t n_res( addr_t arg_index , const Vector& arg_vec ) const = 0; // END_N_RES // // BEGIN_EVAL virtual void eval( const tape_t* tape , bool trace , addr_t arg_index , addr_t res_index , Vector& val_vec , Vector< Vector >& ind_vec_vec , size_t& compare_false ) const = 0; // END_EVAL // // BEGIN_IS_UNARY virtual bool is_unary(void) const // END_IS_UNARY { return false; } // // BEGIN_IS_BINARY virtual bool is_binary(void) const // END_IS_BINARY { return false; } }; } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/binary_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_BINARY_OP_HPP # define CPPAD_LOCAL_VAL_GRAPH_BINARY_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include namespace CppAD { namespace local { namespace val_graph { # define CPPAD_VAL_GRAPH_BINARY(Name, Op) \ template \ class Name##_op_t : public binary_op_t { \ public: \ /* get_instance */ \ static Name##_op_t* get_instance(void) \ { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; \ static Name##_op_t instance; \ return &instance; \ } \ /* op_enum */ \ op_enum_t op_enum(void) const override \ { return Name##_op_enum; \ } \ /* eval */ \ void eval( \ const tape_t* tape , \ bool trace , \ addr_t arg_index , \ addr_t res_index , \ Vector& val_vec , \ Vector< Vector >& ind_vec_vec , \ size_t& compare_false ) const override \ { const Vector& arg_vec( tape->arg_vec() ); \ const Value& left = val_vec[ arg_vec[arg_index + 0] ]; \ const Value& right = val_vec[ arg_vec[arg_index + 1] ]; \ val_vec[res_index] = left Op right; \ if( trace ) this->print_op( \ #Name , arg_index, tape->arg_vec(), res_index, val_vec \ ); \ } \ } /* ------------------------------------------------------------------------------ {xrst_begin_parent val_binary_op dev} Binary Value Operators ###################### {xrst_end val_binary_op} ------------------------------------------------------------------------------ {xrst_begin val_binary_base_op dev} The Value Operator Binary Class ############################### Prototype ********* {xrst_literal // BEGIN_BINARY_OP_T // END_BINARY_OP_T } Purpose ******* The class is derived from :ref:`base_op `. It overrides the *is_binary*, *n_before*, *n_after*, *n_arg*, *n_res_*, and *print_op* member functions. The *op_enum* and *eval* member functions are still pure virtual. is_binary ********* This override of :ref:`val_base_op@is_binary` returns true. n_before ******** This override of :ref:`val_base_op@n_before` returns 0. n_after ******* This override of :ref:`val_base_op@n_after` returns 0. n_arg ***** This override of :ref:`val_base_op@n_arg` returns 2. n_res ***** This override of :ref:`val_base_op@n_res` returns 1. print_op ******** {xrst_literal // BEGIN_PRINT_OP // END_PRINT_OP } This member function uses :ref:`val_print_op-name` to print binary operators. {xrst_end val_binary_base_op} */ // BEGIN_BINARY_OP_T template class binary_op_t : public base_op_t { // END_BINARY_OP_T public: // n_before addr_t n_before(void) const override { return 0; } // // n_after addr_t n_after(void) const override { return 0; } // // is_binary bool is_binary(void) const override { return true; } // // n_arg addr_t n_arg( addr_t arg_index , const Vector& arg_vec ) const override { return 2; } // // n_res addr_t n_res( addr_t arg_index , const Vector& arg_vec ) const override { return 1; } // // op_enum virtual op_enum_t op_enum(void) const override = 0; // // eval virtual void eval( const tape_t* tape , bool trace , addr_t arg_index , addr_t res_index , Vector& val_vec , Vector< Vector >& ind_vec_vec , size_t& compare_false ) const override = 0; // // BEGIN_PRINT_OP void print_op( const char* name , addr_t arg_index , const Vector& arg_vec , addr_t res_index , Vector& val_vec ) const // END_PRINT_OP { // addr_t left_index = arg_vec[ arg_index + 0 ]; addr_t right_index = arg_vec[ arg_index + 1 ]; Vector arg_val_index = { left_index, right_index }; Vector res_value = { val_vec[res_index] }; CppAD::local::val_graph::print_op( name, arg_val_index, res_index, res_value ); } }; /* ------------------------------------------------------------------------------ {xrst_begin val_binary_op_derived dev} The Binary Value Operator Derived Classes ######################################### Prototype ********* | |tab| ``template `` | |tab| ``class`` *Name*\ ``_opt_t : public binary_op_t`` *Name* ****** Unary operators are defined for the following names: .. csv-table:: Unary Operators :widths: auto :header-rows: 1 Name,description add,result is the sum of the two operands sub,result is the first operand minus the second Context ******* This class is derived from :ref:`val_binary_op-name` . It overrides the *op_enum* and *eval* member functions and is a concrete class (it has no pure virtual functions). get_instance ************ This static member function returns a pointer to a *Name*\ ``_op_t`` object. op_enum ******* This override of :ref:`val_base_op@op_enum` returns *Name*\ ``_op_enum`` . eval **** This override of :ref:`val_base_op@eval` sets the result equal to the binary operator applied to the operands; see :ref:`val_base_op@arg_vec@Unary Operators` . {xrst_toc_hidden val_graph/binary_xam.cpp } Example ******* The file :ref:`binary_xam.cpp ` is an example and test that uses a binary operator. {xrst_end val_binary_op_derived} */ CPPAD_VAL_GRAPH_BINARY(add, +); CPPAD_VAL_GRAPH_BINARY(sub, -); CPPAD_VAL_GRAPH_BINARY(mul, *); CPPAD_VAL_GRAPH_BINARY(div, /); template class pow_op_t : public binary_op_t { public: /* get_instance */ static pow_op_t* get_instance(void) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; static pow_op_t instance; return &instance; } /* op_enum */ op_enum_t op_enum(void) const override { return pow_op_enum; } /* eval */ void eval( const tape_t* tape , bool trace , addr_t arg_index , addr_t res_index , Vector& val_vec , Vector< Vector >& ind_vec_vec , size_t& compare_false ) const override { const Vector& arg_vec( tape->arg_vec() ); const Value& left = val_vec[ arg_vec[arg_index + 0] ]; const Value& right = val_vec[ arg_vec[arg_index + 1] ]; // Only the line directly below is different from // CPPAD_VAL_GRAPH_BINARY(pow, pow) val_vec[res_index] = pow(left, right); if( trace ) this->print_op( "pow", arg_index, tape->arg_vec(), res_index, val_vec ); } }; } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # undef CPPAD_VAL_GRAPH_BINARY # endif ================================================ FILE: include/cppad/local/val_graph/call_atomic.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_CALL_ATOMIC_HPP # define CPPAD_LOCAL_VAL_GRAPH_CALL_ATOMIC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include namespace CppAD { namespace local { namespace val_graph { /* This is similar to the routines in cppad/local/sweep/call_atomic.hpp, but it is used by val_graph instead of the sweeps. */ /* {xrst_begin atomic_for_type_callback dev} Forward Type Callback to Atomic Functions ######################################### Prototype ********* {xrst_literal // BEGIN_FORWARD // END_FORWARD } Value ***** is the base type corresponding to the atomic function. CppAD::vector ************* this routine uses the CppAD::vector template class (and not CppAD::local::val_graph::Vector) because atomic functions expect CppAD::vector. constant_x ********** contains the values, in afun(ax, ay), for arguments that are constants. type_x ****** what is the type, in the call, for each component of x. type_y ****** the input values in this vector do not matter. Upon return it is the type for each component of y. atom_index ********** is the index, in local::atomic_index, corresponding to this atomic function. call_id ******* see the atomic_four :ref:`atomic_four_call@call_id` and the atomic_one :ref:`atomic_one@id` . {xrst_end atomic_for_type_callback} */ // BEGIN_FORWARD template void call_atomic_for_type( const CppAD::vector& constant_x , const CppAD::vector& type_x , CppAD::vector& type_y , size_t atom_index , size_t call_id ) // END_FORWARD { CPPAD_ASSERT_UNKNOWN( 0 < atom_index ); // // type, v_ptr bool set_null = false; size_t type = 0; // set to avoid warning std::string name; void* v_ptr = nullptr; // set to avoid warning local::atomic_index(set_null, atom_index, type, &name, v_ptr); // # ifndef NDEBUG bool ok = v_ptr != nullptr; if( ok ) { if( type == 2 ) { size_t p = 0, q = 0, nx = type_x.size(), ny = type_y.size(); CppAD::vector vx(nx), vy(ny); for(size_t i = 0; i < nx; ++i) vx[i] = type_x[i] > constant_enum; const CppAD::vector& taylor_x = constant_x; CppAD::vector taylor_y(ny); atomic_base* afun = reinterpret_cast< atomic_base* >(v_ptr); afun->set_old(call_id); ok = afun->forward( p, q, vx, vy, taylor_x, taylor_y ); for(size_t i = 0; i < ny; ++i) { if( vy[i] ) type_y[i] = variable_enum; else type_y[i] = constant_enum; } } else if( type == 3 ) { CPPAD_ASSERT_UNKNOWN( type == 3 ); atomic_three* afun = reinterpret_cast< atomic_three* >(v_ptr); ok = afun->for_type(constant_x, type_x, type_y ); } else { CPPAD_ASSERT_UNKNOWN( type == 4 ); atomic_four* afun = reinterpret_cast< atomic_four* >(v_ptr); ok = afun->for_type(call_id, type_x, type_y); } } if( ! ok ) { // now take the extra time to copy the name std::string msg = name; if( v_ptr == nullptr ) msg += ": this atomic function has been deleted"; else msg += ": atomic for_type returned false"; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } # else if( type == 2 ) { size_t p = 0, q = 0, nx = type_x.size(), ny = type_y.size(); CppAD::vector vx(nx), vy(ny); for(size_t i = 0; i < nx; ++i) vx[i] = type_x[i] > constant_enum; const CppAD::vector& taylor_x = constant_x; CppAD::vector taylor_y(ny); atomic_base* afun = reinterpret_cast< atomic_base* >(v_ptr); afun->set_old(call_id); afun->forward( p, q, vx, vy, taylor_x, taylor_y ); for(size_t i = 0; i < ny; ++i) { if( vy[i] ) type_y[i] = variable_enum; else type_y[i] = constant_enum; } } else if( type == 3 ) { CPPAD_ASSERT_UNKNOWN( type == 3 ); atomic_three* afun = reinterpret_cast< atomic_three* >(v_ptr); afun->for_type(constant_x, type_x, type_y ); } else { CPPAD_ASSERT_UNKNOWN( type == 4 ); atomic_four* afun = reinterpret_cast< atomic_four* >(v_ptr); afun->for_type(call_id, type_x, type_y); } # endif } } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/call_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_CALL_OP_HPP # define CPPAD_LOCAL_VAL_GRAPH_CALL_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include # include // define CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL # include namespace CppAD { namespace local { namespace val_graph { /* {xrst_begin val_call_op dev} The Call Value Operator ####################### Prototype ********* {xrst_literal // BEGIN_CALL_OP_T // END_CALL_OP_T } Context ******* The class is derived from :ref:`val_base_op-name` . It overrides all its base class virtual member functions and is a concrete class (it has no pure virtual functions). get_instance ************ This static member function returns a pointer to a call_op_t object. op_enum ******* This override of :ref:`val_base_op@op_enum` returns ``call_op_enum`` . n_before ******** This override of :ref:`val_base_op@n_before` returns 4. {xrst_literal // BEGIN_ARG_BEFORE // END_ARG_BEFORE } n_after ******* This override of :ref:`val_base_op@n_after` returns 1. This is for a second copy of *n_arg* that can be used to iterate through the operators in reverse. n_arg ***** see base_op :ref:`val_base_op@n_arg` . n_res ***** see base_op :ref:`val_base_op@n_res` . atomic_index ************ This member function returns the *atomic_index* for the mapping; see :ref:`atomic_index@index_out` in the case where *index_in* is zero. call_id ******* This member function returns the *call_id* for this use of the mapping; see :ref:`atomic_four_call@call_id` . eval **** This override of :ref:`val_base_op@eval` makes the atomic function call identified by *atomic_index* , with the *call_id*, to evaluate *n_res* results given *n_arg* - 4 arguments. #. The arguments for the function call are :: val_vec[ arg_vec[ arg_index + 4 ] ] , val_vec[ arg_vec[ arg_index + 5 ] ] , ... val_vec[ arg_vec[ arg_index + n_arg - 1 ] ] #. The results of the function call are placed in :: val_vec[res_index + 0] , val_vec[res_index + 1] , ... val_vec[res_index + n_res - 1] trace ===== If trace is true, :ref:`val_print_op-name` is called to print this operator. {xrst_toc_hidden val_graph/call_xam.cpp } Example ******* The file :ref:`call_xam.cpp ` is an example and test that uses this operator. {xrst_end val_call_op} */ // BEGIN_CALL_OP_T template class call_op_t : public base_op_t { public: // n_before addr_t n_before(void) const override { return 4; } // // n_after addr_t n_after(void) const override { return 1; } // // get_instance static call_op_t* get_instance(void) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; static call_op_t instance; return &instance; } // op_enum // type of this operator op_enum_t op_enum(void) const override { return call_op_enum; } // // n_arg addr_t n_arg( addr_t arg_index , const Vector& arg_vec ) const override { return arg_vec[arg_index + 0]; } // // n_res addr_t n_res( addr_t arg_index , const Vector& arg_vec ) const override { return arg_vec[arg_index + 1]; } // // atomic_index size_t atomic_index( addr_t arg_index , const Vector& arg_vec ) { return size_t( arg_vec[arg_index + 2] ); } // // call_id size_t call_id( addr_t arg_index , const Vector& arg_vec ) { return size_t( arg_vec[arg_index + 3] ); } // // eval void eval( const tape_t* tape , bool trace , addr_t arg_index , addr_t res_index , Vector& val_vec , Vector< Vector >& ind_vec_vec , size_t& compare_false ) const override; // END_CALL_OP_T }; // // eval template void call_op_t::eval( const tape_t* tape , bool trace , addr_t arg_index , addr_t res_index , Vector& val_vec , Vector< Vector >& ind_vec_vec , size_t& compare_false ) const { // // arg_vec const Vector& arg_vec( tape->arg_vec() ); // // n_arg, n_res, atomic_index, call_id // BEGIN_ARG_BEFORE addr_t n_arg = arg_vec[arg_index + 0] ; addr_t n_res = arg_vec[arg_index + 1] ; size_t atomic_index = size_t( arg_vec[arg_index + 2] ); size_t call_id = size_t( arg_vec[arg_index + 3] ); CPPAD_ASSERT_UNKNOWN( atomic_index != 0 ); // END_ARG_BEFORE // // n_x addr_t n_x = n_arg - n_before() - n_after(); // // x CppAD::vector x(n_x); for(addr_t i = 0; i < n_x; ++i) x[i] = val_vec[ arg_vec[arg_index + n_before() + i] ]; // // type_x CppAD::vector type_x(n_x); for(addr_t i = 0; i < n_x; ++i) type_x[i] = variable_enum; // // need_y size_t need_y = size_t( number_ad_type_enum ); // // order_low, order_up size_t order_low = 0, order_up = 0; // // select_y CppAD::vector select_y(n_res); for(addr_t i = 0; i < n_res; ++i) select_y[i] = true; // // y CppAD::vector y(n_res); local::sweep::call_atomic_forward( x, type_x, need_y, select_y, order_low, order_up, atomic_index, call_id, x, y ); // // val_vec for(addr_t i = 0; i < n_res; ++i) val_vec[res_index + i] = y[i]; // // trace if( ! trace ) return; // // v_ptr, name bool set_null = false; size_t type = 0; // result: set to avoid warning std::string name; // result: void* v_ptr = nullptr; // result: set to avoid warning local::atomic_index(set_null, atomic_index, type, &name, v_ptr); // Vector arg_val_index(n_x); for(addr_t i = 0; i < n_x; ++i) arg_val_index[i] = arg_vec[arg_index + n_before() + i]; // Vector res_value(n_res); for(addr_t i = 0; i < n_res; ++i) res_value[i] = val_vec[res_index + i]; // print_op(name, arg_val_index, res_index, res_value); return; } } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/cexp_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_CEXP_OP_HPP # define CPPAD_LOCAL_VAL_GRAPH_CEXP_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include // define CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL # include namespace CppAD { namespace local { namespace val_graph { /* {xrst_begin val_cexp_op dev} The Conditional Expression Value Operator ######################################### Prototype ********* {xrst_literal // BEGIN_CEXP_OP_T // END_CEXP_OP_T } Context ******* This class computes the result for a :ref:`conditional expression ` . It is derived from :ref:`val_base_op-name` . It overrides all its base class virtual member functions and is a concrete class (it has no pure virtual functions). get_instance ************ This static member function returns a pointer to a cexp_op_t object. op_enum ******* This override of :ref:`val_base_op@op_enum` returns ``cexp_op_enum`` . n_before ******** This override of :ref:`val_base_op@n_before` returns 1. n_after ******* This override of :ref:`val_base_op@n_after` returns 0. n_arg ***** This override of :ref:`val_base_op@n_arg` returns 5. n_res ***** This override of :ref:`val_base_op@n_res` returns 1. eval **** This override of :ref:`val_base_op@eval` . If the comparison is false (true), one (zero) is added to the compare_false argument to eval. {xrst_literal // BEGIN_ARGS // END_ARGS } trace ===== If trace is true, :ref:`val_print_op-name` is called to print this operator. {xrst_toc_hidden val_graph/cexp_xam.cpp } Example ******* The file :ref:`com_xam.cpp ` is an example and test that uses this operator. {xrst_end val_cexp_op} */ // BEGIN_CEXP_OP_T template class cexp_op_t : public base_op_t { public: // n_before addr_t n_before(void) const override { return 1; } // // n_after addr_t n_after(void) const override { return 0; } // // get_instance static cexp_op_t* get_instance(void) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; static cexp_op_t instance; return &instance; } // op_enum op_enum_t op_enum(void) const override { return cexp_op_enum; } // END_CEXP_OP_T // // n_arg addr_t n_arg( addr_t arg_index , const Vector& arg_vec ) const override { return 5; } // // n_res addr_t n_res( addr_t arg_index , const Vector& arg_vec ) const override { return 1; } // // eval void eval( const tape_t* tape , bool trace , addr_t arg_index , addr_t res_index , Vector& val_vec , Vector< Vector >& ind_vec_vec , size_t& compare_false ) const override { // // arg_vec const Vector& arg_vec( tape->arg_vec() ); // // compare_enum, left_index, right_index // BEGIN_ARGS compare_enum_t compare_enum = compare_enum_t( arg_vec[arg_index + 0] ); addr_t left_index = arg_vec[arg_index + 1]; addr_t right_index = arg_vec[arg_index + 2]; addr_t if_true_index = arg_vec[arg_index + 3]; addr_t if_false_index = arg_vec[arg_index + 4]; // END_ARGS // // left, right const Value& left = val_vec[left_index]; const Value& right = val_vec[right_index]; const Value& if_true = val_vec[if_true_index]; const Value& if_false = val_vec[if_false_index]; // // res, name const char* name; switch( compare_enum ) { // // e_eq case compare_eq_enum: val_vec[res_index] = CondExpEq(left, right, if_true, if_false); name = "e_eq"; break; // // e_lt case compare_lt_enum: val_vec[res_index] = CondExpLt(left, right, if_true, if_false); name = "e_lt"; break; // // e_le case compare_le_enum: val_vec[res_index] = CondExpLe(left, right, if_true, if_false); name = "e_le"; break; // default: CPPAD_ASSERT_UNKNOWN(false); val_vec[res_index] = CppAD::numeric_limits::quiet_NaN(); name = ""; } // // trace if( ! trace ) return; // // print_op Vector arg_val_index(4); for(addr_t i = 0; i < 4; ++i) arg_val_index[i] = arg_vec[arg_index + i + 1]; Vector res_value = { val_vec[res_index] }; print_op(name, arg_val_index, res_index, res_value); // return; } }; } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/comp_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_COMP_OP_HPP # define CPPAD_LOCAL_VAL_GRAPH_COMP_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include // define CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL # include namespace CppAD { namespace local { namespace val_graph { /* {xrst_begin val_comp_op dev} The Compare Value Operator ########################## Prototype ********* {xrst_literal // BEGIN_COMP_OP_T // END_COMP_OP_T } Context ******* This class checks if the comparison between two values has changed. It is derived from :ref:`val_base_op-name` . It overrides all its base class virtual member functions and is a concrete class (it has no pure virtual functions). get_instance ************ This static member function returns a pointer to a comp_op_t object. op_enum ******* This override of :ref:`val_base_op@op_enum` returns ``comp_op_enum`` . n_before ******** This override of :ref:`val_base_op@n_before` returns 1. n_after ******* This override of :ref:`val_base_op@n_after` returns 0. n_arg ***** This override of :ref:`val_base_op@n_arg` returns 3. n_res ***** This override of :ref:`val_base_op@n_res` returns 1. eval **** This override of :ref:`val_base_op@eval` . If the comparison is false (true), one (zero) is added to the compare_false argument to eval. {xrst_literal // BEGIN_ARGS // END_ARGS } trace ===== If trace is true, :ref:`val_print_comp_op-name` is called to print this operator. {xrst_toc_hidden val_graph/comp_xam.cpp } Example ******* The file :ref:`com_xam.cpp ` is an example and test that uses this operator. {xrst_end val_comp_op} */ // BEGIN_COMP_OP_T template class comp_op_t : public base_op_t { public: // n_before addr_t n_before(void) const override { return 1; } // // n_after addr_t n_after(void) const override { return 0; } // // get_instance static comp_op_t* get_instance(void) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; static comp_op_t instance; return &instance; } // op_enum op_enum_t op_enum(void) const override { return comp_op_enum; } // END_COMP_OP_T // // n_arg addr_t n_arg( addr_t arg_index , const Vector& arg_vec ) const override { return 3; } // // n_res addr_t n_res( addr_t arg_index , const Vector& arg_vec ) const override { return 0; } // // eval void eval( const tape_t* tape , bool trace , addr_t arg_index , addr_t res_index , Vector& val_vec , Vector< Vector >& ind_vec_vec , size_t& compare_false ) const override { // // arg_vec const Vector& arg_vec( tape->arg_vec() ); // // compare_enum, left_index, right_index // BEGIN_ARGS compare_enum_t compare_enum = compare_enum_t( arg_vec[arg_index + 0] ); addr_t left_index = arg_vec[arg_index + 1]; addr_t right_index = arg_vec[arg_index + 2]; // END_ARGS // // left, right const Value& left = val_vec[left_index]; const Value& right = val_vec[right_index]; // // result, comp_name bool result; const char* comp_name; switch( compare_enum ) { // case compare_eq_enum: result = left == right; comp_name = "eq"; break; // // ne case compare_ne_enum: result = left != right; comp_name = "ne"; break; // // lt case compare_lt_enum: result = left < right; comp_name = "lt"; break; // // le case compare_le_enum: result = left <= right; comp_name = "le"; break; // // no case compare_no_enum: result = true; comp_name = "no"; break; // default: CPPAD_ASSERT_UNKNOWN(false); result = false; // to avoid compiler warning comp_name = ""; } // // compare_false if( ! result ) ++compare_false; // // trace if( ! trace ) return; // // print_comp_op print_comp_op(comp_name, left_index, right_index, result); } }; } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/compress.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_COMPRESS_HPP # define CPPAD_LOCAL_VAL_GRAPH_COMPRESS_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-25 Bradley M. Bell // --------------------------------------------------------------------------- # include # include # include namespace CppAD { namespace local { namespace val_graph { /* {xrst_begin val_tape_compress dev} {xrst_spell dep } Tape Compression ################ This is :ref:`val_tape_renumber-name` and :ref:`val_tape_dead_code-name` in one step. I should be faster, and require more memory, than separate execution. (Testing with :ref:`cppad_det_minor.cpp-name` indicates that it is only slightly faster.) Changes ******* Only the following values, for this tape, are guaranteed to be same: #. The number of independent values :ref:`val_tape@n_ind` . #. The size of the dependent vector :ref:`dep_vec.size() ` . #. The mapping from the independent to the dependent variables. keep_compare ************ see :ref:`val_tape_option@keep_compare` . If *keep_compare* is false, no comparison operators are in the new tape. Otherwise, if two or more comparison operators are identical, only the first such operator is in the new tape. keep_print ********** see :ref:`val_tape_option@keep_print` . If *keep_print* is false, no print operators are in the new tape. Otherwise, if two or more print operators are identical, only the first such operator is in the new tape. Algorithm ********* #. The dependent variables are marked as used. #. The operators are scanned in reverse and if an operator's result is used, the corresponding arguments are marked as used. The call operator has a more complicated version of this marking. #. A forward pass is made though the operators and only the operators that are used ones are considered. #. If an operator is equivalent to a previous operator, the previous operator is used in it's place. #. An operators result may have a new index because some previous results were left out. A mapping from the old result indices to the new result indices enables subsequent operators to adjust their argument indices. #. After the forward pass, the mapping from old indices to new indices is used to adjust the dependent variable indices. {xrst_toc_hidden val_graph/compress_xam.cpp } Example ******* The file :ref:`compress_xam.cpp ` is an example and test of tape.compress(). {xrst_end val_tape_compress} */ // BEGIN_COMPRESS // new_use_val = compress() template vectorBool tape_t::compress(void) // END_COMPRESS { # if CPPAD_VAL_GRAPH_TAPE_TRACE // thread, initial_inuse size_t thread = thread_alloc::thread_num(); size_t initial_inuse = thread_alloc::inuse(thread); # endif // // val_use_case, vec_last_load Vector val_use_case, vec_last_load; rev_depend(val_use_case, vec_last_load); // // op2arg_index, op2res_index Vector op2arg_index( n_op() ), op2res_index( n_op() ); { op_iterator op_itr(*this, 0); for(addr_t i_op = 0; i_op < n_op(); ++i_op) { op2arg_index[i_op] = op_itr.arg_index(); op2res_index[i_op] = op_itr.res_index(); ++op_itr; } } // // op_hash_table addr_t n_hash_code = 1 + (n_val_ / 2); op_hash_table_t op_hash_table(*this, op2arg_index, n_hash_code); // // keep_compare bool keep_compare = option_map_["keep_compare"] == "true"; // // keep_print bool keep_print = option_map_["keep_print"] == "true"; // // new_tape tape_t new_tape; new_tape.set_ind(n_ind_); // // new_which_vec // initialize all dynamic vectors as not used Vector new_which_vec( vec_initial_.size() ); for(size_t i = 0; i < vec_initial_.size(); ++i) new_which_vec[i] = addr_t( vec_initial_.size() ); // // new_val_index // include nan at index n_ind_ in val_vec Vector new_val_index( n_val_ ); for(addr_t i = 0; i <= n_ind_; ++i) new_val_index[i] = addr_t(i); for(addr_t i = n_ind_ + 1; i < n_val_; ++i) new_val_index[i] = addr_t( n_val_ ); // # ifndef NDEBUG // nan at index n_ind_ assert( op_enum_t( new_tape.op_enum_vec_[0] ) == con_op_enum ); assert( new_tape.var_arg_[0] == 0 ); assert( CppAD::isnan( new_tape.con_vec_[0] ) ); # endif // // new_use_val // Because the call operator can have more than one result, not all the // results for all the needed operators are used. Initialize as all the // independent values and the nan after them are used. vectorBool new_use_val(n_ind_ + 1); for(addr_t i = 0; i <= n_ind_; ++i) new_use_val[i] = true; // // work Vector work; // // // i_op for(addr_t i_op = 1; i_op < n_op(); ++i_op) { // // op_ptr const base_op_t* op_ptr = base_op_ptr(i_op); // // arg_index_i, res_index_i addr_t arg_index_i = op2arg_index[i_op]; addr_t res_index_i = op2res_index[i_op]; addr_t n_res = op_ptr->n_res(arg_index_i, var_arg_); // // use_op bool use_op = false; if( n_res == 0 ) { op_enum_t op_enum = op_ptr->op_enum(); if( op_enum == vec_op_enum || op_enum == store_op_enum ) { addr_t which_vector = var_arg_[arg_index_i + 0]; use_op = i_op < vec_last_load[which_vector]; } else if( op_enum == pri_op_enum ) use_op = keep_print; else { CPPAD_ASSERT_UNKNOWN( op_enum == comp_op_enum ); use_op = keep_compare; } } else for(addr_t k = 0; k < n_res; ++k) use_op |= 0 != val_use_case[ res_index_i + k]; // if(use_op) { // // j_op addr_t j_op = op_hash_table.match_op(i_op, new_val_index); // if( j_op != i_op ) { // use j_op every where that i_op was used assert( j_op < i_op ); // // new_val_index // mapping so use op_j results in place of op_i results. addr_t res_index_j = op2res_index[j_op]; for(addr_t k = 0; k < n_res; ++k) new_val_index[res_index_i + k] = res_index_j + k; } else { // record i_op operator on the new tape addr_t new_res_index = record_new( new_tape , new_which_vec , work , new_val_index , val_use_case , op_ptr , arg_index_i , res_index_i ); // // new_val_index for(addr_t k = 0; k < n_res; ++k) new_val_index[ res_index_i + k ] = new_res_index + k; // // new_use_val for(addr_t k = 0; k < n_res; ++k) { bool use_val_k = val_use_case[ res_index_i + k ] != 0; new_use_val.push_back( use_val_k ); } } } } // // dep_vec Vector dep_vec( dep_vec_.size() ); for(size_t k = 0; k < dep_vec_.size(); ++k) dep_vec[k] = new_val_index[ dep_vec_[k] ]; new_tape.set_dep( dep_vec ); // // swap swap(new_tape); // # if CPPAD_VAL_GRAPH_TAPE_TRACE // inuse size_t final_inuse = thread_alloc::inuse(thread); std::cout << "dead_code: inuse = " << final_inuse - initial_inuse << "\n"; # endif // BEGIN_RETURN CPPAD_ASSERT_UNKNOWN( size_t( n_val() ) == new_use_val.size() ); return new_use_val; // END_RETURN } } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/con_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_CON_OP_HPP # define CPPAD_LOCAL_VAL_GRAPH_CON_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include // define CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL # include namespace CppAD { namespace local { namespace val_graph { /* {xrst_begin val_con_op dev} The Constant Value Operator ########################### Prototype ********* {xrst_literal // BEGIN_CON_OP_T // END_CON_OP_T } Context ******* The class is derived from :ref:`val_base_op-name` . It overrides all its base class virtual member functions and is a concrete class (it has no pure virtual functions). This is not a unary operator because the operand is in the constant vector and not the value vector. get_instance ************ This static member function returns a pointer to a con_op_t object. op_enum ******* This override of :ref:`val_base_op@op_enum` returns ``con_op_enum`` . n_before ******** This override of :ref:`val_base_op@n_before` returns 1. n_after ******* This override of :ref:`val_base_op@n_after` returns 0. n_arg ***** This override of :ref:`val_base_op@n_arg` returns 1. n_res ***** This override of :ref:`val_base_op@n_res` returns 1. eval **** This override of :ref:`val_base_op@eval` sets the result equal to :: con_vec[ arg_vec[ arg_index + 0 ] ] trace ===== If trace is true, :ref:`val_print_con_op-name` is called to print this operator. {xrst_toc_hidden val_graph/con_xam.cpp } Example ******* The file :ref:`con_xam.cpp ` is an example and test that uses this operator. {xrst_end val_con_op} */ // BEGIN_CON_OP_T template class con_op_t : public base_op_t { public: // n_before addr_t n_before(void) const override { return 1; } // // n_after addr_t n_after(void) const override { return 0; } // get_instance static con_op_t* get_instance(void) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; static con_op_t instance; return &instance; } // op_enum op_enum_t op_enum(void) const override { return con_op_enum; } // END_CON_OP_T // // n_arg addr_t n_arg( addr_t arg_index , const Vector& arg_vec ) const override { return 1; } // // n_res addr_t n_res( addr_t arg_index , const Vector& arg_vec ) const override { return 1; } // // eval void eval( const tape_t* tape , bool trace , addr_t arg_index , addr_t res_index , Vector& val_vec , Vector< Vector >& ind_vec_vec , size_t& compare_false ) const override { // // arg_vec, con_vec const Vector& arg_vec( tape->arg_vec() ); const Vector& con_vec( tape->con_vec() ); // // val_index, val_vec addr_t val_index = arg_vec[ arg_index + 0 ]; val_vec[res_index] = con_vec[ val_index ]; // // trace if( ! trace ) return; // // print_con_op Vector arg = { arg_vec[ arg_index + 0 ] }; Vector res_value = { val_vec[res_index] }; print_con_op(arg, res_index, res_value); } }; } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/csum_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_CSUM_OP_HPP # define CPPAD_LOCAL_VAL_GRAPH_CSUM_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include # include // define CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL # include namespace CppAD { namespace local { namespace val_graph { /* {xrst_begin val_csum_op dev} The Cumulative Summation Value Operator ####################################### Prototype ********* {xrst_literal // BEGIN_CSUM_OP_T // END_CSUM_OP_T } Context ******* The class is derived from :ref:`val_base_op-name` . It overrides all its base class virtual member functions and is a concrete class (it has no pure virtual functions). get_instance ************ This static member function returns a pointer to a csum_op_t object. op_enum ******* This override of :ref:`val_base_op@op_enum` returns ``csum_op_enum`` . n_before ******** This override of :ref:`val_base_op@n_before` returns 2. {xrst_literal // BEGIN_ARG_BEFORE // END_ARG_BEFORE } n_after ******* This override of :ref:`val_base_op@n_after` returns 1. This is for a copy of *n_arg* that can be used to iterate through the operators in reverse. n_res ***** This override of :ref:`val_base_op@n_res` returns 1. n_arg ***** see base_op :ref:`val_base_op@n_arg` . n_add ***** This member function returns the number of additions in the summation. n_sub ***** This member function returns the number of subtractions in the summation. eval **** This override of :ref:`val_base_op@eval` computes the summation. #. The additions in the summation are :: val_vec[ arg_vec[ arg_index + 2 ] ] , val_vec[ arg_vec[ arg_index + 3 ] ] , ... val_vec[ arg_vec[ arg_index + 1 + n_add ] ] #. The subtractions in the summation are :: val_vec[ arg_vec[ arg_index + 2 + n_add ] ] , val_vec[ arg_vec[ arg_index + 3 + n_add ] ] , ... val_vec[ arg_vec[ arg_index + 1 + n_add + n_sub ] ] trace ===== If trace is true, :ref:`val_print_csum_op-name` is called to print this operator. {xrst_toc_hidden val_graph/csum_xam.cpp } Example ******* The file :ref:`csum_xam.cpp ` is an example and test that uses this operator. {xrst_end val_csum_op} */ // BEGIN_CSUM_OP_T template class csum_op_t : public base_op_t { public: // n_before addr_t n_before(void) const override { return 2; } // // n_after addr_t n_after(void) const override { return 1; } // // get_instance static csum_op_t* get_instance(void) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; static csum_op_t instance; return &instance; } // op_enum // type of this operator op_enum_t op_enum(void) const override { return csum_op_enum; } // // n_arg addr_t n_arg( addr_t arg_index , const Vector& arg_vec ) const override { addr_t n_add = arg_vec[arg_index + 0]; addr_t n_sub = arg_vec[arg_index + 1]; return 3 + n_add + n_sub; } // // n_res addr_t n_res( addr_t arg_index , const Vector& arg_vec ) const override { return 1; } // // n_add addr_t n_add( addr_t arg_index , const Vector& arg_vec ) const { return size_t( arg_vec[arg_index + 0] ); } // // n_sub addr_t n_sub( addr_t arg_index , const Vector& arg_vec ) const { return size_t( arg_vec[arg_index + 1] ); } // // eval void eval( const tape_t* tape , bool trace , addr_t arg_index , addr_t res_index , Vector& val_vec , Vector< Vector >& ind_vec_vec , size_t& compare_false ) const override; // END_CSUM_OP_T }; // // eval template void csum_op_t::eval( const tape_t* tape , bool trace , addr_t arg_index , addr_t res_index , Vector& val_vec , Vector< Vector >& ind_vec_vec , size_t& compare_false ) const { // // arg_vec const Vector& arg_vec( tape->arg_vec() ); // // n_add, n_sub // BEGIN_ARG_BEFORE addr_t n_add = arg_vec[arg_index + 0] ; addr_t n_sub = arg_vec[arg_index + 1] ; // END_ARG_BEFORE // // sum Value sum(0.0); for(addr_t i = 0; i < n_add; ++i) sum += val_vec[ arg_vec[arg_index + 2 + i] ]; for(addr_t i = 0; i < n_sub; ++i) sum -= val_vec[ arg_vec[arg_index + 2 + n_add + i] ]; // // val_vec val_vec[res_index] = sum; // // trace if( ! trace ) return; // // print_csum_op Vector arg(3 + n_add + n_sub); for(addr_t i = 0; i < addr_t( arg.size() ); ++i) arg[i] = arg_vec[ arg_index + i ]; Vector res_value = { val_vec[res_index] }; print_csum_op(arg, res_index, res_value ); // return; } } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/cumulative.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_CUMULATIVE_HPP # define CPPAD_LOCAL_VAL_GRAPH_CUMULATIVE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-25 Bradley M. Bell // --------------------------------------------------------------------------- # include namespace CppAD { namespace local { namespace val_graph { /* {xrst_begin val_op2csum dev} {xrst_spell neg } Convert Add, Subtract, or Negative to Cumulative Summation ########################################################## These are ideas under construction for a better way to combine cumulative summation operators; i.e., an alternative to the summation.hpp file. Prototype ********* {xrst_literal // BEGIN_OP2CSUM // END_OP2CSUM } Replacement Operator ******************** This will replace an addition, subtraction, or negative value operator with an equivalent cumulative summation operator. op_index ******** Is the operator index in this tape for the operator that is being replaced. On input it must correspond to an add_op_enum, sub_op_enum, or neg_op_enum value operator. Upon return it will correspond to a csum_op_enum operator. {xrst_toc_hidden val_graph/cumulative_xam.cpp } Example ******* see :ref:`val_cumulative_xam-name` . {xrst_end val_op2csum} */ // BEGIN_OP2CSUM template void tape_t::op2csum(addr_t op_index) // END_OP2CSUM { // op2arg_index_ CPPAD_ASSERT_UNKNOWN( op2arg_index_.size() == size_t( n_op() ) ); // // arg_index addr_t arg_index = op2arg_index_[op_index]; // // op_ptr const base_op_t< Value>* op_ptr = base_op_ptr(op_index); // // op_enum op_enum_t op_enum = op_ptr->op_enum(); // // n_add, n_sub addr_t n_add = 0; addr_t n_sub = 0; // // n_add, n_sub switch( op_enum ) { // default : // op_enum is not add_op, sub_op, or neg_op CPPAD_ASSERT_UNKNOWN( false ); break; // add_op_enum case add_op_enum: n_add = 2; break; // sub_op_enum case sub_op_enum: n_add = 1; n_sub = 1; break; // neg_op_enum case neg_op_enum: n_add = 0; n_sub = 1; break; } // // op_enum_vec_ op_enum_vec_[op_index] = uint8_t( csum_op_enum ); // // op2arg_index_ op2arg_index_[op_index] = addr_t( var_arg_.size() ); // // var_arg_ var_arg_.push_back( n_add ); var_arg_.push_back( n_sub ); for(addr_t i = 0; i < n_add + n_sub; ++i) { addr_t arg_i = var_arg_[arg_index + i]; var_arg_.push_back( arg_i ); } // // var_arg_ addr_t n_arg = 3 + n_add + n_sub; var_arg_.push_back( n_arg ); // return; } } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/dead_code.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_DEAD_CODE_HPP # define CPPAD_LOCAL_VAL_GRAPH_DEAD_CODE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-25 Bradley M. Bell // --------------------------------------------------------------------------- # include # include namespace CppAD { namespace local { namespace val_graph { /* {xrst_begin val_tape_dead_code dev} {xrst_spell comp dep } Dead Code Elimination ##################### Prototype ********* {xrst_literal // BEGIN_DEAD_CODE // END_DEAD_CODE } keep_compare ************ see :ref:`val_tape_option@keep_compare`: #. If this is false, all the :ref`val_comp_op-name` operators will be removed. In this case future calls to eval will not modify :ref:`val_tape@eval@compare_false` . #. If this is true, the compare_no_enum compare operators are removed and other comparisons are kept. keep_print ********** see :ref:`val_tape_option@keep_print`: Algorithm ********* #. The dependent variables are marked as needed. #. The operators are scanned in reverse and if an operator's result is needed, the corresponding arguments are marked as needed. The call operator has a more complicated version of this marking. #. A forward pass is made though the operators and only the needed ones are included. #. An operators result may have a new index because some previous results were left out. A mapping from the old result indices to the new result indices enables subsequent operators to adjust their argument indices. #. After the forward pass, the mapping from old indices to new indices is used to adjust the dependent variable indices. Changes ******* Only the following values, for this tape, are guaranteed to be same: #. The number of independent values :ref:`val_tape@n_ind` . #. The size of the dependent vector :ref:`dep_vec.size() ` . #. The mapping from the independent to the dependent variables. new_use_val *********** The i-th element of the return vector *new_use_val* is true (false), if and only if the i-th element of the value vector is used to compute the dependent values. There are two exceptions to this rule. One exception is that all the independent values are marked as used no matter what. The other exception is that the nan, after the independent variables, is also marked as used no matter what. If *new_use_val*\ [i] is false, the i-th value must be the result of a call operator. In addition, at least one result for each call will have a corresponding *new_use_val* of true. Reference ********* `dead-code elimination `_ {xrst_toc_hidden val_graph/dead_xam.cpp } Example ******* The file :ref:`dead_xam.cpp ` is an example and test of tape.dead_code(). {xrst_end val_tape_dead_code} */ // BEGIN_DEAD_CODE // new_use_val = dead_code() template vectorBool tape_t::dead_code(void) // END_DEAD_CODE { // ----------------------------------------------------------------------- // Dead Code Elimination // https://en.wikipedia.org/wiki/Dead-code_elimination // ----------------------------------------------------------------------- # if CPPAD_VAL_GRAPH_TAPE_TRACE // thread, initial_inuse size_t thread = thread_alloc::thread_num(); size_t initial_inuse = thread_alloc::inuse(thread); # endif // // keep_compare bool keep_compare = option_map_["keep_compare"] == "true"; // // keep_print bool keep_print = option_map_["keep_print"] == "true"; // // val_use_case, vec_last_load Vector val_use_case, vec_last_load; rev_depend(val_use_case, vec_last_load); // // new_tape tape_t new_tape; new_tape.set_ind(n_ind_); // // new_which_vec // initialize all dynamic vectors as not used Vector new_which_vec( vec_initial_.size() ); for(size_t i = 0; i < vec_initial_.size(); ++i) new_which_vec[i] = addr_t( vec_initial_.size() ); // // new_val_index // include nan at index n_ind_ in val_vec Vector new_val_index( n_val_ ); for(addr_t i = 0; i <= n_ind_; ++i) new_val_index[i] = addr_t(i); for(addr_t i = n_ind_ + 1; i < n_val_; ++i) new_val_index[i] = addr_t( n_val_ ); // # ifndef NDEBUG // nan at index n_ind_ assert( op_enum_t( new_tape.op_enum_vec_[0] ) == con_op_enum ); assert( new_tape.var_arg_[0] == 0 ); assert( CppAD::isnan( new_tape.con_vec_[0] ) ); # endif // // new_use_val // Because the call operator can have more than one result, not all the // results for all the needed operators are used. Initialize as all the // independent values and the nan after them are used. vectorBool new_use_val(n_ind_ + 1); for(addr_t i = 0; i <= n_ind_; ++i) new_use_val[i] = true; // // work Vector work; // // op_itr_forward op_iterator op_itr_forward(*this, 0); // // i_op for(addr_t i_op = 1; i_op < n_op(); ++i_op) { // // op_itr_forward ++op_itr_forward; // skip index zero // // op_ptr, arg_index, res_index const base_op_t* op_ptr = op_itr_forward.op_ptr(); addr_t res_index = op_itr_forward.res_index(); addr_t arg_index = op_itr_forward.arg_index(); // // op_enum, n_res op_enum_t op_enum = op_ptr->op_enum(); addr_t n_res = op_ptr->n_res(arg_index, var_arg_); // // need_op bool need_op = false; if( n_res == 0 ) { if( op_enum == vec_op_enum || op_enum == store_op_enum ) { addr_t which_vector = var_arg_[arg_index + 0]; need_op = i_op < vec_last_load[which_vector]; } else if( op_enum == pri_op_enum ) { need_op = keep_print; need_op &= var_arg_[arg_index + 2] != this->n_ind(); } else { CPPAD_ASSERT_UNKNOWN( op_enum == comp_op_enum ); need_op = keep_compare; need_op &= var_arg_[arg_index + 0] != addr_t(compare_no_enum); } } else for(addr_t k = 0; k < n_res; ++k) need_op |= 0 != val_use_case[ res_index + k]; // if( need_op ) { addr_t new_res_index = record_new( new_tape , new_which_vec , work , new_val_index , val_use_case , op_ptr , arg_index , res_index ); // // new_val_index for(addr_t k = 0; k < n_res; ++k) new_val_index[ res_index + k ] = new_res_index + k; // // new_use_val for(addr_t k = 0; k < n_res; ++k) { bool use_val_k = val_use_case[ res_index + k ] != 0; new_use_val.push_back( use_val_k ); } } } // // dep_vec Vector dep_vec( dep_vec_.size() ); for(size_t k = 0; k < dep_vec_.size(); ++k) dep_vec[k] = new_val_index[ dep_vec_[k] ]; new_tape.set_dep( dep_vec ); // // swap swap(new_tape); // # if CPPAD_VAL_GRAPH_TAPE_TRACE // inuse size_t final_inuse = thread_alloc::inuse(thread); std::cout << "dead_code: inuse = " << final_inuse - initial_inuse << "\n"; # endif // BEGIN_RETURN CPPAD_ASSERT_UNKNOWN( size_t( n_val() ) == new_use_val.size() ); return new_use_val; // END_RETURN } } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/dis_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_DIS_OP_HPP # define CPPAD_LOCAL_VAL_GRAPH_DIS_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include // define CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL # include namespace CppAD { namespace local { namespace val_graph { /* {xrst_begin val_dis_op dev} The Discrete Value Operator ########################### Prototype ********* {xrst_literal // BEGIN_DIS_OP_T // END_DIS_OP_T } Context ******* The class is derived from :ref:`val_base_op-name` . It overrides all its base class virtual member functions and is a concrete class (it has no pure virtual functions). get_instance ************ This static member function returns a pointer to a dis_op_t object. op_enum ******* This override of :ref:`val_base_op@op_enum` returns ``dis_op_enum`` . n_before ******** This override of :ref:`val_base_op@n_before` returns 1. {xrst_literal // BEGIN_ARG_BEFORE // END_ARG_BEFORE } n_after ******* This override of :ref:`val_base_op@n_after` returns 0. n_arg ***** This override of :ref:`val_base_op@n_arg` returns 2. n_res ***** This override of :ref:`val_base_op@n_res` returns 1. eval **** This override of :ref:`val_base_op@eval` sets the result equal to the discrete function evaluated at :: val_vec[ arg_vec[ arg_index + 1 ] ] trace ===== If trace is true, :ref:`val_print_op-name` is called to print this operator. {xrst_toc_hidden val_graph/dis_xam.cpp } Example ******* The file :ref:`dis_xam.cpp ` is an example and test that uses this operator. {xrst_end val_dis_op} */ // BEGIN_DIS_OP_T template class dis_op_t : public base_op_t { public: // n_before addr_t n_before(void) const override { return 1; } // // n_after addr_t n_after(void) const override { return 0; } // // get_instance static dis_op_t* get_instance(void) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; static dis_op_t instance; return &instance; } // op_enum op_enum_t op_enum(void) const override { return dis_op_enum; } // // discrete_index size_t discrete_index( addr_t arg_index , const Vector& arg_vec ) { return size_t( arg_vec[arg_index + 0] ); } // END_DIS_OP_T // // n_arg addr_t n_arg( addr_t arg_index , const Vector& arg_vec ) const override { return 2; } // // n_res addr_t n_res( addr_t arg_index , const Vector& arg_vec ) const override { return 1; } // // eval void eval( const tape_t* tape , bool trace , addr_t arg_index , addr_t res_index , Vector& val_vec , Vector< Vector >& ind_vec_vec , size_t& compare_false ) const override { // // arg_vec const Vector& arg_vec( tape->arg_vec() ); // // discrete_index // BEGIN_ARG_BEFORE size_t discrete_index = size_t( arg_vec[arg_index + 0] ); // END_ARG_BEFORE // // val_vec addr_t val_index = arg_vec[ arg_index + 1 ]; const Value& value = val_vec[val_index]; val_vec[res_index] = discrete::eval(discrete_index, value); // // trace if( ! trace ) return; // std::string name = discrete::name(discrete_index); Vector arg_val_index = { arg_vec[arg_index + 1] }; Vector res_value = { val_vec[res_index] }; print_op(name, arg_val_index, res_index, res_value); // return; } }; } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/dyn_type.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_DYN_TYPE_HPP # define CPPAD_LOCAL_VAL_GRAPH_DYN_TYPE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* ------------------------------------------------------------------------------- {xrst_begin_parent type_dyn_op dev} Types of Dynamic Parameter Operators #################################### {xrst_end type_dyn_op} ------------------------------------------------------------------------------- {xrst_begin unary_dyn_op dev} Is This a Binary Dynamic Parameter Operator ########################################### Prototype ********* {xrst_literal // BEGIN_UNARY_DYN_OP // END_UNARY_DYN_OP } dyn_op ****** is the dynamic parameter operator. Return ****** The return is true if this operator has one operands and is recorded by specifying the operator and parameter address of the operand on the tape. {xrst_end unary_dyn_op} */ namespace CppAD { namespace local { namespace val_graph { // BEGIN_UNARY_DYN_OP inline bool unary_dyn_op(op_code_dyn dyn_op) // END_UNARY_DYN_OP { bool result; switch(dyn_op) { default: result = false; break; // case abs_dyn: case acos_dyn: case acosh_dyn: case asin_dyn: case asinh_dyn: case atan_dyn: case atanh_dyn: case cos_dyn: case cosh_dyn: case erf_dyn: case erfc_dyn: case exp_dyn: case expm1_dyn: case fabs_dyn: case log1p_dyn: case log_dyn: case neg_dyn: case sign_dyn: case sin_dyn: case sinh_dyn: case sqrt_dyn: case tan_dyn: case tanh_dyn: result = true; break; } return result; } /* ------------------------------------------------------------------------------- {xrst_begin binary_dyn_op dev} Is This a Binary Dynamic Parameter Operator ########################################### Prototype ********* {xrst_literal // BEGIN_BINARY_DYN_OP // END_BINARY_DYN_OP } dyn_op ****** is the dynamic parameter operator. Return ****** The return is true if this operator has two operands and is recorded by specifying the operator and parameter address for each of the operands. {xrst_end binary_dyn_op} */ // BEGIN_BINARY_DYN_OP inline bool binary_dyn_op(op_code_dyn dyn_op) // END_BINARY_DYN_OP { bool result; switch(dyn_op) { default: result = false; break; case add_dyn: case div_dyn: case mul_dyn: case pow_dyn: case sub_dyn: case zmul_dyn: result = true; break; } return result; } } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/enable_parallel.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_ENABLE_PARALLEL_HPP # define CPPAD_LOCAL_VAL_GRAPH_ENABLE_PARALLEL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-24 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN_SORT_THIS_LINE_PLUS_1 # include # include # include # include # include # include # include # include # include # include # include // END_SORT_THIS_LINE_MINUS_1 // namespace CppAD { namespace local { namespace val_graph { /* {xrst_begin val_enable_parallel dev} Enable val_graph Operators During Parallel Mode ############################################### Prototype ********* {xrst_literal // BEGIN_ENABLE_PARALLEL // END_ENABLE_PARALLEL } Purpose ******* Convert an operator enum type :ref:`val_graph_type@op_enum_t` to a pointer to an operator base class object :ref:`val_base_op-name` . {xrst_end val_enable_parallel} */ // ---------------------------------------------------------------------------- // BEGIN_ENABLE_PARALLEL // CppAD::local::val_graph enable_parallel() template void enable_parallel(void) // END_ENABLE_PARALLEL { // // BEGIN_SORT_THIS_LINE_PLUS_1 abs_op_t::get_instance(); acos_op_t::get_instance(); acosh_op_t::get_instance(); add_op_t::get_instance(); asin_op_t::get_instance(); asinh_op_t::get_instance(); atan_op_t::get_instance(); atanh_op_t::get_instance(); call_op_t::get_instance(); cexp_op_t::get_instance(); comp_op_t::get_instance(); con_op_t::get_instance(); cos_op_t::get_instance(); cosh_op_t::get_instance(); csum_op_t::get_instance(); dis_op_t::get_instance(); div_op_t::get_instance(); erf_op_t::get_instance(); erfc_op_t::get_instance(); exp_op_t::get_instance(); expm1_op_t::get_instance(); load_op_t::get_instance(); log1p_op_t::get_instance(); log_op_t::get_instance(); mul_op_t::get_instance(); neg_op_t::get_instance(); pow_op_t::get_instance(); pri_op_t::get_instance(); sign_op_t::get_instance(); sin_op_t::get_instance(); sinh_op_t::get_instance(); sqrt_op_t::get_instance(); store_op_t::get_instance(); sub_op_t::get_instance(); tan_op_t::get_instance(); tanh_op_t::get_instance(); vec_op_t::get_instance(); // END_SORT_THIS_LINE_MINUS_1 return; } } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/fold_con.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_FOLD_CON_HPP # define CPPAD_LOCAL_VAL_GRAPH_FOLD_CON_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-25 Bradley M. Bell /* ------------------------------------------------------------------------------- {xrst_begin val_tape_fold_con dev} {xrst_spell dep } Constant Folding ################ Prototype ********* {xrst_literal // BEGIN_FOLD_CON // END_FOLD_CON } Discussion ********** This is like :ref:`value numbering ` but a major difference is that it adds a new operator for each folded constant. #. operator results that are constants get replaced by con_op operators. Thus all the constants that are used to compute the dependent values are the result of a con_op operator. #. If all the results for an operator get replaced, the operator becomes dead code. CppAD ===== CppAD functions fold constants before recording but constants that result from an atomic function do not have separate constant operators. #. Perhaps fun2val could make a separate operator for these constants and it would not be necessary to fold constants here. #. On the other hand, folding will be necessary if we allow for creating a new tape where some of the independent values are constants #. Currently, this uses more memory than the other optimization steps and is of dubious value in the context of CppAD. Changes ******* Only the following values, for this tape, are guaranteed to be same: #. The number of independent values :ref:`val_tape@n_ind` . #. The size of the dependent vector :ref:`dep_vec.size() ` . Reference ********* `constant folding `_ . {xrst_toc_hidden val_graph/fold_con_xam.cpp } Example ******* The file :ref:`fold_con_xam.cpp ` is an example and test of tape.fold_con(). {xrst_end val_tape_fold_con} ------------------------------------------------------------------------------- */ # include # include namespace CppAD { namespace local { namespace val_graph { // BEGIN_FOLD_CON template void tape_t::fold_con(void) // END_FOLD_CON { # if CPPAD_VAL_GRAPH_TAPE_TRACE // thread, initial_inuse size_t thread = thread_alloc::thread_num(); size_t initial_inuse = thread_alloc::inuse(thread); # endif // // nan Value nan = CppAD::numeric_limits::quiet_NaN(); // // val_index2con Vector val_index2con(n_val_); for(addr_t i = 0; i < n_ind_; ++i) val_index2con[i] = nan; bool trace = false; eval(trace, val_index2con); // // is_constant vectorBool is_constant( static_cast(n_val_) ); for(addr_t i = 0; i < n_val_; ++i) is_constant[i] = false; // // con_x, type_x, type_y // use CppAD::vector because call_atomic_for_type expects it CppAD::vector con_x; CppAD::vector type_x, type_y; // // op_arg Vector op_arg; // // new_tape tape_t new_tape; new_tape.set_ind(n_ind_); // // old2new_index Vector old2new_index; for(addr_t i = 0; i < n_ind_; ++i) old2new_index.push_back( i ); // // we will skip nan at index zero old2new_index.push_back( n_ind_ ); is_constant[n_ind_] = true; // // op_itr op_iterator op_itr(*this, 0); // // i_op for(addr_t i_op = 1; i_op < n_op(); ++i_op) { // // op_itr ++op_itr; // skip nan at index zero // // op_ptr, arg_index, res_index const base_op_t* op_ptr = op_itr.op_ptr(); addr_t arg_index = op_itr.arg_index(); addr_t res_index = op_itr.res_index(); // // op_enum, n_arg, n_before, n_after, n_res op_enum_t op_enum = op_ptr->op_enum(); addr_t n_before = op_ptr->n_before(); addr_t n_after = op_ptr->n_after(); addr_t n_arg = op_ptr->n_arg(arg_index, var_arg_); addr_t n_res = op_ptr->n_res(arg_index, var_arg_); // CPPAD_ASSERT_UNKNOWN( size_t( res_index ) == old2new_index.size() ); // // new_tape, is_constant, old2new_index if( n_res == 1 && op_enum != con_op_enum ) { bool fold = true; for(addr_t i = n_before; i < n_arg - n_after; ++i) fold &= is_constant[ var_arg_[arg_index + i] ]; if( fold ) { is_constant[res_index] = true; const Value& value = val_index2con[res_index]; addr_t new_res_index = new_tape.record_con_op(value); old2new_index.push_back( new_res_index ); } else { op_arg.resize(n_arg); for(addr_t k = 0; k < n_before; ++k) op_arg[k] = var_arg_[arg_index + k]; for(addr_t k = n_before; k < n_arg - n_after; ++k) { addr_t old_index = var_arg_[arg_index + k]; assert( old_index < res_index ); op_arg[k] = old2new_index[old_index]; } for(addr_t k = 1; k <= n_after; ++k) op_arg[n_arg - k] = var_arg_[arg_index + n_arg - k]; // addr_t new_res_index = new_tape.record_op(op_enum, op_arg); old2new_index.push_back( new_res_index ); } } else switch(op_enum) { // // default default: CPPAD_ASSERT_KNOWN(false, "val_graph::fold_con: This operator not yet implemented" ); break; // ---------------------------------------------------------------- // con_op case con_op_enum: CPPAD_ASSERT_UNKNOWN( n_arg == 1); { is_constant[res_index] = true; const Value& value = val_index2con[res_index]; addr_t new_res_index = new_tape.record_con_op(value); old2new_index.push_back( new_res_index ); } break; // ---------------------------------------------------------------- // call_op case call_op_enum: { // // atomic_index, call_id addr_t atomic_index = var_arg_[arg_index + 2]; addr_t call_id = var_arg_[arg_index + 3]; CPPAD_ASSERT_UNKNOWN( atomic_index > 0 ); // // n_before, n_x addr_t n_x = n_arg - n_before - op_ptr->n_after(); // // con_x, type_x type_x.resize(n_x); con_x.resize(n_x); for(addr_t i = 0; i < n_x; ++i) { addr_t val_index = var_arg_[arg_index + n_before + i]; con_x[i] = val_index2con[val_index]; if( is_constant[val_index] ) type_x[i] = constant_enum; else type_x[i] = variable_enum;; } // // type_y type_y.resize(n_res); call_atomic_for_type( con_x, type_x, type_y, size_t(atomic_index), size_t(call_id) ); // // new_tape, new_res_index // record the function call op_arg.resize(n_x); for(addr_t k = 0; k < n_x; ++k) { addr_t old_index = var_arg_[arg_index + n_before + k]; op_arg[k] = old2new_index[old_index]; } addr_t new_res_index = new_tape.record_call_op( atomic_index, call_id, n_res, op_arg ); // // is_constant, new_tape, old2new_index for(addr_t i = 0; i < n_res; ++i) { is_constant[res_index + i] = type_y[i] <= constant_enum; if( is_constant[res_index + i] ) { const Value& value = val_index2con[res_index + i]; addr_t con_res_index = new_tape.record_con_op(value); old2new_index.push_back( con_res_index ); } else { // if none of these results get used, this call is dead code old2new_index.push_back(new_res_index + i); } } } break; } } // // dep_vec Vector dep_vec( dep_vec_.size() ); for(size_t k = 0; k < dep_vec_.size(); ++k) dep_vec[k] = old2new_index[ dep_vec_[k] ]; new_tape.set_dep( dep_vec ); // // swap swap(new_tape); # if CPPAD_VAL_GRAPH_TAPE_TRACE // inuse size_t final_inuse = thread_alloc::inuse(thread); std::cout << "fold_con: inuse = " << final_inuse - initial_inuse << "\n"; # endif return; } } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/fun2val.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_FUN2VAL_HPP # define CPPAD_LOCAL_VAL_GRAPH_FUN2VAL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // -------------------------------------------------------------------------- /* ------------------------------------------------------------------------------ {xrst_begin fun2val_graph dev} Create a Value Graph Corresponding to an ADFun Object ##################################################### Syntax ****** | |tab| ``ADFun`` < *Base* > *fun* | |tab| *fun* . ``fun2val`` ( *val_tape* ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } Base **** is the type corresponding to this :ref:`adfun-name` object; i.e., its calculations are done using the type *Base* . It is also :ref:`val_tape@Value` type for the tape. RecBase ******* in the prototype above, *RecBase* is the same type as *Base* . val_tape ******** This is a :ref:`val_tape-name` object. The input contents of the tape does not matter. Upon return it is a :ref:`val_graph-name` representation of the function. The independent dynamic parameters have the same order as in *fun* and come first (in the value graph independent vector). The independent variables have the same order as in *fun* and come after the independent dynamic parameters. fun *** This is the function that the value graph will correspond to. Under Construction ****************** This routine is under construction and is only implemented for a few of the possible :ref:`ADFun-name` operators. {xrst_toc_hidden val_graph/fun2val_xam.cpp } Examples ******** The file :ref:`val_fun2val_xam.cpp-name` is an example an test of this conversion. {xrst_end fun2val_graph} */ # include # include # include # include # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN_PROTOTYPE template void ADFun::fun2val( local::val_graph::tape_t& val_tape ) // END_PROTOTYPE { // // Vector, addr_t, op_enum_t using local::val_graph::Vector; using local::val_graph::addr_t; using local::val_graph::op_enum_t; op_enum_t number_op_enum = local::val_graph::number_op_enum; // // op_code_dyn, number_dyn, op_code_var using local::op_code_dyn; op_code_dyn number_dyn = local::number_dyn; using local::op_code_var; // // pod_vector, opcode_t using local::pod_vector; using local::opcode_t; // // invalid_addr_t addr_t invalid_addr_t = std::numeric_limits::max(); // // parameter const Base* parameter = play_.par_ptr(); // // dyn_op2val_op Vector dyn_op2val_op(number_dyn); for(size_t i = 0; i < size_t(number_dyn); ++i) dyn_op2val_op[i] = number_op_enum; // invalid dyn_op2val_op[local::add_dyn] = local::val_graph::add_op_enum; dyn_op2val_op[local::neg_dyn] = local::val_graph::neg_op_enum; dyn_op2val_op[local::sub_dyn] = local::val_graph::sub_op_enum; // ------------------------------------------------------------------------ // var_op2val_op Vector var_op2val_op(local::NumberOp); for(size_t i = 0; i < size_t(local::NumberOp); ++i) var_op2val_op[i] = number_op_enum; // invalid // // unary operators // BEGIN_SORT_THIS_LINE_PLUS_1 var_op2val_op[local::AbsOp] = local::val_graph::abs_op_enum; var_op2val_op[local::AcosOp] = local::val_graph::acos_op_enum; var_op2val_op[local::AcoshOp] = local::val_graph::acosh_op_enum; var_op2val_op[local::AsinOp] = local::val_graph::asin_op_enum; var_op2val_op[local::AsinhOp] = local::val_graph::asinh_op_enum; var_op2val_op[local::AtanOp] = local::val_graph::atan_op_enum; var_op2val_op[local::AtanhOp] = local::val_graph::atanh_op_enum; var_op2val_op[local::CosOp] = local::val_graph::cos_op_enum; var_op2val_op[local::CoshOp] = local::val_graph::cosh_op_enum; var_op2val_op[local::ErfOp] = local::val_graph::erf_op_enum; var_op2val_op[local::ErfcOp] = local::val_graph::erfc_op_enum; var_op2val_op[local::ExpOp] = local::val_graph::exp_op_enum; var_op2val_op[local::Expm1Op] = local::val_graph::expm1_op_enum; var_op2val_op[local::Log1pOp] = local::val_graph::log1p_op_enum; var_op2val_op[local::LogOp] = local::val_graph::log_op_enum; var_op2val_op[local::NegOp] = local::val_graph::neg_op_enum; var_op2val_op[local::SignOp] = local::val_graph::sign_op_enum; var_op2val_op[local::SinOp] = local::val_graph::sin_op_enum; var_op2val_op[local::SinhOp] = local::val_graph::sinh_op_enum; var_op2val_op[local::SqrtOp] = local::val_graph::sqrt_op_enum; var_op2val_op[local::TanOp] = local::val_graph::tan_op_enum; var_op2val_op[local::TanhOp] = local::val_graph::tanh_op_enum; // END_SORT_THIS_LINE_MINUS_1 // // add var_op2val_op[local::AddpvOp] = local::val_graph::add_op_enum; var_op2val_op[local::AddvvOp] = local::val_graph::add_op_enum; // // sub var_op2val_op[local::SubpvOp] = local::val_graph::sub_op_enum; var_op2val_op[local::SubvpOp] = local::val_graph::sub_op_enum; var_op2val_op[local::SubvvOp] = local::val_graph::sub_op_enum; // // mul var_op2val_op[local::MulpvOp] = local::val_graph::mul_op_enum; var_op2val_op[local::MulvvOp] = local::val_graph::mul_op_enum; // // div var_op2val_op[local::DivpvOp] = local::val_graph::div_op_enum; var_op2val_op[local::DivvpOp] = local::val_graph::div_op_enum; var_op2val_op[local::DivvvOp] = local::val_graph::div_op_enum; // // pow var_op2val_op[local::PowpvOp] = local::val_graph::pow_op_enum; var_op2val_op[local::PowvpOp] = local::val_graph::pow_op_enum; var_op2val_op[local::PowvvOp] = local::val_graph::pow_op_enum; // ------------------------------------------------------------------------ // // dyn_par_op // mapping from dynamic parameter index to operator const pod_vector& dyn_par_op ( play_.dyn_par_op() ); // // dyn2par_index // mapping from dynamic parameter index to parameter index const pod_vector& dyn2par_index ( play_.dyn2par_index() ); // // dyn_par_arg // vector that contains arguments to all the dynamic parameter operators const pod_vector& dyn_par_arg( play_.dyn_par_arg() ); // // n_parameter // number of parameters size_t n_parameter = play_.num_par_all(); // // n_dynamic // number of dynamic parameters size_t n_dynamic = dyn2par_index.size(); // // n_dynamic_ind // number of independent dynamic parameters size_t n_dynamic_ind = play_.n_dyn_independent(); // // n_variables // number of variables const size_t n_variable = play_.num_var(); // // n_variable_ind // number of independent variables size_t n_variable_ind = ind_taddr_.size(); // // n_val_ind // number of independent values addr_t n_val_ind = addr_t( n_dynamic_ind + n_variable_ind ); // # ifndef NDEBUG // nan_val_index // initialize value vector tape addr_t nan_val_index = val_tape.set_ind( n_val_ind ); CPPAD_ASSERT_UNKNOWN( nan_val_index == n_val_ind ); # else val_tape.set_ind( n_val_ind ); # endif // // val_tape, vec_info_vec // Put dynamic vectors in val_tape and create vec_info_vec struct vec_info_t { size_t size; size_t offset; }; Vector vec_info_vec; { size_t n_vecad_ind = play_.num_var_vec_ind(); size_t index = 0; while(index < n_vecad_ind) { size_t size = play_.GetVecInd(index++); size_t offset = index; // Vector initial(size); for(size_t i = 0; i < size; ++i) { size_t par_index = play_.GetVecInd(index++); addr_t val_index = val_tape.record_con_op( parameter[par_index] ); initial[i] = val_index; } # ifdef NDEBUG val_tape.record_vec_op(initial); # else addr_t which_vector = val_tape.record_vec_op(initial); CPPAD_ASSERT_UNKNOWN( size_t(which_vector) == vec_info_vec.size() ); # endif vec_info_t vec_info = {size, offset}; vec_info_vec.push_back( vec_info ); } } // // vec_offset2index // mapping from vecad offset to index in vec_info_vec auto vec_offset2index = [&vec_info_vec](addr_t offset) { addr_t index = 0; size_t offset_s = size_t(offset); while( vec_info_vec[index].offset < offset_s ) ++index; CPPAD_ASSERT_UNKNOWN( vec_info_vec[index].offset == offset_s ); return index; }; // // par2val_index // Initialize mapping from parameter index to index in value vector. Vector par2val_index(n_parameter); for(size_t i = 0; i < n_parameter; ++i) par2val_index[i] = invalid_addr_t; for(addr_t i = 0; i < addr_t( n_dynamic_ind ); ++i) par2val_index[i + 1] = i; // // val_op_arg, var_op_res, res_is_par; Vector val_op_arg, var_op_res; Vector res_is_par; // // i_arg // initial index in dyn_par_arg addr_t i_arg = 0; // // ensure_par2val_index // val_tape, par2val_index auto ensure_par2val_index = [&val_tape, &par2val_index, ¶meter, invalid_addr_t] (addr_t par_index) { addr_t result = par2val_index[par_index]; if( result == invalid_addr_t ) { Base constant = parameter[par_index]; result = val_tape.record_con_op(constant); par2val_index[par_index] = result; } return result; }; // // val_tape // record dynamic parameter operations // // val_tape, val_index, par2val_index addr_t val_index = invalid_addr_t; for(size_t i_dyn = n_dynamic_ind; i_dyn < n_dynamic; ++i_dyn) { // // i_par size_t i_par = size_t( dyn2par_index[i_dyn] ); // // dyn_op // operator for this dynamic parameter local::op_code_dyn dyn_op = local::op_code_dyn( dyn_par_op[i_dyn] ); // // is_unary, is_binary bool is_unary = local::val_graph::unary_dyn_op(dyn_op); bool is_binary = local::val_graph::binary_dyn_op(dyn_op); // // n_arg, val_tape, val_index, par2val_index addr_t n_arg; if( is_unary || is_binary ) { // // val_tape, val_index, par2val_index n_arg = addr_t( num_arg_dyn(dyn_op) ); val_op_arg.resize(n_arg); for(addr_t i = 0; i < n_arg; ++i) { addr_t par_index = dyn_par_arg[i_arg + i]; val_op_arg[i] = ensure_par2val_index(par_index); } // val_tape, val_index op_enum_t val_op = dyn_op2val_op[dyn_op]; val_index = val_tape.record_op(val_op, val_op_arg); } else switch( dyn_op ) { // default: CPPAD_ASSERT_KNOWN(false, "val_graph::fun2val: This dynamic operator not yet implemented" ); // ------------------------------------------------------------------- // result_dyn // This is a place holder for multiple result operators case local::result_dyn: CPPAD_ASSERT_UNKNOWN( val_index != invalid_addr_t ); n_arg = 0; val_index += 1; break; // ------------------------------------------------------------------- // dis_dyn case local::dis_dyn: { // val_tape, val_index, par2val_index val_op_arg.resize(1); addr_t discrete_index = dyn_par_arg[i_arg + 0]; addr_t par_index = dyn_par_arg[i_arg + 1]; val_op_arg[0] = ensure_par2val_index(par_index); // // n_arg, val_tape, val_index n_arg = 2; val_index = val_tape.record_dis_op( discrete_index, val_op_arg[0] ); } break; // ------------------------------------------------------------------- // cond_exp case local::cond_exp_dyn: { CompareOp cop = CompareOp( dyn_par_arg[i_arg + 0] ); addr_t left = ensure_par2val_index( dyn_par_arg[i_arg + 1] ); addr_t right = ensure_par2val_index( dyn_par_arg[i_arg + 2] ); addr_t if_true = ensure_par2val_index( dyn_par_arg[i_arg + 3] ); addr_t if_false = ensure_par2val_index( dyn_par_arg[i_arg + 4] ); local::val_graph::compare_enum_t compare_enum; switch( cop ) { case CompareLt: compare_enum = local::val_graph::compare_lt_enum; break; case CompareLe: compare_enum = local::val_graph::compare_le_enum; break; case CompareEq: compare_enum = local::val_graph::compare_eq_enum; break; case CompareGe: compare_enum = local::val_graph::compare_lt_enum; std::swap(if_true, if_false); break; case CompareGt: compare_enum = local::val_graph::compare_le_enum; std::swap(if_true, if_false); break; case CompareNe: compare_enum = local::val_graph::compare_eq_enum; std::swap(if_true, if_false); break; default: CPPAD_ASSERT_UNKNOWN(false); // avoid warning about value not set compare_enum = local::val_graph::compare_no_enum; break; } // // n_arg, val_tape, val_index n_arg = 5; val_index = val_tape.record_cexp_op( compare_enum, left, right, if_true, if_false ); } break; // ------------------------------------------------------------------- // atom_dyn case local::atom_dyn: { // // atomic_index, call_id, n_call_arg, n_res addr_t atomic_index = dyn_par_arg[i_arg + 0]; addr_t call_id = dyn_par_arg[i_arg + 1]; addr_t n_call_arg = dyn_par_arg[i_arg + 2]; addr_t n_res = dyn_par_arg[i_arg + 3] ; // num_dyn = size_t( dyn_par_arg[i_age + 4] ); // // val_op_arg, val_tape, par2val_index val_op_arg.resize( n_call_arg ); for(addr_t i = 0; i < n_call_arg; i++) { addr_t par_index = dyn_par_arg[i_arg + i + 5]; val_op_arg[i] = ensure_par2val_index(par_index); } // n_arg, val_tape, val_index n_arg = n_call_arg + 5; val_index = val_tape.record_call_op( atomic_index, call_id, n_res, val_op_arg ); } break; } // par2val_index // Each dyn_op has one result, result_dyn ops are added to make this so par2val_index[i_par] = val_index; // // i_arg i_arg += n_arg; } // // var2val_index // Initialize mapping from variable index to index in value vector. Vector var2val_index(n_variable); for(size_t i = 0; i < n_variable; ++i) var2val_index[i] = invalid_addr_t; for(addr_t i = 0; i < addr_t(n_variable_ind); ++i) var2val_index[i + 1] = addr_t( n_dynamic_ind ) + i; // // itr, is_var, more_operators local::play::const_sequential_iterator itr = play_.begin(); Vector is_var(2); bool more_operators = true; bool in_atomic_call = false; // // val_tape, var2val_index, par2val_index while(more_operators) { // // // itr, var_op, var_op_arg, i_var local::op_code_var var_op; const addr_t* var_op_arg; size_t i_var; (++itr).op_info(var_op, var_op_arg, i_var); // // is_unary, is_binary, is_compare // initialize to avoid compiler warning bool is_unary = false, is_binary = false, is_compare = false; local::val_graph::type_var_op( var_op, is_unary, is_binary, is_compare ); // ---------------------------------------------------------------------- if( is_unary ) { // // val_op op_enum_t val_op = var_op2val_op[var_op]; CPPAD_ASSERT_UNKNOWN( val_op < number_op_enum ); // // val_tape, val_index val_op_arg.resize(1); val_op_arg[0] = var2val_index[ var_op_arg[0] ]; val_index = val_tape.record_op(val_op, val_op_arg); // // var2val_index var2val_index[i_var] = val_index; } // ---------------------------------------------------------------------- else if( is_binary ) { switch( var_op ) { default: CPPAD_ASSERT_KNOWN(false, "val_graph::fun2val: This variable operator not yet implemented" ); // first argument a parameter, second argument a variable case local::AddpvOp: case local::SubpvOp: case local::MulpvOp: case local::DivpvOp: case local::PowpvOp: is_var[0] = false; is_var[1] = true; break; // first argument a variable, second argument a parameter case local::SubvpOp: case local::DivvpOp: case local::PowvpOp: is_var[0] = true; is_var[1] = false; break; // first argument a variable, second argument a variable case local::AddvvOp: case local::SubvvOp: case local::MulvvOp: case local::DivvvOp: case local::PowvvOp: is_var[0] = true; is_var[1] = true; break; } // // varl_op_arg, val_tape, par2val_index val_op_arg.resize(2); for(size_t i = 0; i < 2; ++i) { if( is_var[i] ) val_op_arg[i] = var2val_index[ var_op_arg[i] ]; else val_op_arg[i] = ensure_par2val_index( var_op_arg[i] ); } // // val_op op_enum_t val_op = var_op2val_op[var_op]; CPPAD_ASSERT_UNKNOWN( val_op < number_op_enum ); // // record_op, val_index val_index = val_tape.record_op(val_op, val_op_arg); // // var2val_index var2val_index[i_var] = val_index; } // ---------------------------------------------------------------------- else if( is_compare ) { // // left_index, right_index addr_t left_index, right_index; switch( var_op ) { // default default: CPPAD_ASSERT_UNKNOWN(false); left_index = 0; // to avoid compiler warning right_index = 0; break; // both nodes parameters case local::EqppOp: case local::NeppOp: case local::LtppOp: case local::LeppOp: left_index = par2val_index[ var_op_arg[0] ]; right_index = par2val_index[ var_op_arg[1] ]; break; // first node parameter, second variable case local::EqpvOp: case local::NepvOp: case local::LtpvOp: case local::LepvOp: left_index = par2val_index[ var_op_arg[0] ]; right_index = var2val_index[ var_op_arg[1] ]; break; // first node variable, second parameter case local::LtvpOp: case local::LevpOp: left_index = var2val_index[ var_op_arg[0] ]; right_index = par2val_index[ var_op_arg[1] ]; break; // both nodes variables case local::EqvvOp: case local::NevvOp: case local::LtvvOp: case local::LevvOp: left_index = var2val_index[ var_op_arg[0] ]; right_index = var2val_index[ var_op_arg[1] ]; break; } // compare_enum local::val_graph::compare_enum_t compare_enum; // Set graph_op switch( var_op ) { // default default: CPPAD_ASSERT_UNKNOWN(false); // set to avoid compiler warning compare_enum = local::val_graph::number_compare_enum; break; case local::EqppOp: case local::EqpvOp: case local::EqvvOp: compare_enum = local::val_graph::compare_eq_enum; break; case local::NeppOp: case local::NepvOp: case local::NevvOp: compare_enum = local::val_graph::compare_ne_enum; break; case local::LtppOp: case local::LtpvOp: case local::LtvpOp: case local::LtvvOp: compare_enum = local::val_graph::compare_lt_enum; break; case local::LeppOp: case local::LepvOp: case local::LevpOp: case local::LevvOp: compare_enum = local::val_graph::compare_le_enum; break; } // // tape val_index = val_tape.record_comp_op( compare_enum, left_index, right_index ); CPPAD_ASSERT_UNKNOWN(val_index == 0); // no result for this operator } // ---------------------------------------------------------------------- else switch(var_op) { default: CPPAD_ASSERT_KNOWN(false, "val_graph::fun2val: This variable operator not yet implemented" ); break; // ParOp: // val_tape, par2val_index, var2val_index case local::ParOp: val_index = ensure_par2val_index( var_op_arg[0] ); var2val_index[i_var] = val_index; break; // EndOp: case local::EndOp: more_operators = false; break; // InvOp: // The independent variable indices are already assigned case local::InvOp: break; // // DisOp case local::DisOp: { // val_tape, var2val_index val_op_arg.resize(1); addr_t discrete_index = var_op_arg[0]; val_op_arg[0] = var2val_index[ var_op_arg[1] ]; val_index = val_tape.record_dis_op( discrete_index, val_op_arg[0] ); var2val_index[i_var] = val_index; } break; // -------------------------------------------------------------- case local::LdpOp: { addr_t which_vector = vec_offset2index( var_op_arg[0] ); addr_t vector_index = ensure_par2val_index( var_op_arg[1] ); // var2val_index[i_var] = val_tape.record_load_op( which_vector, vector_index ); } break; // case local::LdvOp: { addr_t which_vector = vec_offset2index( var_op_arg[0] ); addr_t vector_index = var2val_index[ var_op_arg[1] ]; // var2val_index[i_var] = val_tape.record_load_op( which_vector, vector_index ); } break; // case local::StppOp: { addr_t which_vector = vec_offset2index( var_op_arg[0] ); addr_t vector_index = ensure_par2val_index( var_op_arg[1] ); addr_t value_index = ensure_par2val_index( var_op_arg[2] ); // val_tape.record_store_op( which_vector, vector_index, value_index ); } break; // case local::StpvOp: { addr_t which_vector = vec_offset2index( var_op_arg[0] ); addr_t vector_index = ensure_par2val_index( var_op_arg[1] ); addr_t value_index = var2val_index[ var_op_arg[2] ]; // val_tape.record_store_op( which_vector, vector_index, value_index ); } break; // case local::StvpOp: { addr_t which_vector = vec_offset2index( var_op_arg[0] ); addr_t vector_index = var2val_index[ var_op_arg[1] ]; addr_t value_index = ensure_par2val_index( var_op_arg[2] ); // val_tape.record_store_op( which_vector, vector_index, value_index ); } break; // case local::StvvOp: { addr_t which_vector = vec_offset2index( var_op_arg[0] ); addr_t vector_index = var2val_index[ var_op_arg[1] ]; addr_t value_index = var2val_index[ var_op_arg[2] ]; // val_tape.record_store_op( which_vector, vector_index, value_index ); } break; // -------------------------------------------------------------- case local::PriOp: { // // before, after std::string before( play_.GetTxt( size_t( var_op_arg[2] ) ) ); std::string after( play_.GetTxt( size_t( var_op_arg[4] ) ) ); // // flag_index addr_t flag_index; if( var_op_arg[0] & 1 ) flag_index = var2val_index[ var_op_arg[1] ]; else flag_index = ensure_par2val_index( var_op_arg[1] ); // // value_index addr_t value_index; if( var_op_arg[0] & 1 ) value_index = var2val_index[ var_op_arg[3] ]; else value_index = ensure_par2val_index( var_op_arg[3] ); // val_index = val_tape.record_pri_op( before, after, flag_index, value_index ); } CPPAD_ASSERT_UNKNOWN(val_index == 0); // no result for this operator break; // -------------------------------------------------------------- case local::CSumOp: { // // add, sub Vector add, sub; // // add: constant term add.push_back( ensure_par2val_index( var_op_arg[0] ) ); // // add: variables for(addr_t i = 5; i < var_op_arg[1]; ++i) add.push_back( var2val_index[ var_op_arg[i] ] ); // // sub: variables for(addr_t i = var_op_arg[1]; i < var_op_arg[2]; ++i) sub.push_back( var2val_index[ var_op_arg[i] ] ); // // add: dynamic parameters for(addr_t i = var_op_arg[2]; i < var_op_arg[3]; ++i) add.push_back( ensure_par2val_index( var_op_arg[i] ) ); // // sub: dynamic parameters for(addr_t i = var_op_arg[3]; i < var_op_arg[4]; ++i) sub.push_back( ensure_par2val_index( var_op_arg[i] ) ); // // val_tape, var2val_index var2val_index[i_var] = val_tape.record_csum_op(add, sub); } itr.correct_before_increment(); break; // -------------------------------------------------------------- case local::CExpOp: { // cop, left, right, if_true, if_false CompareOp cop = CompareOp( var_op_arg[0] ); addr_t left, right, if_true, if_false; if( var_op_arg[1] & 1 ) left = var2val_index[ var_op_arg[2] ]; else left = ensure_par2val_index( var_op_arg[2] ); if( var_op_arg[1] & 2 ) right = var2val_index[ var_op_arg[3] ]; else right = ensure_par2val_index( var_op_arg[3] ); if( var_op_arg[1] & 4 ) if_true = var2val_index[ var_op_arg[4] ]; else if_true = ensure_par2val_index( var_op_arg[4] ); if( var_op_arg[1] & 8 ) if_false = var2val_index[ var_op_arg[5] ]; else if_false = ensure_par2val_index( var_op_arg[5] ); // // compare_enum local::val_graph::compare_enum_t compare_enum; switch( cop ) { case CompareLt: compare_enum = local::val_graph::compare_lt_enum; break; case CompareLe: compare_enum = local::val_graph::compare_le_enum; break; case CompareEq: compare_enum = local::val_graph::compare_eq_enum; break; case CompareGe: compare_enum = local::val_graph::compare_lt_enum; std::swap(if_true, if_false); break; case CompareGt: compare_enum = local::val_graph::compare_le_enum; std::swap(if_true, if_false); break; case CompareNe: compare_enum = local::val_graph::compare_eq_enum; std::swap(if_true, if_false); break; default: CPPAD_ASSERT_UNKNOWN(false); // set to avoid compiler warning compare_enum = local::val_graph::number_compare_enum; break; } val_index = val_tape.record_cexp_op( compare_enum, left, right, if_true, if_false ); var2val_index[i_var] = val_index; } break; // -------------------------------------------------------------- case local::FunapOp: val_op_arg.push_back( ensure_par2val_index( var_op_arg[0] ) ); break; case local::FunavOp: CPPAD_ASSERT_UNKNOWN( size_t(var_op_arg[0]) <= i_var ); val_op_arg.push_back( var2val_index[var_op_arg[0]] ); break; case local::FunrpOp: var_op_res.push_back( var_op_arg[0] ); res_is_par.push_back(true); break; case local::FunrvOp: var_op_res.push_back( addr_t(i_var) ); res_is_par.push_back(false); break; case local::AFunOp: in_atomic_call = ! in_atomic_call; if( in_atomic_call ) { val_op_arg.resize(0); var_op_res.resize(0); res_is_par.resize(0); } else { // atomic_index, call_id, n_res addr_t atomic_index = var_op_arg[0]; addr_t call_id = var_op_arg[1]; addr_t n_res = var_op_arg[3]; # ifndef NDEBUG addr_t n_arg = var_op_arg[2]; CPPAD_ASSERT_UNKNOWN( val_op_arg.size() == size_t(n_arg) ); CPPAD_ASSERT_UNKNOWN( var_op_res.size() == size_t(n_res) ); CPPAD_ASSERT_UNKNOWN( res_is_par.size() == size_t(n_res) ); # endif // var2val_index addr_t res_index = val_tape.record_call_op( atomic_index, call_id, n_res, val_op_arg ); for(addr_t i = 0; i < n_res; ++i) { if( res_is_par[i] ) par2val_index[ var_op_res[i] ] = res_index + i; else var2val_index[ var_op_res[i] ] = res_index + i; } } break; } } // dep_vec size_t n_dependent = dep_taddr_.size(); Vector dep_vec(n_dependent); for(size_t i = 0; i < n_dependent; ++i) dep_vec[i] = var2val_index[ dep_taddr_[i] ]; val_tape.set_dep( dep_vec ); } } // END_CPPAD_NAMESPACE // -------------------------------------------------------------------------- # endif ================================================ FILE: include/cppad/local/val_graph/op2arg_index.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_OP2ARG_INDEX_HPP # define CPPAD_LOCAL_VAL_GRAPH_OP2ARG_INDEX_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell // --------------------------------------------------------------------------- # include # include namespace CppAD { namespace local { namespace val_graph { /* ------------------------------------------------------------------------------- {xrst_begin val_op2arg_index dev} Set and Get the op2arg_index Vector ################################### This vector maps an operator index to the corresponding arg_index; i.e., the index in arg_vec of the first argument for this operator. set *** This ensures that the op2arg_index vector has been set. {xrst_literal // BEGIN_SET_OP2ARG_INDEX // END_SET_OP2ARG_INDEX } get *** This gets th op2arg_index vector. If its size is zero, it has not been set. {xrst_literal // BEGIN_OP2ARG_INDEX // END_OP2ARG_INDEX } {xrst_end val_op2arg_index} */ // BEGIN_SET_OP2ARG_INDEX template void tape_t::set_op2arg_index(void) // END_SET_OP2ARG_INDEX { if( 0 < op2arg_index_.size() ) { CPPAD_ASSERT_UNKNOWN( op2arg_index_.size() == size_t( n_op() ) ); return; } // // op2arg_indeex Vector op2arg_index( n_op() ); op_iterator op_itr(*this, 0); for(addr_t i_op = 0; i_op < n_op(); ++i_op) { op2arg_index[i_op] = op_itr.arg_index(); ++op_itr; } // // op2arg_index_ // We needed a complete op2arg_index, before we could set op2arg_index_, // otherwise op_iterator would not work properly. op2arg_index_.swap(op2arg_index); } // // BEGIN_OP2ARG_INDEX template const Vector& tape_t::op2arg_index(void) const // END_OP2ARG_INDEX { return op2arg_index_; } } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/op_enum2class.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_OP_ENUM2CLASS_HPP # define CPPAD_LOCAL_VAL_GRAPH_OP_ENUM2CLASS_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN_SORT_THIS_LINE_PLUS_1 # include # include # include # include # include # include # include # include # include # include # include // END_SORT_THIS_LINE_MINUS_1 // namespace CppAD { namespace local { namespace val_graph { /* {xrst_begin val_op_enum2class dev} Mapping From Operator Enum To Class ################################### Prototype ********* {xrst_literal // BEGIN_OP_ENUM2CLASS // END_OP_ENUM2CLASS } Purpose ******* Convert an operator enum type :ref:`val_graph_type@op_enum_t` to a pointer to an operator base class object :ref:`val_base_op-name` . {xrst_end val_op_enum2class} */ // ---------------------------------------------------------------------------- // BEGIN_OP_ENUM2CLASS # define CPPAD_VAL_GRAPH_INSTANCE(name) \ case name##_op_enum: \ op_ptr = name##_op_t::get_instance(); \ break; template base_op_t* op_enum2class(op_enum_t op_enum) // END_OP_ENUM2CLASS { // base_op_t* op_ptr; switch(op_enum) { default: assert( false ); op_ptr = nullptr; // set in this case to avoid compiler warning break; // BEGIN_SORT_THIS_LINE_PLUS_1 CPPAD_VAL_GRAPH_INSTANCE(abs) CPPAD_VAL_GRAPH_INSTANCE(acos) CPPAD_VAL_GRAPH_INSTANCE(acosh) CPPAD_VAL_GRAPH_INSTANCE(add) CPPAD_VAL_GRAPH_INSTANCE(asin) CPPAD_VAL_GRAPH_INSTANCE(asinh) CPPAD_VAL_GRAPH_INSTANCE(atan) CPPAD_VAL_GRAPH_INSTANCE(atanh) CPPAD_VAL_GRAPH_INSTANCE(call) CPPAD_VAL_GRAPH_INSTANCE(cexp) CPPAD_VAL_GRAPH_INSTANCE(comp) CPPAD_VAL_GRAPH_INSTANCE(con) CPPAD_VAL_GRAPH_INSTANCE(cos) CPPAD_VAL_GRAPH_INSTANCE(cosh) CPPAD_VAL_GRAPH_INSTANCE(csum) CPPAD_VAL_GRAPH_INSTANCE(dis) CPPAD_VAL_GRAPH_INSTANCE(div) CPPAD_VAL_GRAPH_INSTANCE(erf) CPPAD_VAL_GRAPH_INSTANCE(erfc) CPPAD_VAL_GRAPH_INSTANCE(exp) CPPAD_VAL_GRAPH_INSTANCE(expm1) CPPAD_VAL_GRAPH_INSTANCE(load) CPPAD_VAL_GRAPH_INSTANCE(log) CPPAD_VAL_GRAPH_INSTANCE(log1p) CPPAD_VAL_GRAPH_INSTANCE(mul) CPPAD_VAL_GRAPH_INSTANCE(neg) CPPAD_VAL_GRAPH_INSTANCE(pow) CPPAD_VAL_GRAPH_INSTANCE(pri) CPPAD_VAL_GRAPH_INSTANCE(sign) CPPAD_VAL_GRAPH_INSTANCE(sin) CPPAD_VAL_GRAPH_INSTANCE(sinh) CPPAD_VAL_GRAPH_INSTANCE(sqrt) CPPAD_VAL_GRAPH_INSTANCE(store) CPPAD_VAL_GRAPH_INSTANCE(sub) CPPAD_VAL_GRAPH_INSTANCE(tan) CPPAD_VAL_GRAPH_INSTANCE(tanh) CPPAD_VAL_GRAPH_INSTANCE(vec) // END_SORT_THIS_LINE_MINUS_1 } return op_ptr; } # undef CPPAD_VAL_GRAPH_INSTANCE } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/op_hash_table.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_OP_HASH_TABLE_HPP # define CPPAD_LOCAL_VAL_GRAPH_OP_HASH_TABLE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-25 Bradley M. Bell // --------------------------------------------------------------------------- # include # include # include /* {xrst_begin val_op_hash_table dev} The Value Operator Hash Table ############################# Constructor *********** {xrst_literal // BEGIN_OP_HASH_TABLE_T // END_OP_HASH_TABLE_T } op_hash_table ============= This is the operator hash table that is constructed tape ==== A reference to *tape* is stored in *op_hash_tale* ; i.e., *tape* must not be destroyed before *op_hash_table* . op2arg_index ============ This is a mapping from operator index to corresponding offset in the tape argument vector *tape*.var_arg_ . n_hash_code =========== This is the number of possible hash codes in the operator hash table *op_hash_table* . match_op ******** {xrst_literal // BEGIN_MATCH_OP // END_MATCH_OP } op_hash_table ============= This is a hash table for the tape. i_op ==== This is the index of the operator that we are searching for a match. new_val_index ============= This maps old value indices to new value indices for indices less than the first result for the *i_op* operator. New value indices are the result of using previous operator matches. For arguments that are value indices, the new indices are used when checking to see if operators match. j_op ==== The return value *j_op* is the lowest operator index that corresponds to a match for *i_op* . If it is equal to *i_op* , then this operator has been placed in the hash table (for future matches). Otherwise *j_op* is less than *i_op* and its results are equivalent to *i_op*. size_count ********** {xrst_literal // BEGIN_SIZE_COUNT // END_SIZE_COUNT } For each valid index *i*, *size_count* ()[ *i* ] is the number of hash code values, in the table, that have *i* collisions. A collision occurs when two operators that do not match have the same hash code. {xrst_end val_op_hash_table} */ // Tell pod_vector class that each size_setvec::pair_s_type is // plain old data and hence the corresponding constructor need not be called. namespace CppAD { namespace local { # if ! CPPAD_IS_SAME_TAPE_ADDR_TYPE_SIZE_T // This pair gets defined in list_setvec.hpp for the type size_t. // Would be better to have conditional compile depending of if defined. template <> inline bool is_pod< sparse::size_setvec::pair_s_type > (void) { return true; } # endif } } namespace CppAD { namespace local { namespace val_graph { // hash_value template size_t hash_value(const Value& value) { CPPAD_ASSERT_UNKNOWN( sizeof(unsigned short) == 2 ); CPPAD_ASSERT_UNKNOWN( sizeof(value) % 2 == 0 ); // // v const unsigned short* v = reinterpret_cast(& value); // // sum size_t i = sizeof(value) / 2 - 1; size_t sum = v[i]; while(i--) sum += v[i]; // return sum; } // // op_hash_table_t template class op_hash_table_t { private: // // tape_ const tape_t& tape_; // // op2arg_index_ const Vector& op2arg_index_; // // table CppAD::local::sparse::size_setvec table_; // // hash_code addr_t hash_code( const base_op_t* op_ptr , addr_t arg_index , const Vector& new_val_index ) { // // arg_vec, con_vec, op_enum const Vector& arg_vec = tape_.arg_vec(); const Vector& con_vec = tape_.con_vec(); op_enum_t op_enum = op_ptr->op_enum(); // size_t code; if( op_enum == con_op_enum ) code = hash_value( con_vec[ arg_vec[arg_index] ] ); else { addr_t n_arg = op_ptr->n_arg(arg_index, arg_vec); addr_t n_before = op_ptr->n_before(); addr_t n_after = op_ptr->n_after(); // // code code = 0; // // These are auxiliary indices for(addr_t i = 0; i < n_before; ++i) code += size_t( arg_vec[arg_index + i] ); // // These arguments are indices in the value vector, so check for a // match with the lowest equivalent value vector index. for(addr_t i = n_before; i < n_arg - n_after; ++i) code += size_t( new_val_index[ arg_vec[arg_index + i] ] ); // // These are auxiliary indices for(addr_t i = n_arg - n_after; i < n_arg ; ++i) code += size_t( arg_vec[arg_index + i] ); } code = code % size_t( table_.n_set() ); return addr_t( code ); } public: // ------------------------------------------------------------------------- // BEGIN_OP_HASH_TABLE_T // op_hash_table_t op_hash_table(tape, op2arg_index, n_hash_code) op_hash_table_t( const tape_t& tape , const Vector& op2arg_index , addr_t n_hash_code ) // END_OP_HASH_TABLE_T : tape_( tape ), op2arg_index_(op2arg_index) { // table_ addr_t n_op = tape.n_op(); // table_.resize( n_hash_code, n_op ); } // ------------------------------------------------------------------------- // BEGIN_SIZE_COUNT // size_count = op_hash_table.size_count() Vector size_count(void) // END_SIZE_COUNT { Vector count; addr_t n_set = table_.n_set(); for(addr_t i = 0; i < n_set; ++i) { addr_t number_elements = table_.number_elements(i); if( size_t( number_elements ) >= count.size() ) { size_t old_size = count.size(); addr_t new_size = number_elements + 1; count.resize(new_size); for(size_t j = old_size; j < new_size; ++j) count[j] = 0; } count[number_elements] += 1; } return count; } // ------------------------------------------------------------------------- // BEGIN_MATCH_OP // j_op = op_hash_table.match_op(i_op, new_val_index) addr_t match_op(addr_t i_op, const Vector& new_val_index) // END_MATCH_OP { assert( i_op < table_.end() ); // // arg_vec, con_vec const Vector& arg_vec = tape_.arg_vec(); const Vector& con_vec = tape_.con_vec(); // // op_enum, op_ptr const base_op_t* op_ptr = tape_.base_op_ptr(i_op); op_enum_t op_enum = op_ptr->op_enum(); // // arg_index, n_arg addr_t arg_index = op2arg_index_[i_op]; addr_t n_arg = op_ptr->n_arg(arg_index, arg_vec); // // nan if( op_enum == con_op_enum ) { if( CppAD::isnan( con_vec[ arg_vec[arg_index] ] ) ) { CPPAD_ASSERT_UNKNOWN( op2arg_index_[0] == 0 ); CPPAD_ASSERT_UNKNOWN( arg_vec[0] == 0 ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( con_vec[0] ) ); return 0; } } // // code addr_t code = hash_code(op_ptr, arg_index, new_val_index); // // itr local::sparse::size_setvec_const_iterator itr(table_, code); while( *itr != table_.end() ) { // op_enum_j, arg_index_j addr_t j_op = *itr; const base_op_t* op_ptr_j = tape_.base_op_ptr(j_op); op_enum_t op_enum_j = op_ptr_j->op_enum(); addr_t arg_index_j = op2arg_index_[j_op]; addr_t n_arg_j = op_ptr_j->n_arg(arg_index_j, arg_vec); // // match bool match = (op_enum == op_enum_j) && (n_arg == n_arg_j); if( match && op_enum == con_op_enum ) { // const Value& c = con_vec[ arg_vec[arg_index] ]; const Value& c_j = con_vec[ arg_vec[arg_index_j] ]; match = IdenticalEqualCon(c, c_j); } else if( match ) { addr_t n_before = op_ptr->n_before(); addr_t n_after = op_ptr->n_after(); // for(addr_t k = 0; k < n_before; ++k) match &= arg_vec[arg_index + k] == arg_vec[arg_index_j + k]; // for(addr_t k = n_before; k < n_arg - n_after; ++k) { addr_t val_index = new_val_index[ arg_vec[arg_index + k] ]; addr_t val_index_j = new_val_index[ arg_vec[arg_index_j + k] ]; match &= val_index == val_index_j; } // for(addr_t k = n_arg - n_after; k < n_arg; ++k) match &= arg_vec[arg_index + k] == arg_vec[arg_index_j + k]; // bool communative = op_enum == add_op_enum; communative |= op_enum == mul_op_enum; if( communative && ! match ) { addr_t val_index = new_val_index[ arg_vec[arg_index + 0] ]; addr_t val_index_j = new_val_index[ arg_vec[arg_index_j + 1] ]; match = val_index == val_index_j; // val_index = new_val_index[ arg_vec[arg_index + 1] ]; val_index_j = new_val_index[ arg_vec[arg_index_j + 0] ]; match &= val_index == val_index_j; } } if( match ) return j_op; // // itr ++itr; } table_.add_element(code, i_op); return i_op; } }; } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/op_iterator.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_OP_ITERATOR_HPP # define CPPAD_LOCAL_VAL_GRAPH_OP_ITERATOR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // -------------------------------------------------------------------------- /* ------------------------------------------------------------------------------ {xrst_begin val_op_iterator dev} Class For Iterating Over Operators in a Value Tape ################################################## Syntax ****** | |tab| ``op_iterator`` < *Value* > *op_itr* ( *tape* , *op_index* ) | |tab| *op_itr* . ``op_ptr`` () | |tab| *op_itr* . ``arg_index`` () | |tab| *op_itr* . ``res_index`` () Prototype ********* {xrst_literal // BEGIN_CTOR // END_CTOR } {xrst_literal // BEGIN_MEMBER_FUNCTIONS // END_MEMBER_FUNCTIONS } Value ***** is the :ref:`val_tape@Value` type for the tape. tape **** is the :ref:`val_tape-name` containing the operators we are iterating over. op_index ******** is the operator index where the iterator starts. This must be zero (for the beginning of the tape) or the number of operators in the tape (for the end of the tape). op_itr ****** is the operator iterator. {xrst_end val_op_iterator} */ # include # include // BEGIN_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE namespace CppAD { namespace local { namespace val_graph { // // tape_t; template class tape_t; template class op_iterator { // private: // // tape_ const tape_t& tape_; // // op_ptr_ base_op_t* op_ptr_; // // op_index_ addr_t op_index_; // // arg_index_ addr_t arg_index_; // // res_index_ addr_t res_index_; // public: // // BEGIN_MEMBER_FUNCTIONS const base_op_t* op_ptr(void) const { return op_ptr_; } addr_t arg_index(void) const { return arg_index_; } addr_t res_index(void) const { return res_index_; } // END_MEMBER_FUNCTIONS // // BEGIN_CTOR op_iterator(const tape_t& tape, addr_t op_index) // END_CTOR : tape_ ( tape ) , op_ptr_(nullptr) , op_index_ ( op_index ) { addr_t n_op = tape.n_op(); // if( op_index == 0 ) { op_enum_t op_enum = op_enum_t( tape.op_enum_vec()[op_index] ); // arg_index_ = 0; res_index_ = tape.n_ind(); op_ptr_ = op_enum2class(op_enum); } else if( op_index == n_op ) { arg_index_ = addr_t( tape.arg_vec().size() ); res_index_ = tape.n_val(); } else { CPPAD_ASSERT_KNOWN( false, "op_iterator: op_index is not zero or number of operators in tape" ); } } // // BEGIN_INCREMENT void operator++(void) // END_INCREMENT { CPPAD_ASSERT_KNOWN( op_index_ < addr_t( tape_.n_op() ), "op_iterator: attempt to increment past the end of the tape" ); // // n_arg, n_res addr_t n_arg = op_ptr_->n_arg(arg_index_, tape_.arg_vec()); addr_t n_res = op_ptr_->n_res(arg_index_, tape_.arg_vec()); // // op_index_, arg_index_, res_index_ ++op_index_; res_index_ += n_res; // // op_ptr_ if( op_index_ == tape_.n_op() ) { op_ptr_ = nullptr; arg_index_ = addr_t( tape_.arg_vec().size() ); // invalid value } else { if( tape_.op2arg_index().size() == 0 ) arg_index_ += n_arg; else arg_index_ = tape_.op2arg_index()[op_index_]; // op_enum_t op_enum = op_enum_t( tape_.op_enum_vec()[op_index_] ); op_ptr_ = op_enum2class(op_enum); } } // // BEGIN_DECREMENT void operator--(void) // END_DECREMENT { CPPAD_ASSERT_KNOWN( 0 < op_index_, "op_iterator: attempt to decrement below the beginning of the tape" ); // // op_index_ --op_index_; // // op_ptr_ op_enum_t op_enum = op_enum_t( tape_.op_enum_vec()[op_index_] ); op_ptr_ = op_enum2class(op_enum); // // n_after addr_t n_after = op_ptr_->n_after(); CPPAD_ASSERT_UNKNOWN( n_after == 0 || n_after == 1 ); // // arg_index_ if( 0 < tape_.op2arg_index().size() ) arg_index_ = tape_.op2arg_index()[op_index_]; else { addr_t n_arg; if( n_after ) n_arg = tape_.arg_vec()[arg_index_ - 1]; else { // arg_index // set to an invalid value for an index in the arg_vec vector. addr_t arg_index = addr_t( tape_.arg_vec().size() ); n_arg = op_ptr_->n_arg(arg_index, tape_.arg_vec()); } arg_index_ -= n_arg; } // // res_index_ addr_t n_res = op_ptr_->n_res(arg_index_, tape_.arg_vec()); res_index_ -= n_res; } }; } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE // -------------------------------------------------------------------------- # endif ================================================ FILE: include/cppad/local/val_graph/option.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_OPTION_HPP # define CPPAD_LOCAL_VAL_GRAPH_OPTION_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell // --------------------------------------------------------------------------- # include namespace CppAD { namespace local { namespace val_graph { /* ------------------------------------------------------------------------------- {xrst_begin val_tape_option dev} Set an Option's Value ##################### Prototype ********* {xrst_literal // BEGIN_SET_OPTION // END_SET_OPTION } keep_compare ************ If *name* is keep_compare, *value* must be true or false . If it is false (true), :ref:`val_comp_op-name` operators will (will not) be removed during :ref:`val_tape_dead_code-name` optimization. keep_print ********** If *name* is keep_print, *value* must be true or false . If it is false (true), :ref:`val_pri_op-name` operators will (will not) be removed during dead code optimization. {xrst_end val_tape_option} */ // --------------------------------------------------------------------------- // BEGIN_INITIALIZE_OPTION template void tape_t::initialize_option(void) // END_INITIALIZE_OPTION { option_map_["keep_compare"] = "true"; option_map_["keep_print"] = "true"; // return; } // --------------------------------------------------------------------------- // BEGIN_SET_OPTION template void tape_t::set_option( const std::string& name , const std::string& value ) // END_SET_OPTION { // if( option_map_.find(name) == option_map_.end() ) { std::string msg = "value tape: There is no option named " + name; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } if( value != "true" && value != "false" ) { std::string msg = "value tape: option [" + name + "]"; msg += " value is not true or false "; CPPAD_ASSERT_KNOWN(false, msg.c_str() ); } option_map_[name] = value; // return; } } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/pri_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_PRI_OP_HPP # define CPPAD_LOCAL_VAL_GRAPH_PRI_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include // define CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL # include namespace CppAD { namespace local { namespace val_graph { /* {xrst_begin val_pri_op dev} {xrst_spell str } The Print Value Operator ######################## Prototype ********* {xrst_literal // BEGIN_PRI_OP_T // END_PRI_OP_T } Context ******* The class is derived from :ref:`val_base_op-name` . It overrides all its base class virtual member functions and is a concrete class (it has no pure virtual functions). get_instance ************ This static member function returns a pointer to a pri_op_t object. op_enum ******* This override of :ref:`val_base_op@op_enum` returns ``pri_op_enum`` . n_before ******** This override of :ref:`val_base_op@n_before` returns 2. n_after ******* This override of :ref:`val_base_op@n_after` returns 0. n_arg ***** This override of :ref:`val_base_op@n_arg` returns 4. n_res ***** This override of :ref:`val_base_op@n_res` returns 0. eval **** This override of :ref:`val_base_op@eval` defines the following values: .. csv-table:: :header: Type,Name,Definition string, *before* , str_vec[ arg_vec[ arg_index + 0 ] ] string, *after* , str_vec[ arg_vec[ arg_index + 1 ] ] Value, *flag* , val_vec[ arg_vec[ arg_index + 2 ] ] Value, *value* , val_vec[ arg_vec[ arg_index + 3 ] ] before ====== This text is printed before the value. after ===== This text is printed after the value. flag ==== If *flag* is less than or equal zero, print the output. If *flag* is greater than zero, nothing is printed by this operator. In the special case where arg_vec[ arg_index + 2] == tape.n_ind() *flag* has the value nan and nothing is printed. This fact is used by :ref:`val_tape_renumber-name` to change repeated prints of the same value to no-ops. The :ref:`val_tape_dead_code-name` routine will remove this operators. value ===== This value is printed between *before* and *after*. trace ===== If trace is true, :ref:`val_print_pri_op-name` is used to print this operator. Printing the operator is separate from printing the value. {xrst_toc_hidden val_graph/pri_xam.cpp } Example ******* The file :ref:`pri_xam.cpp ` is an example and test that uses this operator. {xrst_end val_pri_op} */ // BEGIN_PRI_OP_T template class pri_op_t : public base_op_t { public: // n_before addr_t n_before(void) const override { return 2; } // // n_after addr_t n_after(void) const override { return 0; } // // get_instance static pri_op_t* get_instance(void) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; static pri_op_t instance; return &instance; } // op_enum op_enum_t op_enum(void) const override { return pri_op_enum; } // END_PRI_OP_T // // n_arg addr_t n_arg( addr_t arg_index , const Vector& arg_vec ) const override { return 4; } // // n_res addr_t n_res( addr_t arg_index , const Vector& arg_vec ) const override { return 0; } // // eval void eval( const tape_t* tape , bool trace , addr_t arg_index , addr_t res_index , Vector& val_vec , Vector< Vector >& ind_vec_vec , size_t& compare_false ) const override { // // arg_vec, str_vec const Vector& arg_vec( tape->arg_vec() ); const Vector& str_vec( tape->str_vec() ); // // Special case where flag is nan and this is a no op. if( arg_vec[ arg_index + 2] == tape->n_ind() ) return; // // arg Vector arg(4); for(addr_t i = 0; i < 4; ++i) arg[i] = arg_vec[ arg_index + i ]; // if( trace ) { // print_pri_op(before_index, after_index, flag_index, value_index) print_pri_op(arg[0], arg[1], arg[2], arg[3]); } // // before, after, flag, value const std::string& before = str_vec[ arg[0] ]; const std::string& after = str_vec[ arg[1] ]; const Value& flag = val_vec[ arg[2] ]; const Value& value = val_vec[ arg[3] ]; if( flag <= Value(0) ) std::cout << before << value << after; } }; } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/print_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_PRINT_OP_HPP # define CPPAD_LOCAL_VAL_GRAPH_PRINT_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include /* {xrst_begin_parent val_print_op dev} Printing Value Operators ######################## Prototype ********* {xrst_literal // BEGIN_PRINT_OP // END_PRINT_OP } Output Notation *************** #. The first column is a value index and the second column is the corresponding value. #. Values inside of parenthesis are arguments that are result indices for the operator name that comes before the left parenthesis. #. Values inside of brackets are indices for vector name that comes before the left bracket. name **** Is the name of this operator. In the case of a function call operator, it is the name of the function being called. val_arg ******* is a vector containing the operator arguments that are value indices (in order). res_index ********* is the index of the first result for this operator. res_value ********* is a non-empty vector of results for this operator. Special Cases ************* {xrst_toc_table } {xrst_end val_print_op} */ // BEGIN_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE namespace CppAD { namespace local { namespace val_graph { // BEGIN_PRINT_OP template void print_op( const std::string& name , const Vector& val_arg , addr_t res_index , const Vector& res_value ) // END_PRINT_OP { size_t n_arg = val_arg.size(); size_t n_res = res_value.size(); using std::setw; using std::right; using std::cout; // // One result if( n_res == 1 ) { cout << right << setw(5) << res_index; cout << " " << right << setw(10) << res_value[0]; cout << " = " << right << setw(5) << name << "("; for(size_t i = 0; i < n_arg; ++i) { cout << right << setw(5) << std::right << val_arg[i]; if( i + 1 < n_arg ) cout << ","; } cout << ")" << std::endl; return; } // // Multiple results CPPAD_ASSERT_UNKNOWN( n_res > 1 ); cout << right << setw(5) << name << "("; for(size_t i = 0; i < n_arg; ++i) { cout << right << setw(5) << val_arg[i]; if( i + 1 < n_arg ) cout << ","; else cout << ")"; } for(addr_t i = 0; i < addr_t( n_res ); ++i) { cout << std::endl; cout << right << setw(5) << res_index + i; cout << " " << setw(10) << res_value[i]; } cout << std::endl; } /* ------------------------------------------------------------------------------ {xrst_begin val_print_con_op dev} Printing Constant Operators ########################### Prototype ********* {xrst_literal // BEGIN_PRINT_CON_OP // END_PRINT_CON_OP } arg *** is a vector containing all the operator arguments (must be length one). res_index ********* is the index of the result for this operator. res_value ********* is a vector of results for this operator (must be length one). {xrst_end val_print_con_op} */ // BEGIN_PRINT_CON_OP template void print_con_op( const Vector& arg , addr_t res_index , const Vector& res_value ) // END_PRINT_CON_OP { CPPAD_ASSERT_UNKNOWN( arg.size() == 1 ); CPPAD_ASSERT_UNKNOWN( res_value.size() == 1 ); using std::setw; using std::right; using std::cout; { cout << right << setw(5) << res_index; cout << " " << right << setw(10) << res_value[0]; cout << " = " << right << setw(5) << "con" << "["; cout << right << setw(5) << std::right << arg[0] << "]"; cout << std::endl; return; } } /* ------------------------------------------------------------------------------ {xrst_begin val_print_csum_op dev} Printing Cumulative Summation Operators ####################################### Prototype ********* {xrst_literal // BEGIN_PRINT_CSUM_OP // END_PRINT_CSUM_OP } arg *** is a vector containing all the operator arguments (must be length one). res_index ********* is the index of the result for this operator. res_value ********* is a vector of results for this operator (must be length one). {xrst_end val_print_csum_op} */ // BEGIN_PRINT_CSUM_OP template void print_csum_op( const Vector& arg , addr_t res_index , const Vector& res_value ) // END_PRINT_CSUM_OP { CPPAD_ASSERT_UNKNOWN( res_value.size() == 1); // using std::setw; using std::right; using std::cout; // // n_add, n_sub addr_t n_add = arg[0]; addr_t n_sub = arg[1]; // CPPAD_ASSERT_UNKNOWN( arg.size() == size_t(3 + n_add + n_sub ) ); // cout << right << setw(5) << res_index; cout << " " << right << setw(10) << res_value[0] << " = "; if( n_add > 0 ) { cout << right << setw(5) << "csum+" << "("; for(addr_t i = 0; i < n_add; ++i) { cout << right << setw(5) << arg[2 + i]; if( i + 1 < n_add ) cout << ","; } cout << ")" << std::endl; if( n_sub > 0 ) { cout << setw(19) << "" << right << setw(5) << "csum-" << "("; for(addr_t i = 0; i < n_sub; ++i) { cout << right << setw(5) << arg[2 + n_add + i]; if( i + 1 < n_sub ) cout << ","; } cout << ")" << std::endl; } } else { CPPAD_ASSERT_UNKNOWN( n_sub > 0 ); cout << right << setw(5) << "csum-" << "("; for(addr_t i = 0; i < n_sub; ++i) { cout << right << setw(5) << arg[2 + n_add + i]; if( i + 1 < n_sub ) cout << ","; } cout << ")" << std::endl; } } /* ------------------------------------------------------------------------------ {xrst_begin val_print_comp_op dev} Printing Compare Operators ########################## Prototype ********* {xrst_literal // BEGIN_PRINT_COMP_OP // END_PRINT_COMP_OP } comp_name ********* is the name of the comparison operator; e.g., "lt" left_index ********** is the value index corresponding to the left operand in the comparison. right_index *********** is the value index corresponding to the right operand in the comparison. result ****** is the result of the comparison. {xrst_end val_print_comp_op} */ // BEGIN_PRINT_COMP_OP inline void print_comp_op( const char* comp_name , addr_t left_index , addr_t right_index , bool result ) // END_PRINT_COMP_OP { // using std::setw; using std::right; using std::cout; // const char* res_str; if( result ) res_str = "true"; else res_str = "false"; // cout << setw(19) << "" << right << setw(5) << comp_name << "("; cout << right << setw(5) << left_index << ","; cout << right << setw(5) << right_index << ") = "; cout << right << setw(5) << res_str << std::endl; } /* ------------------------------------------------------------------------------ {xrst_begin val_print_pri_op dev} {xrst_spell str } Printing Print Operators ######################## Prototype ********* {xrst_literal // BEGIN_PRINT_PRI_OP // END_PRINT_PRI_OP } before_index ************ is the index in str_vec corresponding to the before text. after_index *********** is the index in str_vec corresponding to the after text. flag_index ********** is the index in the value vector corresponding to the flag. value_index *********** is the index in the value vector corresponding to the value that is printed if the flag is positive. {xrst_end val_print_pri_op} */ // BEGIN_PRINT_PRI_OP inline void print_pri_op( addr_t before_index , addr_t after_index , addr_t flag_index , addr_t value_index ) // END_PRINT_PRI_OP { // using std::setw; using std::right; using std::cout; // cout << setw(19) << "" << right << setw(5) << "pri" << "["; cout << right << setw(5) << before_index << ","; cout << right << setw(5) << after_index << "]("; cout << right << setw(5) << flag_index << ","; cout << right << setw(5) << value_index << ")" << std::endl; } /* ------------------------------------------------------------------------------ {xrst_begin val_print_store_op dev} Printing Store Operators ######################## Prototype ********* {xrst_literal // BEGIN_PRINT_STORE_OP // END_PRINT_STORE_OP } which_vector ************ is the index in val_vec_vec corresponding to this vector. vector_index ************ is the index in val_vec corresponding to the index for this vector element. value_index *********** is the index in val_vec corresponding to the new value for this vector element. {xrst_end val_print_store_op} */ // BEGIN_PRINT_STORE_OP inline void print_store_op( addr_t which_vector , addr_t vector_index , addr_t value_index ) // END_PRINT_STORE_OP { // using std::setw; using std::right; using std::cout; // cout << setw(19) << "" << right << setw(5) << "store" << "["; cout << right << setw(5) << which_vector << "]("; cout << right << setw(5) << vector_index << ","; cout << right << setw(5) << value_index << ")" << std::endl; } /* ------------------------------------------------------------------------------ {xrst_begin val_print_load_op dev} Printing Load Operators ####################### Prototype ********* {xrst_literal // BEGIN_PRINT_LOAD_OP // END_PRINT_LOAD_OP } which_vector ************ is the index in val_vec_vec corresponding to this vector. vector_index ************ is the index in val_vec corresponding to the index for this vector element. res_index ********* in the index in val_vec corresponding to the result for this operator. {xrst_end val_print_load_op} */ // BEGIN_PRINT_LOAD_OP template inline void print_load_op( addr_t which_vector , addr_t vector_index , addr_t res_index , const Value& res_value ) // END_PRINT_LOAD_OP { // using std::setw; using std::right; using std::cout; // cout << right << setw(5) << res_index; cout << " " << right << setw(10) << res_value; cout << " = " << setw(5) << "load" << "["; cout << right << setw(5) << which_vector << "]("; cout << right << setw(5) << vector_index << ")" << std::endl; } /* ------------------------------------------------------------------------------ {xrst_begin val_print_vec_op dev} Printing New Dynamic Vector Operators ##################################### Prototype ********* {xrst_literal // BEGIN_PRINT_VEC_OP // END_PRINT_VEC_OP } which_vector ************ is the index in val_vec_vec corresponding to this vector. initial ******* is the vector of indices in val_vec (the value vector) corresponding to the initial values for this dynamic vector. {xrst_end val_print_vec_op} */ // BEGIN_PRINT_VEC_OP inline void print_vec_op( addr_t which_vector , const Vector& initial ) // END_PRINT_VEC_OP { // using std::setw; using std::right; using std::cout; // cout << setw(19) << "" << right << setw(5) << "vec" << "["; cout << right << setw(5) << which_vector << "]("; for(size_t i = 0; i < initial.size(); ++i) { cout << right << setw(5) << initial[i]; if( i + 1 < initial.size() ) cout << ","; } cout << ")" << std::endl; } } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/record.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_RECORD_HPP # define CPPAD_LOCAL_VAL_GRAPH_RECORD_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include // namespace CppAD { namespace local { namespace val_graph { /* {xrst_begin_parent val_record dev} Recording Value Operations on a Tape #################################### Purpose ******* These operations store a function in the :ref:`val_tape-name`. {xrst_end val_record} ------------------------------------------------------------------------------ {xrst_begin val_set_ind dev} {xrst_spell str } Setting Independent Variables ############################# set_ind ******* {xrst_literal // BEGIN_SET_IND // END_SET_IND } This is the first step in a creating a recording. It does the following: #. Clear all of the memory that is currently used by the tape except for the options settings. #. Set the number of independent values. #. The empty string is placed in the string constant vector str_vec\_. This string has index zero in the string constant vector str_vec\_. #. Place the constant nan directly after the last independent value. This value has index zero in the value constant vector con_vec\_. #. The return value is the index in the value vector where the nan will be placed; i.e., *n_ind* . Directly after this operation: {xrst_literal // BEGIN_POST_CONDITION // END_POST_CONDITION } {xrst_end val_set_ind} */ // ---------------------------------------------------------------------------- // BEGIN_SET_IND template addr_t tape_t::set_ind(addr_t n_ind) // END_SET_IND { Value nan = CppAD::numeric_limits::quiet_NaN(); n_ind_ = n_ind; n_val_ = n_ind; var_arg_.clear(); con_vec_.clear(); str_vec_.clear(); vec_initial_.clear(); dep_vec_.clear(); op_enum_vec_.clear(); op2arg_index_.clear(); // option_map_ does not change during this operation. # if CPPAD_VAL_GRAPH_TAPE_TRACE // set_ind_inue size_t thread = thread_alloc::thread_num(); set_ind_inuse_ = thread_alloc::inuse(thread); # endif // str_vec_.push_back( "" ); addr_t nan_addr = record_con_op(nan); assert( n_val_ == n_ind + 1 ); // // BEGIN_POST_CONDITION CPPAD_ASSERT_UNKNOWN( op_enum_vec_.size() == 1 ); // one operator CPPAD_ASSERT_UNKNOWN( var_arg_.size() == 1 ); // one argument CPPAD_ASSERT_UNKNOWN( con_vec_.size() == 1 ); // one value constant CPPAD_ASSERT_UNKNOWN( str_vec_[0] == "" ); // empty string CPPAD_ASSERT_UNKNOWN( vec_initial_.size() == 0 ); // no dynamic vectors CPPAD_ASSERT_UNKNOWN( nan_addr == n_ind ); // return value return nan_addr; // END_POST_CONDITION } /* ------------------------------------------------------------------------------- {xrst_begin val_set_dep dev} Setting the Dependent Variables ############################### set_dep ******* {xrst_literal // BEGIN_SET_DEP // END_SET_DEP } This sets the dependent vector to the corresponding indices in the value vector. This is last step in creating a recording. {xrst_end val_set_dep} */ // ---------------------------------------------------------------------------- // BEGIN_SET_DEP template void tape_t::set_dep(const Vector& dep_vec) // END_SET_DEP { dep_vec_ = dep_vec; # if CPPAD_VAL_GRAPH_TAPE_TRACE // inuse size_t thread = thread_alloc::thread_num(); size_t set_dep_inuse = thread_alloc::inuse(thread); std::cout << "tape: inuse = " << set_dep_inuse-set_ind_inuse_ << "\n"; # endif } /* ------------------------------------------------------------------------------- {xrst_begin val_record_op dev} Recording Operators with One Result ################################### record_op ********* {xrst_literal // BEGIN_RECORD_OP // END_RECORD_OP } This can be used to place any operator that has one result in the tape; e.g., the unary and binary operators. op_enum ******* The argument identifies the operator. op_arg ****** The vector contains the arg_vec values for this operator. Its size must be equal to :ref:`val_base_op@n_arg` for this operator. return ****** The return value is the index were the result of the operation is placed in the value vector. {xrst_end val_record_op} */ // ---------------------------------------------------------------------------- // BEGIN_RECORD_OP template addr_t tape_t::record_op(op_enum_t op_enum, const Vector& op_arg) // END_RECORD_OP { // // res_index addr_t res_index = n_val_; # ifndef NDEBUG // arg_index addr_t arg_index = addr_t( var_arg_.size() ); # endif // // op_enum_vec_ op_enum_vec_.push_back( uint8_t( op_enum ) ); // // var_arg_ for(size_t i = 0; i < op_arg.size(); ++i) var_arg_.push_back( op_arg[i] ); // // n_val_ ++n_val_; // # ifndef NDEBUG base_op_t* op_ptr = op_enum2class(op_enum); CPPAD_ASSERT_UNKNOWN( size_t( op_ptr->n_arg(arg_index, var_arg_) ) == op_arg.size() ); CPPAD_ASSERT_UNKNOWN( size_t( op_ptr->n_res(arg_index, var_arg_) ) == 1 ); # endif // return res_index; } /* {xrst_betin val_record_con_op dev} Recording Constants ################### record_con_op ************* {xrst_literal // BEGIN_RECORD_CON_OP // END_RECORD_CON_OP } This places a :ref:`val_con_op-name` operator in the tape. The return value is the index were *constant* will be placed in the value vector. Note that if *constant* is nan, the return value will always be *n_ind*; i.e, only one nan gets paced in the constant vector. {xrst_end val_record_con_op} */ // ---------------------------------------------------------------------------- // BEGIN_RECORD_CON_OP template addr_t tape_t::record_con_op(const Value& constant) // END_RECORD_CON_OP { // // nan if( op_enum_vec_.size() == 0 ) { CPPAD_ASSERT_UNKNOWN( CppAD::isnan(constant) && n_val_ == n_ind_ ); } else if( CppAD::isnan(constant) ) return n_ind_; // // con_index addr_t con_index = addr_t( con_vec_.size() ); con_vec_.push_back( constant ); // // res_index addr_t res_index = n_val_; // // op_enum_vec_ op_enum_vec_.push_back( uint8_t( con_op_enum ) ); // // var_arg_ var_arg_.push_back( con_index ); // // n_val_ ++n_val_; // return res_index; } /* ------------------------------------------------------------------------------- {xrst_begin val_record_dis_op dev} Recording The Discrete Operations ################################# record_dis_op ************* {xrst_literal // BEGIN_RECORD_DIS_OP // END_RECORD_DIS_OP } This places a :ref:`val_dis_op-name` operator in the tape. The argument *discrete_index* identifies which discrete function to use. The argument *val_index* is the index of the operand in the value vector. The return value is the index were the result will be placed in the value vector. {xrst_end val_record_dis_op} */ // ---------------------------------------------------------------------------- // BEGIN_RECORD_DIS_OP template addr_t tape_t::record_dis_op(addr_t discrete_index, addr_t val_index) // END_RECORD_DIS_OP { // // res_index addr_t res_index = n_val_; // // op_enum_vec_ op_enum_vec_.push_back( uint8_t( dis_op_enum ) ); // // var_arg_ var_arg_.push_back( discrete_index ); var_arg_.push_back( val_index ); // // n_val_ ++n_val_; // return res_index; } /* ------------------------------------------------------------------------------ {xrst_begin val_record_comp_op dev} Recording Comparison Operations ############################### record_comp_op ************** {xrst_literal // BEGIN_RECORD_COMP_OP // END_RECORD_COMP_OP } This places a :ref:`val_comp_op-name` operator in the tape. The return value is always zero because there is no value vector result for this operator. compare_enum ============ This identifies which comparison is done by this use of the compare operator. left_index ========== This is the value vector index for the left operand in the comparison. right_index =========== This is the value vector index for the right operand in the comparison. return ====== The return value is zero because this operator does not have a result (and zero is used for an invalid result value). {xrst_end val_record_comp_op} */ // ---------------------------------------------------------------------------- // BEGIN_RECORD_COMP_OP template addr_t tape_t::record_comp_op( compare_enum_t compare_enum , addr_t left_index , addr_t right_index ) // END_RECORD_COMP_OP { // res_index addr_t res_index = 0; // invalid result index // // op_enum_vec_ op_enum_vec_.push_back( uint8_t( comp_op_enum ) ); // // var_arg_ var_arg_.push_back( addr_t(compare_enum) ); var_arg_.push_back( left_index ); var_arg_.push_back( right_index ); // return res_index; } /* ------------------------------------------------------------------------------ {xrst_begin val_record_call_op dev} Recording Call Operations ######################### record_call_op ************** {xrst_literal // BEGIN_RECORD_CALL_OP // END_RECORD_CALL_OP } This places a :ref:`val_call_op-name` operator in the tape. atomic_index ============ This is the *atomic_index* for the atomic function; see :ref:`atomic_index@index_out` in the case where *index_in* is zero. call_id ======= This is the *call_id* for this use of the atomic function; see :ref:`atomic_four_call@call_id` . n_res ===== This is the number of values returned by the atomic function call and placed in the value vector. fun_arg ======= This vector has size equal to the number of arguments to the atomic function for this *call_id* . (The combination of *atomic_index* and *call_id* must specify a function.) The *j*-th element of *fun_arg* is the index on the value vector of the *j*-th argument to the function. return ====== The ``record_con_op`` function returns the index in the value vector where the first result is placed in the value vector. A total of *n_res* results are placed in the value vector. {xrst_end val_record_call_op} */ // ---------------------------------------------------------------------------- // BEGIN_RECORD_CALL_OP template addr_t tape_t::record_call_op( addr_t atomic_index , addr_t call_id , addr_t n_res , const Vector& fun_arg ) // END_RECORD_CALL_OP { // // res_index addr_t res_index = n_val_; // // op_enum_vec_ op_enum_vec_.push_back( uint8_t( call_op_enum ) ); // // var_arg_ addr_t n_arg = 5 + addr_t( fun_arg.size() ); var_arg_.push_back( n_arg ); var_arg_.push_back( n_res ); var_arg_.push_back( atomic_index ); var_arg_.push_back( call_id ); for(size_t i = 0; i < fun_arg.size(); ++i) var_arg_.push_back( fun_arg[i] ); var_arg_.push_back( n_arg ); // // n_val_ n_val_ = n_val_ + addr_t(n_res); // return res_index; } /* ------------------------------------------------------------------------------ {xrst_begin val_record_csum_op dev} Recording Cumulative Summation Operations ######################################### record_csum_op ************** {xrst_literal // BEGIN_RECORD_CSUM_OP // END_RECORD_CSUM_OP } This places a :ref:`val_csum_op-name` operator in the tape. add === This is the vector of value indices corresponding to the values that are added to the sum. sub === This is the vector of value indices corresponding to the values that are subtracted from the sum. return ====== The ``record_con_op`` function returns the index in the value vector where the sum is placed. {xrst_end val_record_csum_op} */ // ---------------------------------------------------------------------------- // BEGIN_RECORD_CSUM_OP template addr_t tape_t::record_csum_op( const Vector& add , const Vector& sub ) // END_RECORD_CSUM_OP { // // n_add, n_sub, n_arg addr_t n_add = addr_t( add.size() ); addr_t n_sub = addr_t( sub.size() ); addr_t n_arg = 3 + n_add + n_sub; // // res_index addr_t res_index = n_val_; // // op_enum_vec_ op_enum_vec_.push_back( uint8_t( csum_op_enum ) ); // // var_arg_ var_arg_.push_back( n_add ); var_arg_.push_back( n_sub ); for(size_t i = 0; i < add.size(); ++i) var_arg_.push_back( add[i] ); for(size_t i = 0; i < sub.size(); ++i) var_arg_.push_back( sub[i] ); var_arg_.push_back( n_arg ); // // n_val_ ++n_val_; // return res_index; } /* ------------------------------------------------------------------------------ {xrst_begin val_record_cexp_op dev} Recording Comparison Operations ############################### record_cexp_op ************** {xrst_literal // BEGIN_RECORD_CEXP_OP // END_RECORD_CEXP_OP } This places a :ref:`val_cexp_op-name` operator in the tape. compare_enum ============ This identifies which comparison is used by this conditional expression. left ==== This is the value vector index for the left operand in the comparison. right ===== This is the value vector index for the right operand in the comparison. if_true ======= This is the index of the value of the value that is used for the result if the comparison is true. if_false ======== This is the index of the value of the value that is used for the result if the comparison is false. return ====== The ``record_con_op`` function returns the index in the value vector where the result is placed in the value vector. {xrst_end val_record_cexp_op} */ // ---------------------------------------------------------------------------- // BEGIN_RECORD_CEXP_OP template addr_t tape_t::record_cexp_op( compare_enum_t comp_enum , addr_t left , addr_t right , addr_t if_true , addr_t if_false ) // END_RECORD_CEXP_OP { // // res_index addr_t res_index = n_val_; // // op_enum_vec_ op_enum_vec_.push_back( uint8_t(cexp_op_enum) ); // // var_arg_ var_arg_.push_back( addr_t(comp_enum) ); var_arg_.push_back( left ); var_arg_.push_back( right ); var_arg_.push_back( if_true ); var_arg_.push_back( if_false ); // // n_val_ ++n_val_; // return res_index; } /* ------------------------------------------------------------------------------ {xrst_begin val_record_pri_op dev} {xrst_spell str } Recording Print Operations ########################## record_pri_op ************* {xrst_literal // BEGIN_RECORD_PRI_OP // END_RECORD_PRI_OP } This places a :ref:`val_pri_op-name` operator in the tape. The return value is always zero because there is no value vector result for this operator. before ====== This is the text that is printed before the value when *flag* is positive. If a previous str_vec\_ entry is equal to before, it is used. Otherwise a new entry equal to before is added to str_vec\_. after ===== This is the text that is printed after the value when *flag* is positive. If a previous str_vec\_ entry is equal to after, it is used. Otherwise a new entry equal to after is added to str_vec\_. flag_index ========== This is the value vector index for the flag operand. value_index =========== This is the value vector index for the value that is printed when the flag is positive. return ====== The return value is zero because this operator does not have a result (and zero is used for an invalid result value). {xrst_end val_record_pri_op} */ // ---------------------------------------------------------------------------- // BEGIN_RECORD_PRI_OP template addr_t tape_t::record_pri_op( const std::string& before , const std::string& after , addr_t flag_index , addr_t value_index ) // END_RECORD_PRI_OP { // // str_index using std::string; auto str_index = [](const string& str, Vector& str_vec) { size_t result = str_vec.size(); for(size_t i = 0; i < str_vec.size(); ++i) { if( str == str_vec[i] ) result = i; } if( result == str_vec.size() ) str_vec.push_back(str); return addr_t( result ); }; // empty string CPPAD_ASSERT_UNKNOWN( str_vec_[0] == "" ); // // res_index addr_t res_index = 0; // invalid result index // // op_enum_vec_ op_enum_vec_.push_back( uint8_t(pri_op_enum) ); // // str_vec_, var_arg_: before_index addr_t before_index = str_index(before, str_vec_); var_arg_.push_back( before_index ); // // str_vec_, var_arg_: after_index addr_t after_index = str_index(after, str_vec_); var_arg_.push_back( after_index ); // // var_arg_: flag_index, value_index var_arg_.push_back( flag_index ); var_arg_.push_back( value_index ); // return res_index; } /* ------------------------------------------------------------------------------ {xrst_begin val_record_vec_op dev} Record Adding a Dynamic Vector ############################## record_vec_op ************* {xrst_literal // BEGIN_RECORD_VEC_OP // END_RECORD_VEC_OP } This creates a new dynamic vector with the specified initial values. A dynamic vector must be created before a load or store can be recorded for the corresponding vector. initial ******* The size of this vector is the size of the dynamic vector being created. The initial value for the dynamic vector at index *vector_index* is the value vector at index *initial*[ *vector_index* ]; i.e., *val_vec* [ *initial* [ *vector_index* ] ] where *val_vec* is the value vector. which_vector ************ The return value is an index identifying which dynamic vector is created. This is the *which_vector* argument for subsequent :ref:`load ` and :ref:`store ` operators that use this dynamic vector. {xrst_end val_record_vec_op} */ // BEGIN_RECORD_VEC_OP template addr_t tape_t::record_vec_op(const Vector& initial) // END_RECORD_VEC_OP { // // which_vector addr_t which_vector = addr_t( vec_initial_.size() ); // // vec_initial_ vec_initial_.push_back( initial ); // // op_enum_vec_ op_enum_vec_.push_back( uint8_t(vec_op_enum) ); // // var_arg_ var_arg_.push_back( which_vector ); // return which_vector; } /* ------------------------------------------------------------------------------ {xrst_begin val_record_load_op dev} Recording Load Operations ######################### record_load_op ************** {xrst_literal // BEGIN_RECORD_LOAD_OP // END_RECORD_LOAD_OP } This places a :ref:`val_load_op-name` operator in the tape. which_vector ============ This is the index returned by :ref:`val_vec_op-name` when it created this dynamic vector. vector_index ============ This is the index, in the value vector, of the index in the dynamic vector, of the element for this load operation. return ****** The return value is the index were the result of the operation is placed in the value vector. {xrst_end val_record_load_op} */ // ---------------------------------------------------------------------------- // BEGIN_RECORD_LOAD_OP template addr_t tape_t::record_load_op( addr_t which_vector , addr_t vector_index ) // END_RECORD_LOAD_OP { // // check that vec_op_enum comes before load_op_enum CPPAD_ASSERT_UNKNOWN( size_t(which_vector) < vec_initial_.size() ); // // res_index addr_t res_index = n_val_; // // op_enum_vec_ op_enum_vec_.push_back( uint8_t(load_op_enum) ); // // var_arg_ var_arg_.push_back( which_vector ); var_arg_.push_back( vector_index ); // // n_val_ ++n_val_; // return res_index; } /* ------------------------------------------------------------------------------ {xrst_begin val_record_store_op dev} Recording Store Operations ########################## record_store_op *************** {xrst_literal // BEGIN_RECORD_STORE_OP // END_RECORD_STORE_OP } This places a :ref:`val_store_op-name` operator in the tape. which_vector ============ This is the index returned by :ref:`val_vec_op-name` when it created this dynamic vector. vector_index ============ This is the index, in the value vector, of the index in the dynamic vector, of the element for this store operation. return ****** The return value is always zero because there is no value vector result for this operator. {xrst_end val_record_store_op} */ // ---------------------------------------------------------------------------- // BEGIN_RECORD_STORE_OP template addr_t tape_t::record_store_op( addr_t which_vector , addr_t vector_index , addr_t value_index ) // END_RECORD_STORE_OP { // // check that vec_op_enum comes before store_op_enum CPPAD_ASSERT_UNKNOWN( size_t(which_vector) < vec_initial_.size() ); // // res_index addr_t res_index = 0; // // op_enum_vec_ op_enum_vec_.push_back( uint8_t(store_op_enum) ); // // var_arg_ var_arg_.push_back( which_vector ); var_arg_.push_back( vector_index ); var_arg_.push_back( value_index ); // return res_index; } } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/record_new.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_RECORD_NEW_HPP # define CPPAD_LOCAL_VAL_GRAPH_RECORD_NEW_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-25 Bradley M. Bell // --------------------------------------------------------------------------- # include namespace CppAD { namespace local { namespace val_graph { /* {xrst_begin val_record_new dev} Copy an Old Operator to a New Tape ################################## Prototype ********* {xrst_literal // BEGIN_RECORD_NEW // END_RECORD_NEW } Old Tape ******** We refer to ``*this`` as the old tape. new_tape ******** The is the new tape that the operator is recorded on. new_which_vec ************* This vector has size ``vec_initial_.size()`` . It is a mapping from each dynamic vector index in the old tape to the corresponding dynamic vector index in the new tape. #. This starts out as the invalid value ``vec_initial_.size()`` for each old dynamic vector index. #. If the operator being recorded is an ``vec_op_enum`` operator, a dynamic vector is added to the new tape and the corresponding *new_which_vec* entry is modified. #. If this is a load or store operation, *new_which_vec* is used to map from the dynamic vector index in the old tape to the dynamic vector index in the new tape. work **** This is work space used by record_new and not otherwise specified. new_val_index ************* This vector has size ``n_val()`` but it is only defined for indices less than *res_index* (see below). It is a mapping from each value index in the old tape to the corresponding value index in the new tape. val_use_case ************ If *val_use_case* [ *val_index* ] is zero, the value with index *val_index* is not needed to compute the dependent variables. op_ptr ****** This is a pointer to the :ref:`val_base_op-name` for the operator on this tape. arg_index ********* This is the value index corresponding to the first argument for the operator on this tape. res_index ********* This is the value index corresponding to the first result for the operator on this tape. new_res_index ************* This is the value index in the new tape that corresponds to the first result for this operator. {xrst_end val_record_new} */ // BEGIN_RECORD_NEW // new_res_index = record_new( ... ) template addr_t tape_t::record_new( tape_t& new_tape , Vector& new_which_vec , Vector& work , const Vector& new_val_index , const Vector& val_use_case , const base_op_t* op_ptr , addr_t arg_index , addr_t res_index ) // END_RECORD_NEW { // // op_arg Vector& op_arg(work); // // new_res_index // set to avoid compiler warning addr_t new_res_index = 0; // // op_enum, n_arg, n_before, n_after, n_res op_enum_t op_enum = op_ptr->op_enum(); addr_t n_before = op_ptr->n_before(); addr_t n_after = op_ptr->n_after(); addr_t n_arg = op_ptr->n_arg(arg_index, var_arg_); addr_t n_res = op_ptr->n_res(arg_index, var_arg_); // // new_tape, new_res_index bool simple = n_res == 1; simple &= op_enum != con_op_enum; simple &= op_enum != call_op_enum; simple &= op_enum != load_op_enum; if( simple ) { op_arg.resize(n_arg); for(addr_t k = 0; k < n_before; ++k) op_arg[k] = var_arg_[arg_index + k]; for(addr_t k = n_before; k < n_arg - n_after; ++k) { addr_t old_index = var_arg_[arg_index + k]; assert( old_index < res_index ); CPPAD_ASSERT_UNKNOWN( val_use_case[old_index] != 0 ); op_arg[k] = new_val_index[old_index]; } for(addr_t k = 1; k <= n_after; ++k) op_arg[n_arg - k] = var_arg_[arg_index + n_arg - k]; // new_res_index = new_tape.record_op(op_enum, op_arg); } else switch( op_enum ) { // // default default: CPPAD_ASSERT_UNKNOWN(false); // // new_res_index break; // // load_op_enum // new_val_index case load_op_enum: CPPAD_ASSERT_UNKNOWN( n_res == 1); { addr_t which_vector = new_which_vec[ var_arg_[arg_index + 0] ]; addr_t vector_index = new_val_index[ var_arg_[arg_index + 1] ]; // // record_con_op, new_val_index new_res_index = new_tape.record_load_op( which_vector, vector_index); } break; // // con_op_enum // new_val_index case con_op_enum: CPPAD_ASSERT_UNKNOWN( n_res == 1 ); { Value value = con_vec_[ var_arg_[ arg_index ] ]; // // record_con_op, new_val_index new_res_index = new_tape.record_con_op(value); } break; // // store_op_enum case store_op_enum: CPPAD_ASSERT_UNKNOWN( n_res == 0 ); { addr_t which_vector = new_which_vec[ var_arg_[arg_index + 0] ]; addr_t vector_index = new_val_index[ var_arg_[arg_index + 1] ]; addr_t value_index = new_val_index[ var_arg_[arg_index + 2] ]; CPPAD_ASSERT_UNKNOWN( val_use_case[ var_arg_[arg_index + 1] ] != 0 ); CPPAD_ASSERT_UNKNOWN( val_use_case[ var_arg_[arg_index + 2] ] != 0 ); new_tape.record_store_op( which_vector, vector_index, value_index ); } break; // // vec_op_enum case vec_op_enum: CPPAD_ASSERT_UNKNOWN( n_res == 0 ); { addr_t old_which_vector = var_arg_[arg_index + 0]; const Vector& initial = vec_initial_[old_which_vector]; addr_t which_vector = new_tape.record_vec_op(initial); // // new_which_vec new_which_vec[old_which_vector] = which_vector; } break; // // comp_op_enum case comp_op_enum: CPPAD_ASSERT_UNKNOWN( n_res == 0 ); { compare_enum_t compare_enum; compare_enum = compare_enum_t( var_arg_[arg_index + 0] ); addr_t left_index = new_val_index[ var_arg_[arg_index + 1] ]; addr_t right_index = new_val_index[ var_arg_[arg_index + 2] ]; CPPAD_ASSERT_UNKNOWN( val_use_case[ var_arg_[arg_index + 1] ] != 0 ); CPPAD_ASSERT_UNKNOWN( val_use_case[ var_arg_[arg_index + 2] ] != 0 ); new_tape.record_comp_op( compare_enum, left_index, right_index ); } break; // // pri_op_enum CPPAD_ASSERT_UNKNOWN( n_res == 0 ); case pri_op_enum: { std::string before = str_vec_[ var_arg_[arg_index + 0] ]; std::string after = str_vec_[ var_arg_[arg_index + 1] ]; addr_t left_index = new_val_index[ var_arg_[arg_index + 2] ]; addr_t right_index = new_val_index[ var_arg_[arg_index + 3] ]; CPPAD_ASSERT_UNKNOWN( val_use_case[ var_arg_[arg_index + 2] ] != 0 ); CPPAD_ASSERT_UNKNOWN( val_use_case[ var_arg_[arg_index + 3] ] != 0 ); new_tape.record_pri_op( before, after, left_index, right_index ); } break; // // call_op_enum // new_val_index case call_op_enum: { // // n_x addr_t n_x = n_arg - n_before - op_ptr->n_after(); # ifndef NDEBUG bool one_arg_needed = false; # endif // op_arg.resize(n_x); for(addr_t k = 0; k < n_x; ++k) { addr_t val_index = var_arg_[arg_index + n_before + k]; if( val_use_case[val_index] != 0 ) { op_arg[k] = new_val_index[val_index]; # ifndef NDEBUG one_arg_needed = true; # endif } else { // nan in the new tape op_arg[k] = new_tape.n_ind(); } } CPPAD_ASSERT_UNKNOWN( one_arg_needed ); addr_t atomic_index = var_arg_[arg_index + 2]; addr_t call_id = var_arg_[arg_index + 3]; new_res_index = new_tape.record_call_op( atomic_index, call_id, n_res, op_arg ); } break; } return new_res_index; } } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/renumber.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_RENUMBER_HPP # define CPPAD_LOCAL_VAL_GRAPH_RENUMBER_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-25 Bradley M. Bell // --------------------------------------------------------------------------- # include /* ------------------------------------------------------------------------------- {xrst_begin val_tape_renumber dev} {xrst_spell dep } Value Re-Numbering ################## Prototype ********* {xrst_literal // BEGIN_RENUMBER // END_RENUMBER } Discussion ********** This routine uses hash coding to find operators that are equivalent to a previous operators in the tape. It changes the use of an operator's results to use of the results for the equivalent previous operator with lowest index. This creates an equivalent tape where replaced operators are not removed, but the are dead code in the new tape. Compare Operators ***************** If two or more compare operators are identical, the first will be kept as is and the others will be changed to compare_no_enum operators. In this case future calls to eval will on add one to :ref:`val_tape@eval@compare_false`, for each unique comparison that is false. be removed. Changes ******* Only the following values, for this tape, are guaranteed to be same: #. The number of independent values :ref:`val_tape@n_ind` . #. The size of the dependent vector :ref:`dep_vec.size() ` . #. The mapping from the independent to the dependent variables. Reference ********* `value numbering `_ . {xrst_toc_hidden val_graph/renumber_xam.cpp } Example ******* The file :ref:`renumber_xam.cpp ` is an example and test of tape.renumber(). {xrst_end val_tape_renumber} ------------------------------------------------------------------------------- */ namespace CppAD { namespace local { namespace val_graph { // BEGIN_RENUMBER template void tape_t::renumber(void) // END_RENUMBER { // ----------------------------------------------------------------------- // SAS Global Value Renumbering // https://en.wikipedia.org/wiki/Value_numbering // ----------------------------------------------------------------------- # if CPPAD_VAL_GRAPH_TAPE_TRACE // thread, initial_inuse size_t thread = thread_alloc::thread_num(); size_t initial_inuse = thread_alloc::inuse(thread); # endif // // op2arg_index, op2res_index Vector op2arg_index( n_op() ), op2res_index( n_op() ); { op_iterator op_itr(*this, 0); for(addr_t i_op = 0; i_op < n_op(); ++i_op) { op2arg_index[i_op] = op_itr.arg_index(); op2res_index[i_op] = op_itr.res_index(); ++op_itr; } } // // op_hash_table addr_t n_hash_code = 1 + (n_val_ / 2); op_hash_table_t op_hash_table(*this, op2arg_index, n_hash_code); // // new_val_index // value used for operators that are not replaced. Vector new_val_index( n_val_ ); for(addr_t i = 0; i < addr_t(n_val_); ++i) new_val_index[i] = i; // // i_op for(addr_t i_op = 0; i_op < n_op(); ++i_op) { // // op_ptr const base_op_t* op_ptr = base_op_ptr(i_op); // // arg_index_i, res_index_i addr_t arg_index_i = op2arg_index[i_op]; addr_t res_index_i = op2res_index[i_op]; // // j_op addr_t j_op = op_hash_table.match_op(i_op, new_val_index); if( j_op != i_op ) { assert( j_op < i_op ); // // new_val_index // mapping so that op_j results will be used instead of op_i results; // i.e., op_i becomes dead code. addr_t res_index_j = op2res_index[j_op]; addr_t n_res = op_ptr->n_res(arg_index_i, var_arg_); if( n_res == 0 ) { // // change the i_op operator to a no op if( op_ptr->op_enum() == pri_op_enum ) var_arg_[arg_index_i + 2] = this->n_ind(); else { CPPAD_ASSERT_UNKNOWN( op_ptr->op_enum() == comp_op_enum ); var_arg_[arg_index_i + 0] = compare_no_enum; } } else for(addr_t k = 0; k < n_res; ++k) new_val_index[res_index_i + k] = res_index_j + k; } } // // var_arg_ for(addr_t i_op = 0; i_op < n_op(); ++i_op) { // // op_ptr const base_op_t* op_ptr = base_op_ptr(i_op); // // arg_index, n_arg addr_t arg_index = op2arg_index[i_op]; addr_t n_arg = op_ptr->n_arg(arg_index, var_arg_); // // n_before, n_x addr_t n_before = op_ptr->n_before(); addr_t n_x = n_arg - n_before - op_ptr->n_after(); // for(addr_t i = 0; i < n_x; ++i) { addr_t val_index = arg_index + n_before + i; var_arg_[val_index] = new_val_index[ var_arg_[val_index] ]; } } // // dep_vec_ for(size_t i = 0; i < dep_vec_.size(); ++i) dep_vec_[i] = new_val_index[ dep_vec_[i] ]; # if CPPAD_VAL_GRAPH_TAPE_TRACE // A set size more than one represents a collision Vector size_count = op_hash_table.size_count(); for(size_t i = 0; i < size_count.size(); ++i) std::cout << "size = " << i << ", count = " << size_count[i] << "\n"; // // inuse size_t final_inuse = thread_alloc::inuse(thread); std::cout << "renumber: inuse = " << final_inuse - initial_inuse << "\n"; # endif return; } } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/rev_depend.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_REV_DEPEND_HPP # define CPPAD_LOCAL_VAL_GRAPH_REV_DEPEND_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-25 Bradley M. Bell // --------------------------------------------------------------------------- # include # include namespace CppAD { namespace local { namespace val_graph { /* {xrst_begin val_tape_rev_depend dev} Reverse Dependency Analysis ########################### Prototype ********* {xrst_literal // BEGIN_REV_DEPEND // END_REV_DEPEND } tape **** Is the :ref:`val_tape-name` that we are analyzing. val_use_case ************ This vector is empty on input. Upon return, it has size equal to the number of values; i.e., :ref:`val_tape@n_val` . Zero ==== If *val_use_case* [ *val_index* ] is zero, the value with index *val_index* is not needed to compute the dependent variables. n_op ==== If *val_use_case* [ *val_index* ] is equal to n_op (the number of operators), the value with index *val_index* satisfies one of the following conditions: #. It is a dependent variable. #. It is used by more than one operator. #. It is used more than once by one operator and that operator is not a binary operator. Otherwise ========= If *val_use_case* [ *val_index* ] is not zero or n_op, it is the index of the only operator that uses the value with index *val_index* as an argument. vec_last_load ************* This vector is empty on input. Upon return, it has size equal the number of dynamic vectors; i.e., vec_size\_.size() . The value *i_op* = *vec_last_load* [ *which_vector* ] is the index of the last load operator, that used the dynamic vector with index *which_vector* , and is needed to compute the dependent variables. Improvement =========== This could be done differently as a vector of maps. *i_op* = *vec_last_load*[ *which_vector* ] [ *value_index* ] would be the operator index of the last load operator with the same :ref:`val_load_op@eval@which_vector` and :ref:`val_load_op@eval@vector_index` . Then we could avoid two stores to the same vector and index with no load in between. {xrst_end val_tape_rev_depend} */ // BEGIN_REV_DEPEND // tape.rev_depend(val_use_case, vec_last_load) template void tape_t::rev_depend( Vector& val_use_case , Vector& vec_last_load ) // END_REV_DEPEND { CPPAD_ASSERT_UNKNOWN( val_use_case.size() == 0 ); CPPAD_ASSERT_UNKNOWN( vec_last_load.size() == 0 ); // # if CPPAD_VAL_GRAPH_TAPE_TRACE // thread, initial_inuse size_t thread = thread_alloc::thread_num(); size_t initial_inuse = thread_alloc::inuse(thread); # endif // // con_x, type_x, depend_x, depend_y // use CppAD::vector because call_atomic_rev_depend expect it CppAD::vector con_x; CppAD::vector type_x; CppAD::vector depend_x, depend_y; // // val_index2con Vector< Vector > val_vec_vec( vec_initial_.size() ); for(size_t i = 0; i < vec_initial_.size(); ++i) val_vec_vec[i].resize( vec_initial_[i].size() ); Value nan = CppAD::numeric_limits::quiet_NaN(); Vector val_index2con(n_val_); for(addr_t i = 0; i < n_val_; ++i) val_index2con[i] = nan; bool trace = false; eval(trace, val_index2con); // // val_use_case // initialize as no operator uses any value val_use_case.resize(n_val_); for(addr_t i = 0; i < n_val_; ++i) val_use_case[i] = 0; // // vec_last_load // initialize as the no operator uses any dynamic vector vec_last_load.resize( vec_initial_.size() ); for(size_t i = 0; i < vec_initial_.size(); ++i) vec_last_load[i] = 0; // // val_use_case for(size_t i = 0; i < dep_vec_.size(); ++i) val_use_case[ dep_vec_[i] ] = n_op(); // result is a dependent var // // inc_val_use_case auto inc_val_use_case = [this, &val_use_case](addr_t val_index, addr_t op_index) { if( val_use_case[val_index] == 0 ) val_use_case[val_index] = op_index; // only used by this operator else val_use_case[val_index] = n_op(); // is used multiple times }; // // op_itr op_iterator op_itr(*this, n_op() ); // // use_case addr_t i_op = n_op(); while( i_op-- ) { // // op_itr --op_itr; // // op_ptr, arg_index, res_index const base_op_t* op_ptr = op_itr.op_ptr(); addr_t res_index = op_itr.res_index(); addr_t arg_index = op_itr.arg_index(); // // op_enum, n_before, n_after, n_arg, n_res, is_binary op_enum_t op_enum = op_ptr->op_enum(); addr_t n_before = op_ptr->n_before(); addr_t n_after = op_ptr->n_after(); addr_t n_arg = op_ptr->n_arg(arg_index, var_arg_); addr_t n_res = op_ptr->n_res(arg_index, var_arg_); bool is_binary = op_ptr->is_binary(); // if( 0 < n_res && op_enum != call_op_enum ) { CPPAD_ASSERT_UNKNOWN( n_res == 1 ); // // need_op bool need_op = bool( val_use_case[res_index + 0] ); // // val_use_case if( need_op ) { if( is_binary ) { addr_t left_index = var_arg_[arg_index + 0]; addr_t right_index = var_arg_[arg_index + 1]; inc_val_use_case(left_index, i_op); if( left_index != right_index ) inc_val_use_case(right_index, i_op); } else if( op_enum == load_op_enum ) { CPPAD_ASSERT_UNKNOWN( i_op != 0 ); addr_t which_vector = var_arg_[arg_index + 0]; addr_t val_index = var_arg_[arg_index + 1]; if( vec_last_load[which_vector] == 0 ) vec_last_load[which_vector] = i_op; inc_val_use_case(val_index, i_op); } else { for(addr_t i = n_before; i < n_arg - n_after; ++i) { addr_t val_index = var_arg_[arg_index + i]; inc_val_use_case(val_index, i_op); } } } } else if( 0 < n_res ) { // // call_op_enum CPPAD_ASSERT_UNKNOWN( op_enum == call_op_enum ); // size_t atomic_index = size_t( var_arg_[arg_index + 2] ); size_t call_id = size_t( var_arg_[arg_index + 3] ); // // n_x addr_t n_x = n_arg - n_before - n_after; size_t nx = size_t(n_x); // // con_x, type_x con_x.resize(nx); type_x.resize(nx); for(addr_t i = 0; i < n_x; ++i) { con_x[i] = val_index2con[ var_arg_[arg_index + n_before + i] ]; if( CppAD::isnan( con_x[i] ) ) type_x[i] = variable_enum; else type_x[i] = constant_enum; } // // depend_y depend_y.resize(n_res); for(addr_t i = 0; i < n_res; ++i) depend_y[i] = bool( val_use_case[ res_index + i ] ); // // depend_x // only constants (not dynamic parameters) are included in con_x depend_x.resize(nx); local::sweep::call_atomic_rev_depend( atomic_index, call_id, con_x, type_x, depend_x, depend_y ); // // val_use_case for(addr_t k = 0; k < n_x; ++k) { addr_t val_index = var_arg_[arg_index + n_before + k]; if( depend_x[k] ) inc_val_use_case(val_index, i_op); } } } # if CPPAD_VAL_GRAPH_TAPE_TRACE // inuse size_t final_inuse = thread_alloc::inuse(thread); std::cout << "rev_depend: inuse = " << final_inuse - initial_inuse << "\n"; # endif return; } } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/summation.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_SUMMATION_HPP # define CPPAD_LOCAL_VAL_GRAPH_SUMMATION_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-25 Bradley M. Bell // --------------------------------------------------------------------------- # include # include namespace CppAD { namespace local { namespace val_graph { /* ------------------------------------------------------------------------------ {xrst_begin_parent val_summation dev} {xrst_spell csum dep neg } Combine Multiple sum Operators into a csum Operator ################################################### Prototype ********* {xrst_literal // BEGIN_SUMMATION // END_SUMMATION } Discussion ********** We refer to an add, sub, neg, or csum operator as a sum operation. If the result for one of these operations is only used by a sum operator, the two operations can be replaced by a single csum operator. This process is repeated until the result is used by some other operation or the result is a dependent variable. Limitation ********** The process above terminates before reaching a csum operator in the original tape. To get full optimization in this case, csum operators should first be converted to add and sub operations. Changes ******* Only the following values, for this tape, are guaranteed to be same: #. The number of independent values :ref:`val_tape@n_ind` . #. The size of the dependent vector :ref:`dep_vec.size() ` . Example ******* The file :ref:`val_summation_xam.cpp-name` is an example and test using tape.summation(). Contents ******** {xrst_toc_table val_graph/summation_xam.cpp } {xrst_end val_summation} ----------------------------------------------------------------------------- {xrst_begin val_csum_info dev} {xrst_spell ctor struct } Information for a Cumulative Summation ###################################### {xrst_code cpp} */ struct csum_info_t { bool first_done; // is first operator argument included in lists bool second_done; // is second operator argument included in lists std::list add_list; // list of value indices to add std::list sub_list; // list of value indices to subtract // ctor csum_info_t() : first_done(false), second_done(false) { } }; /* {xrst_code} {xrst_end val_csum_info} ------------------------------------------------------------------------------ {xrst_begin val_replace_csum_op dev} {xrst_spell neg } Replace An Operator with a Cumulative Summation ############################################### Prototype ********* {xrst_literal // BEGIN_REPLACE_CSUM_OP // END_REPLACE_CSUM_OP } Replacement Operator Use ************************ This will replace an operator use in the tape. The :ref:`set_op2arg_index ` must have been executed on this tape before a replacement can be done. res_index ********* is the value index of the result for this operator. op_index ******** is the index of the operator that we are replacing This operator must have just one result with index res_index. csum_info ********* Is the cumulative summation information for the operator that we are replacing. The idea here is that there are multiple add, sub, and neg operators that become dead code when this replacement is done. {xrst_end val_replace_csum_op} */ // // BEGIN_REPLACE_CSUM_OP template void tape_t::replace_csum_op( addr_t res_index , addr_t i_op , csum_info_t& csum_info ) // END_REPLACE_CSUM_OP { // // This is a replace // // op_enum_vec_ op_enum_vec_[i_op] = uint8_t( csum_op_enum ); // // arg_index_vec_ op2arg_index_[i_op] = addr_t( var_arg_.size() ); // // n_add, var_arg_ addr_t n_add = addr_t( csum_info.add_list.size() ); var_arg_.push_back( n_add ); // // n_sub, var_arg_ addr_t n_sub = addr_t( csum_info.sub_list.size() ); var_arg_.push_back( n_sub ); // // itr std::list::const_iterator itr; // // var_arg_: addition variables for(itr = csum_info.add_list.begin(); itr != csum_info.add_list.end(); ++itr) { CPPAD_ASSERT_UNKNOWN( *itr < res_index ); var_arg_.push_back( *itr ); } // // var_arg_: subtraction variables for(itr = csum_info.sub_list.begin(); itr != csum_info.sub_list.end(); ++itr) { CPPAD_ASSERT_UNKNOWN( *itr < res_index ); var_arg_.push_back( *itr ); } // // n_arg, var_arg_ addr_t n_arg = 3 + n_add + n_sub; var_arg_.push_back( n_arg ); // return; } // --------------------------------------------------------------------------- // BEGIN_SUMMATION template void tape_t::summation(void) // END_SUMMATION { // # if CPPAD_VAL_GRAPH_TAPE_TRACE // thread, initial_inuse size_t thread = thread_alloc::thread_num(); size_t initial_inuse = thread_alloc::inuse(thread); # endif // // op_arg Vector op_arg; // // csum_map // information for all the cumulative summation operations std::map csum_map; // // sum_op auto sum_op = [] (op_enum_t op_enum) { bool result = false; result |= op_enum == neg_op_enum; result |= op_enum == add_op_enum; result |= op_enum == sub_op_enum; return result; }; // // splice_list auto splice_list = [] (std::list& des, std::list& src) { std::list::iterator itr_pos = des.end(); des.splice(itr_pos, src); return; }; // // cat_list auto cat_list = [] (std::list& des, std::list& src) { std::list::iterator itr_pos = des.end(); std::list::const_iterator itr_begin = src.begin(); std::list::const_iterator itr_end = src.end(); des.insert(itr_pos, itr_begin, itr_end); return; }; // // set_op2arg_index // This is necessary before calling replace_csum_op. set_op2arg_index(); // // val_use_case, vec_last_load Vector val_use_case, vec_last_load; rev_depend(val_use_case, vec_last_load); // // op_itr op_iterator op_itr(*this, 0); // // i_op for(addr_t i_op = 1; i_op < n_op(); ++i_op) { // // op_itr ++op_itr; // skip index zero // // op_ptr, arg_index, res_index const base_op_t* op_ptr_i = op_itr.op_ptr(); addr_t res_index_i = op_itr.res_index(); addr_t arg_index_i = op_itr.arg_index(); // // op_enum_i, n_arg op_enum_t op_enum_i = op_ptr_i->op_enum(); addr_t n_arg = op_ptr_i->n_arg(arg_index_i, var_arg_); // // use_i, sum_i bool sum_i = sum_op( op_enum_i ); bool use_i = false; if( sum_i ) { # ifndef NDEBUG addr_t n_res = op_ptr_i->n_res(arg_index_i, var_arg_); CPPAD_ASSERT_UNKNOWN( n_res == 1 ); # endif use_i = 0 != val_use_case[res_index_i]; } if( use_i & sum_i ) { // i_op is one of neg, add, or sub // // op_arg op_arg.resize(n_arg); for(addr_t i = 0; i < n_arg; ++i) op_arg[i] = var_arg_[arg_index_i + i]; // // op_arg_equal_i bool op_arg_equal_i = false; if( op_ptr_i->is_binary() ) op_arg_equal_i = op_arg[0] == op_arg[1]; // // is_csum_i, csum_map[i_op] bool is_csum_i = 0 < csum_map.count(i_op); if( is_csum_i ) { csum_info_t& csum_info_i = csum_map[i_op]; switch(op_enum_i) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // case neg_op_enum: CPPAD_ASSERT_UNKNOWN( csum_info_i.first_done ); break; // case add_op_enum: if( ! csum_info_i.first_done ) { CPPAD_ASSERT_UNKNOWN( ! op_arg_equal_i ); csum_info_i.add_list.push_back(op_arg[0]); } if( ! csum_info_i.second_done ) { CPPAD_ASSERT_UNKNOWN( ! op_arg_equal_i ); csum_info_i.add_list.push_back(op_arg[1]); } break; // case sub_op_enum: if( ! csum_info_i.first_done ) { CPPAD_ASSERT_UNKNOWN( ! op_arg_equal_i ); csum_info_i.add_list.push_back(op_arg[0]); } if( ! csum_info_i.second_done ) { CPPAD_ASSERT_UNKNOWN( ! op_arg_equal_i ); csum_info_i.sub_list.push_back(op_arg[1]); } break; } csum_info_i.first_done = true; csum_info_i.second_done = true; } // if( val_use_case[res_index_i] == n_op() ) { // i_op is a dependent variable or used more than once // if( is_csum_i ) { replace_csum_op(res_index_i, i_op, csum_map[i_op]); csum_map.erase( i_op ); } } else { // // j_op // i_op result is only used by the j_op operator addr_t j_op = val_use_case[res_index_i]; // // op_enum_j op_enum_t op_enum_j = op_enum_t( op_enum_vec_[j_op] ); // // sum_j bool sum_j = sum_op( op_enum_j ); // if( ! sum_j ) { // The only use of i_op is not a summation operator if( is_csum_i ) { replace_csum_op(res_index_i, i_op, csum_map[i_op]); csum_map.erase( i_op ); } } else { // The only use of i_op result is in a summation operator // // csum_map[i_op] if( ! is_csum_i ) { csum_info_t csum_info; switch(op_enum_i) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // // neg_op_enum case neg_op_enum: csum_info.sub_list.push_back(op_arg[0]); break; // // add_op_enum case add_op_enum: csum_info.add_list.push_back(op_arg[0]); csum_info.add_list.push_back(op_arg[1]); break; // // sub_op_enum // no use is adding and subtracting the same argument case sub_op_enum: if( ! op_arg_equal_i ) { csum_info.add_list.push_back(op_arg[0]); csum_info.sub_list.push_back(op_arg[1]); } break; } csum_map[i_op] = csum_info; } // // csum_info_i csum_info_t& csum_info_i = csum_map[i_op]; // // second_operand bool second_operand = false; bool op_arg_equal_j = false; if( (op_enum_j == add_op_enum) || (op_enum_j == sub_op_enum) ) { addr_t arg_index_j = op2arg_index_[j_op]; addr_t right_index = var_arg_[arg_index_j + 1]; addr_t left_index = var_arg_[arg_index_j + 0]; second_operand = right_index == res_index_i; op_arg_equal_j = right_index == left_index; CPPAD_ASSERT_UNKNOWN( left_index == res_index_i || right_index == res_index_i ); } // // is_csum_j bool is_csum_j = 0 < csum_map.count(j_op); // // csum_map[j_op] if( ! is_csum_j ) csum_map[j_op] = csum_info_t(); csum_info_t& csum_info_j = csum_map[j_op]; switch( op_enum_j ) { // default: CPPAD_ASSERT_UNKNOWN(false) break; // case neg_op_enum: splice_list(csum_info_j.add_list, csum_info_i.sub_list); splice_list(csum_info_j.sub_list, csum_info_i.add_list); break; // case add_op_enum: if( op_arg_equal_j ) { cat_list(csum_info_j.add_list, csum_info_i.add_list); cat_list(csum_info_j.sub_list, csum_info_i.sub_list); } splice_list(csum_info_j.add_list, csum_info_i.add_list); splice_list(csum_info_j.sub_list, csum_info_i.sub_list); break; // case sub_op_enum: if( second_operand ) { if( ! op_arg_equal_j ) { splice_list(csum_info_j.add_list, csum_info_i.sub_list); splice_list(csum_info_j.sub_list, csum_info_i.add_list); } } else if( op_arg_equal_j ) { cat_list(csum_info_j.add_list, csum_info_i.add_list); cat_list(csum_info_j.sub_list, csum_info_i.sub_list); splice_list(csum_info_j.add_list, csum_info_i.add_list); splice_list(csum_info_j.sub_list, csum_info_i.sub_list); } else { splice_list(csum_info_j.add_list, csum_info_i.add_list); splice_list(csum_info_j.sub_list, csum_info_i.sub_list); } break; } if( op_arg_equal_j ) { csum_info_j.first_done = true; csum_info_j.second_done = true; } else if( second_operand ) csum_info_j.second_done = true; else csum_info_j.first_done = true; } } } } # if CPPAD_VAL_GRAPH_TAPE_TRACE // inuse size_t final_inuse = thread_alloc::inuse(thread); std::cout << "summation: inuse = " << final_inuse - initial_inuse << "\n"; # endif } } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/tape.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_TAPE_HPP # define CPPAD_LOCAL_VAL_GRAPH_TAPE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # define CPPAD_VAL_GRAPH_TAPE_TRACE 0 namespace CppAD { namespace local { namespace val_graph { // csum_info_t: forward declare struct csum_info_t; /* {xrst_begin val_tape dev} {xrst_spell dep str } The Value Operator Tape ####################### Prototype ********* {xrst_literal // BEGIN_TAPE_T // END_TAPE_T } Purpose ******* This class is used to define a function using a sequence of operators. Value ***** This is the type used for evaluations using the tape. op_ptr ****** {xrst_literal // BEGIN_OP_PTR // END_OP_PTR } This is the :ref:`base_op ` corresponding to *op_index* in this tape. n_val ***** {xrst_literal // BEGIN_N_VAL // END_N_VAL } This is the number of elements in the value vector. n_op **** {xrst_literal // BEGIN_N_OP // END_N_OP } This is the number of operators in the tape. n_ind ***** {xrst_literal // BEGIN_N_IND // END_N_IND } This is the size of the independent vector. arg_vec ******* {xrst_literal // BEGIN_ARG_VEC // END_ARG_VEC } The elements of this vector at non-negative integers that connect with an operator to make an operator usage. con_vec ******* {xrst_literal // BEGIN_CON_VEC // END_CON_VEC } This is the vector of value constants. str_vec ******* {xrst_literal // BEGIN_STR_VEC // END_STR_VEC } This is the vector of string constants. vec_initial *********** {xrst_literal // BEGIN_VEC_INITIAL // END_VEC_INITIAL } This vector has length equal to the number of dynamic vectors. This i-th element of this vector is the vector of initial indices for the i-th dynamic vector; see :ref:`val_vec_op-name` . op_enum_vec *********** {xrst_literal // BEGIN_OP_ENUM_VEC // END_OP_ENUM_VEC } Each element of this vector corresponds to an operator usage. The order of the vector is the order of operations. dep_vec ******* {xrst_literal // BEGIN_DEP_VEC // END_DEP_VEC } This is the vector of dependent indices in the value vector. The function corresponding to a tape maps the independent values to the dependent values. swap **** {xrst_literal // BEGIN_SWAP // END_SWAP } This swaps the contents of this tape with another tape. eval **** {xrst_literal // BEGIN_EVAL // END_EVAL } Given the independent values, this routine execute the operators in order to evaluate the rest of the value vector and the compare_false counter. trace ===== If this is true, the :ref:`val_print_op-name` functions are used to print the operators. val_vec ======= This vector has size equal to *n_val*. The first *n_ind* elements are inputs. The rest of the elements are outputs. compare_false ============= This argument is optional. It is the number of :ref:`val_comp_op-name` that had a false result for their comparisons. This is both an input and output; i.e., each false comparison will add one to this value. Operations on Tape ****************** {xrst_comment BEGIN_SORT_THIS_LINE_PLUS_2} {xrst_toc_table include/cppad/local/val_graph/compress.hpp include/cppad/local/val_graph/cumulative.hpp include/cppad/local/val_graph/dead_code.hpp include/cppad/local/val_graph/fold_con.hpp include/cppad/local/val_graph/op2arg_index.hpp include/cppad/local/val_graph/op_hash_table.hpp include/cppad/local/val_graph/op_iterator.hpp include/cppad/local/val_graph/option.hpp include/cppad/local/val_graph/record.hpp include/cppad/local/val_graph/record_new.hpp include/cppad/local/val_graph/renumber.hpp include/cppad/local/val_graph/rev_depend.hpp include/cppad/local/val_graph/summation.hpp } {xrst_comment END_SORT_THIS_LINE_MINUS_2} {xrst_end val_tape} */ // BEGIN_TAPE_T template class tape_t { // END_TAPE_T private : addr_t n_ind_; // number of independent values addr_t n_val_; // total number of values Vector var_arg_; // arguments for all operator uses Vector con_vec_; // value constants Vector str_vec_; // string constants Vector dep_vec_; // dependent variable indices in val_vec Vector op_enum_vec_; // one byte per operator enum value. // // vec_initial_ // initial indices for all the dynamic vectors Vector< Vector > vec_initial_; // // op2arg_index_ // Optional vector that changes how op_iterator works; e.g., // this is necessary is we are using replace_csum_op with this tape. Vector op2arg_index_; // // option_map_ std::map< std::string, std::string > option_map_; // # if CPPAD_VAL_GRAPH_TAPE_TRACE // set by set_ind, used by set_dep size_t set_ind_inuse_; # endif // public : // default constructor tape_t(void) { initialize_option(); } // // BEGIN_OP_PTR const base_op_t* base_op_ptr(addr_t op_index) const { op_enum_t op_enum = op_enum_t( op_enum_vec_[op_index] ); return op_enum2class(op_enum); } // END_OP_PTR // ------------------------------------------------------------------------ // BEGIN_N_VAL addr_t n_val(void) const { return n_val_; } // END_N_VAL // // BEGIN_N_OP addr_t n_op(void) const { return addr_t( op_enum_vec_.size() ); } // END_N_OP // // BEGIN_N_IND addr_t n_ind(void) const { return n_ind_; } // END_N_IND // // BEGIN_ARG_VEC const Vector& arg_vec(void) const { return var_arg_; } // END_ARG_VEC // // BEGIN_CON_VEC const Vector& con_vec(void) const { return con_vec_; } // END_CON_VEC // // BEGIN_STR_VEC const Vector& str_vec(void) const { return str_vec_; } // END_STR_VEC // // BEGIN_VEC_INITIAL const Vector< Vector >& vec_initial(void) const { return vec_initial_; } // END_VEC_INITIAL // // BEGIN_OP_ENUM_VEC const Vector& op_enum_vec(void) const { return op_enum_vec_; } // END_OP_ENUM_VEC // // BEGIN_DEP_VEC const Vector& dep_vec(void) const { return dep_vec_; } // END_DEP_VEC // ------------------------------------------------------------------------ // BEGIN_SWAP void swap(tape_t& other) // END_SWAP { // same order as declaration of member variables just below private: std::swap( n_ind_, other.n_ind_ ); std::swap( n_val_, other.n_val_); var_arg_.swap( other.var_arg_ ); con_vec_.swap( other.con_vec_ ); str_vec_.swap( other.str_vec_ ); dep_vec_.swap( other.dep_vec_ ); op_enum_vec_.swap( other.op_enum_vec_ ); vec_initial_.swap( other.vec_initial_ ); op2arg_index_.swap( other.op2arg_index_ ); option_map_.swap( other.option_map_ ); } // eval(trace, val_vec) void eval( bool trace , Vector& val_vec ) const { size_t compare_false = 0; eval(trace, val_vec, compare_false); } // BEGIN_EVAL // eval(trace, val_vec, compare_false) void eval( bool trace , Vector& val_vec , size_t& compare_false ) const // END_EVAL { CPPAD_ASSERT_KNOWN( val_vec.size() == size_t(n_val_), "eval: size of val_vec not equal to tape.n_val()" ); using std::setw; using std::right; using std::cout; // // ind_vec_vec // Only the vector_op routines use this eval argument Vector< Vector > ind_vec_vec; // // trace if( trace ) { // no operators for independent variables std::cout << "independent vector\n"; for(addr_t res_index = 0; res_index < n_ind_; ++res_index) { Value res = val_vec[res_index]; cout << right << setw(5) << res_index; cout << " " << right << setw(10) << res << "\n"; } std::printf("operators\n"); } // // op_itr, i_op op_iterator op_itr(*this, 0); for(addr_t i_op = 0; i_op < n_op(); ++i_op) { // // op_ptr, arg_index, res_index const base_op_t* op_ptr = op_itr.op_ptr(); addr_t arg_index = op_itr.arg_index(); addr_t res_index = op_itr.res_index(); // // base_op_t::eval op_ptr->eval( this, trace, arg_index, res_index, val_vec, ind_vec_vec, compare_false ); // // op_itr ++op_itr; } // trace if( trace ) { // no operators for dependent variables std::cout << "dependent vector\n"; for(size_t i = 0; i < dep_vec_.size(); ++i) { addr_t res_index = dep_vec_[i]; Value res = val_vec[res_index]; cout << right << setw(5) << res_index; cout << " " << right << setw(10) << res << "\n"; } // space after end of this tape std::printf("\n"); } return; } // ------------------------------------------------------------------------ // functions in record.hpp // ------------------------------------------------------------------------ // // set_ind addr_t set_ind(addr_t n_ind); // // record_op addr_t record_op(op_enum_t op_enum, const Vector& op_arg); // // record_con_op addr_t record_con_op(const Value& constant); // // record_dis_op addr_t record_dis_op(addr_t discrete_index, addr_t op_arg); // // record_comp_op addr_t record_comp_op( compare_enum_t compare_enum , addr_t left_index , addr_t right_index ); // // record_call_op addr_t record_call_op( addr_t atomic_index , addr_t call_id , addr_t n_res , const Vector& fun_arg ); // // record_csum_op addr_t record_csum_op( const Vector& add, const Vector& sub ); // // record_cexp_op addr_t record_cexp_op( compare_enum_t compare_enum , addr_t left , addr_t right , addr_t if_true , addr_t if_false ); // // record_pri_op addr_t record_pri_op( const std::string& before , const std::string& after , addr_t flag_index , addr_t value_index ); // // record_vec_op addr_t record_vec_op(const Vector& initial); // // record_load_op addr_t record_load_op( addr_t which_vector , addr_t vector_index ); // // record_store_op addr_t record_store_op( addr_t which_vector , addr_t vector_index , addr_t value_index ); // // set_dep void set_dep(const Vector& dep_vec); // ------------------------------------------------------------------------ // functions in their own files // ------------------------------------------------------------------------ // // fold_con void fold_con(void); // // renumber void renumber(void); // // rev_depend void rev_depend( Vector& val_use_case , Vector& vec_last_load ); // // dead_code vectorBool dead_code(void); // // compress vectorBool compress(void); // // record_new addr_t record_new( tape_t& new_tape , Vector& new_which_vec , Vector& work , const Vector& new_val_index , const Vector& val_use_case , const base_op_t* op_ptr , addr_t arg_index , addr_t res_index ); // // ------------------------------------------------------------------------ // functions in summation.hpp // ------------------------------------------------------------------------ // // summation void summation(void); // // set_op2arg_index void set_op2arg_index(); // // op2arg_index const Vector& op2arg_index(void) const; // // replace_csum_op void replace_csum_op( addr_t res_index , addr_t i_op , csum_info_t& csum_info ); // ----------------------------------------------------------------------- // functions in cumulative.hpp // ----------------------------------------------------------------------- // // op2csum void op2csum(addr_t op_index); // // ------------------------------------------------------------------------ // functions in option.hpp // ------------------------------------------------------------------------ // // initialize_option void initialize_option(void); // // set_option void set_option(const std::string& name, const std::string& value); }; } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE // BEGIN_SORT_THIS_LINE_PLUS_1 # include # include # include # include # include # include # include # include # include # include # include // END_SORT_THIS_LINE_MINUS_1 # undef CPPAD_VAL_GRAPH_TAPE_TRACE # endif ================================================ FILE: include/cppad/local/val_graph/unary_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_UNARY_OP_HPP # define CPPAD_LOCAL_VAL_GRAPH_UNARY_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # define CPPAD_VAL_GRAPH_UNARY(Name, Op) \ template \ class Name##_op_t : public unary_op_t { \ public: \ /* get_instance */ \ static Name##_op_t* get_instance(void) \ { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; \ static Name##_op_t instance; \ return &instance; \ } \ /* op_enum */ \ op_enum_t op_enum(void) const override \ { return Name##_op_enum; \ } \ /* eval */ \ void eval( \ const tape_t* tape , \ bool trace , \ addr_t arg_index , \ addr_t res_index , \ Vector& val_vec , \ Vector< Vector >& ind_vec_vec , \ size_t& compare_false ) const override \ { const Vector& arg_vec( tape->arg_vec() ); \ const Value& value = val_vec[ arg_vec[arg_index + 0] ]; \ val_vec[res_index] = Op ( value ); \ if( trace ) this->print_op( \ #Name , arg_index, arg_vec, res_index, val_vec \ ); \ } \ } namespace CppAD { namespace local { namespace val_graph { /* ------------------------------------------------------------------------------ {xrst_begin_parent val_unary_op dev} Unary Value Operators ##################### {xrst_end val_unary_op} ------------------------------------------------------------------------------ {xrst_begin val_unary_base_op dev} The Unary Value Operator Base Class ################################### Prototype ********* {xrst_literal // BEGIN_UNARY_OP_T // END_UNARY_OP_T } Purpose ******* The class is derived from :ref:`base_op `. It overrides the *is_unary*, *n_before*, *n_after*, *n_arg*, *n_res_*, and *print_op* member functions. The *op_enum* and *eval* member functions are still pure virtual. is_unary ******** This override of :ref:`val_base_op@is_unary` returns true. n_before ******** This override of :ref:`val_base_op@n_before` returns 0. n_after ******* This override of :ref:`val_base_op@n_after` returns 0. n_arg ***** This override of :ref:`val_base_op@n_arg` returns 1. n_res ***** This override of :ref:`val_base_op@n_res` returns 1. print_op ******** {xrst_literal // BEGIN_PRINT_OP // END_PRINT_OP } This member function uses :ref:`val_print_op-name` to print unary operators. {xrst_end val_unary_base_op} */ // BEGIN_UNARY_OP_T template class unary_op_t : public base_op_t { // END_UNARY_OP_T public: // n_before addr_t n_before(void) const override { return 0; } // // n_after addr_t n_after(void) const override { return 0; } // // is_unary bool is_unary(void) const override { return true; } // // n_arg addr_t n_arg( addr_t arg_index , const Vector& arg_vec ) const override { return 1; } // // n_res addr_t n_res( addr_t arg_index , const Vector& arg_vec ) const override { return 1; } // // op_enum virtual op_enum_t op_enum(void) const override = 0; // // eval virtual void eval( const tape_t* tape , bool trace , addr_t arg_index , addr_t res_index , Vector& val_vec , Vector< Vector >& ind_vec_vec , size_t& compare_false ) const override = 0; // // BEGIN_PRINT_OP void print_op( const char* name , addr_t arg_index , const Vector& arg_vec , addr_t res_index , Vector& val_vec ) const // END_PRINT_OP { // Vector arg_val_index = { arg_vec[ arg_index + 0 ] }; Vector res_value = { val_vec[res_index] }; CppAD::local::val_graph::print_op( name, arg_val_index, res_index, res_value ); } }; /* ------------------------------------------------------------------------------ {xrst_begin val_unary_op_derived dev} {xrst_spell neg } The Unary Value Operator Derived Classes ######################################## Prototype ********* | |tab| ``template `` | |tab| ``class`` *Name*\ ``_opt_t : public unary_op_t`` *Name* ****** Unary operators are defined for the following names: .. csv-table:: Unary Operators :widths: auto :header-rows: 1 Name,description neg,result is negative of operand Context ******* This class is derived from :ref:`val_unary_op-name` . It overrides the *op_enum* and *eval* member functions and is a concrete class (it has no pure virtual functions). get_instance ************ This static member function returns a pointer to a *Name*\ ``_op_t`` object. op_enum ******* This override of :ref:`val_base_op@op_enum` returns *Name*\ ``_op_enum`` . eval **** This override of :ref:`val_base_op@eval` sets the result equal to the unary operator applied to the operand; see :ref:`val_base_op@arg_vec@Unary Operators` . {xrst_toc_hidden val_graph/unary_xam.cpp } Example ******* The file :ref:`unary_xam.cpp ` is an example and test that uses a unary operator. {xrst_end val_unary_op_derived} */ // BEGIN_SORT_THIS_LINE_PLUS_1 CPPAD_VAL_GRAPH_UNARY(abs, fabs); CPPAD_VAL_GRAPH_UNARY(acos, acos); CPPAD_VAL_GRAPH_UNARY(acosh, acosh); CPPAD_VAL_GRAPH_UNARY(asin, asin); CPPAD_VAL_GRAPH_UNARY(asinh, asinh); CPPAD_VAL_GRAPH_UNARY(atan, atan); CPPAD_VAL_GRAPH_UNARY(atanh, atanh); CPPAD_VAL_GRAPH_UNARY(cos, cos); CPPAD_VAL_GRAPH_UNARY(cosh, cosh); CPPAD_VAL_GRAPH_UNARY(erf, erf); CPPAD_VAL_GRAPH_UNARY(erfc, erfc); CPPAD_VAL_GRAPH_UNARY(exp, exp); CPPAD_VAL_GRAPH_UNARY(expm1, expm1); CPPAD_VAL_GRAPH_UNARY(log, log); CPPAD_VAL_GRAPH_UNARY(log1p, log1p); CPPAD_VAL_GRAPH_UNARY(neg, -); CPPAD_VAL_GRAPH_UNARY(sign, sign); CPPAD_VAL_GRAPH_UNARY(sin, sin); CPPAD_VAL_GRAPH_UNARY(sinh, sinh); CPPAD_VAL_GRAPH_UNARY(sqrt, sqrt); CPPAD_VAL_GRAPH_UNARY(tan, tan); CPPAD_VAL_GRAPH_UNARY(tanh, tanh); // END_SORT_THIS_LINE_MINUS_1 // --------------------------------------------------------------------------- } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # undef CPPAD_VAL_GRAPH_UNARY # endif ================================================ FILE: include/cppad/local/val_graph/val2fun.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_VAL2FUN_HPP # define CPPAD_LOCAL_VAL_GRAPH_VAL2FUN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // -------------------------------------------------------------------------- /* {xrst_begin val2fun_graph dev} {xrst_spell dyn } Create an ADFun Object Corresponding to a Value Graph ##################################################### Syntax ****** | |tab| ``ADFun`` < *Base* > *fun* | |tab| *fun* . ``val2fun`` ( *val_tape* , *dyn_ind* , *var_ind* ) | |tab| *fun* . ``val2fun`` ( *val_tape* , *dyn_ind* , *var_ind* , *use_val* ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } Base **** is the type corresponding to this :ref:`adfun-name` object; i.e., its calculations are done using the type *Base* . It is also :ref:`val_tape@Value` type for the tape. RecBase ******* in the prototype above, *RecBase* is the same type as *Base* . val_tape ******** This is a :ref:`val_tape-name` representation of the function. *dyn_ind* ********* The *i*-th element of the vector is the index in the value graph of the *i*-th independent dynamic parameter in *fun* . *var_ind* ********* The *i*-th element of the vector is the index in *val_tape* of the *i*-th independent variable in *fun* . No two elements of *dyn_ind* or *var_ind* can have the same value. Furthermore, the total number of elements in these two vectors must be the number of independent variables in *val_tape* . use_val ******* If this vector is present extra detection is done to convert variables to parameters. It has size equal to *val_tape*\ .n_val(). For each value index, that is a result for a :ref:`val_call_op-name`, the corresponding *use_val*\ [i] is true (false) if the result is used to compute one of the dependent variables. *fun* ***** The input contents of *fun* do not matter. Upon return it is an ADFun representation of the function. Under Construction ****************** This routine is under construction and is only implemented for a few of the possible :ref:`ADFun-name` operators. {xrst_toc_hidden val_graph/val2fun_xam.cpp } Examples ******** The file :ref:`val_val2fun_xam.cpp-name` is an example an test of this conversion. {xrst_end val2fun_graph} */ # include # include # include # include # include # include # include # define CPPAD_VAL2FUN_DYN_UNARY(name) \ case local::val_graph::name##_op_enum: \ tmp_addr = rec.put_dyn_par( \ nan, local::name##_dyn, fun_arg[0] \ ); \ break; # define CPPAD_VAL2FUN_DYN_BINARY(name) \ case local::val_graph::name##_op_enum: \ tmp_addr = rec.put_dyn_par( \ nan, local::name##_dyn, fun_arg[0], fun_arg[1] \ ); \ break; # define CPPAD_VAL2FUN_VAR_UNARY(name, Op) \ case local::val_graph::name##_op_enum: \ tmp_addr = rec.PutOp(local::Op); \ rec.PutArg( fun_arg[0] ); \ break; # define CPPAD_VAL2FUN_VAR_BINARY(name, Op) \ case local::val_graph::name##_op_enum: \ tmp_addr = rec.PutOp(local::Op); \ break; namespace CppAD { // BEGIN_CPPAD_NAMESPACE template void ADFun::val2fun( const local::val_graph::tape_t& val_tape , const CppAD::local::val_graph::Vector& dyn_ind , const CppAD::local::val_graph::Vector& var_ind ) { vectorBool use_val; val2fun(val_tape, dyn_ind, var_ind, use_val); } // BEGIN_PROTOTYPE template void ADFun::val2fun( const local::val_graph::tape_t& val_tape , const CppAD::local::val_graph::Vector& dyn_ind , const CppAD::local::val_graph::Vector& var_ind , const CppAD::vectorBool& use_val ) // END_PROTOTYPE { // // vector using CppAD::local::val_graph::Vector; // // base_op_t, op_enum_t, compare_enum_t using local::val_graph::base_op_t; using local::val_graph::op_enum_t; using local::val_graph::compare_enum_t; // // val_arg_vec, val_con_vec, val_str_vec, val_dep_vec const Vector& val_arg_vec = val_tape.arg_vec(); const Vector& val_con_vec = val_tape.con_vec(); const Vector& val_str_vec = val_tape.str_vec(); const Vector& val_dep_vec = val_tape.dep_vec(); // // nan Base nan = CppAD::numeric_limits::quiet_NaN(); // // par_addr // a temporary parameter index addr_t par_addr; // // var_addr // a temporary variable index addr_t var_addr; // // tmp_addr // a parameter or variable temporary index // initialize to zero to avoid compiler warnings addr_t tmp_addr = 0; // # ifndef NDEBUG // val_n_ind // size of the independent value vector size_t val_n_ind = size_t( val_tape.n_ind() ); # endif // // dyn_n_ind // number of independent dynamic parameters size_t dyn_n_ind = dyn_ind.size(); // // var_n_ind // number of independent variables size_t var_n_ind = var_ind.size(); // // val_n_op addr_t val_n_op = val_tape.n_op(); // CPPAD_ASSERT_KNOWN( dyn_n_ind + var_n_ind == val_n_ind, "val2fun: The number of independent variables and dynamic parameters\n" "is not equal to the size of the independent value vector" ); // // n_val addr_t n_val = val_tape.n_val(); // // fun_ad_type Vector fun_ad_type(n_val); for(addr_t i = 0; i < n_val; ++i) fun_ad_type[i] = number_ad_type_enum; // invalid // // val_index2con // After fold_con, all the constants that get used are op_con results. Vector val_index2con(n_val); for(addr_t i = 0; i < n_val; ++i) val_index2con[i] = nan; bool trace = false; val_tape.eval(trace, val_index2con); // // val2fun_index // mapping from value index to index in the AD function object. // The meaning of this index depends on its ad_type. Vector val2fun_index(n_val); for(addr_t i = 0; i < n_val; ++i) val2fun_index[i] = std::numeric_limits::max(); // invalid // // rec // start a function recording local::recorder rec; CPPAD_ASSERT_UNKNOWN( rec.num_var_op() == 0 ); rec.set_n_dyn_independent(dyn_n_ind); rec.set_abort_op_index(0); rec.set_record_compare(false); // // parameter # ifndef NDEBUG const local::pod_vector_maybe& parameter( rec.par_all()); CPPAD_ASSERT_UNKNOWN( parameter.size() == 0 ); # endif // // rec // initialize with the value nan at index nan par_addr = rec.put_con_par(nan); CPPAD_ASSERT_UNKNOWN( par_addr == 0 ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[par_addr] ) ); // // rec // Place the variable with index 0 in the tape CPPAD_ASSERT_UNKNOWN( NumArg(local::BeginOp) == 1); CPPAD_ASSERT_UNKNOWN( NumRes(local::BeginOp) == 1); rec.PutOp(local::BeginOp); rec.PutArg(0); // parameter argument is the nan above // // rec, vecad_offset // place the VecAD objects in the recording Vector vecad_offset; addr_t offset = 1; for(size_t i = 0; i < val_tape.vec_initial().size(); ++i) { const Vector& val_initial = val_tape.vec_initial()[i]; size_t size = val_initial.size(); local::pod_vector fun_initial(size); for(size_t j = 0; j < size; ++j) { addr_t val_index = val_initial[j]; const Base& value = val_index2con[val_index]; CPPAD_ASSERT_KNOWN( ! CppAD::isnan(value), "val2fun: an initial value for a dynamic array is not constant" ); fun_initial[j] = rec.put_con_par( value ); } rec.put_var_vecad(size, fun_initial); vecad_offset.push_back( offset ); offset += addr_t(size + 1); } // // rec, fun_ad_type, val2fun_index // put the independent dynamic parameters in the function recording for(size_t i = 0; i < dyn_n_ind; ++i) { CPPAD_ASSERT_KNOWN( dyn_ind[i] < val_n_ind, "val2fun: number of independent values is <= dyn_ind[i]" ); CPPAD_ASSERT_KNOWN( fun_ad_type[ dyn_ind[i] ] == number_ad_type_enum, "val2fun: dep_ind[i] == dep_ind[j] for some i and j" ); fun_ad_type[ dyn_ind[i] ] = dynamic_enum; par_addr = rec.put_dyn_par(nan, local::ind_dyn); val2fun_index[ dyn_ind[i] ] = par_addr; CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[par_addr] ) ); } // put the independent variables in the function recording for(size_t i = 0; i < var_n_ind; ++i) { CPPAD_ASSERT_KNOWN( var_ind[i] < val_n_ind, "val2fun: number of independent values is <= var_ind[i]" ); CPPAD_ASSERT_KNOWN( fun_ad_type[ var_ind[i] ] == number_ad_type_enum, "val2fun: var_ind[i] == dep_ind[j] for some i and j\n" "or var_ind[i] == var_ind[j] for some i and j" ); fun_ad_type[ var_ind[i] ] = variable_enum; var_addr = rec.PutOp( local::InvOp ); val2fun_index[ var_ind[i] ] = var_addr; } // // ind_taddr_ // address of the independent variables on variable tape ind_taddr_.resize(var_n_ind); for(size_t i = 0; i < var_n_ind; ++i) ind_taddr_[i] = i + 1; // // rec_con_index // First constant in CppAD recording and val_graph is nan Vector rec_con_index( val_con_vec.size() ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( val_con_vec[0] ) ); rec_con_index[0] = 0; for(size_t i = 1; i < val_con_vec.size(); ++i) rec_con_index[i] = rec.put_con_par( val_con_vec[i] ); // // zero_index addr_t zero_index = rec.put_con_par( Base(0.0) ); // // two_sqrt_pi_index: 2 / sqrt(pi) addr_t two_sqrt_pi_index = rec.put_con_par( Base(1.0) / sqrt( atan( Base(1.0) ) ) ); // // rec_str_index Vector rec_str_index( val_str_vec.size() ); for(size_t i = 0; i < val_str_vec.size(); ++i) { const std::string& str = val_str_vec[i]; rec_str_index[i] = rec.PutTxt( str.c_str() ); } // // ad_type_x, ad_type_y, fun_arg, csum_arg, select_y Vector fun_arg, csum_arg; Vector select_y; Vector< AD > ax, ay; // // con_x, at_type_x, ad_type_y; // Use CppAD::vector because call_atomic_for_type requires it CppAD::vector con_x; CppAD::vector ad_type_x, ad_type_y; // // op_itr local::val_graph::op_iterator op_itr(val_tape, 0); // // i_op for(addr_t i_op = 0; i_op < val_n_op; ++i_op) { // // op_itr if( 0 < i_op ) ++op_itr; // // op_ptr, arg_index, res_index const base_op_t* op_ptr = op_itr.op_ptr(); addr_t arg_index = op_itr.arg_index(); addr_t res_index = op_itr.res_index(); // // op_enum, is_unary, is_binary, n_arg op_enum_t op_enum = op_ptr->op_enum(); bool is_unary = op_ptr->is_unary(); bool is_binary = op_ptr->is_binary(); addr_t n_before = op_ptr->n_before(); addr_t n_after = op_ptr->n_after(); addr_t n_arg = op_ptr->n_arg(arg_index, val_arg_vec); CPPAD_ASSERT_UNKNOWN( n_before + n_after <= n_arg ); // // n_x addr_t n_x = n_arg - n_before - n_after; // // ad_type_x, fun_arg, con_x, max_ad_type ad_type_x.resize(n_x); fun_arg.resize(n_x); con_x.resize(n_x); ad_type_enum max_ad_type = constant_enum; for(addr_t i = 0; i < n_x; ++i) { addr_t val_index = val_arg_vec[arg_index + n_before + i]; ad_type_x[i] = fun_ad_type[val_index]; fun_arg[i] = val2fun_index[val_index]; con_x[i] = val_index2con[val_index]; max_ad_type = std::max(max_ad_type, ad_type_x[i] ); } /* CPPAD_ASSERT_KNOWN( n_x == 0 || constant_enum < max_ad_type, "val2fun: must first call fold_con" ); */ // // rec, fun_ad_type, val2fun_index if( is_unary ) { // fun_ad_type[res_index] = max_ad_type; CPPAD_ASSERT_UNKNOWN( n_arg == 1 ); // // rec, val2fun_index if( max_ad_type == dynamic_enum ) { switch( op_enum ) { // // default default: CPPAD_ASSERT_UNKNOWN(false); tmp_addr = 0; // to avoid compiler warning break; // // BEGIN_SORT_THIS_LINE_PLUS_1 CPPAD_VAL2FUN_DYN_UNARY(abs); CPPAD_VAL2FUN_DYN_UNARY(acos); CPPAD_VAL2FUN_DYN_UNARY(acosh); CPPAD_VAL2FUN_DYN_UNARY(asin); CPPAD_VAL2FUN_DYN_UNARY(asinh); CPPAD_VAL2FUN_DYN_UNARY(atan); CPPAD_VAL2FUN_DYN_UNARY(atanh); CPPAD_VAL2FUN_DYN_UNARY(cos); CPPAD_VAL2FUN_DYN_UNARY(cosh); CPPAD_VAL2FUN_DYN_UNARY(erf); CPPAD_VAL2FUN_DYN_UNARY(erfc); CPPAD_VAL2FUN_DYN_UNARY(exp); CPPAD_VAL2FUN_DYN_UNARY(expm1); CPPAD_VAL2FUN_DYN_UNARY(log); CPPAD_VAL2FUN_DYN_UNARY(log1p); CPPAD_VAL2FUN_DYN_UNARY(neg); CPPAD_VAL2FUN_DYN_UNARY(sign); CPPAD_VAL2FUN_DYN_UNARY(sin); CPPAD_VAL2FUN_DYN_UNARY(sinh); CPPAD_VAL2FUN_DYN_UNARY(sqrt); CPPAD_VAL2FUN_DYN_UNARY(tan); // END_SORT_THIS_LINE_MINUS_1 } } else { switch( op_enum ) { // default default: CPPAD_ASSERT_UNKNOWN(false); tmp_addr = 0; // to avoid compiler warning break; // // BEGIN_SORT_THIS_LINE_PLUS_1 CPPAD_VAL2FUN_VAR_UNARY(abs, AbsOp); CPPAD_VAL2FUN_VAR_UNARY(acos, AcosOp); CPPAD_VAL2FUN_VAR_UNARY(acosh, AcoshOp); CPPAD_VAL2FUN_VAR_UNARY(asin, AsinOp); CPPAD_VAL2FUN_VAR_UNARY(asinh, AsinhOp); CPPAD_VAL2FUN_VAR_UNARY(atan, AtanOp); CPPAD_VAL2FUN_VAR_UNARY(atanh, AtanhOp); CPPAD_VAL2FUN_VAR_UNARY(cos, CosOp); CPPAD_VAL2FUN_VAR_UNARY(cosh, CoshOp); CPPAD_VAL2FUN_VAR_UNARY(exp, ExpOp); CPPAD_VAL2FUN_VAR_UNARY(expm1, Expm1Op); CPPAD_VAL2FUN_VAR_UNARY(log, LogOp); CPPAD_VAL2FUN_VAR_UNARY(log1p, Log1pOp); CPPAD_VAL2FUN_VAR_UNARY(neg, NegOp); CPPAD_VAL2FUN_VAR_UNARY(sign, SignOp); CPPAD_VAL2FUN_VAR_UNARY(sin, SinOp); CPPAD_VAL2FUN_VAR_UNARY(sinh, SinhOp); CPPAD_VAL2FUN_VAR_UNARY(sqrt, SqrtOp); CPPAD_VAL2FUN_VAR_UNARY(tan, TanOp); CPPAD_VAL2FUN_VAR_UNARY(tanh, TanhOp); // END_SORT_THIS_LINE_MINUS_1 // // erf case local::val_graph::erf_op_enum: { tmp_addr = rec.PutOp(local::ErfOp); CPPAD_ASSERT_UNKNOWN( NumArg(local::ErfOp) == 3 ); rec.PutArg( fun_arg[0] ); rec.PutArg( zero_index ); rec.PutArg( two_sqrt_pi_index ); } break; // // erfc case local::val_graph::erfc_op_enum: { tmp_addr = rec.PutOp(local::ErfcOp); CPPAD_ASSERT_UNKNOWN( NumArg(local::ErfcOp) == 3 ); rec.PutArg( fun_arg[0] ); rec.PutArg( zero_index ); rec.PutArg( two_sqrt_pi_index ); } break; } } val2fun_index[res_index] = tmp_addr; } else if( is_binary ) { // fun_ad_type[res_index] = max_ad_type; CPPAD_ASSERT_UNKNOWN( n_arg == 2 ); // if( max_ad_type == dynamic_enum ) { switch(op_enum) { default: CPPAD_ASSERT_UNKNOWN(false); break; // CPPAD_VAL2FUN_DYN_BINARY(add); CPPAD_VAL2FUN_DYN_BINARY(sub); CPPAD_VAL2FUN_DYN_BINARY(mul); CPPAD_VAL2FUN_DYN_BINARY(div); CPPAD_VAL2FUN_DYN_BINARY(pow); } CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[tmp_addr] ) ); } else if( ad_type_x[0] == variable_enum && ad_type_x[1] == variable_enum ) { switch(op_enum) { default: CPPAD_ASSERT_UNKNOWN(false); break; // CPPAD_VAL2FUN_VAR_BINARY(add, AddvvOp); CPPAD_VAL2FUN_VAR_BINARY(sub, SubvvOp); CPPAD_VAL2FUN_VAR_BINARY(mul, MulvvOp); CPPAD_VAL2FUN_VAR_BINARY(div, DivvvOp); CPPAD_VAL2FUN_VAR_BINARY(pow, PowvvOp); } rec.PutArg(fun_arg[0], fun_arg[1]); } else if( ad_type_x[1] == variable_enum ) { switch(op_enum) { default: CPPAD_ASSERT_UNKNOWN(false); break; // CPPAD_VAL2FUN_VAR_BINARY(add, AddpvOp); CPPAD_VAL2FUN_VAR_BINARY(sub, SubpvOp); CPPAD_VAL2FUN_VAR_BINARY(mul, MulpvOp); CPPAD_VAL2FUN_VAR_BINARY(div, DivpvOp); CPPAD_VAL2FUN_VAR_BINARY(pow, PowpvOp); } rec.PutArg(fun_arg[0], fun_arg[1]); } else { CPPAD_ASSERT_UNKNOWN( ad_type_x[0] == variable_enum ); switch(op_enum) { default: CPPAD_ASSERT_UNKNOWN(false); break; // // add case local::val_graph::add_op_enum: tmp_addr = rec.PutOp(local::AddpvOp); std::swap(fun_arg[0], fun_arg[1]); break; // // mul case local::val_graph::mul_op_enum: tmp_addr = rec.PutOp(local::MulpvOp); std::swap(fun_arg[0], fun_arg[1]); break; // CPPAD_VAL2FUN_VAR_BINARY(sub, SubvpOp); CPPAD_VAL2FUN_VAR_BINARY(div, DivvpOp); CPPAD_VAL2FUN_VAR_BINARY(pow, PowvpOp); } rec.PutArg(fun_arg[0], fun_arg[1]); } val2fun_index[res_index] = tmp_addr; } else switch( op_enum ) { // ! ( is_unary || is_binary) // // default default: CPPAD_ASSERT_KNOWN( op_enum > local::val_graph::number_op_enum, "val_graph::val2fun: op_enum is not yet implemented" ); // ------------------------------------------------------------------ // dis_op // rec, fun_ad_type, val2fun_index case local::val_graph::dis_op_enum: fun_arg.resize(1); CPPAD_ASSERT_UNKNOWN( n_arg = 2 ); { addr_t dynamic_index = val_arg_vec[arg_index + 0]; if( max_ad_type == dynamic_enum ) { tmp_addr = rec.put_dyn_par( nan, local::dis_dyn, dynamic_index, fun_arg[0] ); } else { CPPAD_ASSERT_UNKNOWN( max_ad_type == variable_enum ); tmp_addr = rec.PutOp(local::DisOp); rec.PutArg( dynamic_index ); rec.PutArg( fun_arg[0] ); } fun_ad_type[res_index] = max_ad_type; val2fun_index[res_index] = tmp_addr; } break; // ------------------------------------------------------------------ // con_op // rec, fun_ad_type, val2fun_index case local::val_graph::con_op_enum: CPPAD_ASSERT_UNKNOWN( n_arg = 1 ); { par_addr = rec_con_index[ val_arg_vec[arg_index] ]; fun_ad_type[res_index] = constant_enum; val2fun_index[res_index] = par_addr; # ifndef NDEBUG const Base& constant = val_con_vec[ val_arg_vec[arg_index] ]; CPPAD_ASSERT_UNKNOWN( CppAD::isnan(constant) || parameter[par_addr] == constant ); # endif } break; // ------------------------------------------------------------------- // pri_op: rec case local::val_graph::pri_op_enum: { // // before, after, flag, value addr_t before = rec_str_index[ val_arg_vec[arg_index + 0] ]; addr_t after = rec_str_index[ val_arg_vec[arg_index + 1] ]; addr_t flag = fun_arg[0]; addr_t value = fun_arg[1]; // // is_var // base 2 representation of [ is_var(flag), is_var(value) ] addr_t is_var = 0; if( fun_ad_type[flag] == variable_enum ) is_var += 1; if( fun_ad_type[value] == variable_enum ) is_var += 2; // // rec rec.PutOp(local::PriOp); rec.PutArg(is_var, flag, before, value, after); } break; // ------------------------------------------------------------------- // cexp_op case local::val_graph::cexp_op_enum: { // // compare, compare_enum, cop addr_t compare = val_arg_vec[arg_index + 0]; compare_enum_t compare_enum = compare_enum_t( compare ); CompareOp cop; if( compare_enum == local::val_graph::compare_eq_enum ) cop = CompareEq; else if( compare_enum == local::val_graph::compare_le_enum ) cop = CompareLe; else { CPPAD_ASSERT_UNKNOWN( compare_enum == local::val_graph::compare_lt_enum ); cop = CompareLt; } if( max_ad_type == dynamic_enum ) { tmp_addr = rec.put_dyn_cond_exp( nan, cop, fun_arg[0], fun_arg[1], fun_arg[2], fun_arg[3] ); } else { addr_t flag = 0; addr_t bit = 1; for(size_t j = 0; j < 4; ++j) { if( ad_type_x[j] == variable_enum ) flag |= bit; bit = 2 * bit; } CPPAD_ASSERT_UNKNOWN( flag != 0 ); rec.PutArg( cop, flag, fun_arg[0], fun_arg[1], fun_arg[2], fun_arg[3] ); tmp_addr = rec.PutOp(local::CExpOp); } fun_ad_type[res_index] = max_ad_type; val2fun_index[res_index] = tmp_addr; } break; // ------------------------------------------------------------------- // comp_op // rec case local::val_graph::comp_op_enum: { // // compare addr_t compare = val_arg_vec[arg_index + 0]; // // var_left, var_right, dyn_left, dyn_right bool var_left = ad_type_x[0] == variable_enum; bool var_right = ad_type_x[1] == variable_enum; bool dyn_left = ad_type_x[0] == dynamic_enum; bool dyn_right = ad_type_x[1] == dynamic_enum; // // ax ax.resize(2); // if( var_left | dyn_left ) ax[0].taddr_ = fun_arg[0]; else ax[0].value_ = con_x[0]; // if( var_right | dyn_right ) ax[1].taddr_ = fun_arg[1]; else ax[1].value_ = con_x[1]; // // res, compare_enum compare_enum_t compare_enum = compare_enum_t( compare ); bool res; switch(compare_enum) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // case local::val_graph::compare_eq_enum: res = true; rec.comp_eq( var_left, var_right, dyn_left, dyn_right, ax[0], ax[1], res ); break; // case local::val_graph::compare_le_enum: res = true; rec.comp_le( var_left, var_right, dyn_left, dyn_right, ax[0], ax[1], res ); break; // case local::val_graph::compare_lt_enum: res = true; rec.comp_lt( var_left, var_right, dyn_left, dyn_right, ax[0], ax[1], res ); break; // case local::val_graph::compare_ne_enum: res = false; rec.comp_eq( var_left, var_right, dyn_left, dyn_right, ax[0], ax[1], res ); break; // case local::val_graph::compare_no_enum: break; } } break; // ------------------------------------------------------------------- // csum_op // rec, fun_ad_type, val2fun_index case local::val_graph::csum_op_enum: if( dynamic_enum <= max_ad_type ) { // // n_add, n_sub, n_arg addr_t n_add = val_arg_vec[arg_index + 0]; addr_t n_sub = val_arg_vec[arg_index + 1]; CPPAD_ASSERT_UNKNOWN( n_arg == 3 + n_add + n_sub ); // // sum_constant Base sum_constant = Base(0.0); for(addr_t i = 0; i < n_add; ++i) { if( ad_type_x[i] <= constant_enum ) sum_constant += con_x[i]; } for(addr_t i = 0; i < n_sub; ++i) { if( ad_type_x[n_add + i] <= constant_enum ) sum_constant -= con_x[n_add + i]; } // rec, sum_constant_addr addr_t sum_constant_addr = rec.put_con_par(sum_constant); CPPAD_ASSERT_UNKNOWN(parameter[sum_constant_addr] == sum_constant); // if( max_ad_type == dynamic_enum ) { tmp_addr = sum_constant_addr; for(addr_t i = 0; i < n_x; ++i) { tmp_addr = rec.put_dyn_par( nan, local::add_dyn, tmp_addr, fun_arg[i] ); CPPAD_ASSERT_UNKNOWN( CppAD::isnan( parameter[tmp_addr] ) ); } } else if( max_ad_type == variable_enum ) { // // n_add_var, n_add_dyn addr_t n_add_dyn = 0; addr_t n_add_var = 0; for(addr_t i = 0; i < n_add; ++i) { if( ad_type_x[i] == dynamic_enum ) ++n_add_dyn; if( ad_type_x[i] == variable_enum ) ++n_add_var; } // // n_add_var, n_add_dyn addr_t n_sub_dyn = 0; addr_t n_sub_var = 0; for(addr_t i = 0; i < n_sub; ++i) { if( ad_type_x[n_add + i] == dynamic_enum ) ++n_sub_dyn; if( ad_type_x[n_add + i] == variable_enum ) ++n_sub_var; } // // csum_arg addr_t n_tot = 6 + n_add_var + n_sub_var + n_add_dyn + n_sub_dyn; csum_arg.resize(n_tot); csum_arg[0] = sum_constant_addr; csum_arg[1] = 5 + n_add_var; csum_arg[2] = csum_arg[1] + n_sub_var; csum_arg[3] = csum_arg[2] + n_add_dyn; csum_arg[4] = csum_arg[3] + n_sub_dyn; // addr_t i_variable = 5; addr_t i_dynamic = i_variable + n_add_var + n_sub_var; for(addr_t i = 0; i < n_add; ++i) { if( ad_type_x[i] == dynamic_enum ) csum_arg[i_dynamic++] = fun_arg[i]; if( ad_type_x[i] == variable_enum ) csum_arg[i_variable++] = fun_arg[i]; } for(addr_t i = 0; i < n_sub; ++i) { if( ad_type_x[n_add + i] == dynamic_enum ) csum_arg[i_dynamic++] = fun_arg[n_add + i]; if( ad_type_x[n_add + i] == variable_enum ) csum_arg[i_variable++] = fun_arg[n_add + i]; } CPPAD_ASSERT_UNKNOWN( i_dynamic == n_tot - 1 ); csum_arg[n_tot - 1] = n_tot; // // rec, tmp_addr tmp_addr = rec.PutOp(local::CSumOp); for(addr_t i = 0; i < n_tot; ++i) rec.PutArg( csum_arg[i] ); CPPAD_ASSERT_UNKNOWN( local::NumRes(local::CSumOp) == 1 ); } fun_ad_type[res_index] = max_ad_type; val2fun_index[res_index] = tmp_addr; } break; // ------------------------------------------------------------------- // vec_op case local::val_graph::vec_op_enum: // All the VecAD objects have already been initialized break; // // load_op case local::val_graph::load_op_enum: { addr_t which_vector = val_arg_vec[arg_index + 0]; offset = vecad_offset[which_vector]; addr_t load_op_index = addr_t( rec.num_var_load() ); rec.PutArg(offset, fun_arg[0], load_op_index); if( ad_type_x[0] < variable_enum ) var_addr = rec.PutLoadOp(local::LdpOp); else var_addr = rec.PutLoadOp(local::LdvOp); // // The result of a load function is always a variable fun_ad_type[res_index] = variable_enum; val2fun_index[res_index] = var_addr; } break; // // store_op case local::val_graph::store_op_enum: { addr_t which_vector = val_arg_vec[arg_index + 0]; offset = vecad_offset[which_vector]; rec.PutArg(offset, fun_arg[0], fun_arg[1] ); if( ad_type_x[0] < variable_enum ) { if( ad_type_x[1] < variable_enum ) rec.PutOp(local::StppOp); else rec.PutOp(local::StpvOp); } else { if( ad_type_x[1] < variable_enum ) rec.PutOp(local::StvpOp); else rec.PutOp(local::StvvOp); } } break; // // ------------------------------------------------------------------- // call_op // rec, fun_ad_type, val2fun_index case local::val_graph::call_op_enum: { // // atomic_index, call_id addr_t n_res = val_arg_vec[arg_index + 1]; size_t atomic_index = size_t( val_arg_vec[arg_index + 2] ); size_t call_id = size_t( val_arg_vec[arg_index + 3] ); // // ad_type_y ad_type_y.resize(n_res); local::val_graph::call_atomic_for_type( con_x, ad_type_x, ad_type_y, atomic_index, call_id ); // // record_dynamic, record_variable bool record_dynamic = false; bool record_variable = false; for(addr_t i = 0; i < n_res; ++i) { CPPAD_ASSERT_UNKNOWN( ad_type_y[i] <= variable_enum ); record_dynamic |= ad_type_y[i] == dynamic_enum; record_variable |= ad_type_y[i] == variable_enum; } // tape_id // tape_id is zero because not a true recording tape_id_t tape_id = 0; // // ax, ay if( record_dynamic || record_variable ) { // // ax ax.resize(n_x); for(addr_t j = 0; j < n_x; ++j) { addr_t val_index = val_arg_vec[arg_index + n_before + j]; ax[j].taddr_ = val2fun_index[val_index]; ax[j].value_ = val_index2con[val_index]; } // ay, ad_type_y ay.resize(n_res); for(addr_t j = 0; j < n_res; ++j) { ay[j].taddr_ = 0; // not used ay[j].value_ = val_index2con[res_index + j]; if( use_val.size() != 0 ) { if( ! use_val[res_index + j] ) { ay[j].value_ = nan; if( ad_type_y[j] == variable_enum ) ad_type_y[j] = constant_enum; } } } } if( record_dynamic ) rec.put_dyn_atomic( tape_id, atomic_index, call_id, ad_type_x, ad_type_y, ax, ay ); if( record_variable ) rec.put_var_atomic( tape_id, atomic_index, call_id, ad_type_x, ad_type_y, ax, ay ); // // val2fun_index, fun_ad_type for(addr_t i = 0; i < n_res; ++i) { val2fun_index[res_index + i] = ay[i].taddr_; fun_ad_type[res_index + i] = ad_type_y[i]; } } break; } } // dep_taddr_, dep_parameter, rec // dep_taddr_ is address of the dependent variables on variable tape dep_taddr_.resize( val_dep_vec.size() ); dep_parameter_.resize( val_dep_vec.size() ); for(size_t i = 0; i < val_dep_vec.size(); ++i) { addr_t val_index = val_dep_vec[i]; ad_type_enum ad_type = fun_ad_type[val_index]; addr_t fun_index = val2fun_index[val_index]; dep_parameter_[i] = ad_type < variable_enum; if( dep_parameter_[i] ) { // see RecordParOp(const AD& y) rec.PutArg( fun_index ); fun_index = rec.PutOp(local::ParOp); } dep_taddr_[i] = size_t( fun_index ); } // rec rec.PutOp(local::EndOp); // // // ---------------------------------------------------------------------- // End recording, set private member data except for // ---------------------------------------------------------------------- // // bool values in this object except check_for_nan_ has_been_optimized_ = false; // // size_t values in this object compare_change_count_ = 1; compare_change_number_ = 0; compare_change_op_index_ = 0; num_order_taylor_ = 0; cap_order_taylor_ = 0; num_direction_taylor_ = 0; num_var_tape_ = rec.num_var(); // // taylor_ taylor_.resize(0); // // cskip_op_ cskip_op_.resize( rec.num_var_op() ); // // load_op2var_ load_op2var_.resize( rec.num_var_load() ); // // play_ // Now that each dependent variable has a place in the recording, // and there is a EndOp at the end of the record, we can transfer the // recording to the player and and erase the recording. play_.get_recording(rec, var_n_ind); // // ind_taddr_ // Note that play_ has been set, we can use it to check operators CPPAD_ASSERT_UNKNOWN( var_n_ind < num_var_tape_); for(size_t j = 0; j < var_n_ind; j++) { CPPAD_ASSERT_UNKNOWN( play_.GetOp(j+1) == local::InvOp ); CPPAD_ASSERT_UNKNOWN( ind_taddr_[j] = j+1 ); } // // for_jac_sparse_pack_, for_jac_sparse_set_ for_jac_sparse_pack_.resize(0, 0); for_jac_sparse_set_.resize(0,0); // // resize subgraph_info_ subgraph_info_.resize( ind_taddr_.size(), // n_dep dep_taddr_.size(), // n_ind play_.num_var_op(), // n_op play_.num_var() // n_var ); // // set the function name function_name_ = ""; // // return; } # undef CPPAD_VAL2FUN_DYN_UNARY # undef CPPAD_VAL2FUN_DYN_BINARY # undef CPPAD_VAL2FUN_VAR_UNARY # undef CPPAD_VAL2FUN_VAR_BINARY } // END_CPPAD_NAMESPACE // -------------------------------------------------------------------------- # endif ================================================ FILE: include/cppad/local/val_graph/val_graph.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-23 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin val_graph dev} Value Operator Representation of a Function ########################################### See Also ******** :ref:`json_ad_graph-name`, :ref:`cpp_ad_graph-name` Static Single-Assignment ************************ This is an `SSA `_ representation of a function :math:`y = f(x)`. The vector *x* is called the independent vector, and *y* is called the dependent vector. A value may be a CppAD :ref:`glossary@Parameter@Constant`, :ref:`glossary@Parameter@Dynamic` parameter , or a :ref:`glossary@Variable` . Value Vector ************ The values are ordered in a vector :math:`( v_0 , \ldots , v_{p-1} )` where *p* is the number of values. Independent Vector ================== The independent sub-vector is .. math:: x = ( v_0 , \cdots , v_{n-1} ) where :math:`n \leq p` is the length of the dependent vector. Result Vector ============= The result sub-vector is :math:`( v_n , \ldots , v_{p-1} )` . These are the values computed by the algorithm. This is a static single assignment SSA representation; i.e., each result has one and only one assignment. Dependent Vector ================ There is a vector of non-negative integer indices :math:`( d_0 , \cdots , d_{m-1} )` that specify the dependent vector as follows .. math:: y = ( v_{d[0]} , \cdots , v_{d[m-1]} ) note that :math:`d[j]` is used in place of :math:`d_j` above to avoid having two levels of sub-scripting. Constant Vector *************** There is a separate constant vector :math:`( c_0 , \ldots , c_{q-1} )` where *q* is the number of constants. Operator ******** Each component of the result vector is computed by an operator. Constant Operator ================= The constant operator copies a constant from the constant vector to the result vector; i.e. :math:`v_\ell = c_k` where :math:`n \leq \ell < p` and :math:`0 \leq k < q` . Unary Operator ============== A unary operator has one value argument and one result; i.e., :math:`v_\ell = \phi ( v_k )` where :math:`n \leq \ell < p`, :math:`0 \leq k < \ell` and :math:`\phi` is the function corresponding to the operator. Binary Operator =============== A binary operator has two value arguments and one result; i.e., :math:`v_\ell = \phi ( v_j , v_k )` where :math:`n \leq \ell < p`, :math:`0 \leq j < \ell` , :math:`0 \leq k < \ell` and :math:`\phi` is the function corresponding to the operator. Function Operator ================= Each function operator has its own number of argument and result values. The arguments need not be sequential, but the results are; i.e., .. math:: ( v_\ell , v_{\ell+1} , \cdots ) = \phi ( v_j , v_k , \cdots ) where :math:`n \leq \ell < p`, :math:`0 \leq j < \ell` , :math:`0 \leq k < \ell` , ... , and :math:`\phi` is the function corresponding to the operator. Implementation ************** {xrst_toc_table include/cppad/local/val_graph/val_type.hpp include/cppad/local/val_graph/base_op.hpp include/cppad/local/val_graph/print_op.hpp include/cppad/local/val_graph/tape.hpp include/cppad/local/val_graph/op_enum2class.hpp include/cppad/local/val_graph/fun2val.hpp include/cppad/local/val_graph/val2fun.hpp include/cppad/local/val_graph/val_optimize.hpp include/cppad/local/val_graph/dyn_type.hpp include/cppad/local/val_graph/var_type.hpp include/cppad/local/val_graph/call_atomic.hpp include/cppad/local/val_graph/enable_parallel.hpp } {xrst_end val_graph} ================================================ FILE: include/cppad/local/val_graph/val_optimize.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_VAL_OPTIMIZE_HPP # define CPPAD_LOCAL_VAL_GRAPH_VAL_OPTIMIZE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // -------------------------------------------------------------------------- /* ------------------------------------------------------------------------------ {xrst_begin val_optimize dev} Optimize the Value Graph Corresponding to This Function ####################################################### Prototype ********* {xrst_literal // BEGIN_VAL_OPTIMIZE // END_VAL_OPTIMIZE } options ======= See :ref:`optimize@options` . {xrst_end val_optimize} */ # include # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // BEGIN_VAL_OPTIMIZE template void ADFun::val_optimize(const std::string& options) // END_VAL_OPTIMIZE { // // compare_op, cumulative_sum_op, print_for_op, local::optimize::options_t result = local::optimize::extract_option(options); bool compare_op = result.compare_op; bool cumulative_sum_op = result.cumulative_sum_op; bool print_for_op = result.print_for_op; // CPPAD_ASSERT_UNKNOWN( result.val_graph == true ); CPPAD_ASSERT_KNOWN( result.conditional_skip == false, "optimize options: " "val_graph is present and no_conditional_skip is not present" ); // n_dyn_ind, n_var_ind size_t n_dyn_ind = size_dyn_ind(); size_t n_var_ind = Domain(); // // dyn_ind, var_ind using CppAD::local::val_graph::Vector; Vector dyn_ind(n_dyn_ind), var_ind(n_var_ind); for(size_t j = 0; j < n_dyn_ind; ++j) dyn_ind[j] = j; for(size_t j = 0; j < n_var_ind; ++j) var_ind[j] = n_dyn_ind + j; // // val_tape // value operator representation corresponding to this function local::val_graph::tape_t val_tape; fun2val(val_tape); // // // this // free all the memory associated with this function { ADFun g; swap(g); } /* Vector val_vec( val_tape.n_val() ); for(addr_t i = 0; i < val_tape.n_ind(); ++i) val_vec[i] = Base(i + 1); bool trace = true; val_tape.eval(trace, val_vec); */ // val_tape: renumber val_tape.renumber(); // // val_tape: fold_con(); // // val_tape: summation if( cumulative_sum_op ) val_tape.summation(); // // val_tape: dead_code, val_use if( compare_op ) val_tape.set_option("keep_compare", "true"); else val_tape.set_option("keep_compare", "false"); if( print_for_op ) val_tape.set_option("keep_print", "true"); else val_tape.set_option("keep_print", "false"); vectorBool use_val = val_tape.dead_code(); /* val_vec.resize( val_tape.n_val() ); val_tape.eval(trace, val_vec); */ // this // convert optimized value graph to fun val2fun(val_tape, dyn_ind, var_ind, use_val); // // no collision limit in value graph representation. return; } } // END_CPPAD_NAMESPACE // -------------------------------------------------------------------------- # endif ================================================ FILE: include/cppad/local/val_graph/val_type.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_VAL_TYPE_HPP # define CPPAD_LOCAL_VAL_GRAPH_VAL_TYPE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-24 Bradley M. Bell // ------------------------------------------------------------- # include # include # include # include # include namespace CppAD { namespace local { namespace val_graph { /* {xrst_begin val_graph_type dev} {xrst_spell typedef } Value Operator Type Definitions ############################### addr_t ****** This type is used to store vectors of non-negative integers. It must be able to support an index equal to the length of the arg_vec vector. A signed type can be used for testing, but an unsigned type will support more indices with the same memory usage. {xrst_code hpp} */ typedef CPPAD_TAPE_ADDR_TYPE addr_t; /* {xrst_code} Vector ****** This is a :ref:`SimpleVector-name` template class. In addition, these vectors have the following member functions ``push_back`` , ``clear`` . These member functions have he same specifications as for std::vector. The std::vector and CppAD::vector are two example template vector classes that can be used. {xrst_code hpp} */ template using Vector = CppAD::vector; /* {xrst_code} op_enum_t ********* A concrete class does not have any pure virtual functions. This enum type is used to identify the concrete operator class types. {xrst_spell_off} {xrst_comment BEGIN_SORT_THIS_LINE_PLUS_3} {xrst_code hpp}*/ enum op_enum_t { abs_op_enum, // absolute value acos_op_enum, // inverse cosine acosh_op_enum, // inverse hyperbolic cosine add_op_enum, // addition asin_op_enum, // inverse sine asinh_op_enum, // inverse hyperbolic sine atan_op_enum, // inverse tangent atanh_op_enum, // inverse hyperbolic tangent call_op_enum, // atomic functions cexp_op_enum, // conditional expression comp_op_enum, // comparison con_op_enum, // constants cos_op_enum, // cosine cosh_op_enum, // hyperbolic cosine csum_op_enum, // cumulative summation dis_op_enum, // discrete functions div_op_enum, // division erf_op_enum, // error function erfc_op_enum, // complementary error function exp_op_enum, // exponential function expm1_op_enum, // exponential function minus one load_op_enum, // load an element from a dynamic vector log1p_op_enum, // one plust natural log log_op_enum, // natural log function mul_op_enum, // multiplication neg_op_enum, // negative pow_op_enum, // power function pri_op_enum, // print sign_op_enum, // sign function (1, 0, -1) sin_op_enum, // sine function sinh_op_enum, // hyperbolic sine sqrt_op_enum, // square root store_op_enum, // store an element in a dynamic vector sub_op_enum, // subtraction tan_op_enum, // tangent tanh_op_enum, // hyperbolic tangent vec_op_enum, // create a new dynamic vector number_op_enum }; /* {xrst_code} {xrst_comment END_SORT_THIS_LINE_MINUS_4} {xrst_spell_on} The ``number_op_enum`` entry is used for the number of concrete operator class types (and does not correspond to an operator). compare_op_enum_t ***************** This enum type is used to identify which comparison a :ref:`val_comp_op-name` does. {xrst_code hpp}*/ enum compare_enum_t { compare_eq_enum, // equal compare_ne_enum, // not equal compare_lt_enum, // less than compare_le_enum, // less than or equal compare_no_enum, // no comparison (used during optimization) number_compare_enum }; /*{xrst_code} {xrst_end val_graph_type} */ } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/var_type.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_VAR_TYPE_HPP # define CPPAD_LOCAL_VAL_GRAPH_VAR_TYPE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* ------------------------------------------------------------------------------- {xrst_begin type_var_op dev} Types of Variable Operators ########################### Prototype ********* {xrst_literal // BEGIN_TYPE_VAR_OP // END_TYPE_VAR_OP } var_op ****** is the variable operator. is_unary ******** If the return value for is_unary is true, this operator has one operand, one result, and is recorded by specifying the operator and variable address of the operand on the tape. is_binary ********* If the return value for is_binary is true, this operator has two operands, one result, and is recorded by specifying the operator and variable or parameter address for each operand. is_compare ********** If the return value for is_compare is true, this is a compare operator. It has two operands, no result, and is recorded by specifying the operator and variable or parameter address for each operand. {xrst_end type_var_op} */ namespace CppAD { namespace local { namespace val_graph { // BEGIN_TYPE_VAR_OP inline void type_var_op( op_code_var var_op , bool& is_unary , bool& is_binary , bool& is_compare ) // END_TYPE_VAR_OP { // switch(var_op) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // BEGIN_SORT_THIS_LINE_PLUS_1 case AFunOp: case BeginOp: case CExpOp: case CSumOp: case DisOp: case EndOp: case FunapOp: case FunavOp: case FunrpOp: case FunrvOp: case InvOp: case LdpOp: case LdvOp: case ParOp: case PriOp: case StppOp: case StpvOp: case StvpOp: case StvvOp: // END_SORT_THIS_LINE_MINUS_1 is_unary = false; is_binary = false; is_compare = false; break; // // unary operators // BEGIN_SORT_THIS_LINE_PLUS_1 case AbsOp: case AcosOp: case AcoshOp: case AsinOp: case AsinhOp: case AtanOp: case AtanhOp: case CosOp: case CoshOp: case ErfOp: case ErfcOp: case ExpOp: case Expm1Op: case Log1pOp: case LogOp: case NegOp: case SignOp: case SinOp: case SinhOp: case SqrtOp: case TanOp: case TanhOp: // END_SORT_THIS_LINE_MINUS_1 is_unary = true; is_binary = false; is_compare = false; break; // // binary operators // BEGIN_SORT_THIS_LINE_PLUS_1 case AddpvOp: case AddvvOp: case DivpvOp: case DivvpOp: case DivvvOp: case MulpvOp: case MulvvOp: case PowpvOp: case PowvpOp: case PowvvOp: case SubpvOp: case SubvpOp: case SubvvOp: case ZmulpvOp: case ZmulvpOp: case ZmulvvOp: // END_SORT_THIS_LINE_MINUS_1 is_unary = false; is_binary = true; is_compare = false; break; // // compare operators // BEGIN_SORT_THIS_LINE_PLUS_1 case local::EqppOp: case local::EqpvOp: case local::EqvvOp: case local::LeppOp: case local::LepvOp: case local::LevpOp: case local::LevvOp: case local::LtppOp: case local::LtpvOp: case local::LtvpOp: case local::LtvvOp: case local::NeppOp: case local::NepvOp: case local::NevvOp: // END_SORT_THIS_LINE_MINUS_1 is_unary = false; is_binary = false; is_compare = true; break; } return; } } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/val_graph/vector_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAL_GRAPH_VECTOR_OP_HPP # define CPPAD_LOCAL_VAL_GRAPH_VECTOR_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include // define CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL # include namespace CppAD { namespace local { namespace val_graph { /* {xrst_begin_parent val_vector_op dev} The Dynamic Vector Value Operators ################################## These classes implement dynamic vectors; i.e., vector were the indices may depend on the independent values. ind_vec_vec *********** The size of this vector is equal to the number of dynamic vectors. The size of each element of this vector is equal to the size of the corresponding dynamic vector plus one. It is a vector of value indices mapping dynamic vector indices to the index in the value vector for the current value of the dynamic vector. The last element for each dynamic vector is a flag. If it is nan, an index value for a store operation was nan. In this case, all the subsequent load operation results (for that vector) will be nan. In addition, if the index for a load operation is nan, the corresponding result will be nan. {xrst_toc_hidden val_graph/vec_xam.cpp } Operators ********* Only the following operators use the eval :ref:`val_base_op@eval@ind_vec_vec` argument: `:ref:val_vec_op-name` , `:ref:val_load_op-name` , `:ref:val_store_op-name` . Example ******* The file :ref:`com_xam.cpp ` is an example and test that uses this operator. {xrst_end val_vector_op} // --------------------------------------------------------------------------- {xrst_begin val_vec_op dev} Create A Dynamic Vector Operator ################################ Prototype ********* {xrst_literal // BEGIN_VEC_OP_T // END_VEC_OP_T } Context ******* It is derived from :ref:`val_base_op-name` . It overrides all its base class virtual member functions and is a concrete class (it has no pure virtual functions). get_instance ************ This static member function returns a pointer to a vec_op_t object. op_enum ******* This override of :ref:`val_base_op@op_enum` returns ``vec_op_enum`` . n_before ******** This override of :ref:`val_base_op@n_before` returns 1. {xrst_literal // BEGIN_VEC_ARG_BEFORE // END_VEC_ARG_BEFORE } n_after ******* This override of :ref:`val_base_op@n_after` returns 0. n_arg ***** This override of :ref:`val_base_op@n_arg` returns 1. n_res ***** This override of :ref:`val_base_op@n_res` return 0. eval **** This override of :ref:`val_base_op@eval` implement a create dynamic vector operation. trace ===== If trace is true, :ref:`val_print_vec_op-name` is called to print this operator. {xrst_end val_vec_op} */ // BEGIN_VEC_OP_T template class vec_op_t : public base_op_t { public: // n_before addr_t n_before(void) const override { return 1; } // // n_after addr_t n_after(void) const override { return 0; } // // get_instance static vec_op_t* get_instance(void) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; static vec_op_t instance; return &instance; } // op_enum op_enum_t op_enum(void) const override { return vec_op_enum; } // END_VEC_OP_T // // n_arg addr_t n_arg( addr_t arg_index , const Vector& arg_vec ) const override { return 1; } // // n_res addr_t n_res( addr_t arg_index , const Vector& arg_vec ) const override { return 0; } // // eval void eval( const tape_t* tape , bool trace , addr_t arg_index , addr_t res_index , Vector& val_vec , Vector< Vector >& ind_vec_vec , size_t& compare_false ) const override { // // arg_vec, vec_vec const Vector& arg_vec( tape->arg_vec() ); // // which_vector // BEGIN_VEC_ARG_BEFORE addr_t which_vector = arg_vec[arg_index + 0]; // END_VEC_ARG_BEFORE // // initial const Vector initial = tape->vec_initial()[which_vector]; # ifndef NDEBUG for(size_t i = 0; i < initial.size(); ++i) { CPPAD_ASSERT_KNOWN( initial[i] < res_index, "vec_op: initial indices must come before index of next result" ); } # endif // // ind_vec_vec CPPAD_ASSERT_UNKNOWN( ind_vec_vec.size() == size_t(which_vector) ); ind_vec_vec.push_back(initial); ind_vec_vec[which_vector].resize( initial.size() + 1 ); // // ind_vec_vec // Does not point to the nan in the tape which is at index tape->n_ind() CPPAD_ASSERT_UNKNOWN( 0 < tape->n_ind() ); ind_vec_vec[which_vector][ initial.size() ] = 0; // if( ! trace ) return; // // print_vec_op print_vec_op(which_vector, initial); } }; // --------------------------------------------------------------------------- /* {xrst_begin val_load_op dev} Dynamic Vector Load Operator ############################ Prototype ********* {xrst_literal // BEGIN_LOAD_OP_T // END_LOAD_OP_T } Context ******* It is derived from :ref:`val_base_op-name` . It overrides all its base class virtual member functions and is a concrete class (it has no pure virtual functions). get_instance ************ This static member function returns a pointer to a load_op_t object. op_enum ******* This override of :ref:`val_base_op@op_enum` returns ``load_op_enum`` . n_before ******** This override of :ref:`val_base_op@n_before` returns 1. {xrst_literal // BEGIN_LOAD_ARG_BEFORE // END_LOAD_ARG_BEFORE } n_after ******* This override of :ref:`val_base_op@n_after` returns 0. n_arg ***** This override of :ref:`val_base_op@n_arg` returns 2. n_res ***** This override of :ref:`val_base_op@n_res` return 1. eval **** This override of :ref:`val_base_op@eval` implement a dynamic vector load operation. which_vector ============ We use *which_vector* to denote the index corresponding to this dynamic vector; i.e. arg_vec[arg_index + 0]. vector_index ============ We use *vector_index* to denote the index, in the value vector, of the index in the dynamic vector, of the element for this load; i.e. arg_vec[arg_index + 1]. trace ===== If trace is true, :ref:`val_print_load_op-name` is called to print this operator. {xrst_end val_load_op} */ // BEGIN_LOAD_OP_T template class load_op_t : public base_op_t { public: // n_before addr_t n_before(void) const override { return 1; } // // n_after addr_t n_after(void) const override { return 0; } // // get_instance static load_op_t* get_instance(void) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; static load_op_t instance; return &instance; } // op_enum op_enum_t op_enum(void) const override { return load_op_enum; } // END_LOAD_OP_T // // n_arg addr_t n_arg( addr_t arg_index , const Vector& arg_vec ) const override { return 2; } // // n_res addr_t n_res( addr_t arg_index , const Vector& arg_vec ) const override { return 1; } // // eval void eval( const tape_t* tape , bool trace , addr_t arg_index , addr_t res_index , Vector& val_vec , Vector< Vector >& ind_vec_vec , size_t& compare_false ) const override { // // arg_vec, vec_vec const Vector& arg_vec( tape->arg_vec() ); // // this_vector // BEGIN_LOAD_ARG_BEFORE addr_t which_vector = arg_vec[arg_index + 0]; Vector& this_vector = ind_vec_vec[which_vector]; // END_LOAD_ARG_BEFORE // // vector_index addr_t vector_index = arg_vec[arg_index + 1]; // // index Value index = val_vec[vector_index]; // // val_vec addr_t flag = this_vector[ this_vector.size() - 1 ]; if( flag == tape->n_ind() || CppAD::isnan(index) ) { val_vec[res_index] = val_vec[ tape->n_ind() ]; CPPAD_ASSERT_UNKNOWN( CppAD::isnan( val_vec[res_index] ) ); } else { // // dynamic_index addr_t dynamic_index = addr_t( Integer(index) ); CPPAD_ASSERT_KNOWN( size_t(dynamic_index) + 1 < this_vector.size(), "dynamic vector index is greater than or equal vector size" ); // // val_vec val_vec[res_index] = val_vec[ this_vector[dynamic_index] ]; } // // trace if( ! trace ) return; // // print_load_op Value res_value = val_vec[res_index]; print_load_op(which_vector, vector_index, res_index, res_value); } }; // --------------------------------------------------------------------------- /* {xrst_begin val_store_op dev} Dynamic Vector Store Operator ############################# Prototype ********* {xrst_literal // BEGIN_STORE_OP_T // END_STORE_OP_T } Context ******* It is derived from :ref:`val_base_op-name` . It overrides all its base class virtual member functions and is a concrete class (it has no pure virtual functions). get_instance ************ This static member function returns a pointer to a store_op_t object. op_enum ******* This override of :ref:`val_base_op@op_enum` returns ``store_op_enum`` . n_before ******** This override of :ref:`val_base_op@n_before` returns 1. {xrst_literal // BEGIN_STORE_ARG_BEFORE // END_STORE_ARG_BEFORE } n_after ******* This override of :ref:`val_base_op@n_after` returns 0. n_arg ***** This override of :ref:`val_base_op@n_arg` returns 3. n_res ***** This override of :ref:`val_base_op@n_res` return 0. eval **** This override of :ref:`val_base_op@eval` implement a dynamic vector store operation. which_vector ============ We use *which_vector* to denote the index corresponding to this dynamic vector; i.e. arg_vec[arg_index + 0]. vector_index ============ We use *vector_index* to denote the index, in the value vector, of the index in the dynamic vector, of the element for this store; i.e. arg_vec[arg_index + 1]. value_index =========== We use *value_index* to denote the index, in the val_vec vector, of the element for this store; i.e. arg_vec[arg_index + 2]. trace ===== If trace is true, :ref:`val_print_store_op-name` is called to print this operator. {xrst_end val_store_op} */ // BEGIN_STORE_OP_T template class store_op_t : public base_op_t { public: // n_before addr_t n_before(void) const override { return 1; } // // n_after addr_t n_after(void) const override { return 0; } // // get_instance static store_op_t* get_instance(void) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; static store_op_t instance; return &instance; } // op_enum op_enum_t op_enum(void) const override { return store_op_enum; } // END_STORE_OP_T // // n_arg addr_t n_arg( addr_t arg_index , const Vector& arg_vec ) const override { return 3; } // // n_res addr_t n_res( addr_t arg_index , const Vector& arg_vec ) const override { return 0; } // // eval void eval( const tape_t* tape , bool trace , addr_t arg_index , addr_t res_index , Vector& val_vec , Vector< Vector >& ind_vec_vec , size_t& compare_false ) const override { // // arg_vec, vec_vec const Vector& arg_vec( tape->arg_vec() ); // // this_vector // BEGIN_STORE_ARG_BEFORE addr_t which_vector = arg_vec[arg_index + 0]; Vector& this_vector = ind_vec_vec[which_vector]; // END_STORE_ARG_BEFORE // // vector_index, value_index addr_t vector_index = arg_vec[arg_index + 1]; addr_t value_index = arg_vec[arg_index + 2]; // // index Value index = val_vec[vector_index]; if( CppAD::isnan(index) ) { // set flag for this vector this_vector[ this_vector.size() - 1 ] = tape->n_ind(); } else { // dynamic_index addr_t dynamic_index = addr_t( Integer(index) ); CPPAD_ASSERT_KNOWN( size_t(dynamic_index) + 1 < this_vector.size(), "dynamic vector index is greater than or equal vector size" ); // // val_vec_vec this_vector[dynamic_index] = value_index; } // // trace if( ! trace ) return; // // print_store_op print_store_op(which_vector, vector_index, value_index); } }; } } } // END_CPPAD_LOCAL_VAL_GRAPH_NAMESPACE # endif ================================================ FILE: include/cppad/local/var_op/abs_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_ABS_OP_HPP # define CPPAD_LOCAL_VAR_OP_ABS_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // See dev documentation: forward_unary_op template inline void abs_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AbsOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AbsOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; for(size_t j = p; j <= q; j++) z[j] = sign(x[0]) * x[j]; } // See dev documentation: forward_unary_op template inline void abs_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AbsOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AbsOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + i_x * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; size_t m = (q-1) * r + 1; for(size_t ell = 0; ell < r; ell++) z[m + ell] = sign(x[0]) * x[m + ell]; } // See dev documentation: forward_unary_op template inline void abs_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AbsOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AbsOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); // Taylor coefficients corresponding to argument and result Base x0 = *(taylor + i_x * cap_order); Base* z = taylor + i_z * cap_order; z[0] = fabs(x0); } // See dev documentation: reverse_unary_op template inline void abs_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // // // i_x size_t i_x = size_t(arg[0]); // // size_t j; // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AbsOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AbsOp) == 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Taylor coefficients and partials corresponding to argument const Base* x = taylor + i_x * cap_order; Base* px = partial + i_x * n_order; // Taylor coefficients and partials corresponding to result Base* pz = partial + i_z * n_order; // do not need azmul because sign is either +1, -1, or zero for(j = 0; j < n_order; j++) px[j] += sign(x[0]) * pz[j]; } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/acos_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_ACOS_OP_HPP # define CPPAD_LOCAL_VAR_OP_ACOS_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // See dev documentation: forward_unary_op template inline void acos_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AcosOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AcosOp) == 2 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; Base* b = z - cap_order; // called y in documentation size_t k; Base uj; if( p == 0 ) { z[0] = acos( x[0] ); uj = Base(1.0) - x[0] * x[0]; b[0] = sqrt( uj ); p++; } for(size_t j = p; j <= q; j++) { uj = Base(0.0); for(k = 0; k <= j; k++) uj -= x[k] * x[j-k]; b[j] = Base(0.0); z[j] = Base(0.0); for(k = 1; k < j; k++) { b[j] -= Base(double(k)) * b[k] * b[j-k]; z[j] -= Base(double(k)) * z[k] * b[j-k]; } b[j] /= Base(double(j)); z[j] /= Base(double(j)); // b[j] += uj / Base(2.0); z[j] -= x[j]; // b[j] /= b[0]; z[j] /= b[0]; } } // See dev documentation: forward_unary_op template inline void acos_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AcosOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AcosOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + i_x * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; Base* b = z - num_taylor_per_var; // called y in documentation size_t k, ell; size_t m = (q-1) * r + 1; for(ell = 0; ell < r; ell ++) { Base uq = - 2.0 * x[m + ell] * x[0]; for(k = 1; k < q; k++) uq -= x[(k-1)*r+1+ell] * x[(q-k-1)*r+1+ell]; b[m+ell] = Base(0.0); z[m+ell] = Base(0.0); for(k = 1; k < q; k++) { b[m+ell] += Base(double(k)) * b[(k-1)*r+1+ell] * b[(q-k-1)*r+1+ell]; z[m+ell] += Base(double(k)) * z[(k-1)*r+1+ell] * b[(q-k-1)*r+1+ell]; } b[m+ell] = ( uq / Base(2.0) - b[m+ell] / Base(double(q)) ) / b[0]; z[m+ell] = -( x[m+ell] + z[m+ell] / Base(double(q)) ) / b[0]; } } // See dev documentation: forward_unary_op template inline void acos_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AcosOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AcosOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; Base* b = z - cap_order; // called y in documentation z[0] = acos( x[0] ); b[0] = sqrt( Base(1.0) - x[0] * x[0] ); } // See dev documentation: reverse_unary_op template inline void acos_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // // // i_x size_t i_x = size_t(arg[0]); // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AcosOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AcosOp) == 2 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Taylor coefficients and partials corresponding to argument const Base* x = taylor + i_x * cap_order; Base* px = partial + i_x * n_order; // Taylor coefficients and partials corresponding to first result const Base* z = taylor + i_z * cap_order; Base* pz = partial + i_z * n_order; // Taylor coefficients and partials corresponding to auxiliary result const Base* b = z - cap_order; // called y in documentation Base* pb = pz - n_order; Base inv_b0 = Base(1.0) / b[0]; // number of indices to access size_t j = d; size_t k; while(j) { // scale partials w.r.t b[j] by 1 / b[0] pb[j] = azmul(pb[j], inv_b0); // scale partials w.r.t z[j] by 1 / b[0] pz[j] = azmul(pz[j], inv_b0); // update partials w.r.t b^0 pb[0] -= azmul(pz[j], z[j]) + azmul(pb[j], b[j]); // update partial w.r.t. x^0 px[0] -= azmul(pb[j], x[j]); // update partial w.r.t. x^j px[j] -= pz[j] + azmul(pb[j], x[0]); // further scale partial w.r.t. z[j] by 1 / j pz[j] /= Base(double(j)); for(k = 1; k < j; k++) { // update partials w.r.t b^(j-k) pb[j-k] -= Base(double(k)) * azmul(pz[j], z[k]) + azmul(pb[j], b[k]); // update partials w.r.t. x^k px[k] -= azmul(pb[j], x[j-k]); // update partials w.r.t. z^k pz[k] -= Base(double(k)) * azmul(pz[j], b[j-k]); } --j; } // j == 0 case px[0] -= azmul( pz[0] + azmul(pb[0], x[0]), inv_b0); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/acosh_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_ACOSH_OP_HPP # define CPPAD_LOCAL_VAR_OP_ACOSH_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // See dev documentation: forward_unary_op template inline void acosh_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AcoshOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AcoshOp) == 2 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; Base* b = z - cap_order; // called y in documentation size_t k; Base uj; if( p == 0 ) { z[0] = acosh( x[0] ); uj = x[0] * x[0] - Base(1.0); b[0] = sqrt( uj ); p++; } for(size_t j = p; j <= q; j++) { uj = Base(0.0); for(k = 0; k <= j; k++) uj += x[k] * x[j-k]; b[j] = Base(0.0); z[j] = Base(0.0); for(k = 1; k < j; k++) { b[j] -= Base(double(k)) * b[k] * b[j-k]; z[j] -= Base(double(k)) * z[k] * b[j-k]; } b[j] /= Base(double(j)); z[j] /= Base(double(j)); // b[j] += uj / Base(2.0); z[j] += x[j]; // b[j] /= b[0]; z[j] /= b[0]; } } // See dev documentation: forward_unary_op template inline void acosh_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AcoshOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AcoshOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + i_x * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; Base* b = z - num_taylor_per_var; // called y in documentation size_t k, ell; size_t m = (q-1) * r + 1; for(ell = 0; ell < r; ell ++) { Base uq = 2.0 * x[m + ell] * x[0]; for(k = 1; k < q; k++) uq += x[(k-1)*r+1+ell] * x[(q-k-1)*r+1+ell]; b[m+ell] = Base(0.0); z[m+ell] = Base(0.0); for(k = 1; k < q; k++) { b[m+ell] += Base(double(k)) * b[(k-1)*r+1+ell] * b[(q-k-1)*r+1+ell]; z[m+ell] += Base(double(k)) * z[(k-1)*r+1+ell] * b[(q-k-1)*r+1+ell]; } b[m+ell] = ( uq / Base(2.0) - b[m+ell] / Base(double(q)) ) / b[0]; z[m+ell] = ( x[m+ell] - z[m+ell] / Base(double(q)) ) / b[0]; } } // See dev documentation: forward_unary_op template inline void acosh_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AcoshOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AcoshOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; Base* b = z - cap_order; // called y in documentation z[0] = acosh( x[0] ); b[0] = sqrt( x[0] * x[0] - Base(1.0) ); } // See dev documentation: reverse_unary_op template inline void acosh_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // // // i_x size_t i_x = size_t(arg[0]); // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AcoshOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AcoshOp) == 2 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Taylor coefficients and partials corresponding to argument const Base* x = taylor + i_x * cap_order; Base* px = partial + i_x * n_order; // Taylor coefficients and partials corresponding to first result const Base* z = taylor + i_z * cap_order; Base* pz = partial + i_z * n_order; // Taylor coefficients and partials corresponding to auxiliary result const Base* b = z - cap_order; // called y in documentation Base* pb = pz - n_order; Base inv_b0 = Base(1.0) / b[0]; // number of indices to access size_t j = d; size_t k; while(j) { // scale partials w.r.t b[j] by 1 / b[0] pb[j] = azmul(pb[j], inv_b0); // scale partials w.r.t z[j] by 1 / b[0] pz[j] = azmul(pz[j], inv_b0); // update partials w.r.t b^0 pb[0] -= azmul(pz[j], z[j]) + azmul(pb[j], b[j]); // update partial w.r.t. x^0 px[0] += azmul(pb[j], x[j]); // update partial w.r.t. x^j px[j] += pz[j] + azmul(pb[j], x[0]); // further scale partial w.r.t. z[j] by 1 / j pz[j] /= Base(double(j)); for(k = 1; k < j; k++) { // update partials w.r.t b^(j-k) pb[j-k] -= Base(double(k)) * azmul(pz[j], z[k]) + azmul(pb[j], b[k]); // update partials w.r.t. x^k px[k] += azmul(pb[j], x[j-k]); // update partials w.r.t. z^k pz[k] -= Base(double(k)) * azmul(pz[j], b[j-k]); } --j; } // j == 0 case px[0] += azmul(pz[0] + azmul(pb[0], x[0]), inv_b0); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/add_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_ADD_OP_HPP # define CPPAD_LOCAL_VAR_OP_ADD_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // --------------------------- Addvv ----------------------------------------- // See dev documentation: forward_unary_op // See dev documentation: forward_binary_op template inline void addvv_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AddvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(AddvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to arguments and result Base* x = taylor + size_t(arg[0]) * cap_order; Base* y = taylor + size_t(arg[1]) * cap_order; Base* z = taylor + i_z * cap_order; for(size_t j = p; j <= q; j++) z[j] = x[j] + y[j]; } // See dev documentation: forward_unary_op // See dev documentation: forward_binary_op template inline void addvv_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AddvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(AddvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( 0 < q ); // Taylor coefficients corresponding to arguments and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + size_t(arg[0]) * num_taylor_per_var; Base* y = taylor + size_t(arg[1]) * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; size_t m = (q-1)*r + 1 ; for(size_t ell = 0; ell < r; ell++) z[m+ell] = x[m+ell] + y[m+ell]; } // See dev documentation: forward_unary_op // See dev documentation: forward_binary_op template inline void addvv_forward_0( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AddvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(AddvvOp) == 1 ); // Taylor coefficients corresponding to arguments and result Base* x = taylor + size_t(arg[0]) * cap_order; Base* y = taylor + size_t(arg[1]) * cap_order; Base* z = taylor + i_z * cap_order; z[0] = x[0] + y[0]; } // See dev documentation: reverse_unary_op // See dev documentation: reverse_binary_op template inline void addvv_reverse( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AddvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(AddvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Partial derivatives corresponding to arguments and result Base* px = partial + size_t(arg[0]) * n_order; Base* py = partial + size_t(arg[1]) * n_order; Base* pz = partial + i_z * n_order; // number of indices to access size_t i = d + 1; while(i) { --i; px[i] += pz[i]; py[i] += pz[i]; } } // --------------------------- Addpv ----------------------------------------- // See dev documentation: forward_unary_op // See dev documentation: forward_binary_op template inline void addpv_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AddpvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(AddpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to arguments and result Base* y = taylor + size_t(arg[1]) * cap_order; Base* z = taylor + i_z * cap_order; if( p == 0 ) { // Paraemter value Base x = parameter[ arg[0] ]; z[0] = x + y[0]; p++; } for(size_t j = p; j <= q; j++) z[j] = y[j]; } // See dev documentation: forward_unary_op // See dev documentation: forward_binary_op template inline void addpv_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AddpvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(AddpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to arguments and result size_t num_taylor_per_var = (cap_order-1) * r + 1; size_t m = (q-1) * r + 1; Base* y = taylor + size_t(arg[1]) * num_taylor_per_var + m; Base* z = taylor + i_z * num_taylor_per_var + m; for(size_t ell = 0; ell < r; ell++) z[ell] = y[ell]; } // See dev documentation: forward_unary_op // See dev documentation: forward_binary_op template inline void addpv_forward_0( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AddpvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(AddpvOp) == 1 ); // Paraemter value Base x = parameter[ arg[0] ]; // Taylor coefficients corresponding to arguments and result Base* y = taylor + size_t(arg[1]) * cap_order; Base* z = taylor + i_z * cap_order; z[0] = x + y[0]; } // See dev documentation: reverse_unary_op // See dev documentation: reverse_binary_op template inline void addpv_reverse( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AddvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(AddvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Partial derivatives corresponding to arguments and result Base* py = partial + size_t(arg[1]) * n_order; Base* pz = partial + i_z * n_order; // number of indices to access size_t i = d + 1; while(i) { --i; py[i] += pz[i]; } } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/asin_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_ASIN_OP_HPP # define CPPAD_LOCAL_VAR_OP_ASIN_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // See dev documentation: forward_unary_op template inline void asin_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AsinOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AsinOp) == 2 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; Base* b = z - cap_order; // called y in documentation size_t k; Base uj; if( p == 0 ) { z[0] = asin( x[0] ); uj = Base(1.0) - x[0] * x[0]; b[0] = sqrt( uj ); p++; } for(size_t j = p; j <= q; j++) { uj = Base(0.0); for(k = 0; k <= j; k++) uj -= x[k] * x[j-k]; b[j] = Base(0.0); z[j] = Base(0.0); for(k = 1; k < j; k++) { b[j] -= Base(double(k)) * b[k] * b[j-k]; z[j] -= Base(double(k)) * z[k] * b[j-k]; } b[j] /= Base(double(j)); z[j] /= Base(double(j)); // b[j] += uj / Base(2.0); z[j] += x[j]; // b[j] /= b[0]; z[j] /= b[0]; } } // See dev documentation: forward_unary_op template inline void asin_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AcosOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AcosOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + i_x * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; Base* b = z - num_taylor_per_var; // called y in documentation size_t k, ell; size_t m = (q-1) * r + 1; for(ell = 0; ell < r; ell ++) { Base uq = - 2.0 * x[m + ell] * x[0]; for(k = 1; k < q; k++) uq -= x[(k-1)*r+1+ell] * x[(q-k-1)*r+1+ell]; b[m+ell] = Base(0.0); z[m+ell] = Base(0.0); for(k = 1; k < q; k++) { b[m+ell] += Base(double(k)) * b[(k-1)*r+1+ell] * b[(q-k-1)*r+1+ell]; z[m+ell] += Base(double(k)) * z[(k-1)*r+1+ell] * b[(q-k-1)*r+1+ell]; } b[m+ell] = ( uq / Base(2.0) - b[m+ell] / Base(double(q)) ) / b[0]; z[m+ell] = ( x[m+ell] - z[m+ell] / Base(double(q)) ) / b[0]; } } // See dev documentation: forward_unary_op template inline void asin_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AsinOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AsinOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; Base* b = z - cap_order; // called y in documentation z[0] = asin( x[0] ); b[0] = sqrt( Base(1.0) - x[0] * x[0] ); } // See dev documentation: reverse_unary_op template inline void asin_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // // // i_x size_t i_x = size_t(arg[0]); // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AsinOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AsinOp) == 2 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Taylor coefficients and partials corresponding to argument const Base* x = taylor + i_x * cap_order; Base* px = partial + i_x * n_order; // Taylor coefficients and partials corresponding to first result const Base* z = taylor + i_z * cap_order; Base* pz = partial + i_z * n_order; // Taylor coefficients and partials corresponding to auxiliary result const Base* b = z - cap_order; // called y in documentation Base* pb = pz - n_order; Base inv_b0 = Base(1.0) / b[0]; // number of indices to access size_t j = d; size_t k; while(j) { // scale partials w.r.t b[j] by 1 / b[0] pb[j] = azmul(pb[j], inv_b0); // scale partials w.r.t z[j] by 1 / b[0] pz[j] = azmul(pz[j], inv_b0); // update partials w.r.t b^0 pb[0] -= azmul(pz[j], z[j]) + azmul(pb[j], b[j]); // update partial w.r.t. x^0 px[0] -= azmul(pb[j], x[j]); // update partial w.r.t. x^j px[j] += pz[j] - azmul(pb[j], x[0]); // further scale partial w.r.t. z[j] by 1 / j pz[j] /= Base(double(j)); for(k = 1; k < j; k++) { // update partials w.r.t b^(j-k) pb[j-k] -= Base(double(k)) * azmul(pz[j], z[k]) + azmul(pb[j], b[k]); // update partials w.r.t. x^k px[k] -= azmul(pb[j], x[j-k]); // update partials w.r.t. z^k pz[k] -= Base(double(k)) * azmul(pz[j], b[j-k]); } --j; } // j == 0 case px[0] += azmul(pz[0] - azmul(pb[0], x[0]), inv_b0); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/asinh_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_ASINH_OP_HPP # define CPPAD_LOCAL_VAR_OP_ASINH_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // See dev documentation: forward_unary_op template inline void asinh_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AsinhOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AsinhOp) == 2 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; Base* b = z - cap_order; // called y in documentation size_t k; Base uj; if( p == 0 ) { z[0] = asinh( x[0] ); uj = Base(1.0) + x[0] * x[0]; b[0] = sqrt( uj ); p++; } for(size_t j = p; j <= q; j++) { uj = Base(0.0); for(k = 0; k <= j; k++) uj += x[k] * x[j-k]; b[j] = Base(0.0); z[j] = Base(0.0); for(k = 1; k < j; k++) { b[j] -= Base(double(k)) * b[k] * b[j-k]; z[j] -= Base(double(k)) * z[k] * b[j-k]; } b[j] /= Base(double(j)); z[j] /= Base(double(j)); // b[j] += uj / Base(2.0); z[j] += x[j]; // b[j] /= b[0]; z[j] /= b[0]; } } // See dev documentation: forward_unary_op template inline void asinh_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AcosOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AcosOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + i_x * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; Base* b = z - num_taylor_per_var; // called y in documentation size_t k, ell; size_t m = (q-1) * r + 1; for(ell = 0; ell < r; ell ++) { Base uq = 2.0 * x[m + ell] * x[0]; for(k = 1; k < q; k++) uq += x[(k-1)*r+1+ell] * x[(q-k-1)*r+1+ell]; b[m+ell] = Base(0.0); z[m+ell] = Base(0.0); for(k = 1; k < q; k++) { b[m+ell] += Base(double(k)) * b[(k-1)*r+1+ell] * b[(q-k-1)*r+1+ell]; z[m+ell] += Base(double(k)) * z[(k-1)*r+1+ell] * b[(q-k-1)*r+1+ell]; } b[m+ell] = ( uq / Base(2.0) - b[m+ell] / Base(double(q)) ) / b[0]; z[m+ell] = ( x[m+ell] - z[m+ell] / Base(double(q)) ) / b[0]; } } // See dev documentation: forward_unary_op template inline void asinh_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AsinhOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AsinhOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; Base* b = z - cap_order; // called y in documentation z[0] = asinh( x[0] ); b[0] = sqrt( Base(1.0) + x[0] * x[0] ); } // See dev documentation: reverse_unary_op template inline void asinh_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // // // i_x size_t i_x = size_t(arg[0]); // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AsinhOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AsinhOp) == 2 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Taylor coefficients and partials corresponding to argument const Base* x = taylor + i_x * cap_order; Base* px = partial + i_x * n_order; // Taylor coefficients and partials corresponding to first result const Base* z = taylor + i_z * cap_order; Base* pz = partial + i_z * n_order; // Taylor coefficients and partials corresponding to auxiliary result const Base* b = z - cap_order; // called y in documentation Base* pb = pz - n_order; Base inv_b0 = Base(1.0) / b[0]; // number of indices to access size_t j = d; size_t k; while(j) { // scale partials w.r.t b[j] by 1 / b[0] pb[j] = azmul(pb[j], inv_b0); // scale partials w.r.t z[j] by 1 / b[0] pz[j] = azmul(pz[j], inv_b0); // update partials w.r.t b^0 pb[0] -= azmul(pz[j], z[j]) + azmul(pb[j], b[j]); // update partial w.r.t. x^0 px[0] += azmul(pb[j], x[j]); // update partial w.r.t. x^j px[j] += pz[j] + azmul(pb[j], x[0]); // further scale partial w.r.t. z[j] by 1 / j pz[j] /= Base(double(j)); for(k = 1; k < j; k++) { // update partials w.r.t b^(j-k) pb[j-k] -= Base(double(k)) * azmul(pz[j], z[k]) + azmul(pb[j], b[k]); // update partials w.r.t. x^k px[k] += azmul(pb[j], x[j-k]); // update partials w.r.t. z^k pz[k] -= Base(double(k)) * azmul(pz[j], b[j-k]); } --j; } // j == 0 case px[0] += azmul(pz[0] + azmul(pb[0], x[0]), inv_b0); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/atan_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_ATAN_OP_HPP # define CPPAD_LOCAL_VAR_OP_ATAN_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // See dev documentation: forward_unary_op template inline void atan_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AtanOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AtanOp) == 2 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; Base* b = z - cap_order; // called y in documentation size_t k; if( p == 0 ) { z[0] = atan( x[0] ); b[0] = Base(1.0) + x[0] * x[0]; p++; } for(size_t j = p; j <= q; j++) { b[j] = Base(2.0) * x[0] * x[j]; z[j] = Base(0.0); for(k = 1; k < j; k++) { b[j] += x[k] * x[j-k]; z[j] -= Base(double(k)) * z[k] * b[j-k]; } z[j] /= Base(double(j)); z[j] += x[j]; z[j] /= b[0]; } } // See dev documentation: forward_unary_op template inline void atan_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AtanOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AtanOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + i_x * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; Base* b = z - num_taylor_per_var; // called y in documentation size_t m = (q-1) * r + 1; for(size_t ell = 0; ell < r; ell++) { b[m+ell] = Base(2.0) * x[m+ell] * x[0]; z[m+ell] = Base(double(q)) * x[m+ell]; for(size_t k = 1; k < q; k++) { b[m+ell] += x[(k-1)*r+1+ell] * x[(q-k-1)*r+1+ell]; z[m+ell] -= Base(double(k)) * z[(k-1)*r+1+ell] * b[(q-k-1)*r+1+ell]; } z[m+ell] /= ( Base(double(q)) * b[0] ); } } // See dev documentation: forward_unary_op template inline void atan_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AtanOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AtanOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; Base* b = z - cap_order; // called y in documentation z[0] = atan( x[0] ); b[0] = Base(1.0) + x[0] * x[0]; } // See dev documentation: reverse_unary_op template inline void atan_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // // // i_x size_t i_x = size_t(arg[0]); // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AtanOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AtanOp) == 2 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Taylor coefficients and partials corresponding to argument const Base* x = taylor + i_x * cap_order; Base* px = partial + i_x * n_order; // Taylor coefficients and partials corresponding to first result const Base* z = taylor + i_z * cap_order; Base* pz = partial + i_z * n_order; // Taylor coefficients and partials corresponding to auxiliary result const Base* b = z - cap_order; // called y in documentation Base* pb = pz - n_order; Base inv_b0 = Base(1.0) / b[0]; // number of indices to access size_t j = d; size_t k; while(j) { // scale partials w.r.t z[j] and b[j] pz[j] = azmul(pz[j], inv_b0); pb[j] *= Base(2.0); pb[0] -= azmul(pz[j], z[j]); px[j] += pz[j] + azmul(pb[j], x[0]); px[0] += azmul(pb[j], x[j]); // more scaling of partials w.r.t z[j] pz[j] /= Base(double(j)); for(k = 1; k < j; k++) { pb[j-k] -= Base(double(k)) * azmul(pz[j], z[k]); pz[k] -= Base(double(k)) * azmul(pz[j], b[j-k]); px[k] += azmul(pb[j], x[j-k]); } --j; } px[0] += azmul(pz[0], inv_b0) + Base(2.0) * azmul(pb[0], x[0]); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/atanh_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_ATANH_OP_HPP # define CPPAD_LOCAL_VAR_OP_ATANH_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // See dev documentation: forward_unary_op template inline void atanh_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AtanhOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AtanhOp) == 2 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; Base* b = z - cap_order; // called y in documentation size_t k; if( p == 0 ) { z[0] = atanh( x[0] ); b[0] = Base(1.0) - x[0] * x[0]; p++; } for(size_t j = p; j <= q; j++) { b[j] = - Base(2.0) * x[0] * x[j]; z[j] = Base(0.0); for(k = 1; k < j; k++) { b[j] -= x[k] * x[j-k]; z[j] -= Base(double(k)) * z[k] * b[j-k]; } z[j] /= Base(double(j)); z[j] += x[j]; z[j] /= b[0]; } } // See dev documentation: forward_unary_op template inline void atanh_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AtanhOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AtanhOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + i_x * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; Base* b = z - num_taylor_per_var; // called y in documentation size_t m = (q-1) * r + 1; for(size_t ell = 0; ell < r; ell++) { b[m+ell] = - Base(2.0) * x[m+ell] * x[0]; z[m+ell] = Base(double(q)) * x[m+ell]; for(size_t k = 1; k < q; k++) { b[m+ell] -= x[(k-1)*r+1+ell] * x[(q-k-1)*r+1+ell]; z[m+ell] -= Base(double(k)) * z[(k-1)*r+1+ell] * b[(q-k-1)*r+1+ell]; } z[m+ell] /= ( Base(double(q)) * b[0] ); } } // See dev documentation: forward_unary_op template inline void atanh_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AtanhOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AtanhOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; Base* b = z - cap_order; // called y in documentation z[0] = atanh( x[0] ); b[0] = Base(1.0) - x[0] * x[0]; } // See dev documentation: reverse_unary_op template inline void atanh_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // // // i_x size_t i_x = size_t(arg[0]); // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(AtanhOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(AtanhOp) == 2 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Taylor coefficients and partials corresponding to argument const Base* x = taylor + i_x * cap_order; Base* px = partial + i_x * n_order; // Taylor coefficients and partials corresponding to first result const Base* z = taylor + i_z * cap_order; Base* pz = partial + i_z * n_order; // Taylor coefficients and partials corresponding to auxiliary result const Base* b = z - cap_order; // called y in documentation Base* pb = pz - n_order; Base inv_b0 = Base(1.0) / b[0]; // number of indices to access size_t j = d; size_t k; while(j) { // scale partials w.r.t z[j] and b[j] pz[j] = azmul(pz[j], inv_b0); pb[j] *= Base(2.0); pb[0] -= azmul(pz[j], z[j]); px[j] += pz[j] - azmul(pb[j], x[0]); px[0] -= azmul(pb[j], x[j]); // more scaling of partials w.r.t z[j] pz[j] /= Base(double(j)); for(k = 1; k < j; k++) { pb[j-k] -= Base(double(k)) * azmul(pz[j], z[k]); pz[k] -= Base(double(k)) * azmul(pz[j], b[j-k]); px[k] -= azmul(pb[j], x[j-k]); } --j; } px[0] += azmul(pz[0], inv_b0) - Base(2.0) * azmul(pb[0], x[0]); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/atomic_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_ATOMIC_OP_HPP # define CPPAD_LOCAL_VAR_OP_ATOMIC_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN_CPPAD_LOCAL_VAR_OP_NAMESPACE namespace CppAD { namespace local { namespace var_op { // sweep_type enum sweep_type { forward_op_sweep, forward_dir_sweep, reverse_op_sweep, for_jac_sweep, rev_jac_sweep, for_hes_sweep, rev_hes_sweep }; // atomic_op_work template struct atomic_op_work { // parameter_x, taylor_x, type_x, taylor_y, index_y, variable_y CppAD::vector parameter_x; CppAD::vector type_x; // CppAD::vector taylor_x; CppAD::vector taylor_y; // CppAD::vector partial_x; CppAD::vector partial_y; // CppAD::vector index_x; CppAD::vector index_y; // CppAD::vector variable_x; CppAD::vector variable_y; // // resize void resize(size_t m, size_t n, size_t n_order, sweep_type sweep) { // // parameter_x, type_x parameter_x.resize(n); type_x.resize(n); // switch( sweep ) { default: CPPAD_ASSERT_UNKNOWN(false); break; // ------------------------------------------------------------------ // forward_op_sweep case forward_op_sweep: taylor_x.resize(n * n_order); taylor_y.resize(m * n_order); // partial_x.resize(0); partial_y.resize(0); // index_x.resize(0); index_y.resize(m); // variable_x.resize(0); variable_y.resize(m); break; // ------------------------------------------------------------------ // forward_dir_sweep case forward_dir_sweep: taylor_x.resize(n * n_order); taylor_y.resize(m * n_order); // partial_x.resize(0); partial_y.resize(0); // index_x.resize(n); index_y.resize(m); // variable_x.resize(0); variable_y.resize(m); break; // ------------------------------------------------------------------ // reverse_op_sweep case reverse_op_sweep: taylor_x.resize(n * n_order); taylor_y.resize(m * n_order); // partial_x.resize(n * n_order); partial_y.resize(m * n_order); // index_x.resize(n); index_y.resize(0); // variable_x.resize(n); variable_y.resize(0); break; // ------------------------------------------------------------------ // for_jac_sweep, rev_jac_sweep // for_hes_sweep, rev_hes_sweep case for_jac_sweep: case rev_jac_sweep: case for_hes_sweep: case rev_hes_sweep: taylor_x.resize(0); taylor_y.resize(0); // partial_x.resize(0); partial_y.resize(0); // index_x.resize(n); index_y.resize(m); // variable_x.resize(0); variable_y.resize(0); break; } } }; /* ------------------------------------------------------------------------------ {xrst_begin_parent var_atomic_op dev} {xrst_spell funap funav funrp funrv } Atomic Function Call Operators ############################## AFunOp ****** This operator appears at the start and end of every atomic function call. It has four arguments and no results. arg[0] ====== This is the :ref:`atomic_index-name` for this function. arg[1] ====== If this is an atomic_four call, arg[1] is the corresponding :ref:`atomic_four_call@call_id` . If this is an atomic_one call (which has been deprecated) arg[1] is corresponding :ref:`atomic_one@id` arg[2], n ========= is the number of arguments to this atomic function call. We use the notation *n* = *arg* [2] . arg[3], m ========= is the number of results returned by this atomic function call. We use the notation *m* = *arg* [3] . FunapOp, FunavOp **************** #. There are *n* of these operators directly after the AFunOp at the start of a function call, one for each argument to the function call. #. Each of these operators has one argument and no result. #. If the *j*-th argument in the function call is a parameter (variable) the corresponding operator is FunapOp ( FunavOp ). #. The operator argument for FunapOp ( FunavOp ) is the parameter index (variable index) for the corresponding argument in the function call. FunrpOp, FunrvOp **************** #. There are *m* of these operators directly after the FunapOp or FunavOp operators for a function call, one for each result returned by the function call. #. If the *i*-th result is a parameter (variable) the corresponding operator is FunrpOp ( FunrvOp ). #. The FunrpOp operator has one argument and no result (because the corresponding function result is not a variable). The operator argument is the parameter index for the corresponding result of the function call. #. The FunrvOp operator has no arguments and one variable result. The new variable, created by this operator, gets its values from the corresponding result of the function. {xrst_end var_atomic_op} ------------------------------------------------------------------------------- {xrst_begin var_atomic_forward_any dev} Any Order Forward Atomic Function Call ###################################### Prototype ********* {xrst_literal // BEGIN_ATOMIC_FORWARD_ANY // END_ATOMIC_FORWARD_ANY } {xrst_template ; include/cppad/local/var_op/template/atomic_op.xrst headers: n_res, i_z, itr, play, parameter, trace, work @mode@ ; forward } {xrst_template ; include/cppad/local/var_op/template/forward_op.xrst headers: cap_order, order_low, order_up, taylor } {xrst_end var_atomic_forward_any} */ // BEGIN_ATOMIC_FORWARD_ANY template void atomic_forward_any( play::const_sequential_iterator& itr , const player* play , const Base* parameter , bool trace , atomic_op_work& work , size_t cap_order , size_t order_low , size_t order_up , Base* taylor ) // END_ATOMIC_FORWARD_ANY { // // vector using CppAD::vector; // // par_is_dyn const pod_vector& par_is_dyn( play->par_is_dyn() ); // // n_order size_t n_order = order_up + 1; // // op_code, i_var, arg op_code_var op_code; size_t i_var; const addr_t* arg; itr.op_info(op_code, arg, i_var); CPPAD_ASSERT_UNKNOWN( op_code == AFunOp ); CPPAD_ASSERT_NARG_NRES(op_code, 4, 0); // if( trace ) { printOp( std::cout, play, itr.op_index(), i_var, op_code, arg ); std::cout << std::endl; } // // atom_index, call_id, m, n size_t atom_index, call_id, m, n; play::atom_op_info(op_code, arg, atom_index, call_id, m, n); // // parameter_x, type_x, taylor_x, taylor_y, index_y, variable_y work.resize(m, n, n_order, forward_op_sweep); vector& parameter_x( work.parameter_x ); vector& type_x( work.type_x ); // vector& taylor_x( work.taylor_x ); vector& taylor_y( work.taylor_y ); // vector& index_y( work.index_y ); vector& variable_y( work.variable_y ); // // j for(size_t j = 0; j < n; ++j) { // // op_code, arg, i_var (++itr).op_info(op_code, arg, i_var); if( trace ) { printOp( std::cout, play, itr.op_index(), i_var, op_code, arg ); std::cout << std::endl; } // // type_x, parameter_x, taylor_x switch(op_code) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // // FunapOp case FunapOp: CPPAD_ASSERT_NARG_NRES(op_code, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < play->num_par_all() ); if( par_is_dyn[ arg[0] ] ) type_x[j] = dynamic_enum; else type_x[j] = constant_enum; parameter_x[j] = parameter[ arg[0] ]; taylor_x[j * n_order + 0] = parameter[ arg[0] ]; for(size_t k = 1; k < n_order; ++k) taylor_x[j * n_order + k] = Base(0.0); break; // // FunavOp case FunavOp: CPPAD_ASSERT_NARG_NRES(op_code, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < play->num_var() ); type_x[j] = variable_enum; parameter_x[j] = CppAD::numeric_limits::quiet_NaN(); for(size_t k = 0; k < n_order; ++k) taylor_x[j * n_order + k] = taylor[ size_t(arg[0]) * cap_order + k ]; break; } } // // index_y, variable_y for(size_t i = 0; i < m; ++i) { // // op_code, arg, i_var (++itr).op_info(op_code, arg, i_var); // // switch(op_code) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // // FunrpOp case FunrpOp: CPPAD_ASSERT_NARG_NRES(op_code, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < play->num_par_all() ); index_y[i] = std::numeric_limits::max(); variable_y[i] = false; if( 0 < order_low ) taylor_y[i * n_order + 0] = parameter[ arg[0] ]; for(size_t k = 1; k < order_low; ++k) taylor_y[i * n_order + k] = Base(0.0); if( trace ) { printOp( std::cout, play, itr.op_index(), i_var, op_code, arg ); std::cout << std::endl; } break; // // FunrvOp case FunrvOp: CPPAD_ASSERT_NARG_NRES(op_code, 0, 1); CPPAD_ASSERT_UNKNOWN( 0 < i_var ); index_y[i] = i_var; variable_y[i] = true; for(size_t k = 0; k < order_low; ++k) taylor_y[i * n_order + k] = taylor[i_var * cap_order + k]; break; } } // // op_code (++itr).op_info(op_code, arg, i_var); CPPAD_ASSERT_UNKNOWN( op_code == AFunOp ); // // taylor_y size_t need_y = size_t(variable_enum); sweep::call_atomic_forward( parameter_x, type_x, need_y, variable_y, order_low, order_up, atom_index, call_id, taylor_x, taylor_y ); // // taylor for(size_t i = 0; i < m; ++i) { if( variable_y[i] ) { for(size_t k = order_low; k < n_order; ++k) taylor[ index_y[i] * cap_order + k ] = taylor_y[i * n_order + k]; if( trace ) { const addr_t* null_addr = static_cast( nullptr ); printOp( std::cout, play, itr.op_index(), index_y[i], FunrvOp, null_addr ); Base* yi_ptr = taylor + index_y[i] * cap_order + 0; const Base* null_base = static_cast( nullptr ); printOpResult( std::cout, n_order, yi_ptr, 0, null_base ); std::cout << std::endl; } } } // return; } /* ----------------------------------------------------------------------------- {xrst_begin var_atomic_forward_dir dev} Multiple Direction Forward Atomic Function Call ############################################### Prototype ********* {xrst_literal // BEGIN_ATOMIC_FORWARD_DIR // END_ATOMIC_FORWARD_DIR } {xrst_template ; include/cppad/local/var_op/template/atomic_op.xrst headers: n_res, i_z, itr, play, parameter, trace, work @mode@ ; forward } {xrst_template ; include/cppad/local/var_op/template/forward_dir.xrst headers: n_dir, cap_order, order_up, taylor } {xrst_end var_atomic_forward_dir} */ // BEGIN_ATOMIC_FORWARD_DIR template void atomic_forward_dir( play::const_sequential_iterator& itr , const player* play , const Base* parameter , bool trace , atomic_op_work& work , size_t cap_order , size_t order_up , size_t n_dir , Base* taylor ) // END_ATOMIC_FORWARD_DIR { // // order_low size_t order_low = order_up; // // vector using CppAD::vector; // // taylor_index size_t per_variable = (cap_order - 1) * n_dir + 1; auto taylor_index = [per_variable, n_dir] (size_t variable_index, size_t order, size_t dir) { size_t index = variable_index * per_variable; if( order > 0 ) index += (order - 1) * n_dir + 1 + dir; return index; }; // // par_is_dyn const pod_vector& par_is_dyn( play->par_is_dyn() ); // // n_order size_t n_order = order_up + 1; // // op_code, i_var, arg op_code_var op_code; size_t i_var; const addr_t* arg; itr.op_info(op_code, arg, i_var); CPPAD_ASSERT_UNKNOWN( op_code == AFunOp ); CPPAD_ASSERT_NARG_NRES(op_code, 4, 0); // if( trace ) { printOp( std::cout, play, itr.op_index(), i_var, op_code, arg ); std::cout << std::endl; } // // atom_index, call_id, m, n size_t atom_index, call_id, m, n; play::atom_op_info(op_code, arg, atom_index, call_id, m, n); // // parameter_x, type_x, taylor_x, taylor_y, index_x, index_y, variable_y work.resize(m, n, n_order, forward_dir_sweep); vector& parameter_x( work.parameter_x ); vector& type_x( work.type_x ); // vector& taylor_x( work.taylor_x ); vector& taylor_y( work.taylor_y ); // vector& index_x( work.index_x ); vector& index_y( work.index_y ); vector& variable_y(work.variable_y ); // // j for(size_t j = 0; j < n; ++j) { // // op_code, arg, i_var (++itr).op_info(op_code, arg, i_var); if( trace ) { printOp( std::cout, play, itr.op_index(), i_var, op_code, arg ); std::cout << std::endl; } // // index_x, type_x, parameter_x switch(op_code) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // // FunapOp case FunapOp: CPPAD_ASSERT_NARG_NRES(op_code, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < play->num_par_all() ); index_x[j] = std::numeric_limits::max(); if( par_is_dyn[ arg[0] ] ) type_x[j] = dynamic_enum; else type_x[j] = constant_enum; parameter_x[j] = parameter[ arg[0] ]; break; // // FunavOp case FunavOp: CPPAD_ASSERT_NARG_NRES(op_code, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < play->num_var() ); index_x[j] = size_t( arg[0] ); type_x[j] = variable_enum; parameter_x[j] = CppAD::numeric_limits::quiet_NaN(); break; } } // // index_y, variable_y for(size_t i = 0; i < m; ++i) { // // op_code, arg, i_var (++itr).op_info(op_code, arg, i_var); // // switch(op_code) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // // FunrpOp case FunrpOp: CPPAD_ASSERT_NARG_NRES(op_code, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < play->num_par_all() ); index_y[i] = size_t( arg[0] ); variable_y[i] = false; if( trace ) { printOp( std::cout, play, itr.op_index(), i_var, op_code, arg ); std::cout << std::endl; } break; // // FunrvOp case FunrvOp: CPPAD_ASSERT_NARG_NRES(op_code, 0, 1); CPPAD_ASSERT_UNKNOWN( 0 < i_var ); index_y[i] = i_var; variable_y[i] = true; break; } } // // op_code (++itr).op_info(op_code, arg, i_var); CPPAD_ASSERT_UNKNOWN( op_code == AFunOp ); // // dir for(size_t dir = 0; dir < n_dir; ++dir) { // // taylor_x for(size_t j = 0; j < n; ++j) { if( type_x[j] != variable_enum ) { taylor_x[j * n_order + 0] = parameter_x[j]; for(size_t k = 1; k < n_order; ++k) taylor_x[j * n_order + k] = Base(0.0); } else { for(size_t k = 0; k < n_order; ++k) { size_t index = taylor_index(index_x[j], k, dir); taylor_x[j * n_order + k] = taylor[index]; } } } // // taylor_y for(size_t i = 0; i < m; ++i) { if( ! variable_y[i] ) { if( 0 < order_low ) taylor_y[i * n_order + 0] = parameter[ index_y[i] ]; for(size_t k = 1; k < order_low; ++k) taylor_y[i * n_order + k] = Base(0.0); } else { for(size_t k = 0; k < order_low; ++k) { size_t index = taylor_index(index_y[i], k, dir); taylor_y[i * n_order + k] = taylor[index]; } } } size_t need_y = size_t(variable_enum); sweep::call_atomic_forward( parameter_x, type_x, need_y, variable_y, order_low, order_up, atom_index, call_id, taylor_x, taylor_y ); // // taylor for(size_t i = 0; i < m; ++i) if( variable_y[i] ) { for(size_t k = order_low; k < n_order; ++k) { size_t index = taylor_index(index_y[i], k, dir); taylor[index] = taylor_y[i * n_order + k]; } if( trace ) { const addr_t* null_addr = static_cast( nullptr ); if( dir == 0 ) { printOp( std::cout, play, itr.op_index(), index_y[i], FunrvOp, null_addr ); std::cout << std::endl; } Base* yi_ptr = taylor_y.data() + i * n_order; const Base* null_base = static_cast( nullptr ); std::cout << " "; printOpResult( std::cout, n_order, yi_ptr, 0, null_base ); std::cout << std::endl; } } } // return; } /* ------------------------------------------------------------------------------- {xrst_begin var_atomic_reverse dev} {xrst_spell subgraph } Reverse Atomic Function Call ############################ Prototype ********* {xrst_literal // BEGIN_ATOMIC_REVERSE // END_ATOMIC_REVERSE } Iterator ******** this template parameter is either ``play::subgraph_iterator`` or ``play::const_sequential_iterator`` . {xrst_template ; include/cppad/local/var_op/template/atomic_op.xrst headers: n_res, i_z, itr, play, parameter, trace, work headers if @mode@ is reverse: G and H @mode@ ; reverse } cap_order ********* see atomic_forward_any :ref:`var_atomic_forward_any@cap_order` n_order ******* is the number of Taylor coefficient orders that we are computing the partial derivatives with respect to. taylor ****** The Taylor coefficient for the variable with index j and order k is:: taylor[ j * cap_order + k ] partial ******* The partial derivative with respect to the order *k* Taylor coefficient for the variable with index *j* is:: partial[ j * n_order + k ] On input, *partial* contains the partial derivatives of *G* with respect to the Taylor coefficients of the arguments to *G* . On output, *partial* contains the partial derivatives of *H* with respect to the Taylor coefficients of the arguments to *H* . We only have partials with respect to variables; i.e., *partial* does not included derivatives with respect to the parameters in *x* or *y* . ~ {xrst_end var_atomic_reverse} */ // BEGIN_ATOMIC_REVERSE template void atomic_reverse( Iterator& itr , const player* play , const Base* parameter , bool trace , atomic_op_work& work , size_t cap_order , size_t n_order , const Base* taylor , Base* partial ) // END_ATOMIC_REVERSE { CPPAD_ASSERT_UNKNOWN( 0 < n_order ); // // vector using CppAD::vector; // // par_is_dyn const pod_vector& par_is_dyn( play->par_is_dyn() ); // // n_order size_t order_up = n_order - 1; // // op_code, i_var, arg op_code_var op_code; size_t i_var; const addr_t* arg; itr.op_info(op_code, arg, i_var); CPPAD_ASSERT_UNKNOWN( op_code == AFunOp ); CPPAD_ASSERT_NARG_NRES(op_code, 4, 0); // if( trace ) { printOp( std::cout, play, itr.op_index(), i_var, op_code, arg ); std::cout << std::endl; } // // atom_index, call_id, m, n size_t atom_index, call_id, m, n; play::atom_op_info(op_code, arg, atom_index, call_id, m, n); // // parameter_x, type_x, // taylor_x, taylor_y, partial_x, partial_y, variable_x, index_x work.resize(m, n, n_order, reverse_op_sweep); vector& parameter_x( work.parameter_x ); vector& type_x( work.type_x ); // vector& taylor_x( work.taylor_x ); vector& taylor_y( work.taylor_y ); // vector& partial_x( work.partial_x ); vector& partial_y( work.partial_y ); // vector& variable_x( work.variable_x ); vector& index_x( work.index_x ); // // i for(size_t ip1 = m; ip1 > 0; --ip1) { size_t i = ip1 - 1; // // op_code, arg, i_var (--itr).op_info(op_code, arg, i_var); if( trace ) { printOp( std::cout, play, itr.op_index(), i_var, op_code, arg ); } // // taylor_y, partial_y switch(op_code) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // // FunrpOp case FunrpOp: CPPAD_ASSERT_NARG_NRES(op_code, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < play->num_par_all() ); taylor_y[i * n_order + 0] = parameter[ arg[0] ]; partial_y[i * n_order + 0] = Base(0.0); for(size_t k = 1; k < n_order; ++k) { partial_y[i * n_order + k] = Base(0.0); taylor_y[ i * n_order + k] = Base(0.0); } break; // // FunrvOp case FunrvOp: CPPAD_ASSERT_NARG_NRES(op_code, 0, 1); CPPAD_ASSERT_UNKNOWN( 0 < i_var ); for(size_t k = 0; k < n_order; ++k) { taylor_y[ i * n_order + k] = taylor[i_var * cap_order + k]; partial_y[i * n_order + k] = partial[i_var * n_order + k]; } if( trace ) { const Base* t_ptr = taylor + i_var * cap_order; const Base* p_ptr = partial + i_var * n_order; printOpResult( std::cout, n_order, t_ptr, n_order, p_ptr ); } break; } if( trace ) std::cout << std::endl; } // // j for(size_t jp1 = n; jp1 > 0; --jp1) { size_t j = jp1 - 1; // // op_code, arg, i_var (--itr).op_info(op_code, arg, i_var); if( trace ) { printOp( std::cout, play, itr.op_index(), i_var, op_code, arg ); std::cout << std::endl; } // // variable_x, type_x switch(op_code) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // // FunapOp case FunapOp: CPPAD_ASSERT_NARG_NRES(op_code, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < play->num_par_all() ); variable_x[j] = false; index_x[j] = size_t( arg[0] ); if( par_is_dyn[ arg[0] ] ) type_x[j] = dynamic_enum; else type_x[j] = constant_enum; parameter_x[j] = parameter[ arg[0] ]; taylor_x[j * n_order + 0] = parameter[ arg[0] ]; for(size_t k = 1; k < n_order; ++k) taylor_x[j * n_order + k] = Base(0.0); break; // // FunavOp case FunavOp: CPPAD_ASSERT_NARG_NRES(op_code, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < play->num_var() ); variable_x[j] = true; index_x[j] = size_t( arg[0] ); type_x[j] = variable_enum; parameter_x[j] = CppAD::numeric_limits::quiet_NaN(); for(size_t k = 0; k < n_order; ++k) taylor_x[j * n_order + k] = taylor[ size_t(arg[0]) * cap_order + k ]; break; } } // // op_code (--itr).op_info(op_code, arg, i_var); CPPAD_ASSERT_UNKNOWN( op_code == AFunOp ); // // partial_y sweep::call_atomic_reverse( parameter_x, type_x, variable_x, order_up, atom_index, call_id, taylor_x, taylor_y, partial_x, partial_y ); // // taylor for(size_t j = 0; j < n; ++j) { if( variable_x[j] ) { for(size_t k = 0; k < n_order; ++k) { size_t index = index_x[j] * n_order + k; partial[index] += partial_x[j * n_order + k]; } } } // return; } /* ------------------------------------------------------------------------------- {xrst_begin var_atomic_for_jac dev} Forward Jacobian Sparsity Atomic Function Call ############################################## Prototype ********* {xrst_literal // BEGIN_ATOMIC_FOR_JAC // END_ATOMIC_FOR_JAC } {xrst_template ; include/cppad/local/var_op/template/atomic_op.xrst headers: n_res, i_z, itr, play, parameter, trace, work @mode@ ; forward } Vector_set ********** is the type used for vectors of sets. It must satisfy the :ref:`SetVector-name` concept. var_sparsity ************ Input ===== :: for j = 0, ..., i_z - n_res The set with index j in var_sparsity Output ====== :: for j = i_z , ... , i_z - n_res + 1 The set with index j in var_sparsity {xrst_end var_atomic_for_jac} */ // BEGIN_ATOMIC_FOR_JAC template inline void atomic_for_jac( play::const_sequential_iterator& itr , const player* play , const Base* parameter , bool trace , atomic_op_work& work , bool dependency , Vector_set& var_sparsity ) // END_ATOMIC_FOR_JAC { // // // vector using CppAD::vector; // // par_is_dyn const pod_vector& par_is_dyn( play->par_is_dyn() ); // // op_code, i_var, arg op_code_var op_code; size_t i_var; const addr_t* arg; itr.op_info(op_code, arg, i_var); CPPAD_ASSERT_UNKNOWN( op_code == AFunOp ); CPPAD_ASSERT_NARG_NRES(op_code, 4, 0); // if( trace ) { printOp( std::cout, play, itr.op_index(), i_var, op_code, arg ); std::cout << std::endl; } // // atom_index, call_id, m, n size_t atom_index, call_id, m, n; play::atom_op_info(op_code, arg, atom_index, call_id, m, n); // // parameter_x, type_x, index_x, index_y size_t n_order = 0; work.resize(m, n, n_order, for_jac_sweep); vector& parameter_x( work.parameter_x ); vector& type_x( work.type_x ); vector& index_x( work.index_x ); vector& index_y( work.index_y ); // // j for(size_t j = 0; j < n; ++j) { // // op_code, arg, i_var (++itr).op_info(op_code, arg, i_var); if( trace ) { printOp( std::cout, play, itr.op_index(), i_var, op_code, arg ); std::cout << std::endl; } // // type_x, parameter_x, index_x switch(op_code) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // // FunapOp case FunapOp: CPPAD_ASSERT_NARG_NRES(op_code, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < play->num_par_all() ); if( par_is_dyn[ arg[0] ] ) type_x[j] = dynamic_enum; else type_x[j] = constant_enum; parameter_x[j] = parameter[ arg[0] ]; index_x[j] = 0; // special variable index used for parameters break; // // FunavOp case FunavOp: CPPAD_ASSERT_NARG_NRES(op_code, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < play->num_var() ); type_x[j] = variable_enum; parameter_x[j] = CppAD::numeric_limits::quiet_NaN(); index_x[j] = size_t(arg[0]); break; } } // // i for(size_t i = 0; i < m; ++i) { // // op_code, arg, i_var (++itr).op_info(op_code, arg, i_var); // // index_y switch(op_code) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // // FunrpOp case FunrpOp: CPPAD_ASSERT_NARG_NRES(op_code, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < play->num_par_all() ); index_y[i] = 0; // special variable index used for parameters break; // // FunrvOp case FunrvOp: CPPAD_ASSERT_NARG_NRES(op_code, 0, 1); CPPAD_ASSERT_UNKNOWN( 0 < i_var ); index_y[i] = i_var; break; } } // // op_code (++itr).op_info(op_code, arg, i_var); CPPAD_ASSERT_UNKNOWN( op_code == AFunOp ); // // varsparsity sweep::call_atomic_for_jac_sparsity( atom_index, call_id, dependency, parameter_x, type_x, index_x, index_y, var_sparsity ); // if( trace ) { size_t end = var_sparsity.end(); CppAD::vectorBool this_row(end); addr_t* arg_tmp = { 0 }; for(size_t i = 0; i < m; ++i) { size_t j_var = index_y[i]; for(size_t j = 0; j < end; ++j) this_row[j] = false; typename Vector_set::const_iterator itr_sparse(var_sparsity, j_var); size_t j = *itr_sparse; while( j < end ) { this_row[j] = true; j = *(++itr_sparse); } if( 0 < j_var ) { printOp( std::cout, play, itr.op_index() - m + i, j_var, FunrvOp, arg_tmp ); printOpResult( std::cout, 1, &this_row, 0, (CppAD::vectorBool *) nullptr ); std::cout << std::endl; } } } return; } /* ------------------------------------------------------------------------------ {xrst_begin var_atomic_rev_jac dev} Reverse Jacobian Sparsity Atomic Function Call ############################################## Prototype ********* {xrst_literal // BEGIN_ATOMIC_REV_JAC // END_ATOMIC_REV_JAC } {xrst_template ; include/cppad/local/var_op/template/atomic_op.xrst headers: n_res, i_z, itr, play, parameter, trace, work headers if @mode@ is reverse: G and H @mode@ ; reverse } Vector_set ********** is the type used for vectors of sets. It must satisfy the :ref:`SetVector-name` concept. var_sparsity ************ On input, *var_sparsity* contains the sparsity pattern for :math:`G [ y, x, \cdots ]` . On output it contains the sparsity pattern for :math:`H ( x, \cdots )` . {xrst_end var_atomic_rev_jac} */ // BEGIN_ATOMIC_REV_JAC template inline void atomic_rev_jac( play::const_sequential_iterator& itr , const player* play , const Base* parameter , bool trace , atomic_op_work& work , bool dependency , Vector_set& var_sparsity ) // END_ATOMIC_REV_JAC { // // // vector using CppAD::vector; // // par_is_dyn const pod_vector& par_is_dyn( play->par_is_dyn() ); // // op_code, i_var, arg op_code_var op_code; size_t i_var; const addr_t* arg; itr.op_info(op_code, arg, i_var); CPPAD_ASSERT_UNKNOWN( op_code == AFunOp ); CPPAD_ASSERT_NARG_NRES(op_code, 4, 0); // if( trace ) { printOp( std::cout, play, itr.op_index(), i_var, op_code, arg ); std::cout << std::endl; } // // atom_index, call_id, m, n size_t atom_index, call_id, m, n; play::atom_op_info(op_code, arg, atom_index, call_id, m, n); // // parameter_x, type_x, index_x, index_y size_t n_order = 0; work.resize(m, n, n_order, rev_jac_sweep); vector& parameter_x( work.parameter_x ); vector& type_x( work.type_x ); vector& index_x( work.index_x ); vector& index_y( work.index_y ); // // i for(size_t ip1 = m; ip1 > 0; --ip1) { size_t i = ip1 - 1; // // op_code, arg, i_var (--itr).op_info(op_code, arg, i_var); // // index_y switch(op_code) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // // FunrpOp case FunrpOp: CPPAD_ASSERT_NARG_NRES(op_code, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < play->num_par_all() ); index_y[i] = 0; // special variable index used for parameters break; // // FunrvOp case FunrvOp: CPPAD_ASSERT_NARG_NRES(op_code, 0, 1); CPPAD_ASSERT_UNKNOWN( 0 < i_var ); index_y[i] = i_var; break; } } if( trace ) { size_t end = var_sparsity.end(); CppAD::vectorBool this_row(end); const addr_t* arg_null = static_cast(nullptr); for(size_t ip1 = m; ip1 > 0; --ip1) { size_t i = ip1 - 1; if( index_y[i] > 0 ) { for(size_t j = 0; j < end; ++j) this_row[j] = false; // typedef typename Vector_set::const_iterator itr_sparse_t; itr_sparse_t itr_sparse(var_sparsity, index_y[i]); size_t j = *itr_sparse; while( j < end ) { this_row[j] = true; j = *(++itr_sparse); } printOp( std::cout, play, itr.op_index() + i, index_y[i], FunrvOp, arg_null ); printOpResult( std::cout, 1, &this_row, 0, (CppAD::vectorBool *) nullptr ); std::cout << std::endl; } } } // // j for(size_t jp1 = n; jp1 > 0; --jp1) { size_t j = jp1 - 1; // // op_code, arg, i_var (--itr).op_info(op_code, arg, i_var); if( trace ) { printOp( std::cout, play, itr.op_index(), i_var, op_code, arg ); std::cout << std::endl; } // // type_x, parameter_x, index_x switch(op_code) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // // FunapOp case FunapOp: CPPAD_ASSERT_NARG_NRES(op_code, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < play->num_par_all() ); if( par_is_dyn[ arg[0] ] ) type_x[j] = dynamic_enum; else type_x[j] = constant_enum; parameter_x[j] = parameter[ arg[0] ]; index_x[j] = 0; // special variable index used for parameters break; // // FunavOp case FunavOp: CPPAD_ASSERT_NARG_NRES(op_code, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < play->num_var() ); type_x[j] = variable_enum; parameter_x[j] = CppAD::numeric_limits::quiet_NaN(); index_x[j] = size_t(arg[0]); break; } } // // op_code (--itr).op_info(op_code, arg, i_var); CPPAD_ASSERT_UNKNOWN( op_code == AFunOp ); // // varsparsity sweep::call_atomic_rev_jac_sparsity( atom_index, call_id, dependency, parameter_x, type_x, index_x, index_y, var_sparsity ); return; } /* ------------------------------------------------------------------------------ {xrst_begin var_atomic_rev_hes dev} Reverse Hessian Sparsity Atomic Function Call ############################################# Prototype ********* {xrst_literal // BEGIN_ATOMIC_REV_HES // END_ATOMIC_REV_HES } {xrst_template ; include/cppad/local/var_op/template/atomic_op.xrst headers: n_res, i_z, itr, play, parameter, trace, work headers if @mode@ is reverse: G and H @mode@ ; reverse } num_var ******* We use the notation *num_var* for the number of variables on the tape (including the phantom variable at index zero); i.e.:: ``play->num_var()`` . for_jac_sparsity **************** The set with index *j* is the forward Jacobian sparsity pattern for the variable with index *j*. rev_jac_include *************** If the *j* element of this vector is true, the variable with index *j* is included in the Hessian, or affects the value of a variable that is included in the Hessian. Input ===== :: for j = num_var, ... , i_z + 1 rev_jac_include[j] is an input Output ====== :: for j = i_z , ... , i_z - n_res + 1 rev_jac_include[j] is an output rev_hes_sparsity **************** On input (output), this is the sparsity pattern for *G* ( *H* ). For each variable index *j* , *rev_hes_sparsity* [ *j* ] is the set of indices that may have non-zero cross partials with variable index *j* . Example ======= If the indices in the sets correspond to the independent variables, then *rev_hes_sparsity* ``.end()`` is the number of independent variables. For *i* a variable index between 1 and the number of independent variables, *i* - 1 is the corresponding independent variable index. (The index *i* = 0 corresponds to the phantom variable at the beginning of the tape. ) {xrst_end var_atomic_rev_hes} */ // BEGIN_ATOMIC_REV_HES template inline void atomic_rev_hes( play::const_sequential_iterator& itr , const player* play , const Base* parameter , bool trace , atomic_op_work& work , const Vector_set& for_jac_sparsity , bool* rev_jac_include , Vector_set& rev_hes_sparsity ) // END_ATOMIC_REV_HES { // // CPPAD_ASSERT_UNKNOWN( for_jac_sparsity.n_set() == play->num_var() ); CPPAD_ASSERT_UNKNOWN( rev_hes_sparsity.n_set() == play->num_var() ); CPPAD_ASSERT_UNKNOWN( for_jac_sparsity.end() == rev_hes_sparsity.end() ); // // vector using CppAD::vector; // // par_is_dyn const pod_vector& par_is_dyn( play->par_is_dyn() ); // // op_code, i_var, arg op_code_var op_code; size_t i_var; const addr_t* arg; itr.op_info(op_code, arg, i_var); CPPAD_ASSERT_UNKNOWN( op_code == AFunOp ); CPPAD_ASSERT_NARG_NRES(op_code, 4, 0); // if( trace ) { printOp( std::cout, play, itr.op_index(), i_var, op_code, arg ); std::cout << std::endl; } // // atom_index, call_id, m, n size_t atom_index, call_id, m, n; play::atom_op_info(op_code, arg, atom_index, call_id, m, n); // // parameter_x, type_x, index_x, index_y size_t n_order = 0; work.resize(m, n, n_order, rev_hes_sweep); vector& parameter_x( work.parameter_x ); vector& type_x( work.type_x ); vector& index_x( work.index_x ); vector& index_y( work.index_y ); // // i for(size_t ip1 = m; ip1 > 0; --ip1) { size_t i = ip1 - 1; // // op_code, arg, i_var (--itr).op_info(op_code, arg, i_var); // // index_y switch(op_code) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // // FunrpOp case FunrpOp: CPPAD_ASSERT_NARG_NRES(op_code, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < play->num_par_all() ); index_y[i] = 0; // special variable index used for parameters break; // // FunrvOp case FunrvOp: CPPAD_ASSERT_NARG_NRES(op_code, 0, 1); CPPAD_ASSERT_UNKNOWN( 0 < i_var ); index_y[i] = i_var; break; } } // // j for(size_t jp1 = n; jp1 > 0; --jp1) { size_t j = jp1 - 1; // // op_code, arg, i_var (--itr).op_info(op_code, arg, i_var); if( trace ) { printOp( std::cout, play, itr.op_index(), i_var, op_code, arg ); std::cout << std::endl; } // // type_x, parameter_x, index_x switch(op_code) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // // FunapOp case FunapOp: CPPAD_ASSERT_NARG_NRES(op_code, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < play->num_par_all() ); if( par_is_dyn[ arg[0] ] ) type_x[j] = dynamic_enum; else type_x[j] = constant_enum; parameter_x[j] = parameter[ arg[0] ]; index_x[j] = 0; // special variable index used for parameters break; // // FunavOp case FunavOp: CPPAD_ASSERT_NARG_NRES(op_code, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < play->num_var() ); type_x[j] = variable_enum; parameter_x[j] = CppAD::numeric_limits::quiet_NaN(); index_x[j] = size_t(arg[0]); break; } } // // op_code (--itr).op_info(op_code, arg, i_var); CPPAD_ASSERT_UNKNOWN( op_code == AFunOp ); // // rev_jac_include, rev_hes_sparsity sweep::call_atomic_rev_hes_sparsity( atom_index, call_id, parameter_x, type_x, index_x, index_y, for_jac_sparsity, rev_jac_include, rev_hes_sparsity ); // if( trace ) { typedef typename Vector_set::const_iterator itr_sparse_t; size_t end = rev_hes_sparsity.end(); CppAD::vectorBool jac_row(end), hes_row(end); addr_t* arg_tmp = { 0 }; for(size_t i = 0; i < m; ++i) { size_t j_var = index_y[i]; if( 0 < j_var) { for(size_t j = 0; j < end; ++j) { jac_row[j] = false; hes_row[j] = false; } itr_sparse_t itr_jac(for_jac_sparsity, j_var); size_t j = *itr_jac; while( j < end ) { jac_row[j] = true; j = *(++itr_jac); } itr_sparse_t itr_hes(rev_hes_sparsity, j_var); j = *itr_hes; while( j < end ) { hes_row[j] = true; j = *(++itr_hes); } printOp( std::cout, play, itr.op_index() - m + i, j_var, FunrvOp, arg_tmp ); printOpResult( std::cout, 1, &jac_row, 1, &hes_row ); std::cout << std::endl; } } } return; } /* ------------------------------------------------------------------------------- {xrst_begin var_atomic_for_hes dev} Forward Hessian Sparsity Atomic Function Call ############################################# Prototype ********* {xrst_literal // BEGIN_ATOMIC_FOR_HES // END_ATOMIC_FOR_HES } {xrst_template ; include/cppad/local/var_op/template/atomic_op.xrst headers: n_res, i_z, itr, play, parameter, trace, work @mode@ ; forward } Vector_set ********** is the type used for vectors of sets. It must satisfy the :ref:`SetVector-name` concept. n_independent_p1 **************** is the number of independent variables (in the tape) plus one. rev_jac_sparsity **************** is the reverse Jacobian sparsity pattern for the scalar valued function that the Hessian sparsity is being computed for. Zero is the only possible element in each set; i.e.. *ref_jac_sparsity* .end() == 1 . If the set with index *j* is empty, the derivative of the function w.r.t the variable with index *j* is zero. for_hes_sparsity **************** On input, all the linear and nonlinear interactions up to the arguments to the atomic function have been take into account. Upon return, the linear and nonlinear interactions in the atomic function have been take into account. Hessian Sparsity ================ For *j* equal 1 to *n_independent_p1* - 1, if *i* is in set with index *j* , the Hessian may have a non-zero partial with respect to the independent variables with indices ( *i* - 1, *j* - 1 ) . Note that the index zero is not used because it corresponds to the phantom variable on the tape. Jacobian Sparsity ================= If *i* is in the set with index *n_independent_p1* + *j* , the variable with index *j* may have a non-zero partial with resect to the independent variable with index *i* - 1 . {xrst_end var_atomic_for_hes} */ // BEGIN_ATOMIC_FOR_HES template inline void atomic_for_hes( play::const_sequential_iterator& itr , const player* play , const Base* parameter , bool trace , atomic_op_work& work , size_t n_independent_p1 , const Vector_set& rev_jac_sparsity , Vector_set& for_hes_sparsity ) // END_ATOMIC_FOR_HES { CPPAD_ASSERT_UNKNOWN( rev_jac_sparsity.end() == 1 ); // CPPAD_ASSERT_UNKNOWN( for_hes_sparsity.end() == n_independent_p1 ); // // vector using CppAD::vector; // // par_is_dyn const pod_vector& par_is_dyn( play->par_is_dyn() ); // // op_code, i_var, arg op_code_var op_code; size_t i_var; const addr_t* arg; itr.op_info(op_code, arg, i_var); CPPAD_ASSERT_UNKNOWN( op_code == AFunOp ); CPPAD_ASSERT_NARG_NRES(op_code, 4, 0); // if( trace ) { printOp( std::cout, play, itr.op_index(), i_var, op_code, arg ); std::cout << std::endl; } // // atom_index, call_id, m, n size_t atom_index, call_id, m, n; play::atom_op_info(op_code, arg, atom_index, call_id, m, n); // // parameter_x, type_x, index_x, index_y size_t n_order = 0; work.resize(m, n, n_order, for_hes_sweep); vector& parameter_x( work.parameter_x ); vector& type_x( work.type_x ); vector& index_x( work.index_x ); vector& index_y( work.index_y ); // // j for(size_t j = 0; j < n; ++j) { // // op_code, arg, i_var (++itr).op_info(op_code, arg, i_var); if( trace ) { printOp( std::cout, play, itr.op_index(), i_var, op_code, arg ); std::cout << std::endl; } // // type_x, parameter_x, index_x switch(op_code) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // // FunapOp case FunapOp: CPPAD_ASSERT_NARG_NRES(op_code, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < play->num_par_all() ); if( par_is_dyn[ arg[0] ] ) type_x[j] = dynamic_enum; else type_x[j] = constant_enum; parameter_x[j] = parameter[ arg[0] ]; index_x[j] = 0; // special variable index used for parameters break; // // FunavOp case FunavOp: CPPAD_ASSERT_NARG_NRES(op_code, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < play->num_var() ); type_x[j] = variable_enum; parameter_x[j] = CppAD::numeric_limits::quiet_NaN(); index_x[j] = size_t(arg[0]); break; } } // // i for(size_t i = 0; i < m; ++i) { // // op_code, arg, i_var (++itr).op_info(op_code, arg, i_var); // // index_y switch(op_code) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // // FunrpOp case FunrpOp: CPPAD_ASSERT_NARG_NRES(op_code, 1, 0); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < play->num_par_all() ); index_y[i] = 0; // special variable index used for parameters break; // // FunrvOp case FunrvOp: CPPAD_ASSERT_NARG_NRES(op_code, 0, 1); CPPAD_ASSERT_UNKNOWN( 0 < i_var ); index_y[i] = i_var; break; } } // // op_code (++itr).op_info(op_code, arg, i_var); CPPAD_ASSERT_UNKNOWN( op_code == AFunOp ); // // varsparsity size_t num_var = play->num_var(); sweep::call_atomic_for_hes_sparsity( atom_index, call_id, parameter_x, type_x, index_x, index_y, n_independent_p1, num_var, rev_jac_sparsity, for_hes_sparsity ); // if( trace ) { typedef typename Vector_set::const_iterator itr_sparse_t; size_t np1 = n_independent_p1; CPPAD_ASSERT_UNKNOWN( np1 == for_hes_sparsity.end() ); CppAD::vectorBool jac_row(np1); addr_t* arg_tmp = { 0 }; for(size_t i = 0; i < m; ++i) { size_t j_var = index_y[i]; if( 0 < j_var ) { for(size_t j = 0; j < np1; ++j) jac_row[j] = false; itr_sparse_t itr_jac(for_hes_sparsity, np1 + j_var); size_t j = *itr_jac; while( j < np1 ) { jac_row[j] = true; j = *(++itr_jac); } printOp( std::cout, play, itr.op_index() - m + i, j_var, FunrvOp, arg_tmp ); printOpResult( std::cout, 1, &jac_row, 0, (CppAD::vectorBool *) nullptr ); std::cout << std::endl; } } CppAD::vector< CppAD::vectorBool > hes(np1); for(size_t i = 0; i < np1; ++i) { hes[i].resize(np1); for(size_t j = 0; j < np1; ++j) hes[i][j] = false; itr_sparse_t itr_hes(for_hes_sparsity, i); size_t j = *itr_hes; while( j < np1 ) { hes[i][j] = true; j = *(++itr_hes); } } printOpResult( std::cout, np1, hes.data(), 0, (CppAD::vectorBool *) nullptr ); std::cout << std::endl; } return; } } } } // END_CPPAD_LOCAL_VAR_OP_NAMESPACE # endif ================================================ FILE: include/cppad/local/var_op/binary_op.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin_parent var_binary_op dev} {xrst_spell addpv addvv azmul delim divpv divvp divvv mulpv mulvv powpv powvp powvv subpv subvp subvv zmulpv zmulvp zmulvv } Binary Operators ################ All these operators have the same prototype and could be implemented as virtual functions for a binary base case. Compilers can not optimize across virtual function calls and testing indicates that a virtual function implementation is significant slower. User Syntax *********** | *z* = *fun* ( *x* , *y* ) | *z* = *x* *Op* *y* x * is a parameter or variable and is the first argument for this operator. y * is a parameter or variable and is the second argument for this operator. z * is the primary result for this operator which is also a variable. The PowvpOp and PowvvOp operators has a two auxiliary results; see :ref:`var_binary_op@n_res` below. Base **** base type for the operator; i.e., this operation was recorded using AD and computations by these operators done using type Base. Fun *** .. csv-table:: :widths: auto :header-rows: 1 :delim: ; op_code; Fun; fun; Op; x; y; z AddpvOp; addpv; ; \+; parameter; variable; *x* + *y* AddvvOp; addvv; ; \+; variable; variable; *x* + *y* DivpvOp; divpv; ; \/; parameter; variable; *x* / *y* DivvpOp; divvp; ; \/; variable; parameter; *x* / *y* DivvvOp; divvv; ; \/; variable; variable; *x* / *y* MulpvOp; mulpv; ; \*; parameter; variable; *x* * *y* MulvvOp; mulvv; ; \*; variable; variable; *x* * *y* SubpvOp; subpv; ; \-; parameter; variable; *x* - *y* SubvpOp; subvp; ; \-; variable; parameter; *x* - *y* SubvvOp; subvv; ; \-; variable; variable; *x* - *y* PowpvOp; zmulpv; pow; ; parameter; variable; pow( *x* , *y* ) PowvpOp; zmulvp; pow; ; variable; parameter; pow( *x* , *y* ) PowvvOp; zmulvv; pow; ; variable; variable; pow( *x* , *y* ) ZmulpvOp; zmulpv; azmul; ; parameter; variable; azmul( *x* , *y* ) ZmulvpOp; zmulvp; azmul; ; variable; parameter; azmul( *x* , *y* ) ZmulvvOp; zmulvv; azmul; ; variable; variable; azmul( *x* , *y* ) i_z *** is the variable index corresponding to *z* . n_res ***** is the number of results that are variables. This is 1 for all the binary operators except for PowpvOp and PowvvOp. For the PowpvOp and PowvvOP operators, *n_res* is 3 and the variable indices of the auxiliary results are *i_z* - 1 and *i_z* - 2. arg *** arg[0] ====== If *x* is a variable (parameter) *arg* [0] is the variable index (parameter index) corresponding to *x* . arg[1] ====== If *y* is a variable (parameter) *arg* [1] is the variable index (parameter index) corresponding to *y* . parameter ********* maps parameter indices to parameter values. {xrst_end var_binary_op} ------------------------------------------------------------------------------ {xrst_begin var_binary_forward_0 dev} Zero Order Forward Binary Variable Operators ############################################ x, y, z, n_res ************** see :ref:`var_binary_op@x` , :ref:`var_binary_op@y` , :ref:`var_binary_op@z` , :ref:`var_binary_op@n_res` Prototype ********* {xrst_code cpp} template inline void Fun_forward_0( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) {xrst_code} Base, Fun, i_z, arg, parameter ****************************** see :ref:`var_binary_op@Base` , :ref:`var_binary_op@Fun` , :ref:`var_binary_op@i_z` , :ref:`var_binary_op@arg` , :ref:`var_binary_op@parameter` cap_order ********* is the maximum number of orders that can fit in *taylor* . taylor ****** The Taylor coefficient corresponding to variable *i* and order *k* is *taylor* [ *i* * *cap_order* + *k* ] Input ===== The zero order Taylor coefficients for variables with index *i* less than or equal *i_z* - *n_res* . Output ====== The zero order Taylor coefficients for variables with index *i_z* - *n_res* , .. , *i_z* . {xrst_end var_binary_forward_0} ------------------------------------------------------------------------------ {xrst_begin var_binary_forward_any dev} Any Order Forward Binary Variable Operators ########################################### x, y, z, n_res ************** see :ref:`var_binary_op@x` , :ref:`var_binary_op@y` , :ref:`var_binary_op@z` , :ref:`var_binary_op@n_res` Prototype ********* {xrst_code cpp} template inline void Fun_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) {xrst_code} Base, Fun, i_z, arg, parameter ****************************** see :ref:`var_binary_op@Base` , :ref:`var_binary_op@Fun` , :ref:`var_binary_op@i_z` , :ref:`var_binary_op@arg` , :ref:`var_binary_op@parameter` {xrst_template ; include/cppad/local/var_op/template/forward_op.xrst headers: cap_order, order_low, order_up, taylor } {xrst_end var_binary_forward_any} ------------------------------------------------------------------------------ {xrst_begin var_binary_forward_dir dev} Multiple Direction Forward Binary Operators ########################################### x, y, z, n_res ************** see :ref:`var_binary_op@x` , :ref:`var_binary_op@y` , :ref:`var_binary_op@z` , :ref:`var_binary_op@n_res` Prototype ********* {xrst_code cpp} template inline void Fun_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) {xrst_code} Base, Fun, i_z, arg, parameter ****************************** see :ref:`var_binary_op@Base` , :ref:`var_binary_op@Fun` , :ref:`var_binary_op@i_z` , :ref:`var_binary_op@arg` , :ref:`var_binary_op@parameter` {xrst_template ; include/cppad/local/var_op/template/forward_dir.xrst headers: n_dir, cap_order, order_up, taylor } {xrst_end var_binary_forward_dir} ------------------------------------------------------------------------------ {xrst_begin var_binary_reverse dev} Reverse Mode Binary Operators ############################# x, y, z, n_res ************** see :ref:`var_binary_op@x` , :ref:`var_binary_op@y` , :ref:`var_binary_op@z` , :ref:`var_binary_op@n_res` Prototype ********* {xrst_code cpp} template inline void Fun_reverse( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) {xrst_code} Base, Fun, i_z, arg, parameter ****************************** see :ref:`var_binary_op@Base` , :ref:`var_binary_op@Fun` , :ref:`var_binary_op@i_z` , :ref:`var_binary_op@arg` , :ref:`var_binary_op@parameter` cap_order ********* is the maximum number of orders that can fit in *taylor* . taylor ****** The Taylor coefficient corresponding to variable *i* and order *k* is *taylor* [ *i* * *cap_order* + *k* ] {xrst_template ; include/cppad/local/var_op/template/reverse_op.xrst headers: n_order, partial @x, y@ ; x } {xrst_end var_binary_reverse} ------------------------------------------------------------------------------ ================================================ FILE: include/cppad/local/var_op/cexp_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_CEXP_OP_HPP # define CPPAD_LOCAL_VAR_OP_CEXP_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { /* {xrst_begin_parent var_cexp_op dev} The Variable Conditional Expression Operator ############################################ CExpOp ****** is the op code for this operator. User Syntax *********** | *z* = ``CondExp`` *Rel* ( *left* , *right* , *if_true* , *if_false* ) Rel *** is Lt, Le, Eq, Ge, Gt or Ne . left **** is the left operand for the comparison. right ***** is the right operand for the comparison. if_true ******* is the value assigned to *z* if the comparison result is true. if_false ******** is the value assigned to *z* if the comparison result is false. z * is the variable that results from the comparison. arg *** arg[0] ====== is static cast to addr_t from the enum type {xrst_literal include/cppad/local/declare_ad.hpp // BEGIN_COMPARE_OP // END_COMPARE_OP } The operator corresponding to Ne does not appear because that case is converted to the Eq case by switching *if_true* and *if_false* . Thus it must hold that:: arg[0] < addr_t(CompareNe) arg[1] ====== The first four bits of this value are used as flags; see below. arg[2] ====== If arg[1] & 1 is zero (is one) , arg[2] is the parameter (variable) index corresponding to *left* arg[3] ====== If arg[1] & 2 is zero (is one) , arg[3] is the parameter (variable) index corresponding to *right* arg[4] ====== If arg[1] & 4 is zero (is one) , arg[4] is the parameter (variable) index corresponding to *if_true* arg[5] ====== If arg[1] & 8 is zero (is one) , arg[5] is the parameter (variable) index corresponding to *if_false* {xrst_end var_cexp_op} ------------------------------------------------------------------------------ {xrst_begin var_cexp_forward_any dev} Any Order Forward Conditional Expression Variable Operator ########################################################## z * see :ref:`var_cexp_op@z` Prototype ********* {xrst_literal // BEGIN_CEXP_FORWARD_ANY // END_CEXP_FORWARD_ANY } arg *** see :ref:`var_cexp_op@arg` RecBase ******* is the base type use when recording this operator; i.e., this operation was recording using AD< *RecBase* > operations. Base **** is the type used for computations by this operator. This is either *RecBase* or AD< *RecBase* >. i_z *** is the variable index corresponding to the variable *z* . num_par ******* is the total number of values in the *parameter* vector. parameter ********* maps parameter indices to parameter values . {xrst_template ; include/cppad/local/var_op/template/forward_op.xrst headers: cap_order, order_low, order_up, taylor } {xrst_end var_cexp_forward_any} */ // BEGIN_CEXP_FORWARD_ANY template inline void cexp_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t num_par , const Base* parameter , size_t cap_order , Base* taylor ) // END_CEXP_FORWARD_ANY { Base y_0, y_1, y_2, y_3; size_t p = order_low; size_t q = order_up; // Base zero(0); Base* z = taylor + i_z * cap_order; CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < static_cast (CompareNe) ); CPPAD_ASSERT_UNKNOWN( NumArg(CExpOp) == 6 ); CPPAD_ASSERT_UNKNOWN( NumRes(CExpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( arg[1] != 0 ); if( arg[1] & 1 ) { y_0 = taylor[ size_t(arg[2]) * cap_order + 0 ]; } else { CPPAD_ASSERT_UNKNOWN( size_t(arg[2]) < num_par ); y_0 = parameter[ arg[2] ]; } if( arg[1] & 2 ) { y_1 = taylor[ size_t(arg[3]) * cap_order + 0 ]; } else { CPPAD_ASSERT_UNKNOWN( size_t(arg[3]) < num_par ); y_1 = parameter[ arg[3] ]; } if( p == 0 ) { if( arg[1] & 4 ) { y_2 = taylor[ size_t(arg[4]) * cap_order + 0 ]; } else { CPPAD_ASSERT_UNKNOWN( size_t(arg[4]) < num_par ); y_2 = parameter[ arg[4] ]; } if( arg[1] & 8 ) { y_3 = taylor[ size_t(arg[5]) * cap_order + 0 ]; } else { CPPAD_ASSERT_UNKNOWN( size_t(arg[5]) < num_par ); y_3 = parameter[ arg[5] ]; } z[0] = CondExpOp( CompareOp( arg[0] ), y_0, y_1, y_2, y_3 ); p++; } for(size_t d = p; d <= q; d++) { if( arg[1] & 4 ) { y_2 = taylor[ size_t(arg[4]) * cap_order + d]; } else y_2 = zero; if( arg[1] & 8 ) { y_3 = taylor[ size_t(arg[5]) * cap_order + d]; } else y_3 = zero; z[d] = CondExpOp( CompareOp( arg[0] ), y_0, y_1, y_2, y_3 ); } return; } /*! Multiple directions forward mode Taylor coefficients for op = CExpOp. The C++ source code corresponding to this operation is \verbatim z = CondExpRel(y_0, y_1, y_2, y_3) \endverbatim where Rel is one of the following: Lt, Le, Eq, Ge, Gt. \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base. \param i_z is the AD variable index corresponding to the variable z. \param arg \n arg[0] is static cast to size_t from the enum type \verbatim enum CompareOp { CompareLt, CompareLe, CompareEq, CompareGe, CompareGt, CompareNe } \endverbatim for this operation. Note that arg[0] cannot be equal to CompareNe. \n \n arg[1] & 1 \n If this is zero, y_0 is a parameter. Otherwise it is a variable. \n \n arg[1] & 2 \n If this is zero, y_1 is a parameter. Otherwise it is a variable. \n \n arg[1] & 4 \n If this is zero, y_2 is a parameter. Otherwise it is a variable. \n \n arg[1] & 8 \n If this is zero, y_3 is a parameter. Otherwise it is a variable. \n \n arg[2 + j ] for j = 0, 1, 2, 3 \n is the index corresponding to y_j. \param num_par is the total number of values in the vector parameter. \param parameter For j = 0, 1, 2, 3, if y_j is a parameter, parameter [ arg[2 + j] ] is its value. \param cap_order number of columns in the matrix containing the Taylor coefficients. \par Checked Assertions \li NumArg(CExpOp) == 6 \li NumRes(CExpOp) == 1 \li arg[0] < static_cast ( CompareNe ) \li arg[1] != 0; i.e., not all of y_0, y_1, y_2, y_3 are parameters. \li For j = 0, 1, 2, 3 if y_j is a parameter, arg[2+j] < num_par. \param q is order of the Taylor coefficient of z that we are computing. \param r is the number of Taylor coefficient directions that we are computing. \par tpv We use the notation tpv = (cap_order-1) * r + 1 which is the number of Taylor coefficients per variable \param taylor \b Input: For j = 0, 1, 2, 3, k = 1, ..., q, if y_j is a variable then taylor [ arg[2+j] * tpv + 0 ] is the zero order Taylor coefficient corresponding to y_j and taylor [ arg[2+j] * tpv + (k-1)*r+1+ell is its k-th order Taylor coefficient in the ell-th direction. \n \b Input: For j = 0, 1, 2, 3, k = 1, ..., q-1, taylor [ i_z * tpv + 0 ] is the zero order Taylor coefficient corresponding to z and taylor [ i_z * tpv + (k-1)*r+1+ell is its k-th order Taylor coefficient in the ell-th direction. \n \b Output: taylor [ i_z * tpv + (q-1)*r+1+ell ] is the q-th order Taylor coefficient corresponding to z in the ell-th direction. */ template inline void cexp_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t num_par , const Base* parameter , size_t cap_order , Base* taylor ) { Base y_0, y_1, y_2, y_3; size_t q = order_up; size_t r = n_dir; // Base zero(0); size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* z = taylor + i_z * num_taylor_per_var; CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < static_cast (CompareNe) ); CPPAD_ASSERT_UNKNOWN( NumArg(CExpOp) == 6 ); CPPAD_ASSERT_UNKNOWN( NumRes(CExpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( arg[1] != 0 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); if( arg[1] & 1 ) { y_0 = taylor[ size_t(arg[2]) * num_taylor_per_var + 0 ]; } else { CPPAD_ASSERT_UNKNOWN( size_t(arg[2]) < num_par ); y_0 = parameter[ arg[2] ]; } if( arg[1] & 2 ) { y_1 = taylor[ size_t(arg[3]) * num_taylor_per_var + 0 ]; } else { CPPAD_ASSERT_UNKNOWN( size_t(arg[3]) < num_par ); y_1 = parameter[ arg[3] ]; } size_t m = (q-1) * r + 1; for(size_t ell = 0; ell < r; ell++) { if( arg[1] & 4 ) { y_2 = taylor[ size_t(arg[4]) * num_taylor_per_var + m + ell]; } else y_2 = zero; if( arg[1] & 8 ) { y_3 = taylor[ size_t(arg[5]) * num_taylor_per_var + m + ell]; } else y_3 = zero; z[m+ell] = CondExpOp( CompareOp( arg[0] ), y_0, y_1, y_2, y_3 ); } return; } /*! Compute zero order forward mode Taylor coefficients for op = CExpOp. The C++ source code corresponding to this operation is \verbatim z = CondExpRel(y_0, y_1, y_2, y_3) \endverbatim where Rel is one of the following: Lt, Le, Eq, Ge, Gt. \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base. \param i_z is the AD variable index corresponding to the variable z. \param arg \n arg[0] is static cast to size_t from the enum type \verbatim enum CompareOp { CompareLt, CompareLe, CompareEq, CompareGe, CompareGt, CompareNe } \endverbatim for this operation. Note that arg[0] cannot be equal to CompareNe. \n \n arg[1] & 1 \n If this is zero, y_0 is a parameter. Otherwise it is a variable. \n \n arg[1] & 2 \n If this is zero, y_1 is a parameter. Otherwise it is a variable. \n \n arg[1] & 4 \n If this is zero, y_2 is a parameter. Otherwise it is a variable. \n \n arg[1] & 8 \n If this is zero, y_3 is a parameter. Otherwise it is a variable. \n \n arg[2 + j ] for j = 0, 1, 2, 3 \n is the index corresponding to y_j. \param num_par is the total number of values in the vector parameter. \param parameter For j = 0, 1, 2, 3, if y_j is a parameter, parameter [ arg[2 + j] ] is its value. \param cap_order number of columns in the matrix containing the Taylor coefficients. \par Checked Assertions \li NumArg(CExpOp) == 6 \li NumRes(CExpOp) == 1 \li arg[0] < static_cast ( CompareNe ) \li arg[1] != 0; i.e., not all of y_0, y_1, y_2, y_3 are parameters. \li For j = 0, 1, 2, 3 if y_j is a parameter, arg[2+j] < num_par. \param taylor \b Input: For j = 0, 1, 2, 3, if y_j is a variable then taylor [ arg[2+j] * cap_order + 0 ] is the zero order Taylor coefficient corresponding to y_j. \n \b Output: taylor [ i_z * cap_order + 0 ] is the zero order Taylor coefficient corresponding to z. */ template inline void cexp_forward_0( size_t i_z , const addr_t* arg , size_t num_par , const Base* parameter , size_t cap_order , Base* taylor ) { Base y_0, y_1, y_2, y_3; // Base* z; CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < static_cast (CompareNe) ); CPPAD_ASSERT_UNKNOWN( NumArg(CExpOp) == 6 ); CPPAD_ASSERT_UNKNOWN( NumRes(CExpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( arg[1] != 0 ); if( arg[1] & 1 ) { y_0 = taylor[ size_t(arg[2]) * cap_order + 0 ]; } else { CPPAD_ASSERT_UNKNOWN( size_t(arg[2]) < num_par ); y_0 = parameter[ arg[2] ]; } if( arg[1] & 2 ) { y_1 = taylor[ size_t(arg[3]) * cap_order + 0 ]; } else { CPPAD_ASSERT_UNKNOWN( size_t(arg[3]) < num_par ); y_1 = parameter[ arg[3] ]; } if( arg[1] & 4 ) { y_2 = taylor[ size_t(arg[4]) * cap_order + 0 ]; } else { CPPAD_ASSERT_UNKNOWN( size_t(arg[4]) < num_par ); y_2 = parameter[ arg[4] ]; } if( arg[1] & 8 ) { y_3 = taylor[ size_t(arg[5]) * cap_order + 0 ]; } else { CPPAD_ASSERT_UNKNOWN( size_t(arg[5]) < num_par ); y_3 = parameter[ arg[5] ]; } z = taylor + i_z * cap_order; z[0] = CondExpOp( CompareOp( arg[0] ), y_0, y_1, y_2, y_3 ); return; } /*! Compute reverse mode Taylor coefficients for op = CExpOp. This routine is given the partial derivatives of a function G( z , y , x , w , ... ) and it uses them to compute the partial derivatives of \verbatim H( y , x , w , u , ... ) = G[ z(y) , y , x , w , u , ... ] \endverbatim where y above represents y_0, y_1, y_2, y_3. The C++ source code corresponding to this operation is \verbatim z = CondExpRel(y_0, y_1, y_2, y_3) \endverbatim where Rel is one of the following: Lt, Le, Eq, Ge, Gt. \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base. \param i_z is the AD variable index corresponding to the variable z. \param arg \n arg[0] is static cast to size_t from the enum type \verbatim enum CompareOp { CompareLt, CompareLe, CompareEq, CompareGe, CompareGt, CompareNe } \endverbatim for this operation. Note that arg[0] cannot be equal to CompareNe. \n \n arg[1] & 1 \n If this is zero, y_0 is a parameter. Otherwise it is a variable. \n \n arg[1] & 2 \n If this is zero, y_1 is a parameter. Otherwise it is a variable. \n \n arg[1] & 4 \n If this is zero, y_2 is a parameter. Otherwise it is a variable. \n \n arg[1] & 8 \n If this is zero, y_3 is a parameter. Otherwise it is a variable. \n \n arg[2 + j ] for j = 0, 1, 2, 3 \n is the index corresponding to y_j. \param num_par is the total number of values in the vector parameter. \param parameter For j = 0, 1, 2, 3, if y_j is a parameter, parameter [ arg[2 + j] ] is its value. \param cap_order number of columns in the matrix containing the Taylor coefficients. \par Checked Assertions \li NumArg(CExpOp) == 6 \li NumRes(CExpOp) == 1 \li arg[0] < static_cast ( CompareNe ) \li arg[1] != 0; i.e., not all of y_0, y_1, y_2, y_3 are parameters. \li For j = 0, 1, 2, 3 if y_j is a parameter, arg[2+j] < num_par. \param d is the order of the Taylor coefficient of z that we are computing. \param taylor \b Input: For j = 0, 1, 2, 3 and k = 0 , ... , d, if y_j is a variable then taylor [ arg[2+j] * cap_order + k ] is the k-th order Taylor coefficient corresponding to y_j. \n taylor [ i_z * cap_order + k ] for k = 0 , ... , d is the k-th order Taylor coefficient corresponding to z. \param n_order number of columns in the matrix containing the Taylor coefficients. \param partial \b Input: For j = 0, 1, 2, 3 and k = 0 , ... , d, if y_j is a variable then partial [ arg[2+j] * n_order + k ] is the partial derivative of G( z , y , x , w , u , ... ) with respect to the k-th order Taylor coefficient corresponding to y_j. \n \b Input: partial [ i_z * cap_order + k ] for k = 0 , ... , d is the partial derivative of G( z , y , x , w , u , ... ) with respect to the k-th order Taylor coefficient corresponding to z. \n \b Output: For j = 0, 1, 2, 3 and k = 0 , ... , d, if y_j is a variable then partial [ arg[2+j] * n_order + k ] is the partial derivative of H( y , x , w , u , ... ) with respect to the k-th order Taylor coefficient corresponding to y_j. */ template inline void cexp_reverse( size_t i_z , const addr_t* arg , size_t num_par , const Base* parameter , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // size_t d = n_order - 1; // Base y_0, y_1; Base zero(0); Base* pz; Base* py_2; Base* py_3; CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < static_cast (CompareNe) ); CPPAD_ASSERT_UNKNOWN( NumArg(CExpOp) == 6 ); CPPAD_ASSERT_UNKNOWN( NumRes(CExpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( arg[1] != 0 ); pz = partial + i_z * n_order + 0; if( arg[1] & 1 ) { y_0 = taylor[ size_t(arg[2]) * cap_order + 0 ]; } else { CPPAD_ASSERT_UNKNOWN( size_t(arg[2]) < num_par ); y_0 = parameter[ arg[2] ]; } if( arg[1] & 2 ) { y_1 = taylor[ size_t(arg[3]) * cap_order + 0 ]; } else { CPPAD_ASSERT_UNKNOWN( size_t(arg[3]) < num_par ); y_1 = parameter[ arg[3] ]; } if( arg[1] & 4 ) { py_2 = partial + size_t(arg[4]) * n_order; size_t j = d + 1; while(j--) { py_2[j] += CondExpOp( CompareOp( arg[0] ), y_0, y_1, pz[j], zero ); } } if( arg[1] & 8 ) { py_3 = partial + size_t(arg[5]) * n_order; size_t j = d + 1; while(j--) { py_3[j] += CondExpOp( CompareOp( arg[0] ), y_0, y_1, zero, pz[j] ); } } return; } /*! Compute forward Jacobian sparsity patterns for op = CExpOp. The C++ source code corresponding to this operation is \verbatim z = CondExpRel(y_0, y_1, y_2, y_3) \endverbatim where Rel is one of the following: Lt, Le, Eq, Ge, Gt. \tparam Vector_set is the type used for vectors of sets. It can be either sparse::pack_setvec or sparse::list_setvec. \param i_z is the AD variable index corresponding to the variable z. \param arg \n arg[0] is static cast to size_t from the enum type \verbatim enum CompareOp { CompareLt, CompareLe, CompareEq, CompareGe, CompareGt, CompareNe } \endverbatim for this operation. Note that arg[0] cannot be equal to CompareNe. \n \n arg[1] & 1 \n If this is zero, y_0 is a parameter. Otherwise it is a variable. \n \n arg[1] & 2 \n If this is zero, y_1 is a parameter. Otherwise it is a variable. \n \n arg[1] & 4 \n If this is zero, y_2 is a parameter. Otherwise it is a variable. \n \n arg[1] & 8 \n If this is zero, y_3 is a parameter. Otherwise it is a variable. \n \n arg[2 + j ] for j = 0, 1, 2, 3 \n is the index corresponding to y_j. \param num_par is the total number of values in the vector parameter. \par Checked Assertions \li NumArg(CExpOp) == 6 \li NumRes(CExpOp) == 1 \li arg[0] < static_cast ( CompareNe ) \li arg[1] != 0; i.e., not all of y_0, y_1, y_2, y_3 are parameters. \li For j = 0, 1, 2, 3 if y_j is a parameter, arg[2+j] < num_par. \param dependency Are the derivatives with respect to left and right of the expression below considered to be non-zero: \code CondExpRel(left, right, if_true, if_false) \endcode This is used by the optimizer to obtain the correct dependency relations. \param sparsity \b Input: if y_2 is a variable, the set with index t is the sparsity pattern corresponding to y_2. This identifies which of the independent variables the variable y_2 depends on. \n \b Input: if y_3 is a variable, the set with index t is the sparsity pattern corresponding to y_3. This identifies which of the independent variables the variable y_3 depends on. \n \b Output: The set with index T is the sparsity pattern corresponding to z. This identifies which of the independent variables the variable z depends on. */ template inline void cexp_for_jac( bool dependency , size_t i_z , const addr_t* arg , size_t num_par , Vector_set& sparsity ) { // // CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < static_cast (CompareNe) ); CPPAD_ASSERT_UNKNOWN( NumArg(CExpOp) == 6 ); CPPAD_ASSERT_UNKNOWN( NumRes(CExpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( arg[1] != 0 ); # ifndef NDEBUG addr_t k = 1; for( size_t j = 0; j < 4; j++) { if( ! ( arg[1] & k ) ) CPPAD_ASSERT_UNKNOWN( size_t(arg[2+j]) < num_par ); k *= 2; } # endif sparsity.clear(i_z); if( dependency ) { if( arg[1] & 1 ) sparsity.binary_union(i_z, i_z, size_t(arg[2]), sparsity); if( arg[1] & 2 ) sparsity.binary_union(i_z, i_z, size_t(arg[3]), sparsity); } if( arg[1] & 4 ) sparsity.binary_union(i_z, i_z, size_t(arg[4]), sparsity); if( arg[1] & 8 ) sparsity.binary_union(i_z, i_z, size_t(arg[5]), sparsity); return; } /*! Compute reverse Jacobian sparsity patterns for op = CExpOp. This routine is given the sparsity patterns for a function G(z, y, x, ... ) and it uses them to compute the sparsity patterns for \verbatim H( y, x, w , u , ... ) = G[ z(x,y) , y , x , w , u , ... ] \endverbatim where y represents the combination of y_0, y_1, y_2, and y_3. The C++ source code corresponding to this operation is \verbatim z = CondExpRel(y_0, y_1, y_2, y_3) \endverbatim where Rel is one of the following: Lt, Le, Eq, Ge, Gt. \tparam Vector_set is the type used for vectors of sets. It can be either sparse::pack_setvec or sparse::list_setvec. \param i_z is the AD variable index corresponding to the variable z. \param arg \n arg[0] is static cast to size_t from the enum type \verbatim enum CompareOp { CompareLt, CompareLe, CompareEq, CompareGe, CompareGt, CompareNe } \endverbatim for this operation. Note that arg[0] cannot be equal to CompareNe. \n \n arg[1] & 1 \n If this is zero, y_0 is a parameter. Otherwise it is a variable. \n \n arg[1] & 2 \n If this is zero, y_1 is a parameter. Otherwise it is a variable. \n \n arg[1] & 4 \n If this is zero, y_2 is a parameter. Otherwise it is a variable. \n \n arg[1] & 8 \n If this is zero, y_3 is a parameter. Otherwise it is a variable. \n \n arg[2 + j ] for j = 0, 1, 2, 3 \n is the index corresponding to y_j. \param num_par is the total number of values in the vector parameter. \par Checked Assertions \li NumArg(CExpOp) == 6 \li NumRes(CExpOp) == 1 \li arg[0] < static_cast ( CompareNe ) \li arg[1] != 0; i.e., not all of y_0, y_1, y_2, y_3 are parameters. \li For j = 0, 1, 2, 3 if y_j is a parameter, arg[2+j] < num_par. \param dependency Are the derivatives with respect to left and right of the expression below considered to be non-zero: \code CondExpRel(left, right, if_true, if_false) \endcode This is used by the optimizer to obtain the correct dependency relations. \param sparsity if y_2 is a variable, the set with index t is the sparsity pattern corresponding to y_2. This identifies which of the dependent variables depend on the variable y_2. On input, this pattern corresponds to the function G. On output, it corresponds to the function H. \n \n if y_3 is a variable, the set with index t is the sparsity pattern corresponding to y_3. This identifies which of the dependent variables depeond on the variable y_3. On input, this pattern corresponds to the function G. On output, it corresponds to the function H. \n \b Output: The set with index T is the sparsity pattern corresponding to z. This identifies which of the dependent variables depend on the variable z. On input and output, this pattern corresponds to the function G. */ template inline void cexp_rev_jac( bool dependency , size_t i_z , const addr_t* arg , size_t num_par , Vector_set& sparsity ) { // // CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < static_cast (CompareNe) ); CPPAD_ASSERT_UNKNOWN( NumArg(CExpOp) == 6 ); CPPAD_ASSERT_UNKNOWN( NumRes(CExpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( arg[1] != 0 ); # ifndef NDEBUG addr_t k = 1; for( size_t j = 0; j < 4; j++) { if( ! ( arg[1] & k ) ) CPPAD_ASSERT_UNKNOWN( size_t(arg[2+j]) < num_par ); k *= 2; } # endif if( dependency ) { if( arg[1] & 1 ) sparsity.binary_union( size_t(arg[2]), size_t(arg[2]), i_z, sparsity); if( arg[1] & 2 ) sparsity.binary_union( size_t(arg[3]), size_t(arg[3]), i_z, sparsity); } // -------------------------------------------------------------------- if( arg[1] & 4 ) sparsity.binary_union( size_t(arg[4]), size_t(arg[4]), i_z, sparsity); if( arg[1] & 8 ) sparsity.binary_union( size_t(arg[5]), size_t(arg[5]), i_z, sparsity); return; } /*! Compute reverse Hessian sparsity patterns for op = CExpOp. This routine is given the sparsity patterns for a function G(z, y, x, ... ) and it uses them to compute the sparsity patterns for \verbatim H( y, x, w , u , ... ) = G[ z(x,y) , y , x , w , u , ... ] \endverbatim where y represents the combination of y_0, y_1, y_2, and y_3. The C++ source code corresponding to this operation is \verbatim z = CondExpRel(y_0, y_1, y_2, y_3) \endverbatim where Rel is one of the following: Lt, Le, Eq, Ge, Gt. \tparam Vector_set is the type used for vectors of sets. It can be either sparse::pack_setvec or sparse::list_setvec. \param i_z is the AD variable index corresponding to the variable z. \param arg \n arg[0] is static cast to size_t from the enum type \verbatim enum CompareOp { CompareLt, CompareLe, CompareEq, CompareGe, CompareGt, CompareNe } \endverbatim for this operation. Note that arg[0] cannot be equal to CompareNe. \n \n arg[1] & 1 \n If this is zero, y_0 is a parameter. Otherwise it is a variable. \n \n arg[1] & 2 \n If this is zero, y_1 is a parameter. Otherwise it is a variable. \n \n arg[1] & 4 \n If this is zero, y_2 is a parameter. Otherwise it is a variable. \n \n arg[1] & 8 \n If this is zero, y_3 is a parameter. Otherwise it is a variable. \n \n arg[2 + j ] for j = 0, 1, 2, 3 \n is the index corresponding to y_j. \param num_par is the total number of values in the vector parameter. \par Checked Assertions \li NumArg(CExpOp) == 6 \li NumRes(CExpOp) == 1 \li arg[0] < static_cast ( CompareNe ) \li arg[1] != 0; i.e., not all of y_0, y_1, y_2, y_3 are parameters. \li For j = 0, 1, 2, 3 if y_j is a parameter, arg[2+j] < num_par. \param jac_reverse jac_reverse[i_z] is false (true) if the Jacobian of G with respect to z is always zero (may be non-zero). \n \n jac_reverse[ arg[4] ] If y_2 is a variable, jac_reverse[ arg[4] ] is false (true) if the Jacobian with respect to y_2 is always zero (may be non-zero). On input, it corresponds to the function G, and on output it corresponds to the function H. \n \n jac_reverse[ arg[5] ] If y_3 is a variable, jac_reverse[ arg[5] ] is false (true) if the Jacobian with respect to y_3 is always zero (may be non-zero). On input, it corresponds to the function G, and on output it corresponds to the function H. \param hes_sparsity The set with index i_z in hes_sparsity is the Hessian sparsity pattern for the function G where one of the partials is with respect to z. \n \n If y_2 is a variable, the set with index arg[4] in hes_sparsity is the Hessian sparsity pattern where one of the partials is with respect to y_2. On input, this pattern corresponds to the function G. On output, this pattern corresponds to the function H. \n \n If y_3 is a variable, the set with index arg[5] in hes_sparsity is the Hessian sparsity pattern where one of the partials is with respect to y_3. On input, this pattern corresponds to the function G. On output, this pattern corresponds to the function H. */ template inline void cexp_rev_hes( size_t i_z , const addr_t* arg , size_t num_par , bool* jac_reverse , Vector_set& hes_sparsity ) { // // CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < static_cast (CompareNe) ); CPPAD_ASSERT_UNKNOWN( NumArg(CExpOp) == 6 ); CPPAD_ASSERT_UNKNOWN( NumRes(CExpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( arg[1] != 0 ); # ifndef NDEBUG addr_t k = 1; for( size_t j = 0; j < 4; j++) { if( ! ( arg[1] & k ) ) CPPAD_ASSERT_UNKNOWN( size_t(arg[2+j]) < num_par ); k *= 2; } # endif if( arg[1] & 4 ) { hes_sparsity.binary_union( size_t(arg[4]), size_t(arg[4]), i_z, hes_sparsity); jac_reverse[ arg[4] ] |= jac_reverse[i_z]; } if( arg[1] & 8 ) { hes_sparsity.binary_union( size_t(arg[5]), size_t(arg[5]), i_z, hes_sparsity); jac_reverse[ arg[5] ] |= jac_reverse[i_z]; } return; } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/compare_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_COMPARE_OP_HPP # define CPPAD_LOCAL_VAR_OP_COMPARE_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // --------------------------------------------------------------------------- /* {xrst_begin_parent var_compare_op dev} {xrst_spell eqpp eqpv eqvv lepp lepv levp levv ltpp ltpv ltvp ltvv nepp nepv nevv } Comparison Operators #################### User Syntax *********** *z* = *x* *op* *y* x * is the left operand for this comparison. y * is the right operand for this comparison. z * is he result for this operator which is a boolean (not a variable or parameter). op ** is one of the following: ==, <=, <, != . The >= and > comparisons are folded into <= and < by switching *x* and *y* . op_code ******* EqppOp, LeppOp, LtppOp, NeppOp ============================== These operators implement ==. <=, <, and != for the case where both *x* and *y* are parameters. EqpvOp, LepvOp, LtpvOp, NepvOp ============================== These operators implement ==. <=, <, and != for the case where *x* is a parameter and *y* is a variable. LevpOp, LtvpOp ============== These operators implement <= and < for the case where *x* is a variable and *y* is a parameter. The == and != operators are folded into EqpvOp and NepvOp by switching *x* and *y* . EqvvOp, LevvOp, LtvvOp, NevvOp ============================== These operators implement ==. <=, <, and != for the case where both *x* and *y* are variables. arg *** arg[0] ====== If *x* is a variable (parameter) *arg* [0] is the variable index (parameter index) corresponding to *x* . arg[1] ====== If *y* is a variable (parameter) *arg* [1] is the variable index (parameter index) corresponding to *y* . {xrst_end var_compare_op} ------------------------------------------------------------------------------- {xrst_begin var_compare_forward_any dev} Forward Comparison Operators ############################ Prototype ********* {xrst_literal // BEGIN_COMPARE_FORWARD_ANY // END_COMPARE_FORWARD_ANY } RecBase ******* is the base type use when recording this operator; i.e., this operation was recording using AD< *RecBase* > operations. Base **** is the type used for computations by this operator. This is either *RecBase* or AD< *RecBase* >. type *Base* . op_code, arg ************ see :ref:`var_compare_op@op_code` parameter ********* maps parameter indices to parameter values. cap_order ********* is the maximum number of orders that will fit in *taylor* . taylor ****** The Taylor coefficient corresponding to variable index *i* and order zero is *taylor* [ *i* * *cap_order* ] this_op_index ************* Is the operator index for this compare operation. change_count ************ is the change_number at which *this_op_index* is returned as *change_op_index*. If it is zero, the comparison changes are not counted. change_number ************* If *change_count* is zero, this value is not modified. Otherwise, if this operator comparison has changed (is no longer true), *change_number* is incremented by one. change_op_index *************** If *change_count* is zero, this value is not modified. Otherwise, if this operator comparison has changed (is no longer true), and the new value of *change_number* is equal to *change_count* , *change_op_index* is set equal to *this_op_index* . {xrst_end var_compare_forward_any} */ # include # include namespace CppAD { namespace local { namespace var_op { // BEGIN namespace // BEGIN_COMPARE_FORWARD_ANY template void compare_forward_any( op_code_var op_code , const addr_t* arg , const Base* parameter , size_t cap_order , const Base* taylor , size_t this_op_index , size_t change_count , size_t& change_number , size_t& change_op_index ) // END_COMPARE_FORWARD_ANY { // // n_arg, n_res CPPAD_ASSERT_NARG_NRES(op_code, 2, 0); // // special case if( change_count == 0 ) return; // // x, y Base x, y; switch(op_code) { // // pp case EqppOp: case LeppOp: case LtppOp: case NeppOp: x = parameter[ arg[0] ]; y = parameter[ arg[1] ]; break; // // pv case EqpvOp: case LepvOp: case LtpvOp: case NepvOp: x = parameter[ arg[0] ]; y = taylor[ size_t(arg[1]) * cap_order ]; break; // vp case LevpOp: case LtvpOp: x = taylor[ size_t(arg[0]) * cap_order ]; y = parameter[ arg[1] ]; break; // // vv case EqvvOp: case LevvOp: case LtvvOp: case NevvOp: x = taylor[ size_t(arg[0]) * cap_order ]; y = taylor[ size_t(arg[1]) * cap_order ]; break; // default: // assign x and y to avoid compiler warnings CPPAD_ASSERT_UNKNOWN( false ); x = CppAD::numeric_limits::quiet_NaN(); y = CppAD::numeric_limits::quiet_NaN(); } bool change; switch(op_code) { // case EqppOp: case EqpvOp: case EqvvOp: change = x != y; break; case LeppOp: case LepvOp: case LevpOp: case LevvOp: change = GreaterThanZero(x - y); break; case LtppOp: case LtpvOp: case LtvpOp: case LtvvOp: change = GreaterThanOrZero(x - y); break; case NeppOp: case NepvOp: case NevvOp: change = x == y; break; default: // assign change to avoid compiler warning CPPAD_ASSERT_UNKNOWN( false ); change = true; break; } if( change ) { ++change_number; if( change_number == change_count ) change_op_index = this_op_index; } } }}} // END namespace # endif ================================================ FILE: include/cppad/local/var_op/cos_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_COS_OP_HPP # define CPPAD_LOCAL_VAR_OP_COS_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // See dev documentation: forward_unary_op template inline void cos_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(CosOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(CosOp) == 2 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* c = taylor + i_z * cap_order; Base* s = c - cap_order; // rest of this routine is identical for the following cases: // forward_sin_op, forward_cos_op, forward_sinh_op, forward_cosh_op. // (except that there is a sign difference for the hyperbolic case). size_t k; if( p == 0 ) { s[0] = sin( x[0] ); c[0] = cos( x[0] ); p++; } for(size_t j = p; j <= q; j++) { s[j] = Base(0.0); c[j] = Base(0.0); for(k = 1; k <= j; k++) { s[j] += Base(double(k)) * x[k] * c[j-k]; c[j] -= Base(double(k)) * x[k] * s[j-k]; } s[j] /= Base(double(j)); c[j] /= Base(double(j)); } } // See dev documentation: forward_unary_op template inline void cos_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(CosOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(CosOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + i_x * num_taylor_per_var; Base* c = taylor + i_z * num_taylor_per_var; Base* s = c - num_taylor_per_var; // rest of this routine is identical for the following cases: // forward_sin_op, forward_cos_op, forward_sinh_op, forward_cosh_op // (except that there is a sign difference for the hyperbolic case). size_t m = (q-1) * r + 1; for(size_t ell = 0; ell < r; ell++) { s[m+ell] = Base(double(q)) * x[m + ell] * c[0]; c[m+ell] = - Base(double(q)) * x[m + ell] * s[0]; for(size_t k = 1; k < q; k++) { s[m+ell] += Base(double(k)) * x[(k-1)*r+1+ell] * c[(q-k-1)*r+1+ell]; c[m+ell] -= Base(double(k)) * x[(k-1)*r+1+ell] * s[(q-k-1)*r+1+ell]; } s[m+ell] /= Base(double(q)); c[m+ell] /= Base(double(q)); } } // See dev documentation: forward_unary_op template inline void cos_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(CosOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(CosOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* c = taylor + i_z * cap_order; // called z in documentation Base* s = c - cap_order; // called y in documentation c[0] = cos( x[0] ); s[0] = sin( x[0] ); } // See dev documentation: reverse_unary_op template inline void cos_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // // // i_x size_t i_x = size_t(arg[0]); // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(CosOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(CosOp) == 2 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Taylor coefficients and partials corresponding to argument const Base* x = taylor + i_x * cap_order; Base* px = partial + i_x * n_order; // Taylor coefficients and partials corresponding to first result const Base* c = taylor + i_z * cap_order; // called z in doc Base* pc = partial + i_z * n_order; // Taylor coefficients and partials corresponding to auxiliary result const Base* s = c - cap_order; // called y in documentation Base* ps = pc - n_order; // rest of this routine is identical for the following cases: // reverse_sin_op, reverse_cos_op, reverse_sinh_op, reverse_cosh_op. size_t j = d; size_t k; while(j) { ps[j] /= Base(double(j)); pc[j] /= Base(double(j)); for(k = 1; k <= j; k++) { px[k] += Base(double(k)) * azmul(ps[j], c[j-k]); px[k] -= Base(double(k)) * azmul(pc[j], s[j-k]); ps[j-k] -= Base(double(k)) * azmul(pc[j], x[k]); pc[j-k] += Base(double(k)) * azmul(ps[j], x[k]); } --j; } px[0] += azmul(ps[0], c[0]); px[0] -= azmul(pc[0], s[0]); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/cosh_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_COSH_OP_HPP # define CPPAD_LOCAL_VAR_OP_COSH_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // See dev documentation: forward_unary_op template inline void cosh_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(CoshOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(CoshOp) == 2 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* c = taylor + i_z * cap_order; Base* s = c - cap_order; // rest of this routine is identical for the following cases: // forward_sin_op, forward_cos_op, forward_sinh_op, forward_cosh_op. // (except that there is a sign difference for hyperbolic case). size_t k; if( p == 0 ) { s[0] = sinh( x[0] ); c[0] = cosh( x[0] ); p++; } for(size_t j = p; j <= q; j++) { s[j] = Base(0.0); c[j] = Base(0.0); for(k = 1; k <= j; k++) { s[j] += Base(double(k)) * x[k] * c[j-k]; c[j] += Base(double(k)) * x[k] * s[j-k]; } s[j] /= Base(double(j)); c[j] /= Base(double(j)); } } // See dev documentation: forward_unary_op template inline void cosh_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(CoshOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(CoshOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + i_x * num_taylor_per_var; Base* s = taylor + i_z * num_taylor_per_var; Base* c = s - num_taylor_per_var; // rest of this routine is identical for the following cases: // forward_sin_op, forward_cos_op, forward_sinh_op, forward_cosh_op // (except that there is a sign difference for the hyperbolic case). size_t m = (q-1) * r + 1; for(size_t ell = 0; ell < r; ell++) { s[m+ell] = Base(double(q)) * x[m + ell] * c[0]; c[m+ell] = Base(double(q)) * x[m + ell] * s[0]; for(size_t k = 1; k < q; k++) { s[m+ell] += Base(double(k)) * x[(k-1)*r+1+ell] * c[(q-k-1)*r+1+ell]; c[m+ell] += Base(double(k)) * x[(k-1)*r+1+ell] * s[(q-k-1)*r+1+ell]; } s[m+ell] /= Base(double(q)); c[m+ell] /= Base(double(q)); } } // See dev documentation: forward_unary_op template inline void cosh_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(CoshOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(CoshOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* c = taylor + i_z * cap_order; // called z in documentation Base* s = c - cap_order; // called y in documentation c[0] = cosh( x[0] ); s[0] = sinh( x[0] ); } // See dev documentation: reverse_unary_op template inline void cosh_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // // // i_x size_t i_x = size_t(arg[0]); // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(CoshOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(CoshOp) == 2 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Taylor coefficients and partials corresponding to argument const Base* x = taylor + i_x * cap_order; Base* px = partial + i_x * n_order; // Taylor coefficients and partials corresponding to first result const Base* c = taylor + i_z * cap_order; // called z in doc Base* pc = partial + i_z * n_order; // Taylor coefficients and partials corresponding to auxiliary result const Base* s = c - cap_order; // called y in documentation Base* ps = pc - n_order; // rest of this routine is identical for the following cases: // reverse_sin_op, reverse_cos_op, reverse_sinh_op, reverse_cosh_op. size_t j = d; size_t k; while(j) { ps[j] /= Base(double(j)); pc[j] /= Base(double(j)); for(k = 1; k <= j; k++) { px[k] += Base(double(k)) * azmul(ps[j], c[j-k]); px[k] += Base(double(k)) * azmul(pc[j], s[j-k]); ps[j-k] += Base(double(k)) * azmul(pc[j], x[k]); pc[j-k] += Base(double(k)) * azmul(ps[j], x[k]); } --j; } px[0] += azmul(ps[0], c[0]); px[0] += azmul(pc[0], s[0]); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/cskip_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_CSKIP_OP_HPP # define CPPAD_LOCAL_VAR_OP_CSKIP_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { /*! {xrst_begin_parent var_cskip_op dev} The Conditional Skip Operator ############################# CSkipOp ******* is the op code for this operator. User Syntax *********** | *z* = ``CondExp`` *Rel* ( *left* , *right* , *if_true* , *if_false* ) Rel *** is Lt, Le, Eq, Ge, or Gt . arg *** arg[0] ====== Is the comparison operator for this conditional skip as a static cast from CompareOp to addr_t : {xrst_literal include/cppad/local/declare_ad.hpp // BEGIN_COMPARE_OP // END_COMPARE_OP } Note that arg[0] cannot be equal to CompareNe; i.e. the last enum value CompareNe will not appear. arg[1] ====== The first two bits of this value are used as flags; see below. arg[2] ====== If arg[1] & 1 is zero (is one) , arg[2] is the parameter (variable) index corresponding to *left* . arg[3] ====== If arg[1] & 2 is zero (is one) , arg[3] is the parameter (variable) index corresponding to *right* . arg[4] ====== is the number of operations to skip if the comparison result is true and the left and right operands are :ref:`base_identical@Identical@IdenticalCon` . arg[5] ====== is the number of operations to skip if the comparison result is false and the left and right operands are identically constant. arg[6+i] ======== For i = 0 , ... , arg[4] - 1, arg[6 + i] is the index of an operation to skip if the comparison is identically true. arg[6+arg[4]+i] =============== For i = 0 , ... , arg[5] - 1, arg[6 + i] is the index of an operation to skip if the comparison is identically false. arg[6+arg[4]+arg[5]] ==================== This is equal to 6 + arg[4] + arg[5] + 1, which is the total number or arguments to this operator. Having this value at the end enables reverse mode to know how far to back up to get to the start of this operation. {xrst_end var_cskip_op} ------------------------------------------------------------------------------ {xrst_begin var_cskip_forward_0 dev} Zero Order Forward Conditional Skip Operator ############################################ Prototype ********* {xrst_literal // BEGIN_CSKIP_FORWARD_0 // END_CSKIP_FORWARD_0 } RecBase ******* is the base type use when recording this operator; i.e., this operation was recording using AD< *RecBase* > operations. Base **** is the type used for computations by this operator. This is either *RecBase* or AD< *RecBase* >. i_z *** variable index corresponding to the result of the previous operation. This is only used for error checking. To be specific, if the left and right operand for the conditional expression is a variable, its index must be less than or equal this value. num_par ******* is the total number of values in the *parameter* vector. parameter ********* maps parameter indices to parameter values. cap_order ********* is the maximum number of orders that can fit in *taylor* . taylor ****** For *j* <= *i_z* , the Taylor coefficient corresponding to variable *j* and order zero is *taylor* [ *j* * *cap_order* + 0 ] cskip_op ******** is vector specifying which operations are at this point are know to be unnecessary and can be skipped. This is both an input and an output; i.e., the call may add more true values to *cskip_op* . {xrst_end var_cskip_forward_0} */ // BEGIN_CSKIP_FORWARD_0 template inline void cskip_forward_0( size_t i_z , const addr_t* arg , size_t num_par , const Base* parameter , size_t cap_order , Base* taylor , bool* cskip_op ) // END_CSKIP_FORWARD_0 { // // CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < size_t(CompareNe) ); CPPAD_ASSERT_UNKNOWN( arg[1] != 0 ); // Base left, right; if( arg[1] & 1 ) { // If variable arg[2] <= i_z, it has already been computed, // but it will be skipped for higher orders. CPPAD_ASSERT_UNKNOWN( size_t(arg[2]) <= i_z ); left = taylor[ size_t(arg[2]) * cap_order + 0 ]; } else { CPPAD_ASSERT_UNKNOWN( size_t(arg[2]) < num_par ); left = parameter[ arg[2] ]; } if( arg[1] & 2 ) { // If variable arg[3] <= i_z, it has already been computed, // but it will be skipped for higher orders. CPPAD_ASSERT_UNKNOWN( size_t(arg[3]) <= i_z ); right = taylor[ size_t(arg[3]) * cap_order + 0 ]; } else { CPPAD_ASSERT_UNKNOWN( size_t(arg[3]) < num_par ); right = parameter[ arg[3] ]; } bool ok_to_skip = IdenticalCon(left) && IdenticalCon(right); if( ! ok_to_skip ) return; // initialize to avoid compiler warning bool true_case = false; Base diff = left - right; switch( CompareOp( arg[0] ) ) { case CompareLt: true_case = LessThanZero(diff); break; case CompareLe: true_case = LessThanOrZero(diff); break; case CompareEq: true_case = IdenticalZero(diff); break; case CompareGe: true_case = GreaterThanOrZero(diff); break; case CompareGt: true_case = GreaterThanZero(diff); break; case CompareNe: true_case = ! IdenticalZero(diff); break; default: CPPAD_ASSERT_UNKNOWN(false); } if( true_case ) { for(addr_t i = 0; i < arg[4]; i++) cskip_op[ arg[6+i] ] = true; } else { for(addr_t i = 0; i < arg[5]; i++) cskip_op[ arg[6+arg[4]+i] ] = true; } return; } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/csum_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_CSUM_OP_HPP # define CPPAD_LOCAL_VAR_OP_CSUM_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { /* {xrst_begin_parent var_csum_op dev} Variable Cumulative Summation Operator ###################################### CSumOp ****** is the op code for this operator. User Syntax *********** :: z = s + x[0] + ... + x[ n1 - 1 ] - y[0] - ... - y[ n2 - 1 ] + u[0] + ... + u[ n3 - 1 ] - v[0] - ... - v[ n4 - 1 ] s * is the constant parameter that initializes the summation. x * is the vector of addition variables that appear in the sum. y * is the vector of subtraction variables that appear in the sum. u * is the vector of addition dynamic parameters that appear in the sum. v * is the vector of subtraction dynamic parameters that appear in the sum. z * is the result of the summation. RecBase ******* is the base type use when recording this operator; i.e., this operation was recording using AD< *RecBase* > operations. Base **** is the type used for computations by this operator. This is either *RecBase* or AD< *RecBase* >. i_z *** is the variable index corresponding to the result for this operation; i.e. the row index in taylor corresponding to *z* . arg *** arg[0] ====== is the index of the constant parameter *s* in the parameter vector. arg[1] ====== is the index in arg of the end of the addition variables; i.e., *n1* = arg[1] - 5 . arg[2] ====== is the index in arg of the end of the subtraction variables; i.e., *n2* = arg[2] - arg[1] . arg[3] ====== is the index in arg of the end of the addition dynamic parameters; i.e., *n3* = arg[3] - arg[2] . arg[4] ====== is the index in arg of the end of the subtraction dynamic parameters; i.e., *n4* = arg[4] - arg[3] . arg[5+j] ======== for j = 0 , ... , n1 - 1, arg[5 + j] is the index corresponding to the variable x[j] . arg[ arg[1]+j ] =============== for j = 0 , ... , n2 - 1, arg[ arg[1] + j ] is the index corresponding to the variable y[j] . arg[ arg[2]+j ] =============== for j = 0 , ... , n3 - 1, arg[ arg[2] + j ] is the index corresponding to the dynamic parameter u[j] . arg[ arg[3]+j ] =============== for j = 0 , ... , n4 - 1, arg[ arg[3] + j ] is the index corresponding to the dynamic parameter v[j] . arg[ arg[4] ] ============= This is equal to arg[4] + 1 which is the total number or arguments to this operator. Having this value at the end enables reverse mode to know how far to back up to get to the start of this operation. {xrst_end var_csum_op} ----------------------------------------------------------------------------- {xrst_begin var_csum_forward_any dev} Any Order Forward Cumulative Summation Operation ################################################ Prototype ********* {xrst_literal // BEGIN_CSUM_FORWARD_ANY // END_CSUM_FORWARD_ANY } x, y, u, v, z ************* see :ref:`var_csum_op@x` , :ref:`var_csum_op@y` , :ref:`var_csum_op@u` , :ref:`var_csum_op@v` , :ref:`var_csum_op@z` i_z, arg ******** see :ref:`var_csum_op@i_z` , :ref:`var_csum_op@arg` {xrst_template ; include/cppad/local/var_op/template/forward_op.xrst headers: cap_order, order_low, order_up, taylor } num_par ******* is the number of parameters in parameter. parameter ********* is the parameter vector for this operation sequence. {xrst_end var_csum_forward_any} */ // BEGIN_CSUM_FORWARD_ANY template inline void csum_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t num_par , const Base* parameter , size_t cap_order , Base* taylor ) // END_CSUM_FORWARD_ANY { // // // check assumptions CPPAD_ASSERT_UNKNOWN( NumRes(CSumOp) == 1 ); CPPAD_ASSERT_UNKNOWN( order_up < cap_order ); CPPAD_ASSERT_UNKNOWN( order_low <= order_up ); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); CPPAD_ASSERT_UNKNOWN( arg[arg[4]] == arg[4] + 1 ); // // zero Base zero(0); // // z Base* z = taylor + i_z * cap_order; for(size_t k = order_low; k <= order_up; k++) z[k] = zero; // if( order_low == 0 ) { // constant parameter z[order_low] = parameter[ arg[0] ]; // addition dynamic parameters for(addr_t i = arg[2]; i < arg[3]; ++i) z[order_low] += parameter[ arg[i] ]; // subtraction dynamic parameters for(addr_t i = arg[3]; i < arg[4]; ++i) z[order_low] -= parameter[ arg[i] ]; } // addition variables for(addr_t i = 5; i < arg[1]; ++i) { CPPAD_ASSERT_UNKNOWN( size_t(arg[i]) < i_z ); Base* x = taylor + size_t(arg[i]) * cap_order; for(size_t k = order_low; k <= order_up; k++) z[k] += x[k]; } // subtraction variables for(addr_t i = arg[1]; i < arg[2]; ++i) { CPPAD_ASSERT_UNKNOWN( size_t(arg[i]) < i_z ); Base* x = taylor + size_t(arg[i]) * cap_order; for(size_t k = order_low; k <= order_up; k++) z[k] -= x[k]; } } /* {xrst_begin var_csum_forward_dir dev} Multiple Direction Forward Mode Cumulative Summation Operation ############################################################## Prototype ********* {xrst_literal // BEGIN_CSUM_FORWARD_DIR // END_CSUM_FORWARD_DIR } x, y, u, v, z ************* see :ref:`var_csum_op@x` , :ref:`var_csum_op@y` , :ref:`var_csum_op@u` , :ref:`var_csum_op@v` , :ref:`var_csum_op@z` i_z, arg ******** see :ref:`var_csum_op@i_z` , :ref:`var_csum_op@arg` num_par ******* is the number of parameters in parameter. parameter ********* is the parameter vector for this operation sequence. {xrst_template ; include/cppad/local/var_op/template/forward_dir.xrst headers: n_dir, cap_order, order_up, taylor } {xrst_end var_csum_forward_dir} */ // BEGIN_CSUM_FORWARD_DIR template inline void csum_forward_dir( size_t order_up , size_t i_z , const addr_t* arg , size_t num_par , const Base* parameter , size_t n_dir , size_t cap_order , Base* taylor ) // END_CSUM_FORWARD_DIR { // // CPPAD_ASSERT_UNKNOWN( NumRes(CSumOp) == 1 ); CPPAD_ASSERT_UNKNOWN( order_up < cap_order ); CPPAD_ASSERT_UNKNOWN( 0 < order_up ); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); CPPAD_ASSERT_UNKNOWN( arg[arg[4]] == arg[4] + 1 ); // // zero Base zero(0); // // per_variable size_t per_variable = (cap_order-1) * n_dir + 1; // // m size_t m = (order_up - 1) * n_dir + 1; // // z Base* z = taylor + i_z * per_variable + m; for(size_t ell = 0; ell < n_dir; ell++) z[ell] = zero; // // addition variables for(addr_t i = 5; i < arg[1]; ++i) { CPPAD_ASSERT_UNKNOWN( size_t(arg[i]) < i_z ); Base* x = taylor + size_t(arg[i]) * per_variable + m; for(size_t ell = 0; ell < n_dir; ell++) z[ell] += x[ell]; } // // subtraction variables for(addr_t i = arg[1]; i < arg[2]; ++i) { CPPAD_ASSERT_UNKNOWN( size_t(arg[i]) < i_z ); Base* x = taylor + size_t(arg[i]) * per_variable + m; for(size_t ell = 0; ell < n_dir; ell++) z[ell] -= x[ell]; } } /* --------------------------------------------------------------------------- {xrst_begin var_csum_reverse dev} Reverse Mode Cumulative Summation Operation ########################################### Prototype ********* {xrst_literal // BEGIN_CSUM_REVERSE // END_CSUM_REVERSE } x, y, u, v, z ************* see :ref:`var_csum_op@x` , :ref:`var_csum_op@y` , :ref:`var_csum_op@u` , :ref:`var_csum_op@v` , :ref:`var_csum_op@z` i_z, arg ******** see :ref:`var_csum_op@i_z` , :ref:`var_csum_op@arg` {xrst_template ; include/cppad/local/var_op/template/reverse_op.xrst headers: n_order, partial @x, y@ ; x, y, u, v } {xrst_end var_csum_reverse} */ // BEGIN_CSUM_REVERSE template inline void csum_reverse( size_t i_z , const addr_t* arg , size_t n_order , Base* partial ) // END_CSUM_REVERSE { // d // size_t d = n_order - 1; // // CPPAD_ASSERT_UNKNOWN( NumRes(CSumOp) == 1 ); // // pz, dp1 Base* pz = partial + i_z * n_order; size_t dp1 = d + 1; // // addition variables for(addr_t i = 5; i < arg[1]; ++i) { CPPAD_ASSERT_UNKNOWN( size_t(arg[i]) < i_z ); Base* px = partial + size_t(arg[i]) * n_order; size_t k = dp1; while(k--) px[k] += pz[k]; } // // subtraction variables for(addr_t i = arg[1]; i < arg[2]; ++i) { CPPAD_ASSERT_UNKNOWN( size_t(arg[i]) < i_z ); Base* px = partial + size_t(arg[i]) * n_order; size_t k = dp1; while(k--) px[k] -= pz[k]; } } /* ------------------------------------------------------------------------------ {xrst_begin var_csum_for_jac dev} Forward Jacobian Sparsity for Cumulative Summation ################################################## Prototype ********* {xrst_literal // BEGIN_CSUM_FOR_JAC // END_CSUM_FOR_JAC } x, y, u, v, z ************* see :ref:`var_csum_op@x` , :ref:`var_csum_op@y` , :ref:`var_csum_op@u` , :ref:`var_csum_op@v` , :ref:`var_csum_op@z` i_z, arg ******** see :ref:`var_csum_op@i_z` , :ref:`var_csum_op@arg` Vector_set ********** is the type used for vectors of sets. It must satisfy the :ref:`SetVector-name` concept. i_z sparsity ******** Input ===== For k = 0 , ... , i_z - 1, the set with index k in *sparsity* identifies which independent variables the variable with index k depends on. Output ====== The set with index i_z in *sparsity* identifies which independent variables the variable *z* depends on. {xrst_end var_csum_for_jac} */ // BEGIN_CSUM_FOR_JAC template inline void csum_for_jac( size_t i_z , const addr_t* arg , Vector_set& sparsity ) // END_CSUM_FOR_JAC { // // // sparsity sparsity.clear(i_z); // // addition and subtraction variables for(addr_t i = 5; i < arg[2]; ++i) { CPPAD_ASSERT_UNKNOWN( size_t(arg[i]) < i_z ); sparsity.binary_union( i_z , // index in sparsity for result i_z , // index in sparsity for left operand size_t(arg[i]) , // index for right operand sparsity // sparsity vector for right operand ); } } /* ------------------------------------------------------------------------------ {xrst_begin var_csum_rev_jac dev} Reverse Jacobian Sparsity for Cumulative Summation ################################################## Prototype ********* {xrst_literal // BEGIN_CSUM_REV_JAC // END_CSUM_REV_JAC } x, y, u, v, z ************* see :ref:`var_csum_op@x` , :ref:`var_csum_op@y` , :ref:`var_csum_op@u` , :ref:`var_csum_op@v` , :ref:`var_csum_op@z` i_z, arg ******** see :ref:`var_csum_op@i_z` , :ref:`var_csum_op@arg` Vector_set ********** is the type used for vectors of sets. It must satisfy the :ref:`SetVector-name` concept. sparsity ******** The set with index *i_z* is the sparsity pattern for *z* . This sparsity pattern is added th the sparsity pattern for the variables in the vectors *x* and *y* . {xrst_end var_csum_rev_jac} */ // BEGIN_CSUM_REV_JAC template inline void csum_rev_jac( size_t i_z , const addr_t* arg , Vector_set& sparsity ) // END_CSUM_REV_JAC { // // // addition and subtraction variables for(addr_t i = 5; i < arg[2]; ++i) { CPPAD_ASSERT_UNKNOWN( size_t(arg[i]) < i_z ); sparsity.binary_union( size_t(arg[i]), // index in sparsity for result size_t(arg[i]), // index in sparsity for left operand i_z , // index for right operand sparsity // sparsity vector for right operand ); } } /* {xrst_begin var_csum_rev_hes dev} Reverse Hessian Sparsity for Cumulative Summation ################################################# Prototype ********* {xrst_literal // BEGIN_CSUM_REV_HES // END_CSUM_REV_HES } x, y, u, v, z ************* see :ref:`var_csum_op@x` , :ref:`var_csum_op@y` , :ref:`var_csum_op@u` , :ref:`var_csum_op@v` , :ref:`var_csum_op@z` i_z, arg ******** see :ref:`var_csum_op@i_z` , :ref:`var_csum_op@arg` G, H **** We use *G* to denote the scalar function we are computing the sparsity pattern for as a function of the variables up to and including *z* . We use *H* for the function with *z* replaced by its operator; i.e.:: H(x, y, u, v, ...) = G[ z(x, y, u, v) , x, y, u, v, ... ] Vector_set ********** is the type used for vectors of sets. It must satisfy the :ref:`SetVector-name` concept. rev_jacobian ************ #. If rev_jacobian[i_z] is true (false), *G* may depend (does not depend) on *z* . #. If *j* is the index of an addition variable in the vector *x* or *y* , and *G* may depend on *z* , rev_jacobian[j] is set to true. rev_hes_sparsity **************** On input, *rev_hes_sparsity* contains the Hessian sparsity pattern for the function *G* . On output, it contains the Hessian sparsity pattern for the function *H* . {xrst_end var_csum_rev_hes} */ // BEGIN_CSUM_REV_HES template inline void csum_rev_hes( size_t i_z , const addr_t* arg , bool* rev_jacobian , Vector_set& rev_hes_sparsity ) // END_CSUM_REV_HES { // // // addition and subtraction variables for(addr_t i = 5; i < arg[2]; ++i) { CPPAD_ASSERT_UNKNOWN( size_t(arg[i]) < i_z ); rev_hes_sparsity.binary_union( size_t(arg[i]) , // index in sparsity for result size_t(arg[i]) , // index in sparsity for left operand i_z , // index for right operand rev_hes_sparsity // sparsity vector for right operand ); rev_jacobian[arg[i]] |= rev_jacobian[i_z]; } } /* ------------------------------------------------------------------------------ {xrst_begin var_csum_for_hes dev} Forward Hessian Sparsity for Cumulative Summation ################################################# Prototype ********* {xrst_literal // BEGIN_CSUM_FOR_HES // END_CSUM_FOR_HES } x, y, u, v, z ************* see :ref:`var_csum_op@x` , :ref:`var_csum_op@y` , :ref:`var_csum_op@u` , :ref:`var_csum_op@v` , :ref:`var_csum_op@z` i_z, arg ******** see :ref:`var_csum_op@i_z` , :ref:`var_csum_op@arg` Vector_set ********** is the type used for vectors of sets. It must satisfy the :ref:`SetVector-name` concept. n * is the number of independent variables on the tape. for_hes_sparse ************** see :ref:`local_sweep_for_hes@for_hes_sparse` . {xrst_end var_csum_for_hes} */ // BEGIN_CSUM_FOR_HES template inline void csum_for_hes( const addr_t* arg , size_t i_z , size_t n , Vector_set& for_hes_sparse ) // END_CSUM_FOR_HES { // // // np1 size_t np1 = n + 1; // // for_hes_sparse for_hes_sparse.clear(np1 + i_z); // // addition and subtraction variables for(addr_t i = 5; i < arg[2]; ++i) { CPPAD_ASSERT_UNKNOWN( size_t(arg[i]) < i_z ); // // linear functions only modify forward Jacobian sparsity for_hes_sparse.binary_union( np1 + i_z , // index in sparsity for result np1 + i_z , // index in sparsity for left operand np1 + size_t(arg[i]) , // index for right operand for_hes_sparse // sparsity vector for right operand ); } } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/dis_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_DIS_OP_HPP # define CPPAD_LOCAL_VAR_OP_DIS_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin_parent var_dis_op dev} Variable Discrete Operator ########################## DisOp ***** is the op code for this operator. User Syntax *********** | *z* = *name* ( *x* ) name **** is the :ref:`Discrete@name` of he discrete function. x * is the argument for this discrete function. z * is the new variable created by this function evaluation. (Note that this is called :ref:`Discrete@y` is the user documentation for discrete functions.) arg *** arg[0] ====== is the index that identifies this discrete function. arg[1] ====== variable index corresponding to the argument for this function call. {xrst_end var_dis_op} ------------------------------------------------------------------------------ {xrst_begin dis_forward_dir dev} {xrst_spell ataylor tpv } Forward Mode Result for Discrete Functions ########################################## name, x, z, arg *************** see :ref:`var_dis_op@name` , :ref:`var_dis_op@x` , :ref:`var_dis_op@z` , :ref:`var_dis_op@arg` Prototype ********* RecBase ======= {xrst_literal // BEGIN_DIS_FORWARD_ANY // END_DIS_FORWARD_ANY } AD =========== {xrst_literal // BEGIN_AD_DIS_FORWARD_ANY // END_AD_DIS_FORWARD_ANY } RecBase ******* Is the Base type when this function was recorded; i.e., :ref:`Discrete@ax` and :ref:`Discrete@ay` have type ``AD`` < *RecBase* > . order_low ********* is the lowest order Taylor coefficient that will be calculated. order_up ******** is the highest order Taylor coefficient that will be calculated. n_dir ***** is the number of directions, for each order, that will be calculated (except for order zero which only has one direction). i_z *** variable index corresponding to the result for this operation; i.e. the row index in *taylor* or *ataylor* corresponding to *z* . cap_order ********* is the maximum number of orders that can fit in *taylor* or *ataylor* . tpv *** We use the notation *tpv* = ( *cap_order* - 1 ) * *n_dir* + 1 which is the number of Taylor coefficients per variable taylor ****** Input ===== The zero order Taylor coefficient corresponding to x:: taylor[ arg[1] * tpv + 0 ] Output ====== If *order_low* is zero:: taylor[ i_z * tpv + 0 ] is the zero order Taylor coefficient corresponding to z. For k = max(order_low, 1), ... , order_up, taylor[ i_z * tpv + (k-1)*n_dir + 1 + ell ] is the k-th order Taylor coefficient corresponding to z (which is zero). ataylor ******* This has the same description as *taylor* except that its type is ``AD`` < *RecBase* > instead of *RecBase* . {xrst_end dis_forward_dir} */ namespace CppAD { namespace local { namespace var_op { // --------------------------------------------------------------------------- // BEGIN_DIS_FORWARD_ANY template inline void dis_forward_dir( size_t order_low , size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , RecBase* taylor ) // END_DIS_FORWARD_ANY { // p, q size_t p = order_low; size_t q = order_up; size_t r = n_dir; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(DisOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(DisOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( 0 < r ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; RecBase* x = taylor + size_t(arg[1]) * num_taylor_per_var; RecBase* z = taylor + i_z * num_taylor_per_var; if( p == 0 ) { z[0] = discrete::eval(size_t(arg[0]), x[0]); p++; } for(size_t ell = 0; ell < r; ell++) for(size_t k = p; k <= q; k++) z[ (k-1) * r + 1 + ell ] = RecBase(0.0); } // --------------------------------------------------------------------------- // BEGIN_AD_DIS_FORWARD_ANY template inline void dis_forward_dir( size_t order_low , size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , AD* ataylor ) // END_AD_DIS_FORWARD_ANY { // p, q size_t p = order_low; size_t q = order_up; size_t r = n_dir; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(DisOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(DisOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( 0 < r ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; AD* ax = ataylor + size_t(arg[1]) * num_taylor_per_var; AD* az = ataylor + i_z * num_taylor_per_var; if( p == 0 ) { az[0] = discrete::ad_eval(size_t(arg[0]), ax[0]); p++; } for(size_t ell = 0; ell < r; ell++) for(size_t k = p; k <= q; k++) az[ (k-1) * r + 1 + ell ] = AD(0.0); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/div_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_DIV_OP_HPP # define CPPAD_LOCAL_VAR_OP_DIV_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // --------------------------- Divvv ----------------------------------------- // See dev documentation: forward_binary_op template inline void divvv_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(DivvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(DivvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to arguments and result Base* x = taylor + size_t(arg[0]) * cap_order; Base* y = taylor + size_t(arg[1]) * cap_order; Base* z = taylor + i_z * cap_order; // Using CondExp, it can make sense to divide by zero, // so do not make it an error. size_t k; for(size_t d = p; d <= q; d++) { z[d] = x[d]; for(k = 1; k <= d; k++) z[d] -= z[d-k] * y[k]; z[d] /= y[0]; } } // See dev documentation: forward_binary_op template inline void divvv_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(DivvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(DivvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to arguments and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + size_t(arg[0]) * num_taylor_per_var; Base* y = taylor + size_t(arg[1]) * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; // Using CondExp, it can make sense to divide by zero, // so do not make it an error. size_t m = (q-1) * r + 1; for(size_t ell = 0; ell < r; ell++) { z[m+ell] = x[m+ell] - z[0] * y[m+ell]; for(size_t k = 1; k < q; k++) z[m+ell] -= z[(q-k-1)*r+1+ell] * y[(k-1)*r+1+ell]; z[m+ell] /= y[0]; } } // See dev documentation: forward_binary_op template inline void divvv_forward_0( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(DivvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(DivvvOp) == 1 ); // Taylor coefficients corresponding to arguments and result Base* x = taylor + size_t(arg[0]) * cap_order; Base* y = taylor + size_t(arg[1]) * cap_order; Base* z = taylor + i_z * cap_order; z[0] = x[0] / y[0]; } // See dev documentation: reverse_binary_op template inline void divvv_reverse( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(DivvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(DivvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Arguments const Base* y = taylor + size_t(arg[1]) * cap_order; const Base* z = taylor + i_z * cap_order; // Partial derivatives corresponding to arguments and result Base* px = partial + size_t(arg[0]) * n_order; Base* py = partial + size_t(arg[1]) * n_order; Base* pz = partial + i_z * n_order; // Using CondExp, it can make sense to divide by zero // so do not make it an error. Base inv_y0 = Base(1.0) / y[0]; size_t k; // number of indices to access size_t j = d + 1; while(j) { --j; // scale partial w.r.t. z[j] pz[j] = azmul(pz[j], inv_y0); px[j] += pz[j]; for(k = 1; k <= j; k++) { pz[j-k] -= azmul(pz[j], y[k] ); py[k] -= azmul(pz[j], z[j-k]); } py[0] -= azmul(pz[j], z[j]); } } // --------------------------- Divpv ----------------------------------------- // See dev documentation: forward_binary_op template inline void divpv_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(DivpvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(DivpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to arguments and result Base* y = taylor + size_t(arg[1]) * cap_order; Base* z = taylor + i_z * cap_order; // Paraemter value Base x = parameter[ arg[0] ]; // Using CondExp, it can make sense to divide by zero, // so do not make it an error. size_t k; if( p == 0 ) { z[0] = x / y[0]; p++; } for(size_t d = p; d <= q; d++) { z[d] = Base(0.0); for(k = 1; k <= d; k++) z[d] -= z[d-k] * y[k]; z[d] /= y[0]; } } // See dev documentation: forward_binary_op template inline void divpv_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(DivpvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(DivpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to arguments and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* y = taylor + size_t(arg[1]) * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; // Using CondExp, it can make sense to divide by zero, // so do not make it an error. size_t m = (q-1) * r + 1; for(size_t ell = 0; ell < r; ell++) { z[m+ell] = - z[0] * y[m+ell]; for(size_t k = 1; k < q; k++) z[m+ell] -= z[(q-k-1)*r+1+ell] * y[(k-1)*r+1+ell]; z[m+ell] /= y[0]; } } // See dev documentation: forward_binary_op template inline void divpv_forward_0( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(DivpvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(DivpvOp) == 1 ); // Paraemter value Base x = parameter[ arg[0] ]; // Taylor coefficients corresponding to arguments and result Base* y = taylor + size_t(arg[1]) * cap_order; Base* z = taylor + i_z * cap_order; z[0] = x / y[0]; } // See dev documentation: reverse_binary_op template inline void divpv_reverse( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(DivvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(DivvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Arguments const Base* y = taylor + size_t(arg[1]) * cap_order; const Base* z = taylor + i_z * cap_order; // Partial derivatives corresponding to arguments and result Base* py = partial + size_t(arg[1]) * n_order; Base* pz = partial + i_z * n_order; // Using CondExp, it can make sense to divide by zero so do not // make it an error. Base inv_y0 = Base(1.0) / y[0]; size_t k; // number of indices to access size_t j = d + 1; while(j) { --j; // scale partial w.r.t z[j] pz[j] = azmul(pz[j], inv_y0); for(k = 1; k <= j; k++) { pz[j-k] -= azmul(pz[j], y[k] ); py[k] -= azmul(pz[j], z[j-k] ); } py[0] -= azmul(pz[j], z[j]); } } // --------------------------- Divvp ----------------------------------------- // See dev documentation: forward_binary_op template inline void divvp_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(DivvpOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(DivvpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to arguments and result Base* x = taylor + size_t(arg[0]) * cap_order; Base* z = taylor + i_z * cap_order; // Parameter value Base y = parameter[ arg[1] ]; // Using CondExp and multiple levels of AD, it can make sense // to divide by zero so do not make it an error. for(size_t d = p; d <= q; d++) z[d] = x[d] / y; } // See dev documentation: forward_binary_op template inline void divvp_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(DivvpOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(DivvpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( 0 < q ); // Taylor coefficients corresponding to arguments and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + size_t(arg[0]) * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; // Parameter value Base y = parameter[ arg[1] ]; // Using CondExp and multiple levels of AD, it can make sense // to divide by zero so do not make it an error. size_t m = (q-1)*r + 1; for(size_t ell = 0; ell < r; ell++) z[m + ell] = x[m + ell] / y; } // See dev documentation: forward_binary_op template inline void divvp_forward_0( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(DivvpOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(DivvpOp) == 1 ); // Parameter value Base y = parameter[ arg[1] ]; // Taylor coefficients corresponding to arguments and result Base* x = taylor + size_t(arg[0]) * cap_order; Base* z = taylor + i_z * cap_order; z[0] = x[0] / y; } // See dev documentation: reverse_binary_op template inline void divvp_reverse( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(DivvpOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(DivvpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Argument values Base y = parameter[ arg[1] ]; // Partial derivatives corresponding to arguments and result Base* px = partial + size_t(arg[0]) * n_order; Base* pz = partial + i_z * n_order; // Using CondExp, it can make sense to divide by zero // so do not make it an error. Base inv_y = Base(1.0) / y; // number of indices to access size_t j = d + 1; while(j) { --j; px[j] += azmul(pz[j], inv_y); } } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/erf_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_ERF_OP_HPP # define CPPAD_LOCAL_VAR_OP_ERF_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include namespace CppAD { namespace local { namespace var_op { /*! \file erf_op.hpp Forward and reverse mode calculations for z = erf(x) or erfc(x). */ /*! Forward mode Taylor coefficient for result of op = ErfOp or ErfcOp. The C++ source code corresponding to this operation is one of \verbatim z = erf(x) z = erfc(x) \endverbatim \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base. \param op must be either ErfOp or ErfcOp and indicates if this is z = erf(x) or z = erfc(x). \param p lowest order of the Taylor coefficients that we are computing. \param q highest order of the Taylor coefficients that we are computing. \param i_z variable index corresponding to the last (primary) result for this operation; i.e. the row index in taylor corresponding to z. The auxiliary results are called y_j have index i_z - j. \param arg arg[0]: is the variable index corresponding to x. \n arg[1]: is the parameter index corresponding to the value zero. \n arg[2]: is the parameter index correspodning to the value 2 / sqrt(pi). \param parameter parameter[ arg[1] ] is the value zero, and parameter[ arg[2] ] is the value 2 / sqrt(pi). \param cap_order maximum number of orders that will fit in the taylor array. \param taylor \b Input: taylor [ size_t(arg[0]) * cap_order + k ] for k = 0 , ... , q, is the k-th order Taylor coefficient corresponding to x. \n \b Input: taylor [ i_z * cap_order + k ] for k = 0 , ... , p - 1, is the k-th order Taylor coefficient corresponding to z. \n \b Input: taylor [ ( i_z - j) * cap_order + k ] for k = 0 , ... , p-1, and j = 0 , ... , 4, is the k-th order Taylor coefficient corresponding to the j-th result for z. \n \b Output: taylor [ (i_z-j) * cap_order + k ], for k = p , ... , q, and j = 0 , ... , 4, is the k-th order Taylor coefficient corresponding to the j-th result for z. */ template inline void erf_forward_any( op_code_var op , size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // check assumptions CPPAD_ASSERT_UNKNOWN( op == ErfOp || op == ErfcOp ); CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); CPPAD_ASSERT_UNKNOWN( NumRes(op) == 5 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); CPPAD_ASSERT_UNKNOWN( size_t( std::numeric_limits::max() ) >= i_z + 2 ); // array used to pass parameter values for sub-operations addr_t addr[2]; // convert from final result to first result i_z -= 4; // 4 = NumRes(ErfOp) - 1; // z_0 = x * x addr[0] = arg[0]; // x addr[1] = arg[0]; // x mulvv_forward_any(p, q, i_z+0, addr, parameter, cap_order, taylor); // z_1 = - x * x addr[0] = arg[1]; // zero addr[1] = addr_t( i_z ); // z_0 subpv_forward_any(p, q, i_z+1, addr, parameter, cap_order, taylor); // z_2 = exp( - x * x ) addr[0] = addr_t(i_z+1); exp_forward_any(p, q, i_z+2, addr, cap_order, taylor); // z_3 = (2 / sqrt(pi)) * exp( - x * x ) addr[0] = arg[2]; // 2 / sqrt(pi) addr[1] = addr_t( i_z + 2 ); // z_2 mulpv_forward_any(p, q, i_z+3, addr, parameter, cap_order, taylor); // pointers to taylor coefficients for x , z_3, and z_4 Base* x = taylor + size_t(arg[0]) * cap_order; Base* z_3 = taylor + (i_z+3) * cap_order; Base* z_4 = taylor + (i_z+4) * cap_order; // calculate z_4 coefficients if( p == 0 ) { // z4 (t) = erf[x(t)] if( op == ErfOp ) z_4[0] = erf(x[0]); else z_4[0] = erfc(x[0]); p++; } // sign Base sign(1.0); if( op == ErfcOp ) sign = Base(-1.0); // for(size_t j = p; j <= q; j++) { // erf: z_4' (t) = erf'[x(t)] * x'(t) = z3(t) * x'(t) // erfc: z_4' (t) = - erf'[x(t)] * x'(t) = - z3(t) * x'(t) // z_4[1] + 2 * z_4[2] * t + ... = // sign * (z_3[0] + z_3[1] * t + ...) * (x[1] + 2 * x[2] * t + ...) Base base_j = static_cast(double(j)); z_4[j] = static_cast(0); for(size_t k = 1; k <= j; k++) z_4[j] += sign * (Base(double(k)) / base_j) * x[k] * z_3[j-k]; } } /*! Zero order Forward mode Taylor coefficient for result of op = ErfOp or ErfcOp. The C++ source code corresponding to this operation one of \verbatim z = erf(x) z = erfc(x) \endverbatim \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base. \param op must be either ErfOp or ErfcOp and indicates if this is z = erf(x) or z = erfc(x). \param i_z variable index corresponding to the last (primary) result for this operation; i.e. the row index in taylor corresponding to z. The auxiliary results are called y_j have index i_z - j. \param arg arg[0]: is the variable index corresponding to x. \n arg[1]: is the parameter index corresponding to the value zero. \n arg[2]: is the parameter index correspodning to the value 2 / sqrt(pi). \param parameter parameter[ arg[1] ] is the value zero, and parameter[ arg[2] ] is the value 2 / sqrt(pi). \param cap_order maximum number of orders that will fit in the taylor array. \param taylor \b Input: taylor [ size_t(arg[0]) * cap_order + 0 ] is the zero order Taylor coefficient corresponding to x. \n \b Input: taylor [ i_z * cap_order + 0 ] is the zero order Taylor coefficient corresponding to z. \n \b Output: taylor [ (i_z-j) * cap_order + 0 ], for j = 0 , ... , 4, is the zero order Taylor coefficient for j-th result corresponding to z. */ template inline void erf_forward_0( op_code_var op , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // // // check assumptions CPPAD_ASSERT_UNKNOWN( op == ErfOp || op == ErfcOp ); CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); CPPAD_ASSERT_UNKNOWN( NumRes(op) == 5 ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); CPPAD_ASSERT_UNKNOWN( size_t( std::numeric_limits::max() ) >= i_z + 2 ); // array used to pass parameter values for sub-operations addr_t addr[2]; // convert from final result to first result i_z -= 4; // 4 = NumRes(ErfOp) - 1; // z_0 = x * x addr[0] = arg[0]; // x addr[1] = arg[0]; // x mulvv_forward_0(i_z+0, addr, parameter, cap_order, taylor); // z_1 = - x * x addr[0] = arg[1]; // zero addr[1] = addr_t(i_z); // z_0 subpv_forward_0(i_z+1, addr, parameter, cap_order, taylor); // z_2 = exp( - x * x ) addr[0] = addr_t(i_z+1); exp_forward_0(i_z+2, addr, cap_order, taylor); // z_3 = (2 / sqrt(pi)) * exp( - x * x ) addr[0] = arg[2]; // 2 / sqrt(pi) addr[1] = addr_t(i_z + 2); // z_2 mulpv_forward_0(i_z+3, addr, parameter, cap_order, taylor); // zero order Taylor coefficient for z_4 Base* x = taylor + size_t(arg[0]) * cap_order; Base* z_4 = taylor + (i_z + 4) * cap_order; if( op == ErfOp ) z_4[0] = erf(x[0]); else z_4[0] = erfc(x[0]); } /*! Forward mode Taylor coefficient for result of op = ErfOp or ErfcOp. The C++ source code corresponding to this operation is one of \verbatim z = erf(x) z = erfc(x) \endverbatim \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base. \param op must be either ErfOp or ErfcOp and indicates if this is z = erf(x) or z = erfc(x). \param q order of the Taylor coefficients that we are computing. \param r number of directions for the Taylor coefficients that we afre computing. \param i_z variable index corresponding to the last (primary) result for this operation; i.e. the row index in taylor corresponding to z. The auxiliary results have index i_z - j for j = 0 , ... , 4 (and include z). \param arg arg[0]: is the variable index corresponding to x. \n arg[1]: is the parameter index corresponding to the value zero. \n arg[2]: is the parameter index correspodning to the value 2 / sqrt(pi). \param parameter parameter[ arg[1] ] is the value zero, and parameter[ arg[2] ] is the value 2 / sqrt(pi). \param cap_order maximum number of orders that will fit in the taylor array. \par tpv We use the notation tpv = (cap_order-1) * r + 1 which is the number of Taylor coefficients per variable \param taylor \b Input: If x is a variable, taylor [ arg[0] * tpv + 0 ], is the zero order Taylor coefficient for all directions and taylor [ arg[0] * tpv + (k-1)*r + ell + 1 ], for k = 1 , ... , q, ell = 0, ..., r-1, is the k-th order Taylor coefficient corresponding to x and the ell-th direction. \n \b Input: taylor [ (i_z - j) * tpv + 0 ] is the zero order Taylor coefficient for all directions and the j-th result for z. for k = 1 , ... , q-1, ell = 0, ... , r-1, taylor[ (i_z - j) * tpv + (k-1)*r + ell + 1] is the Taylor coefficient for the k-th order, ell-th direction, and j-th auzillary result. \n \b Output: taylor [ (i_z-j) * tpv + (q-1)*r + ell + 1 ], for ell = 0 , ... , r-1, is the Taylor coefficient for the q-th order, ell-th direction, and j-th auzillary result. */ template inline void erf_forward_dir( op_code_var op , size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // check assumptions CPPAD_ASSERT_UNKNOWN( op == ErfOp || op == ErfcOp ); CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); CPPAD_ASSERT_UNKNOWN( NumRes(op) == 5 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( size_t( std::numeric_limits::max() ) >= i_z + 2 ); // array used to pass parameter values for sub-operations addr_t addr[2]; // convert from final result to first result i_z -= 4; // 4 = NumRes(ErfOp) - 1; // z_0 = x * x addr[0] = arg[0]; // x addr[1] = arg[0]; // x mulvv_forward_dir(q, r, i_z+0, addr, parameter, cap_order, taylor); // z_1 = - x * x addr[0] = arg[1]; // zero addr[1] = addr_t( i_z ); // z_0 subpv_forward_dir(q, r, i_z+1, addr, parameter, cap_order, taylor); // z_2 = exp( - x * x ) addr[0] = addr_t(i_z+1); exp_forward_dir(q, r, i_z+2, addr, cap_order, taylor); // z_3 = (2 / sqrt(pi)) * exp( - x * x ) addr[0] = arg[2]; // 2 / sqrt(pi) addr[1] = addr_t( i_z + 2 ); // z_2 mulpv_forward_dir(q, r, i_z+3, addr, parameter, cap_order, taylor); // pointers to taylor coefficients for x , z_3, and z_4 size_t num_taylor_per_var = (cap_order - 1) * r + 1; Base* x = taylor + size_t(arg[0]) * num_taylor_per_var; Base* z_3 = taylor + (i_z+3) * num_taylor_per_var; Base* z_4 = taylor + (i_z+4) * num_taylor_per_var; // sign Base sign(1.0); if( op == ErfcOp ) sign = Base(-1.0); // erf: z_4' (t) = erf'[x(t)] * x'(t) = z3(t) * x'(t) // erfc: z_4' (t) = - erf'[x(t)] * x'(t) = z3(t) * x'(t) // z_4[1] + 2 * z_4[2] * t + ... = // sign * (z_3[0] + z_3[1] * t + ...) * (x[1] + 2 * x[2] * t + ...) Base base_q = static_cast(double(q)); for(size_t ell = 0; ell < r; ell++) { // index in z_4 and x for q-th order term size_t m = (q-1)*r + ell + 1; // initialize q-th order term summation z_4[m] = sign * z_3[0] * x[m]; for(size_t k = 1; k < q; k++) { size_t x_index = (k-1)*r + ell + 1; size_t z3_index = (q-k-1)*r + ell + 1; Base bk = Base(double(k)); z_4[m] += sign * (bk / base_q) * x[x_index] * z_3[z3_index]; } } } /*! Compute reverse mode partial derivatives for result of op = ErfOp or ErfcOp. The C++ source code corresponding to this operation is one of \verbatim z = erf(x) z = erfc(x) \endverbatim \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base. \param op must be either ErfOp or ErfcOp and indicates if this is z = erf(x) or z = erfc(x). \param d highest order Taylor of the Taylor coefficients that we are computing the partial derivatives with respect to. \param i_z variable index corresponding to the last (primary) result for this operation; i.e. the row index in taylor corresponding to z. The auxiliary results are called y_j have index i_z - j. \param arg arg[0]: is the variable index corresponding to x. \n arg[1]: is the parameter index corresponding to the value zero. \n arg[2]: is the parameter index correspodning to the value 2 / sqrt(pi). \param parameter parameter[ arg[1] ] is the value zero, and parameter[ arg[2] ] is the value 2 / sqrt(pi). \param cap_order maximum number of orders that will fit in the taylor array. \param taylor \b Input: taylor [ size_t(arg[0]) * cap_order + k ] for k = 0 , ... , d, is the k-th order Taylor coefficient corresponding to x. \n taylor [ (i_z - j) * cap_order + k ] for k = 0 , ... , d, and for j = 0 , ... , 4, is the k-th order Taylor coefficient corresponding to the j-th result for this operation. \param n_order number of columns in the matrix containing all the partial derivatives \param partial \b Input: partial [ size_t(arg[0]) * n_order + k ] for k = 0 , ... , d, is the partial derivative of G( z , x , w , u , ... ) with respect to the k-th order Taylor coefficient for x. \n \b Input: partial [ (i_z - j) * n_order + k ] for k = 0 , ... , d, and for j = 0 , ... , 4, is the partial derivative of G( z , x , w , u , ... ) with respect to the k-th order Taylor coefficient for the j-th result of this operation. \n \b Output: partial [ size_t(arg[0]) * n_order + k ] for k = 0 , ... , d, is the partial derivative of H( x , w , u , ... ) with respect to the k-th order Taylor coefficient for x. \n \b Output: partial [ (i_z-j) * n_order + k ] for k = 0 , ... , d, and for j = 0 , ... , 4, may be used as work space; i.e., may change in an unspecified manner. */ template inline void erf_reverse( op_code_var op , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( op == ErfOp || op == ErfcOp ); CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); CPPAD_ASSERT_UNKNOWN( NumRes(op) == 5 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); CPPAD_ASSERT_UNKNOWN( size_t( std::numeric_limits::max() ) >= i_z + 2 ); // array used to pass parameter values for sub-operations addr_t addr[2]; // If pz is zero, make sure this operation has no effect // (zero times infinity or nan would be non-zero). Base* pz = partial + i_z * n_order; bool skip(true); for(size_t i_d = 0; i_d <= d; i_d++) skip &= IdenticalZero(pz[i_d]); if( skip ) return; // convert from final result to first result i_z -= 4; // 4 = NumRes(ErfOp) - 1; // Taylor coefficients and partials corresponding to x const Base* x = taylor + size_t(arg[0]) * cap_order; Base* px = partial + size_t(arg[0]) * n_order; // Taylor coefficients and partials corresponding to z_3 const Base* z_3 = taylor + (i_z+3) * cap_order; Base* pz_3 = partial + (i_z+3) * n_order; // Taylor coefficients and partials corresponding to z_4 Base* pz_4 = partial + (i_z+4) * n_order; // sign Base sign(1.0); if( op == ErfcOp ) sign = Base(-1.0); // Reverse z_4 size_t j = d; while(j) { pz_4[j] /= Base(double(j)); for(size_t k = 1; k <= j; k++) { px[k] += sign * azmul(pz_4[j], z_3[j-k]) * Base(double(k)); pz_3[j-k] += sign * azmul(pz_4[j], x[k]) * Base(double(k)); } j--; } px[0] += sign * azmul(pz_4[0], z_3[0]); // z_3 = (2 / sqrt(pi)) * exp( - x * x ) addr[0] = arg[2]; // 2 / sqrt(pi) addr[1] = addr_t( i_z + 2 ); // z_2 mulpv_reverse( i_z+3, addr, parameter, cap_order, taylor, n_order, partial ); // z_2 = exp( - x * x ) addr[0] = addr_t(i_z + 1); exp_reverse( i_z+2, addr, cap_order, taylor, n_order, partial ); // z_1 = - x * x addr[0] = arg[1]; // zero addr[1] = addr_t( i_z ); // z_0 subpv_reverse( i_z+1, addr, parameter, cap_order, taylor, n_order, partial ); // z_0 = x * x addr[0] = arg[0]; // x addr[1] = arg[0]; // x mulvv_reverse( i_z+0, addr, parameter, cap_order, taylor, n_order, partial ); } } } } // END namespace # endif // CPPAD_ERF_OP_INCLUDED ================================================ FILE: include/cppad/local/var_op/exp_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_EXP_OP_HPP # define CPPAD_LOCAL_VAR_OP_EXP_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // See dev documentation: forward_unary_op template inline void exp_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(ExpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(ExpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; size_t k; if( p == 0 ) { z[0] = exp( x[0] ); p++; } for(size_t j = p; j <= q; j++) { z[j] = x[1] * z[j-1]; for(k = 2; k <= j; k++) z[j] += Base(double(k)) * x[k] * z[j-k]; z[j] /= Base(double(j)); } } // See dev documentation: forward_unary_op template inline void exp_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(ExpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(ExpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( 0 < q ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + i_x * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; size_t m = (q-1)*r + 1; for(size_t ell = 0; ell < r; ell++) { z[m+ell] = Base(double(q)) * x[m+ell] * z[0]; for(size_t k = 1; k < q; k++) z[m+ell] += Base(double(k)) * x[(k-1)*r+ell+1] * z[(q-k-1)*r+ell+1]; z[m+ell] /= Base(double(q)); } } // See dev documentation: forward_unary_op template inline void exp_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(ExpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(ExpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; z[0] = exp( x[0] ); } // See dev documentation: reverse_unary_op template inline void exp_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // // // i_x size_t i_x = size_t(arg[0]); // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(ExpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(ExpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Taylor coefficients and partials corresponding to argument const Base* x = taylor + i_x * cap_order; Base* px = partial + i_x * n_order; // Taylor coefficients and partials corresponding to result const Base* z = taylor + i_z * cap_order; Base* pz = partial + i_z * n_order; // If pz is zero, make sure this operation has no effect // (zero times infinity or nan would be non-zero). bool skip(true); for(size_t i_d = 0; i_d <= d; i_d++) skip &= IdenticalZero(pz[i_d]); if( skip ) return; // loop through orders in reverse size_t j, k; j = d; while(j) { // scale partial w.r.t z[j] pz[j] /= Base(double(j)); for(k = 1; k <= j; k++) { px[k] += Base(double(k)) * azmul(pz[j], z[j-k]); pz[j-k] += Base(double(k)) * azmul(pz[j], x[k]); } --j; } px[0] += azmul(pz[0], z[0]); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/expm1_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_EXPM1_OP_HPP # define CPPAD_LOCAL_VAR_OP_EXPM1_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { template inline void expm1_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(Expm1Op) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(Expm1Op) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; size_t k; if( p == 0 ) { z[0] = expm1( x[0] ); p++; } for(size_t j = p; j <= q; j++) { z[j] = x[1] * z[j-1]; for(k = 2; k <= j; k++) z[j] += Base(double(k)) * x[k] * z[j-k]; z[j] /= Base(double(j)); z[j] += x[j]; } } template inline void expm1_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(Expm1Op) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(Expm1Op) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( 0 < q ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + i_x * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; size_t m = (q-1)*r + 1; for(size_t ell = 0; ell < r; ell++) { z[m+ell] = Base(double(q)) * x[m+ell] * z[0]; for(size_t k = 1; k < q; k++) z[m+ell] += Base(double(k)) * x[(k-1)*r+ell+1] * z[(q-k-1)*r+ell+1]; z[m+ell] /= Base(double(q)); z[m+ell] += x[m+ell]; } } template inline void expm1_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(Expm1Op) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(Expm1Op) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; z[0] = expm1( x[0] ); } template inline void expm1_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // // // i_x size_t i_x = size_t(arg[0]); // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(Expm1Op) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(Expm1Op) == 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Taylor coefficients and partials corresponding to argument const Base* x = taylor + i_x * cap_order; Base* px = partial + i_x * n_order; // Taylor coefficients and partials corresponding to result const Base* z = taylor + i_z * cap_order; Base* pz = partial + i_z * n_order; // If pz is zero, make sure this operation has no effect // (zero times infinity or nan would be non-zero). bool skip(true); for(size_t i_d = 0; i_d <= d; i_d++) skip &= IdenticalZero(pz[i_d]); if( skip ) return; // loop through orders in reverse size_t j, k; j = d; while(j) { px[j] += pz[j]; // scale partial w.r.t z[j] pz[j] /= Base(double(j)); for(k = 1; k <= j; k++) { px[k] += Base(double(k)) * azmul(pz[j], z[j-k]); pz[j-k] += Base(double(k)) * azmul(pz[j], x[k]); } --j; } px[0] += pz[0] + azmul(pz[0], z[0]); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/load_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_LOAD_OP_HPP # define CPPAD_LOCAL_VAR_OP_LOAD_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { /* ------------------------------------------------------------------------------ {xrst_begin_parent var_load_op dev} {xrst_spell ldp ldv } Access an Element in a Variable VecAD Vector ############################################ LdpOp, LdvOp ************ are the op codes for these operators. User Syntax *********** | *z* = *v* [ *x* ] v * is the :ref:`VecAD-name` vector for this load operation. If this vector is a constant before the load, the index *x* is a variable and it is a variable after the load. x * is the index for this load. y * is the value that was stored in *v* [ *x* ] prior to this load; see :ref:`var_store_op@y` . z * is the new variable created by this load. (This new variable is like a copy of *y* .) RecBase ******* is the base type use when recording this operator; i.e., this operation was recording using AD< *RecBase* > operations. Base **** is the type used for computations by this operator. This is either *RecBase* or AD< *RecBase* >. op_code ******* .. csv-table:: :widths: auto :header-rows: 1 op_code, x, z LdpOp, parameter, variable LdvOp, variable, variable i_z *** is the variable index corresponding to *z* . num_vecad_ind ************* is the size of the single array that includes all the VecAD vectors together with the size of each vector. arg *** arg[0] ====== this argument is the offset of the vector *v* relative to the beginning of the single array that contains all VecAD elements and sizes. This offset corresponds to the first element of *v* and not its size which comes just before the first element. arg[1] ====== If *x* is a parameter (variable), arg[1] is the parameter index (variable index) to *x* . arg[2] ====== Is the number of this VecAD load instruction that came before this one. {xrst_end var_load_op} ------------------------------------------------------------------------------- {xrst_begin var_load_forward_0 dev} {xrst_spell isvar } Zero Order Forward Load an Element of a VecAD Vector #################################################### v, x, y, z ********** see :ref:`var_load_op@v` , :ref:`var_load_op@x` , :ref:`var_load_op@y` , :ref:`var_load_op@z` Prototype ********* {xrst_literal // BEGIN_LOAD_FORWARD_0 // END_LOAD_FORWARD_0 } Base, op_code, i_z, num_vecad_ind, arg ************************************** see :ref:`var_load_op@Base` , :ref:`var_load_op@op_code` , ref:`var_load_op@i_z` , :ref:`var_load_op@num_vecad_ind` , :ref:`var_load_op@arg` . num_var ******* is the number of variables in this recording. num_par ******* is the number of parameters in this recording. parameter ********* This is the vector of parameters for this recording which has size *num_par* . cap_order ********* is the maximum number of orders that can fit in *taylor* . taylor ****** Is the matrix of Taylor coefficients for all the variables. per_variable ============ For each variable there is one Taylor coefficient of order zero and *n_dir* coefficients for orders greater than zero. The taylor coefficients capacity per variable is:: per_variable = (cap_order - 1) * n_dir + 1 Input ===== For j = 0, ..., i_z - 1, taylor[ j * per_variable + 0 ] is an input. Output ====== taylor[ i_z * per_variable + 0 ] is an output. i_vec ***** We use *i_vec* to denote the ``size_t`` value corresponding to :ref:`var_load_op@x` . If *x* is a parameter (variable) this is a parameter (variable) index. vec_ad2isvar ************ This vector has size :ref:`var_load_op@num_vecad_ind` . If *y* is a parameter (variable), *vec_ad2isvar* [ *arg* [0] + *i_vec* ] is false (true). vec_ad2index ************ This vector has size *num_vecad_ind* . If *y* a parameter (variable), *vec_ad2index* [ *arg* [0] + *i_vec* ] is the parameter (variable) index corresponding to the value being loaded. {xrst_end var_load_forward_0} */ // BEGIN_LOAD_FORWARD_0 template inline void load_forward_0( op_code_var op_code , size_t i_z , size_t num_vec_ind , const addr_t* arg , size_t num_var , size_t num_par , const Base* parameter , size_t cap_order , Base* taylor , const pod_vector& vec_ad2isvar , const pod_vector& vec_ad2index , pod_vector& load_op2var ) // END_LOAD_FORWARD_0 { CPPAD_ASSERT_NARG_NRES(op_code, 3, 1); // CPPAD_ASSERT_UNKNOWN( 0 < arg[0] ); CPPAD_ASSERT_UNKNOWN( vec_ad2isvar.size() == num_vec_ind ); CPPAD_ASSERT_UNKNOWN( size_t(arg[2]) < load_op2var.size() ); // // i_vec // assign here to avoid compiler warning for default case addr_t i_vec = std::numeric_limits::max(); switch(op_code) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // case LdpOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par ); i_vec = addr_t( Integer( parameter[ arg[1] ] ) ); break; // case LdvOp: CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_var ); i_vec = addr_t(Integer( taylor[ size_t(arg[1]) * cap_order + 0 ] )); break; } // CPPAD_ASSERT_KNOWN( size_t(i_vec) < vec_ad2index[ arg[0] - 1 ] , "VecAD: dynamic parameter index out or range during zero order forward" ); CPPAD_ASSERT_UNKNOWN( size_t(arg[0] + i_vec) < num_vec_ind ); // // i_y, isvar size_t i_y = vec_ad2index[ arg[0] + i_vec ]; bool isvar = vec_ad2isvar[ arg[0] + i_vec ]; // // z Base* z = taylor + i_z * cap_order; // // z, load_op2var if( isvar ) { CPPAD_ASSERT_UNKNOWN( i_y < i_z ); load_op2var[ arg[2] ] = addr_t( i_y ); Base* y = taylor + i_y * cap_order; z[0] = y[0]; } else { CPPAD_ASSERT_UNKNOWN( i_y < num_par ); load_op2var[ arg[2] ] = 0; Base y = parameter[i_y]; z[0] = y; } } /* ------------------------------------------------------------------------------ {xrst_begin var_load_forward_nonzero dev} Nonzero Order Forward Load an Element of a VecAD Vector ####################################################### v, x, y, z ********** see :ref:`var_load_op@v` , :ref:`var_load_op@x` , :ref:`var_load_op@y` , :ref:`var_load_op@z` n_res ***** The number of results that are variables, *n_res* , is one for this operation. Prototype ********* {xrst_literal // BEGIN_LOAD_FORWARD_NONZERO // END_LOAD_FORWARD_NONZERO } Base, op_code, i_z, arg *********************** see :ref:`var_load_op@Base` , :ref:`var_load_op@op_code` , :ref:`var_load_op@i_z` , :ref:`var_load_op@arg` . order_low ********* is the lowest order Taylor coefficient that we are computing. {xrst_template ; include/cppad/local/var_op/template/forward_dir.xrst headers: n_dir, cap_order, order_up, taylor } load_op2var *********** maps the load operator index *arg* [2] to the index corresponding to *y* for this load operation. If the case where the index is zero, *y* is a parameter; i.e., *y* is not a variable. {xrst_end var_load_forward_nonzero} */ // BEGIN_LOAD_FORWARD_NONZERO template inline void load_forward_nonzero( op_code_var op_code , size_t i_z , const addr_t* arg , size_t order_low , size_t order_up , size_t n_dir , size_t cap_order , const pod_vector& load_op2var , Base* taylor ) // END_LOAD_FORWARD_NONZERO { // p, q size_t p = order_low; size_t q = order_up; size_t r = n_dir; // CPPAD_ASSERT_NARG_NRES(op_code, 3, 1); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( 0 < r); CPPAD_ASSERT_UNKNOWN( 0 < p); CPPAD_ASSERT_UNKNOWN( p <= q ); CPPAD_ASSERT_UNKNOWN( size_t(arg[2]) < load_op2var.size() ); // // i_y size_t i_y = size_t( load_op2var[ arg[2] ] ); CPPAD_ASSERT_UNKNOWN( i_y < i_z ); // // num_taylor_per_var size_t num_taylor_per_var = (cap_order-1) * r + 1; // // z Base* z = taylor + i_z * num_taylor_per_var; // // z if( i_y > 0 ) { Base* y = taylor + i_y * num_taylor_per_var; for(size_t ell = 0; ell < r; ell++) { for(size_t k = p; k <= q; k++) { size_t m = (k-1) * r + 1 + ell; z[m] = y[m]; } } } else { for(size_t ell = 0; ell < r; ell++) { for(size_t k = p; k <= q; k++) { size_t m = (k-1) * r + 1 + ell; z[m] = Base(0.0); } } } } /* ------------------------------------------------------------------------------ {xrst_begin var_load_reverse dev} Reverse Mode Load an Element of a VecAD Vector ############################################## v, x, y, z ********** see :ref:`var_load_op@v` , :ref:`var_load_op@x` , :ref:`var_load_op@y` , :ref:`var_load_op@z` Prototype ********* {xrst_literal // BEGIN_LOAD_REVERSE // END_LOAD_REVERSE } Base, op_code, i_z, arg *********************** see :ref:`var_load_op@Base` , :ref:`var_load_op@op_code` , :ref:`var_load_op@i_z` , :ref:`var_load_op@arg` . cap_order ********* is the maximum number of orders that can fit in *taylor* . load_op2var *********** This vector maps the load instruction index *arg* [2] to the corresponding *y* variable index. If this index is zero, *y* is a parameter (not a variable). {xrst_template ; include/cppad/local/var_op/template/reverse_op.xrst headers: n_order, partial @x, y@ ; y } If *y* is a parameter, nothing is modified by this call to ``reverse_load_op`` . Otherwise, let *i_y* be the variable index corresponding to *y*; i.e. | |tab| *i_y* = *load_op2var* [ *arg* [2] ] For k = 0 , ... , d the k-th order Taylor coefficient for *z* is added to the k-th order Taylor coefficient for *y*; i.e., |tab| *partial* [ *i_y* * *n_order* + *k* ] += *partial* [ *i_z* * *n_order* + *k* ] {xrst_end var_load_reverse} */ // BEGIN_LOAD_REVERSE template inline void load_reverse( op_code_var op_code , size_t i_z , const addr_t* arg , const pod_vector& load_op2var , size_t cap_order , size_t n_order , Base* partial ) // END_LOAD_REVERSE { // d // size_t d = n_order - 1; // // CPPAD_ASSERT_NARG_NRES(op_code, 3, 1); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // i_y size_t i_y = size_t( load_op2var[ arg[2] ] ); CPPAD_ASSERT_UNKNOWN( i_y < i_z ); // // py if( i_y > 0 ) { Base* pz = partial + i_z * n_order; Base* py = partial + i_y * n_order; size_t j = d + 1; while(j--) py[j] += pz[j]; } } /* ------------------------------------------------------------------------------ {xrst_begin var_load_for_jac dev} Forward Jacobian Sparsity for Store a VecAD Element ################################################### v, x, y, z ********** see :ref:`var_load_op@v` , :ref:`var_load_op@x` , :ref:`var_load_op@y` , :ref:`var_load_op@z` Prototype ********* {xrst_literal // BEGIN_LOAD_FOR_JAC // END_LOAD_FOR_JAC } op_code, num_vecad_ind, arg *************************** see :ref:`var_load_op@op_code` , :ref:`var_load_op@num_vecad_ind` , :ref:`var_load_op@arg` . Vector_set ********** is the type used for vectors of sets. It must satisfy the :ref:`SetVector-name` concept. dependency ********** If true (false) we are including (are not including) dependencies that have derivative zero in the sparsity pattern. For example, the :ref:`Discrete-name` functions have derivative zero, but the value depends on its argument. vecad_ind ********* is a vector with size *num_vec_ind* . We use the notation *i_v* defined by |tab| *i_v* = vecad_ind[ arg[0] - 1 ] This is the index of the VecAD vector and is less than the number of VecAD vectors in the recording. var_sparsity ************ The sparsity pattern for *z* is set equal to the sparsity pattern for *v. If *dependency* is true, and *x* is a variable, the sparsity pattern for *x* is added to the sparsity pattern for *z*. vecad_sparsity ************** The set with index *i_v* in *vecad_sparsity is the sparsity pattern for the vector *v*. {xrst_end var_load_for_jac} */ // BEGIN_LOAD_FOR_JAC template inline void load_for_jac( op_code_var op_code , size_t num_vecad_ind , size_t i_z , const addr_t* arg , bool dependency , const pod_vector& vecad_ind , Vector_set& var_sparsity , const Vector_set& vecad_sparsity ) // END_LOAD_FOR_JAC { // // CPPAD_ASSERT_NARG_NRES(op_code, 3, 1); CPPAD_ASSERT_UNKNOWN( 0 < arg[0] ); CPPAD_ASSERT_UNKNOWN( num_vecad_ind == vecad_ind.size() ); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_vecad_ind ); // // i_v size_t i_v = vecad_ind[ arg[0] - 1 ]; CPPAD_ASSERT_UNKNOWN( i_v < vecad_sparsity.n_set() ); // // var_sparsity[i_z] var_sparsity.assignment(i_z, i_v, vecad_sparsity); // // var_sparsity[i_z] if( dependency & (op_code == LdvOp) ) { size_t i_x = size_t( arg[1] ); var_sparsity.binary_union(i_z, i_z, i_x, var_sparsity); } return; } /* ------------------------------------------------------------------------------ {xrst_begin var_load_rev_jac dev} Reverse Jacobian Sparsity for Load a VecAD Element ################################################## v, x, y, z ********** see :ref:`var_load_op@v` , :ref:`var_load_op@x` , :ref:`var_load_op@y` , :ref:`var_load_op@z` Prototype ********* {xrst_literal // BEGIN_LOAD_REV_JAC // END_LOAD_REV_JAC } op_code, num_vecad_ind, arg *************************** see :ref:`var_load_op@op_code` , :ref:`var_load_op@num_vecad_ind` , :ref:`var_load_op@arg` . Vector_set ********** is the type used for vectors of sets. It must satisfy the :ref:`SetVector-name` concept. dependency ********** If true (false) we are including (are not including) dependencies that have derivative zero in the sparsity pattern. For example, the :ref:`Discrete-name` functions have derivative zero, but the value depends on its argument. vecad_ind ********* is a vector with size *num_vec_ind* . We use the notation *i_v* defined by |tab| *i_v* = vecad_ind[ arg[0] - 1 ] This is the index of the VecAD vector and is less than the number of VecAD vectors in the recording. var_sparsity ************ The set with index *i_z* in *vecad_sparsity is the sparsity pattern for the variable *z*. If *dependency* is true and *x* is a variable, the sparsity pattern for *z* is added to the sparsity pattern *x* . vecad_sparsity ************** The sparsity pattern for *z* is added to the sparsity pattern with index *i_v* in *vecad_sparsity ( the sparsity pattern for *v* ). {xrst_end var_load_rev_jac} */ // BEGIN_LOAD_REV_JAC template inline void load_rev_jac( op_code_var op_code , size_t num_vecad_ind , size_t i_z , const addr_t* arg , bool dependency , const pod_vector& vecad_ind , Vector_set& var_sparsity , Vector_set& vecad_sparsity ) // END_LOAD_REV_JAC { // // CPPAD_ASSERT_NARG_NRES(op_code, 3, 1); CPPAD_ASSERT_UNKNOWN( 0 < arg[0] ); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_vecad_ind ); // // i_v size_t i_v = vecad_ind[ arg[0] - 1 ]; CPPAD_ASSERT_UNKNOWN( i_v < vecad_sparsity.n_set() ); // // vecad_sparsity[i_v] vecad_sparsity.binary_union(i_v, i_v, i_z, var_sparsity); if( dependency & (op_code == LdvOp) ) { size_t i_x = size_t( arg[1] ); var_sparsity.binary_union(i_x, i_x, i_z, var_sparsity); } return; } /* ------------------------------------------------------------------------------ {xrst_begin var_load_rev_hes dev} Reverse Hessian Sparsity for Load a VecAD Element ################################################# v, x, y, z ********** see :ref:`var_load_op@v` , :ref:`var_load_op@x` , :ref:`var_load_op@y` , :ref:`var_load_op@z` Prototype ********* {xrst_literal // BEGIN_LOAD_REV_HES // END_LOAD_REV_HES } op_code, num_vecad_ind, i_z, arg ******************************** see :ref:`var_load_op@op_code` , :ref:`var_load_op@num_vecad_ind` , :ref:`var_load_op@i_z` , :ref:`var_load_op@arg` . Vector_set ********** is the type used for vectors of sets. It must satisfy the :ref:`SetVector-name` concept. vecad_ind ********* is a vector with size *num_vec_ind* . We use the notation *i_v* defined by |tab| *i_v* = vecad_ind[ arg[0] - 1 ] This is the index of the VecAD vector and is less than the number of VecAD vectors in the recording. It is also the index of the hessian sparsity pattern for *v* in *vecad_sparsity*. var_sparsity ************ The set with index *i_z* in *var_sparsity is the hessian sparsity pattern for the variable *z*. vecad_sparsity ************** The set with index *i_v* in *vecad_sparsity is the hessian sparsity pattern for the vector *v*. The sparsity pattern for *z* is added to the sparsity pattern for *v* . var_rev_jac *********** If the scalar function has non-zero partial w.r.t *z* , the *i_z* component of this vector is true. vecad_rev_jac ************* If the scalar function has non-zero partial w.r.t *z* , the *i_v* component of *vecad_rev_jac* is set to true. {xrst_end var_load_rev_hes} */ // BEGIN_LOAD_REV_HES template inline void load_rev_hes( op_code_var op_code , const addr_t* arg , size_t num_vecad_ind , size_t i_z , const pod_vector& vecad_ind , const Vector_set& var_sparsity , Vector_set& vecad_sparsity , const bool* var_rev_jac , pod_vector& vecad_rev_jac ) // END_LOAD_REV_HES { // // CPPAD_ASSERT_NARG_NRES(op_code, 3, 1); CPPAD_ASSERT_UNKNOWN( 0 < arg[0] ); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_vecad_ind ); CPPAD_ASSERT_UNKNOWN( vecad_ind.size() == num_vecad_ind ); // // i_v size_t i_v = vecad_ind[ arg[0] - 1 ]; CPPAD_ASSERT_UNKNOWN( i_v < vecad_sparsity.n_set() ); // // vecad_sparsity[i_v] vecad_sparsity.binary_union(i_v, i_v, i_z, var_sparsity); // // vecad_rev_jac[iv] vecad_rev_jac[i_v] |= var_rev_jac[i_z]; // return; } /* ------------------------------------------------------------------------------ {xrst_begin var_load_for_hes dev} Forward Hessian Sparsity for Load a VecAD Element ################################################# v, x, y, z ********** see :ref:`var_load_op@v` , :ref:`var_load_op@x` , :ref:`var_load_op@y` , :ref:`var_load_op@z` Prototype ********* {xrst_literal // BEGIN_LOAD_FOR_HES // END_LOAD_FOR_HES } op_code, num_vecad_ind, i_z, arg ******************************** see :ref:`var_load_op@op_code` , :ref:`var_load_op@num_vecad_ind` , :ref:`var_load_op@i_z` , :ref:`var_load_op@arg` . Vector_set ********** is the type used for vectors of sets. It must satisfy the :ref:`SetVector-name` concept. n * is the number of independent variables on the tape. vecad_ind ********* is a vector with size *num_vec_ind* . We use the notation *i_v* defined by |tab| *i_v* = vecad_ind[ arg[0] - 1 ] This is the index of the VecAD vector and is less than the number of VecAD vectors in the recording. It is also the index of the hessian sparsity pattern for *v* in *vecad_sparsity*. vecad_sparsity ************** The set with index *i_v* in *vecad_sparsity is the forward Jacobian sparsity pattern for the vector *v*. for_hes_sparse ************** see :ref:`local_sweep_for_hes@for_hes_sparse` . {xrst_end var_load_for_hes} */ // BEGIN_LOAD_FOR_HES template inline void load_for_hes( op_code_var op_code , const addr_t* arg , size_t num_vecad_ind , size_t i_z , size_t n , const pod_vector& vecad_ind , const Vector_set& vecad_sparsity , Vector_set& for_hes_sparse ) // END_LOAD_FOR_HES { // // CPPAD_ASSERT_NARG_NRES(op_code, 3, 1); CPPAD_ASSERT_UNKNOWN( 0 < arg[0] ); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_vecad_ind ); CPPAD_ASSERT_UNKNOWN( vecad_ind.size() == num_vecad_ind ); // // np1 size_t np1 = n + 1; CPPAD_ASSERT_UNKNOWN( for_hes_sparse.end() == np1 ); // // i_v size_t i_v = vecad_ind[ arg[0] - 1 ]; CPPAD_ASSERT_UNKNOWN( i_v < vecad_sparsity.n_set() ); // // for_hes_sparse // set Jacobian sparsity for variable with index i_z for_hes_sparse.assignment(np1 + i_z, i_v, vecad_sparsity); // return; } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/log1p_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_LOG1P_OP_HPP # define CPPAD_LOCAL_VAR_OP_LOG1P_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { template inline void log1p_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // size_t k; // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(Log1pOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(Log1pOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; if( p == 0 ) { z[0] = log1p( x[0] ); p++; if( q == 0 ) return; } if ( p == 1 ) { z[1] = x[1] / (Base(1.0) + x[0]); p++; } for(size_t j = p; j <= q; j++) { z[j] = -z[1] * x[j-1]; for(k = 2; k < j; k++) z[j] -= Base(double(k)) * z[k] * x[j-k]; z[j] /= Base(double(j)); z[j] += x[j]; z[j] /= (Base(1.0) + x[0]); } } template inline void log1p_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(Log1pOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(Log1pOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + i_x * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; size_t m = (q-1) * r + 1; for(size_t ell = 0; ell < r; ell++) { z[m+ell] = Base(double(q)) * x[m+ell]; for(size_t k = 1; k < q; k++) z[m+ell] -= Base(double(k)) * z[(k-1)*r+1+ell] * x[(q-k-1)*r+1+ell]; z[m+ell] /= (Base(double(q)) + Base(q) * x[0]); } } template inline void log1p_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(Log1pOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(Log1pOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; z[0] = log1p( x[0] ); } template inline void log1p_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // // // i_x size_t i_x = size_t(arg[0]); // size_t d = n_order - 1; // size_t j, k; // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(Log1pOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(Log1pOp) == 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Taylor coefficients and partials corresponding to argument const Base* x = taylor + i_x * cap_order; Base* px = partial + i_x * n_order; // Taylor coefficients and partials corresponding to result const Base* z = taylor + i_z * cap_order; Base* pz = partial + i_z * n_order; Base inv_1px0 = Base(1.0) / (Base(1) + x[0]); j = d; while(j) { // scale partial w.r.t z[j] pz[j] = azmul(pz[j] , inv_1px0); px[0] -= azmul(pz[j], z[j]); px[j] += pz[j]; // further scale partial w.r.t. z[j] pz[j] /= Base(double(j)); for(k = 1; k < j; k++) { pz[k] -= Base(double(k)) * azmul(pz[j], x[j-k]); px[j-k] -= Base(double(k)) * azmul(pz[j], z[k]); } --j; } px[0] += azmul(pz[0], inv_1px0); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/log_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_LOG_OP_HPP # define CPPAD_LOCAL_VAR_OP_LOG_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // See dev documentation: forward_unary_op template inline void log_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // size_t k; // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(LogOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(LogOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; if( p == 0 ) { z[0] = log( x[0] ); p++; if( q == 0 ) return; } if ( p == 1 ) { z[1] = x[1] / x[0]; p++; } for(size_t j = p; j <= q; j++) { z[j] = -z[1] * x[j-1]; for(k = 2; k < j; k++) z[j] -= Base(double(k)) * z[k] * x[j-k]; z[j] /= Base(double(j)); z[j] += x[j]; z[j] /= x[0]; } } // See dev documentation: forward_unary_op template inline void log_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(LogOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(LogOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + i_x * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; size_t m = (q-1) * r + 1; for(size_t ell = 0; ell < r; ell++) { z[m+ell] = Base(double(q)) * x[m+ell]; for(size_t k = 1; k < q; k++) z[m+ell] -= Base(double(k)) * z[(k-1)*r+1+ell] * x[(q-k-1)*r+1+ell]; z[m+ell] /= (Base(double(q)) * x[0]); } } // See dev documentation: forward_unary_op template inline void log_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(LogOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(LogOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; z[0] = log( x[0] ); } // See dev documentation: reverse_unary_op template inline void log_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // // // i_x size_t i_x = size_t(arg[0]); // size_t d = n_order - 1; // size_t j, k; // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(LogOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(LogOp) == 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Taylor coefficients and partials corresponding to argument const Base* x = taylor + i_x * cap_order; Base* px = partial + i_x * n_order; // Taylor coefficients and partials corresponding to result const Base* z = taylor + i_z * cap_order; Base* pz = partial + i_z * n_order; Base inv_x0 = Base(1.0) / x[0]; j = d; while(j) { // scale partial w.r.t z[j] pz[j] = azmul(pz[j] , inv_x0); px[0] -= azmul(pz[j], z[j]); px[j] += pz[j]; // further scale partial w.r.t. z[j] pz[j] /= Base(double(j)); for(k = 1; k < j; k++) { pz[k] -= Base(double(k)) * azmul(pz[j], x[j-k]); px[j-k] -= Base(double(k)) * azmul(pz[j], z[k]); } --j; } px[0] += azmul(pz[0], inv_x0); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/mul_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_MUL_OP_HPP # define CPPAD_LOCAL_VAR_OP_MUL_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // --------------------------- Mulvv ----------------------------------------- // See dev documentation: forward_binary_op template inline void mulvv_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(MulvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(MulvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to arguments and result Base* x = taylor + size_t(arg[0]) * cap_order; Base* y = taylor + size_t(arg[1]) * cap_order; Base* z = taylor + i_z * cap_order; size_t k; for(size_t d = p; d <= q; d++) { z[d] = Base(0.0); for(k = 0; k <= d; k++) z[d] += x[d-k] * y[k]; } } // See dev documentation: forward_binary_op template inline void mulvv_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(MulvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(MulvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to arguments and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + size_t(arg[0]) * num_taylor_per_var; Base* y = taylor + size_t(arg[1]) * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; size_t k, ell, m; for(ell = 0; ell < r; ell++) { m = (q-1)*r + ell + 1; z[m] = x[0] * y[m] + x[m] * y[0]; for(k = 1; k < q; k++) z[m] += x[(q-k-1)*r + ell + 1] * y[(k-1)*r + ell + 1]; } } // See dev documentation: forward_binary_op template inline void mulvv_forward_0( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(MulvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(MulvvOp) == 1 ); // Taylor coefficients corresponding to arguments and result Base* x = taylor + size_t(arg[0]) * cap_order; Base* y = taylor + size_t(arg[1]) * cap_order; Base* z = taylor + i_z * cap_order; z[0] = x[0] * y[0]; } // See dev documentation: reverse_binary_op template inline void mulvv_reverse( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(MulvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(MulvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Arguments const Base* x = taylor + size_t(arg[0]) * cap_order; const Base* y = taylor + size_t(arg[1]) * cap_order; // Partial derivatives corresponding to arguments and result Base* px = partial + size_t(arg[0]) * n_order; Base* py = partial + size_t(arg[1]) * n_order; Base* pz = partial + i_z * n_order; // number of indices to access size_t j = d + 1; size_t k; while(j) { --j; for(k = 0; k <= j; k++) { // must use azmul because pz[j] = 0 may mean that this // component of the function was not selected. px[j-k] += azmul(pz[j], y[k]); py[k] += azmul(pz[j], x[j-k]); } } } // --------------------------- Mulpv ----------------------------------------- // See dev documentation: forward_binary_op template inline void mulpv_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(MulpvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(MulpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to arguments and result Base* y = taylor + size_t(arg[1]) * cap_order; Base* z = taylor + i_z * cap_order; // Paraemter value Base x = parameter[ arg[0] ]; for(size_t d = p; d <= q; d++) z[d] = x * y[d]; } // See dev documentation: forward_binary_op template inline void mulpv_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(MulpvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(MulpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to arguments and result size_t num_taylor_per_var = (cap_order-1) * r + 1; size_t m = (q-1) * r + 1; Base* y = taylor + size_t(arg[1]) * num_taylor_per_var + m; Base* z = taylor + i_z * num_taylor_per_var + m; // Paraemter value Base x = parameter[ arg[0] ]; for(size_t ell = 0; ell < r; ell++) z[ell] = x * y[ell]; } // See dev documentation: forward_binary_op template inline void mulpv_forward_0( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(MulpvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(MulpvOp) == 1 ); // Paraemter value Base x = parameter[ arg[0] ]; // Taylor coefficients corresponding to arguments and result Base* y = taylor + size_t(arg[1]) * cap_order; Base* z = taylor + i_z * cap_order; z[0] = x * y[0]; } // See dev documentation: reverse_binary_op template inline void mulpv_reverse( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(MulpvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(MulpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Arguments Base x = parameter[ arg[0] ]; // Partial derivatives corresponding to arguments and result Base* py = partial + size_t(arg[1]) * n_order; Base* pz = partial + i_z * n_order; // number of indices to access size_t j = d + 1; while(j) { --j; // must use azmul because pz[j] = 0 may mean that this // component of the function was not selected. py[j] += azmul(pz[j], x); } } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/neg_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_NEG_OP_HPP # define CPPAD_LOCAL_VAR_OP_NEG_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { template // See forward_unary1_op in developer documentation inline void neg_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_NARG_NRES( NegOp, 1, 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; for(size_t k = p; k <= q; k++) z[k] = - x[k]; } // See forward_unary1_op_dir in developer documentation // See dev documentation: forward_unary_op template inline void neg_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_NARG_NRES( NegOp, 1, 1 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + i_x * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; size_t m = (q-1) * r + 1; for(size_t ell = 0; ell < r; ell++) z[m+ell] = - x[m+ell]; } // See forward_unary1_op_0 in developer documentation // See dev documentation: forward_unary_op template inline void neg_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_NARG_NRES( NegOp, 1, 1 ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; z[0] = - x[0]; } // See reverse_unary1_op in developer documentation // See dev documentation: reverse_unary_op template inline void neg_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // // // i_x size_t i_x = size_t(arg[0]); // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_NARG_NRES( NegOp, 1, 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Taylor coefficients and partials corresponding to argument Base* px = partial + i_x * n_order; // Taylor coefficients and partials corresponding to result Base* pz = partial + i_z * n_order; Base neg_one = Base(-1.0); for(size_t k = 0; k <= d; ++k) px[k] += azmul(pz[k], neg_one); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/one_var.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_ONE_VAR_HPP # define CPPAD_LOCAL_VAR_OP_ONE_VAR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN_CPPAD_LOCAL_SPARSE_NAMESPACE namespace CppAD { namespace local { namespace var_op { /* {xrst_begin_parent var_one_var dev} Sparsity Calculations for Operators With One Variable ##################################################### Unary Operators *************** | *z* = *Fun* ( *x* ) See :ref:`var_unary_op@Fun` for unary operators for some of the possible values of *Fun* and the corresponding operators. Binary Operators **************** | *z* = *fun* ( *x* , *y* ) | *z* = *x* *Op* *y* See :ref:`var_binary_op@Fun` for binary operators, restricted to the case where just *x* or just *y* is a variable, for some of the possible values of *Fun* and the corresponding operators. x * is the first operand for this operator . y * is the second operand for this operator z * is the primary result for this operator. Vector_set ********** is the type used for vectors of sets. It must satisfy the :ref:`SetVector-name` concept. i_z *** is the variable index corresponding to *z* . i_v *** is the variable index corresponding to the argument that is a variable. {xrst_end var_one_var} ------------------------------------------------------------------------------ {xrst_begin var_one_var_for_jac dev} Forward Jacobian Sparsity for One Variable Argument Operators ############################################################# x, y, z ******* see :ref:`var_one_var@x` , :ref:`var_one_var@y` , :ref:`var_one_var@z` Prototype ********* {xrst_literal // BEGIN_ONE_VAR_FOR_JAC // END_ONE_VAR_FOR_JAC } Vector_set, i_z, i_v ******************** see :ref:`var_one_var@Vector_set` , :ref:`var_one_var@i_z` , :ref:`var_one_var@i_v` sparsity ******** Input ===== for *j* < *i_z* and *j* not an auxiliary result, the set with index *j* in *sparsity* is the Jacobian sparsity for the variable with index *j* . Output ====== The set with index *i_z* in *sparsity* is the Jacobian sparsity for the variable *z* . {xrst_end var_one_var_for_jac} */ // BEGIN_ONE_VAR_FOR_JAC template void one_var_for_jac( size_t i_z , size_t i_v , Vector_set& sparsity ) // END_ONE_VAR_FOR_JAC { // check assumptions CPPAD_ASSERT_UNKNOWN( i_v < i_z ); sparsity.assignment(i_z, i_v, sparsity); } /* ------------------------------------------------------------------------------ {xrst_begin var_one_var_rev_jac dev} Reverse Jacobian Sparsity for One Variable Argument Operators ############################################################# x, y, z ******* see :ref:`var_one_var@x` , :ref:`var_one_var@y` , :ref:`var_one_var@z` Prototype ********* {xrst_literal // BEGIN_ONE_VAR_REV_JAC // END_ONE_VAR_REV_JAC } Vector_set, i_z, i_v ******************** see :ref:`var_one_var@Vector_set` , :ref:`var_one_var@i_z` , :ref:`var_one_var@i_v` sparsity ******** Use z(v) to denote the variable *z* as a function of the variable *v* and define H in terms of G by:: H( v , u , ... ) = G[ z(v) , u , ... ] On input, *sparsity* is a sparsity pattern for the Jacobian of *G* . Upon return, *sparsity* is a sparsity pattern for the Jacobian of *H* . {xrst_end var_one_var_rev_jac} */ // BEGIN_ONE_VAR_REV_JAC template void one_var_rev_jac( size_t i_z , size_t i_v , Vector_set& sparsity ) // END_ONE_VAR_REV_JAC { // check assumptions CPPAD_ASSERT_UNKNOWN( i_v < i_z ); sparsity.binary_union(i_v, i_v, i_z, sparsity); return; } // --------------------------------------------------------------------------- /* {xrst_begin var_one_var_rev_hes dev} Reverse Jacobian Sparsity for One Variable Argument Operators ############################################################# x, y, z ******* see :ref:`var_one_var@x` , :ref:`var_one_var@y` , :ref:`var_one_var@z` G and H ******* Use :math:`z(v)` to denote the variable *z* as a function of the variable *v* . We use :math:`G( z, v, \ldots )` to denote a function of the variables up to and including *z* . We define :math:`H(v, \ldost)` by .. math:: H(v, \cdots ) = G [ z(v) , v , \cdots ] num_var ******* We use the notation *num_var* for the number of variables on the tape (including the phantom variable at index zero). Prototype ********* {xrst_literal // BEGIN_ONE_VAR_REV_HES // END_ONE_VAR_REV_HES } Vector_set, i_z, i_v ******************** see :ref:`var_one_var@Vector_set` , :ref:`var_one_var@i_z` , :ref:`var_one_var@i_v` linear[0] ********* This value is true (false) if the :math:`z(v)` must have zero second derivative (may have non-zero second derivative). for_jac_sparsity **************** The set with index *j* is the forward Jacobian sparsity pattern for the variable with index *j*. rev_jac_include *************** If the *j* element of this vector is true, the variable with index *j* is included in the Hessian, or affects the value of a variable that is included in the Hessian. Input ===== :: for j = num_var, ... , i_z + 1 rev_jac_include[j] is an input Output ====== rev_jac_include[i_z] is an output rev_hes_sparsity **************** On input (output), this is the sparsity pattern for *G* ( *H* ). For each variable index *j* , *rev_hes_sparsity* [ *j* ] is the set of indices that may have non-zero cross partials with variable index *j* . Example ======= If the indices in the sets correspond to the independent variables, then *rev_hes_sparsity* ``.end()`` is the number of independent variables. For *i* a variable index between 1 and the number of independent variables, *i* - 1 is the corresponding independent variable index. (The index *i* = 0 corresponds to the phantom variable at the beginning of the tape. ) {xrst_end var_one_var_rev_hes} */ // BEGIN_ONE_VAR_REV_HES template void one_var_rev_hes( size_t i_z , size_t i_v , bool* linear , bool* rev_jacobian , const Vector_set& for_jac_sparsity , Vector_set& rev_hes_sparsity ) // END_ONE_VAR_REV_HES { // check assumptions CPPAD_ASSERT_UNKNOWN( i_v < i_z ); // check for no effect if( ! rev_jacobian[i_z] ) return; rev_hes_sparsity.binary_union(i_v, i_v, i_z, rev_hes_sparsity); if( ! linear[0] ) rev_hes_sparsity.binary_union(i_v, i_v, i_v, for_jac_sparsity); rev_jacobian[i_v] = true; return; } // --------------------------------------------------------------------------- /* {xrst_begin var_one_var_for_hes dev} Forward Hessian Sparsity for One Variable Argument Operators ############################################################ x, y, z ******* see :ref:`var_one_var@x` , :ref:`var_one_var@y` , :ref:`var_one_var@z` Prototype ********* {xrst_literal // BEGIN_ONE_VAR_FOR_HES // END_ONE_VAR_FOR_HES } Vector_set, i_z, i_v ******************** see :ref:`var_one_var@Vector_set` , :ref:`var_one_var@i_z` , :ref:`var_one_var@i_v` linear[0] ********* This value is true (false) if the :math:`z(v)` must have zero second derivative (may have non-zero second derivative). n_independent_p1 **************** is the number of independent variables (in the tape) plus one. num_var ******* This is the total number of variables in the tape (counting the phantom variable at index zero). for_sparsity ************ On input, all the linear and nonlinear interactions up to the arguments to the atomic function have been take into account. Upon return, the linear and nonlinear interactions in the atomic function have been take into account. Hessian Sparsity ================ For *j* equal 1 to *n_independent_p1* - 1, if *i* is in set with index *j* , the Hessian may have a non-zero partial with respect to the independent variables with indices ( *i* - 1, *j* - 1 ) . Note that the index zero is not used because it corresponds to the phantom variable on the tape. Jacobian Sparsity ================= If *i* is in the set with index *n_independent_p1* + *j* , the variable with index *j* may have a non-zero partial with resect to the independent variable with index *i* - 1 . {xrst_end var_one_var_for_hes} */ // BEGIN_ONE_VAR_FOR_HES template void one_var_for_hes( size_t n_independent_p1 , size_t num_var , size_t i_z , size_t i_v , bool* linear , Vector_set& for_sparsity ) // END_ONE_VAR_FOR_HES { // np1 size_t np1 = n_independent_p1; // CPPAD_ASSERT_UNKNOWN( i_v < i_z ); CPPAD_ASSERT_UNKNOWN( i_v < num_var ); CPPAD_ASSERT_UNKNOWN( for_sparsity.end() == np1 ); CPPAD_ASSERT_UNKNOWN( for_sparsity.n_set() == np1 + num_var ); CPPAD_ASSERT_UNKNOWN( for_sparsity.number_elements(np1) == 0 ); // for_sparsity // set Jacobian sparsity for z for_sparsity.assignment(np1 + i_z, np1 + i_v, for_sparsity); // if( ! linear[0] ) { // itr // set of independent variables that v depends on typename Vector_set::const_iterator itr(for_sparsity, i_v + np1); // // i_x // loop over independent variables that v has non-zero partials w.r.t. size_t i_x = *itr; while( i_x < np1 ) { // for_sparsity // union of Hessian sparsity for x and Jacobian sparsity for v for_sparsity.binary_union(i_x, i_x, i_v + np1, for_sparsity); i_x = *(++itr); } } return; } } } } // END_CPPAD_LOCAL_SPARSE_NAMESPACE # endif ================================================ FILE: include/cppad/local/var_op/par_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_PAR_OP_HPP # define CPPAD_LOCAL_VAR_OP_PAR_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { /* {xrst_begin_parent var_par_op dev} Create a Variable From a Parameter Operator ########################################### ParOp ***** is the op code for this operator. Purpose ******* If one of the :ref:`dependent@y` components in an ADFun object is a parameter, it is converted to a variable using this operator. n_res ***** This operator has one variable result (the variable that is created). i_z *** is the variable index corresponding the result of this operator. arg *** arg[0] ====== is the index of the parameter that is converted to a variable {xrst_end var_par_op} ------------------------------------------------------------------------------ {xrst_begin var_par_forward_0 dev} Zero Order Forward Create a Variable From a Parameter ##################################################### Prototype ********* {xrst_literal // BEGIN_PAR_FORWARD_0 // END_PAR_FORWARD_0 } i_z, arg ******** see ref:`var_par_op@i_z` , ref:`var_par_op@arg` num_par ******* is the total number of values in the *parameter* vector. parameter ********* maps parameter indices to parameter values . taylor ****** The Taylor coefficient corresponding to variable *i* and order *k* is *taylor* [ *i* * *cap_order* + *k* ] Input ===== The zero order Taylor coefficients for variables with index *i* less than or equal *i_z* . Output ====== The zero order Taylor coefficients for variables with index *i_z* ; i.e., the value of the parameter. {xrst_end var_par_forward_0} */ // BEGIN_PAR_FORWARD_0 template inline void par_forward_0( size_t i_z , const addr_t* arg , size_t num_par , const Base* parameter , size_t cap_order , Base* taylor ) // END_PAR_FORWARD_0 { // // // check assumptions CPPAD_ASSERT_NARG_NRES(ParOp, 1, 1); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); Base* z = taylor + i_z * cap_order; z[0] = parameter[ arg[0] ]; } /* ------------------------------------------------------------------------------ {xrst_begin var_par_forward_any dev} Any Order Forward Create a Variable From a Parameter #################################################### n_res ***** see :ref:`var_par_op@n_res` . Prototype ********* {xrst_literal // BEGIN_PAR_FORWARD_ANY // END_PAR_FORWARD_ANY } i_z, arg ******** see ref:`var_par_op@i_z` , ref:`var_par_op@arg` num_par ******* is the total number of values in the *parameter* vector. parameter ********* maps parameter indices to parameter values . {xrst_template ; include/cppad/local/var_op/template/forward_op.xrst headers: cap_order, order_low, order_up, taylor } {xrst_end var_par_forward_any} */ // BEGIN_PAR_FORWARD_ANY template inline void par_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t num_par , const Base* parameter , size_t cap_order , Base* taylor ) // END_PAR_FORWARD_ANY { // // // check assumptions CPPAD_ASSERT_NARG_NRES(ParOp, 1, 1); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); CPPAD_ASSERT_UNKNOWN( order_low <= order_up ); Base* z = taylor + i_z * cap_order; if( order_low == 0 ) { z[0] = parameter[ arg[0] ]; ++order_low; } for(size_t k = order_low; k <= order_up; ++k) z[k] = Base(0.0); } /* ------------------------------------------------------------------------------ {xrst_begin var_par_forward_dir dev} Multiple Direction Forward Create a Variable From a Parameter ############################################################# n_res ***** see :ref:`var_par_op@n_res` . Prototype ********* {xrst_literal // BEGIN_PAR_FORWARD_DIR // END_PAR_FORWARD_DIR } i_z, arg ******** see ref:`var_par_op@i_z` , ref:`var_par_op@arg` num_par ******* is the total number of values in the *parameter* vector. parameter ********* maps parameter indices to parameter values . {xrst_template ; include/cppad/local/var_op/template/forward_dir.xrst headers: n_dir, cap_order, order_up, taylor } {xrst_end var_par_forward_dir} */ // BEGIN_PAR_FORWARD_DIR template inline void par_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t num_par , const Base* parameter , size_t cap_order , Base* taylor ) // END_PAR_FORWARD_DIR { // // // check assumptions CPPAD_ASSERT_NARG_NRES(ParOp, 1, 1); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); CPPAD_ASSERT_UNKNOWN( 0 < order_up ); // per_variable size_t per_variable = (cap_order - 1) * n_dir + 1; // // z Base* z = taylor + i_z * per_variable; // // m size_t m = (order_up - 1) * n_dir + 1; // // taylor for(size_t ell = 0; ell < n_dir; ++ell) z[m + ell] = Base(0.0); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/pow_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_POW_OP_HPP # define CPPAD_LOCAL_VAR_OP_POW_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { /*! \file pow_op.hpp Forward and reverse mode calculations for z = pow(x, y). */ // --------------------------- Powvv ----------------------------------------- /*! Compute forward mode Taylor coefficients for result of op = PowvvOp. In the documentation below, this operations is for the case where both x and y are variables and the argument parameter is not used. \copydetails CppAD::local::forward_pow_op */ template inline void powvv_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // convert from final result to first result i_z -= 2; // 2 = NumRes(PowvvOp) - 1; // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(PowvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(PowvvOp) == 3 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); CPPAD_ASSERT_UNKNOWN( size_t( std::numeric_limits::max() ) >= i_z ); // z_0 = log(x) log_forward_any(p, q, i_z, arg, cap_order, taylor); // z_1 = z_0 * y addr_t addr[2]; addr[0] = addr_t( i_z ); addr[1] = arg[1]; mulvv_forward_any(p, q, i_z+1, addr, parameter, cap_order, taylor); // z_2 = exp(z_1) // final result for zero order case is exactly the same as for Base if( p == 0 ) { // Taylor coefficients corresponding to arguments and result Base* x = taylor + size_t(arg[0]) * cap_order; Base* y = taylor + size_t(arg[1]) * cap_order; Base* z_2 = taylor + (i_z+2) * cap_order; z_2[0] = pow(x[0], y[0]); p++; } if( p <= q ) { addr[0] = addr_t(i_z+1); exp_forward_any(p, q, i_z+2, addr, cap_order, taylor); } } /*! Multiple directions forward mode Taylor coefficients for op = PowvvOp. The C++ source code corresponding to this operation is \verbatim z = pow(x, y) \endverbatim In the documentation below, this operations is for the case where x is a variable and y is a parameter. \copydetails CppAD::local::forward_pow_op_dir */ template inline void powvv_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // convert from final result to first result i_z -= 2; // 2 = NumRes(PowvvOp) - 1 // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(PowvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(PowvvOp) == 3 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( size_t( std::numeric_limits::max() ) >= i_z ); // z_0 = log(x) log_forward_dir(q, r, i_z, arg, cap_order, taylor); // z_1 = y * z_0 addr_t addr[2]; addr[0] = addr_t( i_z ); addr[1] = arg[1]; mulvv_forward_dir(q, r, i_z+1, addr, parameter, cap_order, taylor); // z_2 = exp(z_1) addr[0] = addr_t(i_z+1); exp_forward_dir(q, r, i_z+2, addr, cap_order, taylor); } /*! Compute zero order forward mode Taylor coefficients for result of op = PowvvOp. The C++ source code corresponding to this operation is \verbatim z = pow(x, y) \endverbatim In the documentation below, this operations is for the case where both x and y are variables and the argument parameter is not used. \copydetails CppAD::local::forward_pow_op_0 */ template inline void powvv_forward_0( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // // // convert from final result to first result i_z -= 2; // NumRes(PowvvOp) - 1; // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(PowvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(PowvvOp) == 3 ); // Taylor coefficients corresponding to arguments and result Base* x = taylor + size_t(arg[0]) * cap_order; Base* y = taylor + size_t(arg[1]) * cap_order; Base* z_0 = taylor + i_z * cap_order; Base* z_1 = z_0 + cap_order; Base* z_2 = z_1 + cap_order; z_0[0] = log( x[0] ); z_1[0] = z_0[0] * y[0]; z_2[0] = pow(x[0], y[0]); } /*! Compute reverse mode partial derivatives for result of op = PowvvOp. The C++ source code corresponding to this operation is \verbatim z = pow(x, y) \endverbatim In the documentation below, this operations is for the case where both x and y are variables and the argument parameter is not used. \copydetails CppAD::local::reverse_pow_op */ template inline void powvv_reverse( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // // // // convert from final result to first result i_z -= 2; // NumRes(PowvvOp) - 1; // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(PowvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(PowvvOp) == 3 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); CPPAD_ASSERT_UNKNOWN( size_t( std::numeric_limits::max() ) >= i_z ); // z_2 = exp(z_1) addr_t addr[2]; addr[0] = addr_t(i_z + 1); exp_reverse( i_z+2, addr, cap_order, taylor, n_order, partial ); // z_1 = z_0 * y addr[0] = addr_t( i_z ); addr[1] = arg[1]; mulvv_reverse( i_z+1, addr, parameter, cap_order, taylor, n_order, partial ); // z_0 = log(x) log_reverse( i_z, arg, cap_order, taylor, n_order, partial ); } // --------------------------- Powpv ----------------------------------------- /*! Compute forward mode Taylor coefficients for result of op = PowpvOp. The C++ source code corresponding to this operation is \verbatim z = pow(x, y) \endverbatim In the documentation below, this operations is for the case where x is a parameter and y is a variable. \copydetails CppAD::local::forward_pow_op */ template inline void powpv_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // convert from final result to first result i_z -= 2; // 2 = NumRes(PowpvOp) - 1; // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(PowpvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(PowpvOp) == 3 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to arguments and result Base* z_0 = taylor + i_z * cap_order; // z_0 = log(x) Base x = parameter[ arg[0] ]; size_t d; for(d = p; d <= q; d++) { if( d == 0 ) z_0[d] = log(x); else z_0[d] = Base(0.0); } // 2DO: remove requirement that i_z * cap_order <= max addr_t value CPPAD_ASSERT_KNOWN( size_t( std::numeric_limits::max() ) >= i_z * cap_order, "cppad_tape_addr_type maximum value has been exceeded\n" "This is due to a kludge in the pow operation and should be fixed." ); // z_1 = z_0 * y addr_t addr[2]; // offset of z_i in taylor (as if it were a parameter); i.e., log(x) addr[0] = addr_t( i_z * cap_order ); // offset of y in taylor (as a variable) addr[1] = arg[1]; // Trick: use taylor both for the parameter vector and variable values mulpv_forward_any(p, q, i_z+1, addr, taylor, cap_order, taylor); // z_2 = exp(z_1) // zero order case exactly same as Base type operation if( p == 0 ) { Base* y = taylor + size_t(arg[1]) * cap_order; Base* z_2 = taylor + (i_z+2) * cap_order; z_2[0] = pow(x, y[0]); p++; } if( p <= q ) { addr[0] = addr_t(i_z+1); exp_forward_any(p, q, i_z+2, addr, cap_order, taylor); } } /*! Multiple directions forward mode Taylor coefficients for op = PowpvOp. The C++ source code corresponding to this operation is \verbatim z = pow(x, y) \endverbatim In the documentation below, this operations is for the case where x is a parameter and y is a variable. \copydetails CppAD::local::forward_pow_op_dir */ template inline void powpv_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // convert from final result to first result i_z -= 2; // 2 = NumRes(PowpvOp) - 1; // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(PowpvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(PowpvOp) == 3 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to arguments and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* z_0 = taylor + i_z * num_taylor_per_var; // z_0 = log(x) size_t m = (q-1) * r + 1; for(size_t ell = 0; ell < r; ell++) z_0[m+ell] = Base(0.0); // 2DO: remove requirement i_z * num_taylor_per_var <= max addr_t value CPPAD_ASSERT_KNOWN( size_t( std::numeric_limits::max() ) >= i_z * num_taylor_per_var, "cppad_tape_addr_type maximum value has been exceeded\n" "This is due to a kludge in the pow operation and should be fixed." ); // z_1 = z_0 * y addr_t addr[2]; // offset of z_0 in taylor (as if it were a parameter); i.e., log(x) addr[0] = addr_t( i_z * num_taylor_per_var ); // offset of y in taylor (as a variable) addr[1] = arg[1]; // Trick: use taylor both for the parameter vector and variable values mulpv_forward_dir(q, r, i_z+1, addr, taylor, cap_order, taylor); // z_2 = exp(z_1) addr[0] = addr_t(i_z+1); exp_forward_dir(q, r, i_z+2, addr, cap_order, taylor); } /*! Compute zero order forward mode Taylor coefficient for result of op = PowpvOp. The C++ source code corresponding to this operation is \verbatim z = pow(x, y) \endverbatim In the documentation below, this operations is for the case where x is a parameter and y is a variable. \copydetails CppAD::local::forward_pow_op_0 */ template inline void powpv_forward_0( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // // // convert from final result to first result i_z -= 2; // NumRes(PowpvOp) - 1; // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(PowpvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(PowpvOp) == 3 ); // Paraemter value Base x = parameter[ arg[0] ]; // Taylor coefficients corresponding to arguments and result Base* y = taylor + size_t(arg[1]) * cap_order; Base* z_0 = taylor + i_z * cap_order; Base* z_1 = z_0 + cap_order; Base* z_2 = z_1 + cap_order; // z_0 = log(x) z_0[0] = log(x); // z_1 = z_0 * y z_1[0] = z_0[0] * y[0]; // z_2 = exp(z_1) // zero order case exactly same as Base type operation z_2[0] = pow(x, y[0]); } /*! Compute reverse mode partial derivative for result of op = PowpvOp. The C++ source code corresponding to this operation is \verbatim z = pow(x, y) \endverbatim In the documentation below, this operations is for the case where x is a parameter and y is a variable. \copydetails CppAD::local::reverse_pow_op */ template inline void powpv_reverse( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // // // // convert from final result to first result i_z -= 2; // NumRes(PowpvOp) - 1; // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(PowvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(PowvvOp) == 3 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // z_2 = exp(z_1) addr_t addr[2]; addr[0] = addr_t(i_z + 1); exp_reverse( i_z+2, addr, cap_order, taylor, n_order, partial ); // 2DO: remove requirement that i_z * cap_order <= max addr_t value CPPAD_ASSERT_KNOWN( size_t( std::numeric_limits::max() ) >= i_z * cap_order, "cppad_tape_addr_type maximum value has been exceeded\n" "This is due to a kludge in the pow operation and should be fixed." ); // z_1 = z_0 * y addr[0] = addr_t( i_z * cap_order ); // offset of z_0[0] in taylor addr[1] = arg[1]; // index of y in taylor and partial // use taylor both for parameter and variable values mulpv_reverse( i_z+1, addr, taylor, cap_order, taylor, n_order, partial ); // z_0 = log(x) // x is a parameter } // --------------------------- Powvp ----------------------------------------- /*! Compute forward mode Taylor coefficients for result of op = PowvpOp. The C++ source code corresponding to this operation is \verbatim z = pow(x, y) \endverbatim In the documentation below, this operations is for the case where x is a variable and y is a parameter. \copydetails CppAD::local::forward_pow_op */ template inline void powvp_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(PowvpOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(PowvpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); CPPAD_ASSERT_UNKNOWN( size_t( std::numeric_limits::max() ) >= i_z ); // Taylor coefficients corresponding to arguments and result Base* x = taylor + size_t(arg[0]) * cap_order; Base* z = taylor + i_z * cap_order; // Paraemter value Base y = parameter[ arg[1] ]; // Special solution when x[0] is zero Base b0 = Base( 0.0 ); // special case zero order if( p == 0 ) { z[0] = pow(x[0], y); p++; } for(size_t j = p; j <= q; ++j) { Base sum = Base(0); for(size_t k = 1; k < j; ++k) { Base bk = Base( double(k) ); sum += bk * (y * x[k] * z[j-k] - z[k] * x[j-k]); } Base bj = Base( double(j) ); Base zj = ( y * z[0] * x[j] + sum / bj ) / x[0]; z[j] = CondExpEq(x[0], b0, b0, zj); } } /*! Multiple directions forward mode Taylor coefficients for op = PowvpOp. The C++ source code corresponding to this operation is \verbatim z = pow(x, y) \endverbatim In the documentation below, this operations is for the case where x is a variable and y is a parameter. \copydetails CppAD::local::forward_pow_op_dir */ template inline void powvp_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(PowvpOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(PowvpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( size_t( std::numeric_limits::max() ) >= i_z ); // Taylor coefficients corresponding to arguments and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + size_t(arg[0]) * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; // Parameter value Base y = parameter[ arg[1] ]; // special solution when x[0] is zero Base b0 = Base( 0.0 ); // index in Taylor coefficients where multiple directions start size_t m = (q-1)*r + 1; // // loop over directions for(size_t ell = 0; ell < r; ell++) { Base sum = Base(0); for(size_t k = 1; k < q; ++k) { Base xk = x[(k-1)*r + ell + 1]; Base zk = z[(k-1)*r + ell + 1]; Base xqk = x[(q-k-1)*r + ell + 1]; Base zqk = z[(q-k-1)*r + ell + 1]; Base bk = Base( double(k) ); sum += bk * (y * xk * zqk - zk * xqk); } Base xq = x[(q-1)*r + ell + 1]; Base bq = Base( double(q) ); Base zell = ( y * z[0] * xq + sum / bq ) / x[0]; z[m+ell] = CondExpEq(x[0], b0, b0, zell); } } /*! Compute zero order forward mode Taylor coefficients for result of op = PowvpOp. The C++ source code corresponding to this operation is \verbatim z = pow(x, y) \endverbatim In the documentation below, this operations is for the case where x is a variable and y is a parameter. \copydetails CppAD::local::forward_pow_op_0 */ template inline void powvp_forward_0( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(PowvpOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(PowvpOp) == 1 ); // Paraemter value Base y = parameter[ arg[1] ]; // Taylor coefficients corresponding to arguments and result Base* x = taylor + size_t(arg[0]) * cap_order; Base* z = taylor + i_z * cap_order; z[0] = pow(x[0], y); } /*! Compute reverse mode partial derivative for result of op = PowvpOp. The C++ source code corresponding to this operation is \verbatim z = pow(x, y) \endverbatim In the documentation below, this operations is for the case where x is a variable and y is a parameter. \copydetails CppAD::local::reverse_pow_op */ template inline void powvp_reverse( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , const Base* taylor , size_t n_order , Base* partial , CppAD::vector& work ) { // // // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(PowvpOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(PowvpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); CPPAD_ASSERT_UNKNOWN( size_t( std::numeric_limits::max() ) >= i_z ); // Taylor coefficients const Base* x = taylor + size_t( arg[0] ) * cap_order; const Base* z = taylor + i_z * cap_order; // parameter value const Base y = parameter[ arg[1] ]; // Partial derivatives corresponding to arguments and result Base* px = partial + size_t(arg[0]) * n_order; Base* pz = partial + i_z * n_order; // Special solution when x[0] is zero Base b0 = Base( 0.0 ); // Place to hold px for this operator until conditional assignment at end work.resize(n_order); for(size_t j = 0; j < n_order; ++j) work[j] = px[j]; // reverse z^j for j = d, ..., 1 size_t j = n_order - 1; while(j) { // j Base bj = Base( double(j) ); // // x^j term work[j] += azmul(pz[j], y * z[0] / x[0]); // // x^k terms for(size_t k = 1; k < j; ++k) { Base bk = Base( double(k) ); Base term = (bk * y - Base(j-k) ) * z[j-k] / (bj * x[0]); work[k] += azmul(pz[j], term); } // // z^k terms for(size_t k = 1; k < j; ++k) { Base bk = Base( double(k) ); Base term = (Base(j-k) * y - bk) * x[j-k] / (bj * x[0]); pz[k] += azmul(pz[j], term); } // // x^0 term work[0] -= azmul(pz[j], z[j] / x[0]); // // z^0 term pz[0] += azmul(pz[j], y * x[j] / x[0] ); // // next j --j; } // reverse z^0 work[0] += azmul(pz[0], y * z[0] / x[0]); // for(j = 0; j < n_order; ++j) px[j] = CondExpEq(x[0], b0, b0, work[j]); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/pri_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_PRI_OP_HPP # define CPPAD_LOCAL_VAR_OP_PRI_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { /* {xrst_begin_parent var_pri_op dev} {xrst_spell pos } Print A Variable or Parameter Operator ###################################### PriOp ***** is the op code for this operator. User Syntax *********** | ``PrintFor`` ( *pos* , *before* , *value* , *after* ) pos *** This value is expected to be positive and *value* is printed if *pos* is not positive. before ****** is the text printed before the value. value ***** is the value that is printed. after ***** is the text printed after the value. RecBase ******* is the base type use when recording this operator; i.e., this operation was recording using AD< *RecBase* > operations. Base **** is the type used for computations by this operator. This is either *RecBase* or AD< *RecBase* >. arg *** arg[0] ****** The first two bits of this value are used a flags; see below. arg[1] ****** If arg[0] & 1 is zero (is one), arg[1] is the parameter (variable) index corresponding to *pos* . arg[2] ****** is the text index corresponding to *before* . arg[3] ****** If arg[0] & 2 is zero (is one), arg[3] is the parameter (variable) index corresponding to *value* . arg[4] ****** is the text index corresponding to *after* . {xrst_end var_pri_op} ------------------------------------------------------------------------------ {xrst_begin var_pri_forward_0 dev} Zero Order Forward Print A Variable or Parameter ################################################ Prototype ********* {xrst_literal // BEGIN_PRI_FORWARD_0 // END_PRI_FORWARD_0 } s_out ***** the results are printed on this output stream. num_text ******** is the total number of text characters on the tape (only used for error checking). text **** is the tape text vector. The value *before* ( *after* ) is stored in this vector as a null terminated string start at index arg[2] ( arg[4] ). num_par ******* is the total number of values in the parameter vector . parameter ********* maps parameter indices to parameter values. cap_order ********* is the maximum number of orders that can fit in taylor . taylor ****** The zero order Taylor coefficient corresponding to the variable with index j* is taylor[ j * cap_order + 0] . {xrst_end var_pri_forward_0} */ // BEGIN_PRI_FORWARD_0 template inline void pri_forward_0( std::ostream& s_out , const addr_t* arg , size_t num_text , const char* text , size_t num_par , const Base* parameter , size_t cap_order , const Base* taylor ) // END_PRI_FORWARD_0 { Base pos, value; // const char* before; const char* after; CPPAD_ASSERT_NARG_NRES(PriOp, 5, 0); // pos if( arg[0] & 1 ) { pos = taylor[ size_t(arg[1]) * cap_order + 0 ]; } else { CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par ); pos = parameter[ arg[1] ]; } // before CPPAD_ASSERT_UNKNOWN( size_t(arg[2]) < num_text ); before = text + arg[2]; // value if( arg[0] & 2 ) { value = taylor[ size_t(arg[3]) * cap_order + 0 ]; } else { CPPAD_ASSERT_UNKNOWN( size_t(arg[3]) < num_par ); value = parameter[ arg[3] ]; } // after CPPAD_ASSERT_UNKNOWN( size_t(arg[4]) < num_text ); after = text + arg[4]; if( ! GreaterThanZero( pos ) ) s_out << before << value << after; } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/prototype_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_PROTOTYPE_OP_HPP # define CPPAD_LOCAL_VAR_OP_PROTOTYPE_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { /*! \file prototype_op.hpp Documentation for generic cases (these generic cases are never used). */ // ==================== Unary operators with one result ==================== /*! Prototype for forward mode unary operator with one result (not used). \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base. \param p lowest order of the Taylor coefficient that we are computing. \param q highest order of the Taylor coefficient that we are computing. \param i_z variable index corresponding to the result for this operation; i.e. the row index in taylor corresponding to z. \param i_x variable index corresponding to the argument for this operator; i.e. the row index in taylor corresponding to x. \param cap_order maximum number of orders that will fit in the taylor array. \param taylor \b Input: taylor [ i_x * cap_order + k ], for k = 0 , ... , q, is the k-th order Taylor coefficient corresponding to x. \n \b Input: taylor [ i_z * cap_order + k ], for k = 0 , ... , p-1, is the k-th order Taylor coefficient corresponding to z. \n \b Output: taylor [ i_z * cap_order + k ], for k = p , ... , q, is the k-th order Taylor coefficient corresponding to z. \par Checked Assertions \li NumArg(op) == 1 \li NumRes(op) == 1 \li q < cap_order \li p <= q */ template inline void unary1_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // // This routine is only for documentation, it should not be used CPPAD_ASSERT_UNKNOWN( false ); } /*! Prototype for multiple direction forward mode unary operator with one result (not used). \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base. \param q order of the Taylor coefficients that we are computing. \param r number of directions for Taylor coefficients that we are computing. \param i_z variable index corresponding to the last (primary) result for this operation; i.e. the row index in taylor corresponding to z. \param i_x variable index corresponding to the argument for this operator; i.e. the row index in taylor corresponding to x. \param cap_order maximum number of orders that will fit in the taylor array. \par tpv We use the notation tpv = (cap_order-1) * r + 1 which is the number of Taylor coefficients per variable \param taylor \b Input: If x is a variable, taylor [ arg[0] * tpv + 0 ], is the zero order Taylor coefficient for all directions and taylor [ arg[0] * tpv + (k-1)*r + ell + 1 ], for k = 1 , ... , q, ell = 0, ..., r-1, is the k-th order Taylor coefficient corresponding to x and the ell-th direction. \n \b Input: taylor [ i_z * tpv + 0 ], is the zero order Taylor coefficient for all directions and taylor [ i_z * tpv + (k-1)*r + ell + 1 ], for k = 1 , ... , q-1, ell = 0, ..., r-1, is the k-th order Taylor coefficient corresponding to z and the ell-th direction. \n \b Output: taylor [ i_z * tpv + (q-1)*r + ell + 1], ell = 0, ..., r-1, is the q-th order Taylor coefficient corresponding to z and the ell-th direction. \par Checked Assertions \li NumArg(op) == 1 \li NumRes(op) == 2 \li i_x < i_z \li 0 < q \li q < cap_order */ template inline void unary1_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // i_x size_t i_x = size_t(arg[0]); // // This routine is only for documentation, it should not be used CPPAD_ASSERT_UNKNOWN( false ); } /*! Prototype for zero order forward mode unary operator with one result (not used). \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base . \param i_z variable index corresponding to the result for this operation; i.e. the row index in taylor corresponding to z. \param i_x variable index corresponding to the argument for this operator; i.e. the row index in taylor corresponding to x. \param cap_order maximum number of orders that will fit in the taylor array. \param taylor \b Input: taylor [ i_x * cap_order + 0 ] is the zero order Taylor coefficient corresponding to x. \n \b Output: taylor [ i_z * cap_order + 0 ] is the zero order Taylor coefficient corresponding to z. \par Checked Assertions \li NumArg(op) == 1 \li NumRes(op) == 1 \li i_x < i_z \li 0 < cap_order */ template inline void unary1_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // This routine is only for documentation, it should not be used CPPAD_ASSERT_UNKNOWN( false ); } /*! Prototype for reverse mode unary operator with one result (not used). This routine is given the partial derivatives of a function G(z , x , w, u ... ) and it uses them to compute the partial derivatives of \verbatim H( x , w , u , ... ) = G[ z(x) , x , w , u , ... ] \endverbatim \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base . \param d highest order Taylor coefficient that we are computing the partial derivatives with respect to. \param i_z variable index corresponding to the result for this operation; i.e. the row index in taylor to z. \param i_x variable index corresponding to the argument for this operation; i.e. the row index in taylor corresponding to x. \param cap_order maximum number of orders that will fit in the taylor array. \param taylor taylor [ i_x * cap_order + k ] for k = 0 , ... , d is the k-th order Taylor coefficient corresponding to x. \n taylor [ i_z * cap_order + k ] for k = 0 , ... , d is the k-th order Taylor coefficient corresponding to z. \param n_order number of columns in the matrix containing all the partial derivatives. \param partial \b Input: partial [ i_x * n_order + k ] for k = 0 , ... , d is the partial derivative of G( z , x , w , u , ... ) with respect to the k-th order Taylor coefficient for x. \n \b Input: partial [ i_z * n_order + k ] for k = 0 , ... , d is the partial derivative of G( z , x , w , u , ... ) with respect to the k-th order Taylor coefficient for z. \n \b Output: partial [ i_x * n_order + k ] for k = 0 , ... , d is the partial derivative of H( x , w , u , ... ) with respect to the k-th order Taylor coefficient for x. \n \b Output: partial [ i_z * n_order + k ] for k = 0 , ... , d may be used as work space; i.e., may change in an unspecified manner. \par Checked Assumptions \li NumArg(op) == 1 \li NumRes(op) == 1 \li i_x < i_z \li d < cap_order \li d < n_order */ template inline void unary1_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // // // i_x size_t i_x = size_t(arg[0]); // size_t d = n_order - 1; // // This routine is only for documentation, it should not be used CPPAD_ASSERT_UNKNOWN( false ); } // ==================== Unary operators with two results ==================== /*! Prototype for forward mode unary operator with two results (not used). \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base. \param p lowest order of the Taylor coefficients that we are computing. \param q highest order of the Taylor coefficients that we are computing. \param i_z variable index corresponding to the last (primary) result for this operation; i.e. the row index in taylor corresponding to z. The auxiliary result is called y has index i_z - 1. \param i_x variable index corresponding to the argument for this operator; i.e. the row index in taylor corresponding to x. \param cap_order maximum number of orders that will fit in the taylor array. \param taylor \b Input: taylor [ i_x * cap_order + k ] for k = 0 , ... , q, is the k-th order Taylor coefficient corresponding to x. \n \b Input: taylor [ i_z * cap_order + k ] for k = 0 , ... , p - 1, is the k-th order Taylor coefficient corresponding to z. \n \b Input: taylor [ ( i_z - 1) * cap_order + k ] for k = 0 , ... , p-1, is the k-th order Taylor coefficient corresponding to the auxiliary result y. \n \b Output: taylor [ i_z * cap_order + k ], for k = p , ... , q, is the k-th order Taylor coefficient corresponding to z. \n \b Output: taylor [ ( i_z - 1 ) * cap_order + k ], for k = p , ... , q, is the k-th order Taylor coefficient corresponding to the autillary result y. \par Checked Assertions \li NumArg(op) == 1 \li NumRes(op) == 2 \li i_x + 1 < i_z \li q < cap_order \li p <= q */ template inline void unary2_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // // This routine is only for documentation, it should not be used CPPAD_ASSERT_UNKNOWN( false ); } /*! Prototype for multiple direction forward mode unary operator with two results (not used). \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base. \param q order of the Taylor coefficients that we are computing. \param r number of directions for Taylor coefficients that we are computing. \param i_z variable index corresponding to the last (primary) result for this operation; i.e. the row index in taylor corresponding to z. The auxiliary result is called y has index i_z - 1. \param i_x variable index corresponding to the argument for this operator; i.e. the row index in taylor corresponding to x. \param cap_order maximum number of orders that will fit in the taylor array. \par tpv We use the notation tpv = (cap_order-1) * r + 1 which is the number of Taylor coefficients per variable \param taylor \b Input: taylor [ i_x * tpv + 0 ] is the zero order Taylor coefficient for all directions and taylor [ i_x * tpv + (k-1)*r + ell + 1 for k = 1 , ... , q, ell = 0 , ..., r-1, is the k-th order Taylor coefficient corresponding to x and the ell-th direction. \n \b Input: taylor [ i_z * tpv + 0 ], is the zero order Taylor coefficient for all directions and taylor [ i_z * tpv + (k-1)*r + ell + 1 ], for k = 1 , ... , q-1, ell = 0, ..., r-1, is the k-th order Taylor coefficient corresponding to z and the ell-th direction. \n \b Input: taylor [ (i_z-1) * tpv + 0 ], is the zero order Taylor coefficient for all directions and taylor [ (i_z-1) * tpv + (k-1)*r + ell + 1 ], for k = 1 , ... , q-1, ell = 0, ..., r-1, is the k-th order Taylor coefficient corresponding to the auxiliary result y and the ell-th direction. \n \b Output: taylor [ i_z * tpv + (q-1)*r + ell + 1], ell = 0, ..., r-1, is the q-th order Taylor coefficient corresponding to z and the ell-th direction. \par Checked Assertions \li NumArg(op) == 1 \li NumRes(op) == 2 \li i_x + 1 < i_z \li 0 < q \li q < cap_order */ template inline void unary2_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // i_x size_t i_x = size_t(arg[0]); // // This routine is only for documentation, it should not be used CPPAD_ASSERT_UNKNOWN( false ); } /*! Prototype for zero order forward mode unary operator with two results (not used). \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base . \param i_z variable index corresponding to the last (primary) result for this operation; i.e. the row index in taylor corresponding to z. The auxiliary result is called y and has index i_z - 1. \param i_x variable index corresponding to the argument for this operator; i.e. the row index in taylor corresponding to x. \param cap_order maximum number of orders that will fit in the taylor array. \param taylor \b Input: taylor [ i_x * cap_order + 0 ] is the zero order Taylor coefficient corresponding to x. \n \b Output: taylor [ i_z * cap_order + 0 ] is the zero order Taylor coefficient corresponding to z. \n \b Output: taylor [ ( i_z - 1 ) * cap_order + j ] is the j-th order Taylor coefficient corresponding to the autillary result y. \par Checked Assertions \li NumArg(op) == 1 \li NumRes(op) == 2 \li i_x + 1 < i_z \li j < cap_order */ template inline void unary2_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // This routine is only for documentation, it should not be used CPPAD_ASSERT_UNKNOWN( false ); } /*! Prototype for reverse mode unary operator with two results (not used). This routine is given the partial derivatives of a function G( z , y , x , w , ... ) and it uses them to compute the partial derivatives of \verbatim H( x , w , u , ... ) = G[ z(x) , y(x), x , w , u , ... ] \endverbatim \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base . \param d highest order Taylor coefficient that we are computing the partial derivatives with respect to. \param i_z variable index corresponding to the last (primary) result for this operation; i.e. the row index in taylor to z. The auxiliary result is called y and has index i_z - 1. \param i_x variable index corresponding to the argument for this operation; i.e. the row index in taylor corresponding to x. \param cap_order maximum number of orders that will fit in the taylor array. \param taylor taylor [ i_x * cap_order + k ] for k = 0 , ... , d is the k-th order Taylor coefficient corresponding to x. \n taylor [ i_z * cap_order + k ] for k = 0 , ... , d is the k-th order Taylor coefficient corresponding to z. \n taylor [ ( i_z - 1) * cap_order + k ] for k = 0 , ... , d is the k-th order Taylor coefficient corresponding to the auxiliary variable y. \param n_order number of columns in the matrix containing all the partial derivatives. \param partial \b Input: partial [ i_x * n_order + k ] for k = 0 , ... , d is the partial derivative of G( z , y , x , w , u , ... ) with respect to the k-th order Taylor coefficient for x. \n \b Input: partial [ i_z * n_order + k ] for k = 0 , ... , d is the partial derivative of G( z , y , x , w , u , ... ) with respect to the k-th order Taylor coefficient for z. \n \b Input: partial [ ( i_z - 1) * n_order + k ] for k = 0 , ... , d is the partial derivative of G( z , x , w , u , ... ) with respect to the k-th order Taylor coefficient for the auxiliary variable y. \n \b Output: partial [ i_x * n_order + k ] for k = 0 , ... , d is the partial derivative of H( x , w , u , ... ) with respect to the k-th order Taylor coefficient for x. \n \b Output: partial [ ( i_z - j ) * n_order + k ] for j = 0 , 1 , and for k = 0 , ... , d may be used as work space; i.e., may change in an unspecified manner. \par Checked Assumptions \li NumArg(op) == 1 \li NumRes(op) == 2 \li i_x + 1 < i_z \li d < cap_order \li d < n_order */ template inline void unary2_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // // // i_x size_t i_x = size_t(arg[0]); // size_t d = n_order - 1; // // This routine is only for documentation, it should not be used CPPAD_ASSERT_UNKNOWN( false ); } // =================== Binary operators with one result ==================== /*! Prototype forward mode x op y (not used) \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base. \param p lowest order of the Taylor coefficient that we are computing. \param q highest order of the Taylor coefficient that we are computing. \param i_z variable index corresponding to the result for this operation; i.e. the row index in taylor corresponding to z. \param arg arg[0] index corresponding to the left operand for this operator; i.e. the index corresponding to x. \n arg[1] index corresponding to the right operand for this operator; i.e. the index corresponding to y. \param parameter If x is a parameter, parameter [ arg[0] ] is the value corresponding to x. \n If y is a parameter, parameter [ arg[1] ] is the value corresponding to y. \param cap_order maximum number of orders that will fit in the taylor array. \param taylor \b Input: If x is a variable, taylor [ size_t(arg[0]) * cap_order + k ], for k = 0 , ... , q, is the k-th order Taylor coefficient corresponding to x. \n \b Input: If y is a variable, taylor [ size_t(arg[1]) * cap_order + k ], for k = 0 , ... , q, is the k-th order Taylor coefficient corresponding to y. \n \b Input: taylor [ i_z * cap_order + k ], for k = 0 , ... , p-1, is the k-th order Taylor coefficient corresponding to z. \n \b Output: taylor [ i_z * cap_order + k ], for k = p, ... , q, is the k-th order Taylor coefficient corresponding to z. \par Checked Assertions \li NumArg(op) == 2 \li NumRes(op) == 1 \li q < cap_order \li p <= q */ template inline void binary_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // This routine is only for documentation, it should not be used CPPAD_ASSERT_UNKNOWN( false ); } /*! Prototype multiple direction forward mode x op y (not used) \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base. \param q is the order of the Taylor coefficients that we are computing. \param r number of directions for Taylor coefficients that we are computing \param i_z variable index corresponding to the result for this operation; i.e. the row index in taylor corresponding to z. \param arg arg[0] index corresponding to the left operand for this operator; i.e. the index corresponding to x. \n arg[1] index corresponding to the right operand for this operator; i.e. the index corresponding to y. \param parameter If x is a parameter, parameter [ arg[0] ] is the value corresponding to x. \n If y is a parameter, parameter [ arg[1] ] is the value corresponding to y. \param cap_order maximum number of orders that will fit in the taylor array. \par tpv We use the notation tpv = (cap_order-1) * r + 1 which is the number of Taylor coefficients per variable \param taylor \b Input: If x is a variable, taylor [ arg[0] * tpv + 0 ], is the zero order Taylor coefficient for all directions and taylor [ arg[0] * tpv + (k-1)*r + ell + 1 ], for k = 1 , ... , q, ell = 0, ..., r-1, is the k-th order Taylor coefficient corresponding to x and the ell-th direction. \n \b Input: If y is a variable, taylor [ arg[1] * tpv + 0 ], is the zero order Taylor coefficient for all directions and taylor [ arg[1] * tpv + (k-1)*r + ell + 1 ], for k = 1 , ... , q, ell = 0, ..., r-1, is the k-th order Taylor coefficient corresponding to y and the ell-th direction. \n \b Input: taylor [ i_z * tpv + 0 ], is the zero order Taylor coefficient for all directions and taylor [ i_z * tpv + (k-1)*r + ell + 1 ], for k = 1 , ... , q-1, ell = 0, ..., r-1, is the k-th order Taylor coefficient corresponding to z and the ell-th direction. \n \b Output: taylor [ i_z * tpv + (q-1)*r + ell + 1], ell = 0, ..., r-1, is the q-th order Taylor coefficient corresponding to z and the ell-th direction. \par Checked Assertions \li NumArg(op) == 2 \li NumRes(op) == 1 \li 0 < q < cap_order */ template inline void binary_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // This routine is only for documentation, it should not be used CPPAD_ASSERT_UNKNOWN( false ); } /*! Prototype zero order forward mode x op y (not used) \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base. \param i_z variable index corresponding to the result for this operation; i.e. the row index in taylor corresponding to z. \param arg arg[0] index corresponding to the left operand for this operator; i.e. the index corresponding to x. \n arg[1] index corresponding to the right operand for this operator; i.e. the index corresponding to y. \param parameter If x is a parameter, parameter [ arg[0] ] is the value corresponding to x. \n If y is a parameter, parameter [ arg[1] ] is the value corresponding to y. \param cap_order maximum number of orders that will fit in the taylor array. \param taylor \b Input: If x is a variable, taylor [ arg[0] * cap_order + 0 ] is the zero order Taylor coefficient corresponding to x. \n \b Input: If y is a variable, taylor [ arg[1] * cap_order + 0 ] is the zero order Taylor coefficient corresponding to y. \n \b Output: taylor [ i_z * cap_order + 0 ] is the zero order Taylor coefficient corresponding to z. \par Checked Assertions \li NumArg(op) == 2 \li NumRes(op) == 1 */ template inline void binary_forward_0( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // // // This routine is only for documentation, it should not be used CPPAD_ASSERT_UNKNOWN( false ); } /*! Prototype for reverse mode binary operator x op y (not used). This routine is given the partial derivatives of a function G( z , y , x , w , ... ) and it uses them to compute the partial derivatives of \verbatim H( y , x , w , u , ... ) = G[ z(x , y) , y , x , w , u , ... ] \endverbatim \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base . \param d highest order Taylor coefficient that we are computing the partial derivatives with respect to. \param i_z variable index corresponding to the result for this operation; i.e. the row index in taylor corresponding to z. \param arg arg[0] index corresponding to the left operand for this operator; i.e. the index corresponding to x. \n arg[1] index corresponding to the right operand for this operator; i.e. the index corresponding to y. \param parameter If x is a parameter, parameter [ arg[0] ] is the value corresponding to x. \n If y is a parameter, parameter [ arg[1] ] is the value corresponding to y. \param cap_order maximum number of orders that will fit in the taylor array. \param taylor taylor [ i_z * cap_order + k ] for k = 0 , ... , d is the k-th order Taylor coefficient corresponding to z. \n If x is a variable, taylor [ arg[0] * cap_order + k ] for k = 0 , ... , d is the k-th order Taylor coefficient corresponding to x. \n If y is a variable, taylor [ arg[1] * cap_order + k ] for k = 0 , ... , d is the k-th order Taylor coefficient corresponding to y. \param n_order number of columns in the matrix containing all the partial derivatives. \param partial \b Input: partial [ i_z * n_order + k ] for k = 0 , ... , d is the partial derivative of G( z , y , x , w , u , ... ) with respect to the k-th order Taylor coefficient for z. \n \b Input: If x is a variable, partial [ arg[0] * n_order + k ] for k = 0 , ... , d is the partial derivative of G( z , y , x , w , u , ... ) with respect to the k-th order Taylor coefficient for x. \n \b Input: If y is a variable, partial [ arg[1] * n_order + k ] for k = 0 , ... , d is the partial derivative of G( z , x , w , u , ... ) with respect to the k-th order Taylor coefficient for the auxiliary variable y. \n \b Output: If x is a variable, partial [ arg[0] * n_order + k ] for k = 0 , ... , d is the partial derivative of H( y , x , w , u , ... ) with respect to the k-th order Taylor coefficient for x. \n \b Output: If y is a variable, partial [ arg[1] * n_order + k ] for k = 0 , ... , d is the partial derivative of H( y , x , w , u , ... ) with respect to the k-th order Taylor coefficient for y. \n \b Output: partial [ i_z * n_order + k ] for k = 0 , ... , d may be used as work space; i.e., may change in an unspecified manner. \par Checked Assumptions \li NumArg(op) == 2 \li NumRes(op) == 1 \li If x is a variable, arg[0] < i_z \li If y is a variable, arg[1] < i_z \li d < cap_order \li d < n_order */ template inline void binary_reverse( size_t i_z , addr_t* arg , const Base* parameter , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // size_t d = n_order - 1; // // This routine is only for documentation, it should not be used CPPAD_ASSERT_UNKNOWN( false ); } // ======================= Pow Function =================================== /*! Prototype for forward mode z = pow(x, y) (not used). \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base. \param p lowest order of the Taylor coefficient that we are computing. \param q highest order of the Taylor coefficient that we are computing. \param i_z variable index corresponding to the last (primary) result for this operation; i.e. the row index in taylor corresponding to z. Note that there are three results for this operation, below they are referred to as z_0, z_1, z_2 and correspond to \verbatim z_0 = log(x) z_1 = z0 * y z_2 = exp(z1) \endverbatim It follows that the final result is equal to z; i.e., z = z_2 = pow(x, y). \param arg arg[0] index corresponding to the left operand for this operator; i.e. the index corresponding to x. \n arg[1] index corresponding to the right operand for this operator; i.e. the index corresponding to y. \param parameter If x is a parameter, parameter [ arg[0] ] is the value corresponding to x. \n If y is a parameter, parameter [ arg[1] ] is the value corresponding to y. \param cap_order maximum number of orders that will fit in the taylor array. \param taylor \b Input: If x is a variable, taylor [ size_t(arg[0]) * cap_order + k ] for k = 0 , ... , q, is the k-th order Taylor coefficient corresponding to x. \n \b Input: If y is a variable, taylor [ size_t(arg[1]) * cap_order + k ] for k = 0 , ... , q is the k-th order Taylor coefficient corresponding to y. \n \b Input: taylor [ (i_z-2+j) * cap_order + k ], for j = 0, 1, 2 , for k = 0 , ... , p-1, is the k-th order Taylor coefficient corresponding to z_j. \n \b Output: taylor [ (i_z-2+j) * cap_order + k ], is the k-th order Taylor coefficient corresponding to z_j. \par Checked Assertions \li NumArg(op) == 2 \li NumRes(op) == 3 \li If x is a variable, arg[0] < i_z - 2 \li If y is a variable, arg[1] < i_z - 2 \li q < cap_order \li p <= q */ template inline void pow_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // This routine is only for documentation, it should not be used CPPAD_ASSERT_UNKNOWN( false ); } /*! Prototype for multiple direction forward mode z = pow(x, y) (not used). \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base. \param q order of the Taylor coefficient that we are computing. \param r is the number of Taylor coefficient directions that we are computing \param i_z variable index corresponding to the last (primary) result for this operation; i.e. the row index in taylor corresponding to z. Note that there are three results for this operation, below they are referred to as z_0, z_1, z_2 and correspond to \verbatim z_0 = log(x) z_1 = z0 * y z_2 = exp(z1) \endverbatim It follows that the final result is equal to z; i.e., z = z_2 = pow(x, y). \param arg arg[0] index corresponding to the left operand for this operator; i.e. the index corresponding to x. \n arg[1] index corresponding to the right operand for this operator; i.e. the index corresponding to y. \param parameter If x is a parameter, parameter [ arg[0] ] is the value corresponding to x. \n If y is a parameter, parameter [ arg[1] ] is the value corresponding to y. \param cap_order maximum number of orders that will fit in the taylor array. \par tpv We use the notation tpv = (cap_order-1) * r + 1 which is the number of Taylor coefficients per variable \param taylor \b Input: If x is a variable, taylor [ arg[0] * tpv + 0 ] is the zero order coefficient corresponding to x and taylor [ arg[0] * tpv + (k-1)*r+1+ell ] for k = 1 , ... , q, ell = 0 , ... , r-1, is the k-th order Taylor coefficient corresponding to x for the ell-th direction. \n \n \b Input: If y is a variable, taylor [ arg[1] * tpv + 0 ] is the zero order coefficient corresponding to y and taylor [ arg[1] * tpv + (k-1)*r+1+ell ] for k = 1 , ... , q, ell = 0 , ... , r-1, is the k-th order Taylor coefficient corresponding to y for the ell-th direction. \n \n \b Input: taylor [ (i_z-2+j) * tpv + 0 ], is the zero order coefficient corresponding to z_j and taylor [ (i_z-2+j) * tpv + (k-1)*r+1+ell ], for j = 0, 1, 2 , k = 0 , ... , q-1, ell = 0, ... , r-1, is the k-th order Taylor coefficient corresponding to z_j for the ell-th direction. \n \n \b Output: taylor [ (i_z-2+j) * tpv + (q-1)*r+1+ell ], for j = 0, 1, 2 , ell = 0, ... , r-1, is the q-th order Taylor coefficient corresponding to z_j for the ell-th direction. \par Checked Assertions \li NumArg(op) == 2 \li NumRes(op) == 3 \li If x is a variable, arg[0] < i_z - 2 \li If y is a variable, arg[1] < i_z - 2 \li 0 < q \li q < cap_order */ template inline void pow_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // This routine is only for documentation, it should not be used CPPAD_ASSERT_UNKNOWN( false ); } /*! Prototype for zero order forward mode z = pow(x, y) (not used). \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base. \param i_z variable index corresponding to the last (primary) result for this operation; i.e. the row index in taylor corresponding to z. Note that there are three results for this operation, below they are referred to as z_0, z_1, z_2 and correspond to \verbatim z_0 = log(x) z_1 = z0 * y z_2 = exp(z1) \endverbatim It follows that the final result is equal to z; i.e., z = z_2 = pow(x, y). \param arg arg[0] index corresponding to the left operand for this operator; i.e. the index corresponding to x. \n arg[1] index corresponding to the right operand for this operator; i.e. the index corresponding to y. \param parameter If x is a parameter, parameter [ arg[0] ] is the value corresponding to x. \n If y is a parameter, parameter [ arg[1] ] is the value corresponding to y. \param cap_order maximum number of orders that will fit in the taylor array. \param taylor \b Input: If x is a variable, taylor [ arg[0] * cap_order + 0 ] is the zero order Taylor coefficient corresponding to x. \n \b Input: If y is a variable, taylor [ arg[1] * cap_order + 0 ] is the k-th order Taylor coefficient corresponding to y. \n \b Output: taylor [ (i_z - 2 + j) * cap_order + 0 ] is the zero order Taylor coefficient corresponding to z_j. \par Checked Assertions \li NumArg(op) == 2 \li NumRes(op) == 3 \li If x is a variable, arg[0] < i_z - 2 \li If y is a variable, arg[1] < i_z - 2 */ template inline void pow_forward_0( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // // // This routine is only for documentation, it should not be used CPPAD_ASSERT_UNKNOWN( false ); } /*! Prototype for reverse mode z = pow(x, y) (not used). This routine is given the partial derivatives of a function G( z , y , x , w , ... ) and it uses them to compute the partial derivatives of \verbatim H( y , x , w , u , ... ) = G[ pow(x , y) , y , x , w , u , ... ] \endverbatim \tparam Base base type for the operator; i.e., this operation was recorded using AD< Base > and computations by this routine are done using type Base . \param d highest order Taylor coefficient that we are computing the partial derivatives with respect to. \param i_z variable index corresponding to the last (primary) result for this operation; i.e. the row index in taylor corresponding to z. Note that there are three results for this operation, below they are referred to as z_0, z_1, z_2 and correspond to \verbatim z_0 = log(x) z_1 = z0 * y z_2 = exp(z1) \endverbatim It follows that the final result is equal to z; i.e., z = z_2 = pow(x, y). \param arg arg[0] index corresponding to the left operand for this operator; i.e. the index corresponding to x. \n arg[1] index corresponding to the right operand for this operator; i.e. the index corresponding to y. \param parameter If x is a parameter, parameter [ arg[0] ] is the value corresponding to x. \n If y is a parameter, parameter [ arg[1] ] is the value corresponding to y. \param cap_order maximum number of orders that will fit in the taylor array. \param taylor taylor [ (i_z - 2 + j) * cap_order + k ] for j = 0, 1, 2 and k = 0 , ... , d is the k-th order Taylor coefficient corresponding to z_j. \n If x is a variable, taylor [ arg[0] * cap_order + k ] for k = 0 , ... , d is the k-th order Taylor coefficient corresponding to x. \n If y is a variable, taylor [ arg[1] * cap_order + k ] for k = 0 , ... , d is the k-th order Taylor coefficient corresponding to y. \param n_order number of columns in the matrix containing all the partial derivatives. \param partial \b Input: partial [ (i_z - 2 + j) * n_order + k ] for j = 0, 1, 2, and k = 0 , ... , d is the partial derivative of G( z , y , x , w , u , ... ) with respect to the k-th order Taylor coefficient for z_j. \n \b Input: If x is a variable, partial [ arg[0] * n_order + k ] for k = 0 , ... , d is the partial derivative of G( z , y , x , w , u , ... ) with respect to the k-th order Taylor coefficient for x. \n \b Input: If y is a variable, partial [ arg[1] * n_order + k ] for k = 0 , ... , d is the partial derivative of G( z , x , w , u , ... ) with respect to the k-th order Taylor coefficient for the auxiliary variable y. \n \b Output: If x is a variable, partial [ arg[0] * n_order + k ] for k = 0 , ... , d is the partial derivative of H( y , x , w , u , ... ) with respect to the k-th order Taylor coefficient for x. \n \b Output: If y is a variable, partial [ arg[1] * n_order + k ] for k = 0 , ... , d is the partial derivative of H( y , x , w , u , ... ) with respect to the k-th order Taylor coefficient for y. \n \b Output: partial [ ( i_z - j ) * n_order + k ] for j = 0 , 1 , 2 and for k = 0 , ... , d may be used as work space; i.e., may change in an unspecified manner. \par Checked Assumptions \li NumArg(op) == 2 \li NumRes(op) == 3 \li If x is a variable, arg[0] < i_z - 2 \li If y is a variable, arg[1] < i_z - 2 \li d < cap_order \li d < n_order */ template inline void pow_reverse( size_t i_z , addr_t* arg , const Base* parameter , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // size_t d = n_order - 1; // // This routine is only for documentation, it should not be used CPPAD_ASSERT_UNKNOWN( false ); } // ==================== Sparsity Calculations ============================== /*! Prototype for reverse mode Hessian sparsity unary operators. This routine is given the forward mode Jacobian sparsity patterns for x. It is also given the reverse mode dependence of G on z. In addition, it is given the reverse mode Hessian sparsity for the quantity of interest G(z , y , ... ) and it uses them to compute the sparsity patterns for \verbatim H( x , w , u , ... ) = G[ z(x) , x , w , u , ... ] \endverbatim \tparam Vector_set is the type used for vectors of sets. It can be either sparse::pack_setvec or sparse::list_setvec. \param i_z variable index corresponding to the result for this operation; i.e. the row index in sparsity corresponding to z. \param i_x variable index corresponding to the argument for this operator; i.e. the row index in sparsity corresponding to x. \param rev_jacobian rev_jacobian[i_z] is all false (true) if the Jacobian of G with respect to z must be zero (may be non-zero). \n \n rev_jacobian[i_x] is all false (true) if the Jacobian with respect to x must be zero (may be non-zero). On input, it corresponds to the function G, and on output it corresponds to the function H. \param for_jac_sparsity The set with index i_x in for_jac_sparsity is the forward mode Jacobian sparsity pattern for the variable x. \param rev_hes_sparsity The set with index i_z in in rev_hes_sparsity is the Hessian sparsity pattern for the function G where one of the partials derivative is with respect to z. \n \n The set with index i_x in rev_hes_sparsity is the Hessian sparsity pattern where one of the partials derivative is with respect to x. On input, it corresponds to the function G, and on output it corresponds to the function H. \par Checked Assertions: \li i_x < i_z */ template inline void reverse_sparse_hessian_unary_op( size_t i_z , size_t i_x , bool* rev_jacobian , Vector_set& for_jac_sparsity , Vector_set& rev_hes_sparsity ) { // // // This routine is only for documentation, it should not be used CPPAD_ASSERT_UNKNOWN( false ); } /*! Prototype for reverse mode Hessian sparsity binary operators. This routine is given the sparsity patterns the Hessian of a function G(z, y, x, ... ) and it uses them to compute the sparsity patterns for the Hessian of \verbatim H( y, x, w , u , ... ) = G[ z(x,y) , y , x , w , u , ... ] \endverbatim \tparam Vector_set is the type used for vectors of sets. It can be either sparse::pack_setvec or sparse::list_setvec. \param i_z variable index corresponding to the result for this operation; i.e. the row index in sparsity corresponding to z. \param arg arg[0] variable index corresponding to the left operand for this operator; i.e. the set with index arg[0] in var_sparsity is the spasity pattern corresponding to x. \n \n arg[1] variable index corresponding to the right operand for this operator; i.e. the row index in sparsity patterns corresponding to y. \param jac_reverse jac_reverse[i_z] is false (true) if the Jacobian of G with respect to z is always zero (may be non-zero). \n \n jac_reverse[ arg[0] ] is false (true) if the Jacobian with respect to x is always zero (may be non-zero). On input, it corresponds to the function G, and on output it corresponds to the function H. \n \n jac_reverse[ arg[1] ] is false (true) if the Jacobian with respect to y is always zero (may be non-zero). On input, it corresponds to the function G, and on output it corresponds to the function H. \param for_jac_sparsity The set with index arg[0] in for_jac_sparsity for the is the forward Jacobian sparsity pattern for x. \n \n The set with index arg[1] in for_jac_sparsity is the forward sparsity pattern for y. \param rev_hes_sparsity The set with index i_x in rev_hes_sparsity is the Hessian sparsity pattern for the function G where one of the partial derivatives is with respect to z. \n \n The set with index arg[0] in rev_hes_sparsity is the Hessian sparsity pattern where one of the partial derivatives is with respect to x. On input, it corresponds to the function G, and on output it correspondst to H. \n \n The set with index arg[1] in rev_hes_sparsity is the Hessian sparsity pattern where one of the partial derivatives is with respect to y. On input, it corresponds to the function G, and on output it correspondst to H. \par Checked Assertions: \li arg[0] < i_z \li arg[1] < i_z */ template inline void reverse_sparse_hessian_binary_op( size_t i_z , const addr_t* arg , bool* jac_reverse , Vector_set& for_jac_sparsity , Vector_set& rev_hes_sparsity ) { // // // This routine is only for documentation, it should not be used CPPAD_ASSERT_UNKNOWN( false ); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/sign_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_SIGN_OP_HPP # define CPPAD_LOCAL_VAR_OP_SIGN_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // See dev documentation: forward_unary_op template inline void sign_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SignOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(SignOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; if( p == 0 ) { z[0] = sign(x[0]); p++; } for(size_t j = p; j <= q; j++) z[j] = Base(0.); } // See dev documentation: forward_unary_op template inline void sign_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SignOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(SignOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; size_t m = (q - 1) * r + 1; Base* z = taylor + i_z * num_taylor_per_var; for(size_t ell = 0; ell < r; ell++) z[m+ell] = Base(0.); } // See dev documentation: forward_unary_op template inline void sign_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SignOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(SignOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); // Taylor coefficients corresponding to argument and result Base x0 = *(taylor + i_x * cap_order); Base* z = taylor + i_z * cap_order; z[0] = sign(x0); } // See dev documentation: reverse_unary_op template inline void sign_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SignOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(SignOp) == 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // nothing to do because partials of sign are zero return; } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/sin_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_SIN_OP_HPP # define CPPAD_LOCAL_VAR_OP_SIN_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // See dev documentation: forward_unary_op template inline void sin_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SinOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(SinOp) == 2 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* s = taylor + i_z * cap_order; Base* c = s - cap_order; // rest of this routine is identical for the following cases: // forward_sin_op, forward_cos_op, forward_sinh_op, forward_cosh_op. // (except that there is a sign difference for the hyperbolic case). size_t k; if( p == 0 ) { s[0] = sin( x[0] ); c[0] = cos( x[0] ); p++; } for(size_t j = p; j <= q; j++) { s[j] = Base(0.0); c[j] = Base(0.0); for(k = 1; k <= j; k++) { s[j] += Base(double(k)) * x[k] * c[j-k]; c[j] -= Base(double(k)) * x[k] * s[j-k]; } s[j] /= Base(double(j)); c[j] /= Base(double(j)); } } // See dev documentation: forward_unary_op template inline void sin_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SinOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(SinOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + i_x * num_taylor_per_var; Base* s = taylor + i_z * num_taylor_per_var; Base* c = s - num_taylor_per_var; // rest of this routine is identical for the following cases: // forward_sin_op, forward_cos_op, forward_sinh_op, forward_cosh_op // (except that there is a sign difference for the hyperbolic case). size_t m = (q-1) * r + 1; for(size_t ell = 0; ell < r; ell++) { s[m+ell] = Base(double(q)) * x[m + ell] * c[0]; c[m+ell] = - Base(double(q)) * x[m + ell] * s[0]; for(size_t k = 1; k < q; k++) { s[m+ell] += Base(double(k)) * x[(k-1)*r+1+ell] * c[(q-k-1)*r+1+ell]; c[m+ell] -= Base(double(k)) * x[(k-1)*r+1+ell] * s[(q-k-1)*r+1+ell]; } s[m+ell] /= Base(double(q)); c[m+ell] /= Base(double(q)); } } // See dev documentation: forward_unary_op template inline void sin_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SinOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(SinOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* s = taylor + i_z * cap_order; // called z in documentation Base* c = s - cap_order; // called y in documentation s[0] = sin( x[0] ); c[0] = cos( x[0] ); } // See dev documentation: reverse_unary_op template inline void sin_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // // // i_x size_t i_x = size_t(arg[0]); // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SinOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(SinOp) == 2 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Taylor coefficients and partials corresponding to argument const Base* x = taylor + i_x * cap_order; Base* px = partial + i_x * n_order; // Taylor coefficients and partials corresponding to first result const Base* s = taylor + i_z * cap_order; // called z in doc Base* ps = partial + i_z * n_order; // Taylor coefficients and partials corresponding to auxiliary result const Base* c = s - cap_order; // called y in documentation Base* pc = ps - n_order; // rest of this routine is identical for the following cases: // reverse_sin_op, reverse_cos_op, reverse_sinh_op, reverse_cosh_op. size_t j = d; size_t k; while(j) { ps[j] /= Base(double(j)); pc[j] /= Base(double(j)); for(k = 1; k <= j; k++) { px[k] += Base(double(k)) * azmul(ps[j], c[j-k]); px[k] -= Base(double(k)) * azmul(pc[j], s[j-k]); ps[j-k] -= Base(double(k)) * azmul(pc[j], x[k]); pc[j-k] += Base(double(k)) * azmul(ps[j], x[k]); } --j; } px[0] += azmul(ps[0], c[0]); px[0] -= azmul(pc[0], s[0]); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/sinh_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_SINH_OP_HPP # define CPPAD_LOCAL_VAR_OP_SINH_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // See dev documentation: forward_unary_op template inline void sinh_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SinhOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(SinhOp) == 2 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* s = taylor + i_z * cap_order; Base* c = s - cap_order; // rest of this routine is identical for the following cases: // forward_sin_op, forward_cos_op, forward_sinh_op, forward_cosh_op // (except that there is a sign difference for hyperbolic case). size_t k; if( p == 0 ) { s[0] = sinh( x[0] ); c[0] = cosh( x[0] ); p++; } for(size_t j = p; j <= q; j++) { s[j] = Base(0.0); c[j] = Base(0.0); for(k = 1; k <= j; k++) { s[j] += Base(double(k)) * x[k] * c[j-k]; c[j] += Base(double(k)) * x[k] * s[j-k]; } s[j] /= Base(double(j)); c[j] /= Base(double(j)); } } // See dev documentation: forward_unary_op template inline void sinh_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SinhOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(SinhOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + i_x * num_taylor_per_var; Base* s = taylor + i_z * num_taylor_per_var; Base* c = s - num_taylor_per_var; // rest of this routine is identical for the following cases: // forward_sin_op, forward_cos_op, forward_sinh_op, forward_cosh_op // (except that there is a sign difference for the hyperbolic case). size_t m = (q-1) * r + 1; for(size_t ell = 0; ell < r; ell++) { s[m+ell] = Base(double(q)) * x[m + ell] * c[0]; c[m+ell] = Base(double(q)) * x[m + ell] * s[0]; for(size_t k = 1; k < q; k++) { s[m+ell] += Base(double(k)) * x[(k-1)*r+1+ell] * c[(q-k-1)*r+1+ell]; c[m+ell] += Base(double(k)) * x[(k-1)*r+1+ell] * s[(q-k-1)*r+1+ell]; } s[m+ell] /= Base(double(q)); c[m+ell] /= Base(double(q)); } } // See dev documentation: forward_unary_op template inline void sinh_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SinhOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(SinhOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* s = taylor + i_z * cap_order; // called z in documentation Base* c = s - cap_order; // called y in documentation s[0] = sinh( x[0] ); c[0] = cosh( x[0] ); } // See dev documentation: reverse_unary_op template inline void sinh_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // // // i_x size_t i_x = size_t(arg[0]); // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SinhOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(SinhOp) == 2 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Taylor coefficients and partials corresponding to argument const Base* x = taylor + i_x * cap_order; Base* px = partial + i_x * n_order; // Taylor coefficients and partials corresponding to first result const Base* s = taylor + i_z * cap_order; // called z in doc Base* ps = partial + i_z * n_order; // Taylor coefficients and partials corresponding to auxiliary result const Base* c = s - cap_order; // called y in documentation Base* pc = ps - n_order; // rest of this routine is identical for the following cases: // reverse_sin_op, reverse_cos_op, reverse_sinh_op, reverse_cosh_op. size_t j = d; size_t k; while(j) { ps[j] /= Base(double(j)); pc[j] /= Base(double(j)); for(k = 1; k <= j; k++) { px[k] += Base(double(k)) * azmul(ps[j], c[j-k]); px[k] += Base(double(k)) * azmul(pc[j], s[j-k]); ps[j-k] += Base(double(k)) * azmul(pc[j], x[k]); pc[j-k] += Base(double(k)) * azmul(ps[j], x[k]); } --j; } px[0] += azmul(ps[0], c[0]); px[0] += azmul(pc[0], s[0]); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/sqrt_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_SQRT_OP_HPP # define CPPAD_LOCAL_VAR_OP_SQRT_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // See dev documentation: forward_unary_op template inline void sqrt_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SqrtOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(SqrtOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; size_t k; if( p == 0 ) { z[0] = sqrt( x[0] ); p++; } for(size_t j = p; j <= q; j++) { z[j] = Base(0.0); for(k = 1; k < j; k++) z[j] -= Base(double(k)) * z[k] * z[j-k]; z[j] /= Base(double(j)); z[j] += x[j] / Base(2.0); z[j] /= z[0]; } } // See dev documentation: forward_unary_op template inline void sqrt_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SqrtOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(SqrtOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* z = taylor + i_z * num_taylor_per_var; Base* x = taylor + i_x * num_taylor_per_var; size_t m = (q-1) * r + 1; for(size_t ell = 0; ell < r; ell++) { z[m+ell] = Base(0.0); for(size_t k = 1; k < q; k++) z[m+ell] -= Base(double(k)) * z[(k-1)*r+1+ell] * z[(q-k-1)*r+1+ell]; z[m+ell] /= Base(double(q)); z[m+ell] += x[m+ell] / Base(2.0); z[m+ell] /= z[0]; } } // See dev documentation: forward_unary_op template inline void sqrt_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SqrtOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(SqrtOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; z[0] = sqrt( x[0] ); } // See dev documentation: reverse_unary_op template inline void sqrt_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // // // i_x size_t i_x = size_t(arg[0]); // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SqrtOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(SqrtOp) == 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Taylor coefficients and partials corresponding to argument Base* px = partial + i_x * n_order; // Taylor coefficients and partials corresponding to result const Base* z = taylor + i_z * cap_order; Base* pz = partial + i_z * n_order; Base inv_z0 = Base(1.0) / z[0]; // number of indices to access size_t j = d; size_t k; while(j) { // scale partial w.r.t. z[j] pz[j] = azmul(pz[j], inv_z0); pz[0] -= azmul(pz[j], z[j]); px[j] += pz[j] / Base(2.0); for(k = 1; k < j; k++) pz[k] -= azmul(pz[j], z[j-k]); --j; } px[0] += azmul(pz[0], inv_z0) / Base(2.0); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/store_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_STORE_OP_HPP # define CPPAD_LOCAL_VAR_OP_STORE_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { /* {xrst_begin_parent var_store_op dev} {xrst_spell stpp stpv stvp stvv } Store an Element of a Variable VecAD Vector ########################################### StppOp, StpvOp, StvpOp, StvvOp ****************************** are the op codes for these operators. User Syntax *********** | *v* [ *x* ] = *y* v * is the :ref:`VecAD-name` vector for this store operation. This vector is a variable after the store. if this is a StppOp operation, *v* is a variable before the store. x * is the index for this store. y * is the value being stored. RecBase ******* is the base type use when recording this operator; i.e., this operation was recording using AD< *RecBase* > operations. Base **** is the type used for computations by this operator. This is either *RecBase* or AD< *RecBase* >. op_code ******* .. csv-table:: :widths: auto :header-rows: 1 op_code, x, y StppOp, parameter, parameter StpvOp, parameter, variable StvpOp, variable, parameter StvvOp, variable, variable num_vecad_ind ************* is the size of the single array that includes all the VecAD vectors together with the size of each vector. arg *** arg[0] ====== this argument is the offset of the vector *v* relative to the beginning of the single array that contains all VecAD elements and sizes. This offset corresponds to the first element of *v* and not its size which comes just before the first element. arg[1] ====== If *x* is a parameter (variable), arg[1] is the parameter index (variable index) to *x* . arg[2] ====== If *y* is a parameter (variable), arg[2] is the parameter index (variable index) to *y* . {xrst_end var_store_op} ------------------------------------------------------------------------------ {xrst_begin var_store_forward_0 dev} {xrst_spell isvar } Zero Order Forward Store an Element of a VecAD Vector ##################################################### v, x, y ******* see :ref:`var_store_op@v` , :ref:`var_store_op@x` , :ref:`var_store_op@y` Prototype ********* {xrst_literal // BEGIN_STORE_FORWARD_0 // END_STORE_FORWARD_0 } Base, op_code, num_vecad_ind, arg ********************************* see :ref:`var_store_op@Base` , :ref:`var_store_op@op_code` , :ref:`var_store_op@num_vecad_ind` , :ref:`var_store_op@arg` . num_var ******* is the number of variables in this recording. num_par ******* is the number of parameters in this recording. parameter ********* This is the vector of parameters for this recording which has size *num_par* . cap_order ********* is the maximum number of orders that can fit in *taylor* . taylor ****** Is the matrix of Taylor coefficients for all the variables. i_vec ***** We use *i_vec* to denote the ``size_t`` value corresponding to :ref:`var_store_op@x` . If *x* is a parameter (variable) this is a parameter (variable) index. vec_ad2isvar ************ This vector has size :ref:`var_store_op@num_vecad_ind` . The input values of its elements does not matter. If the value being stored is a parameter (variable), *vec_ad2isvar* [ *arg* [0] + *i_vec* ] is set to false (true). vec_ad2index ************ This vector has size *num_vecad_ind* . The input value of its elements does not matter. If the value being stored is a parameter (variable), *vec_ad2index* [ *arg* [0] + *i_vec* ] is set to the parameter (variable) index corresponding to the value being stored. {xrst_end var_store_forward_0} */ // BEGIN_STORE_FORWARD_0 template inline void store_forward_0( op_code_var op_code , const addr_t* arg , size_t num_var , size_t num_par , const Base* parameter , size_t cap_order , const Base* taylor , pod_vector& vec_ad2isvar , pod_vector& vec_ad2index ) // END_STORE_FORWARD_0 { // // CPPAD_ASSERT_NARG_NRES(op_code, 3, 0); CPPAD_ASSERT_UNKNOWN( 0 < arg[0] ); CPPAD_ASSERT_UNKNOWN( vec_ad2isvar.size() == vec_ad2index.size() ) // // i_y size_t i_y = size_t( arg[2] ); // // i_vec, isvar // assign here to avoid compiler warning for default case addr_t i_vec = std::numeric_limits::max(); bool isvar = false; switch(op_code) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // case StppOp: i_vec = addr_t( Integer( parameter[ arg[1] ] ) ); isvar = false; CPPAD_ASSERT_UNKNOWN( i_y < num_par ); break; // case StpvOp: i_vec = addr_t( Integer( parameter[ arg[1] ] ) ); isvar = true; CPPAD_ASSERT_UNKNOWN( i_y < num_var ); break; // case StvpOp: i_vec = addr_t(Integer( taylor[ size_t(arg[1]) * cap_order + 0 ] )); isvar = false; CPPAD_ASSERT_UNKNOWN( i_y < num_par ); break; // case StvvOp: i_vec = addr_t(Integer( taylor[ size_t(arg[1]) * cap_order + 0 ] )); isvar = true; CPPAD_ASSERT_UNKNOWN( i_y < num_var ); break; } // CPPAD_ASSERT_KNOWN( size_t(i_vec) < vec_ad2index[ arg[0] - 1 ] , "VecAD: zero order forward index out of range" ); // // vec_ad2isvar, vec_ad2index vec_ad2isvar[ arg[0] + i_vec ] = isvar; vec_ad2index[ arg[0] + i_vec ] = i_y; } /* ------------------------------------------------------------------------------ {xrst_begin var_store_for_jac dev} Forward Jacobian Sparsity for Store a VecAD Element ################################################### v, x, y ******* see :ref:`var_store_op@v` , :ref:`var_store_op@x` , :ref:`var_store_op@y` Prototype ********* {xrst_literal // BEGIN_STORE_FOR_JAC // END_STORE_FOR_JAC } op_code, num_vecad_ind, arg *************************** see :ref:`var_store_op@op_code` , :ref:`var_store_op@num_vecad_ind` , :ref:`var_store_op@arg` . Vector_set ********** is the type used for vectors of sets. It must satisfy the :ref:`SetVector-name` concept. dependency ********** If true (false) we are including (are not including) dependencies that have derivative zero in the sparsity pattern. For example, the :ref:`Discrete-name` functions have derivative zero, but the value depends on its argument. vecad_ind ********* is a vector with size *num_vec_ind* . We use the notation *i_v* defined by |tab| *i_v* = vecad_ind[ arg[0] - 1 ] This is the index of the VecAD vector and is less than the number of VecAD vectors in the recording. var_sparsity ************ If :ref:`var_store_op@y` is a variable, the sets with index arg[2] in *var_sparsity* is the sparsity pattern for *y* . Otherwise, *y* is a parameter and its sparsity pattern for *y* is empty vecad_sparsity ************** The set with index *i_v* in *vecad_sparsity is the sparsity pattern for the vector *v*. The sparsity pattern for *y* is added to the sparsity pattern for *v* . If *dependency* is true and *x* is a variable, the sparsity pattern for *x* is also added to the sparsity pattern for *v*. {xrst_end var_store_for_jac} */ // BEGIN_STORE_FOR_JAC template inline void store_for_jac( op_code_var op_code , size_t num_vecad_ind , const addr_t* arg , bool dependency , const pod_vector& vecad_ind , const Vector_set& var_sparsity , Vector_set& vecad_sparsity ) // END_STORE_FOR_JAC { // // CPPAD_ASSERT_NARG_NRES(op_code, 3, 0); CPPAD_ASSERT_UNKNOWN( 0 < arg[0] ); CPPAD_ASSERT_UNKNOWN( num_vecad_ind == vecad_ind.size() ); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_vecad_ind ); // // i_v size_t i_v = vecad_ind[ arg[0] - 1 ]; CPPAD_ASSERT_UNKNOWN( i_v < vecad_sparsity.n_set() ); // // i_x, i_y size_t i_x = size_t( arg[1] ); size_t i_y = size_t( arg[2] ); // switch(op_code) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // case StppOp: break; // case StpvOp: vecad_sparsity.binary_union(i_v, i_v, i_y, var_sparsity); break; // case StvpOp: if( dependency ) vecad_sparsity.binary_union(i_v, i_v, i_x, var_sparsity); break; // case StvvOp: if( dependency ) vecad_sparsity.binary_union(i_v, i_v, i_x, var_sparsity); vecad_sparsity.binary_union(i_v, i_v, i_y, var_sparsity); break; } return; } /* ------------------------------------------------------------------------------ {xrst_begin var_store_rev_jac dev} Reverse Jacobian Sparsity for Store a VecAD Element ################################################### v, x, y ******* see :ref:`var_store_op@v` , :ref:`var_store_op@x` , :ref:`var_store_op@y` Prototype ********* {xrst_literal // BEGIN_STORE_REV_JAC // END_STORE_REV_JAC } op_code, num_vecad_ind, arg *************************** see :ref:`var_store_op@op_code` , :ref:`var_store_op@num_vecad_ind` , :ref:`var_store_op@arg` . Vector_set ********** is the type used for vectors of sets. It must satisfy the :ref:`SetVector-name` concept. dependency ********** If true (false) we are including (are not including) dependencies that have derivative zero in the sparsity pattern. For example, the :ref:`Discrete-name` functions have derivative zero, but the value depends on its argument. vecad_ind ********* is a vector with size *num_vec_ind* . We use the notation *i_v* defined by |tab| *i_v* = vecad_ind[ arg[0] - 1 ] This is the index of the VecAD vector and is less than the number of VecAD vectors in the recording. var_sparsity ************ If :ref:`var_store_op@y` is a variable, the sparsity pattern for *v* is added to the sparsity pattern for *y*. If *dependency* is true and *x* is a variable, the sparsity pattern for *v* is also added to the sparsity pattern for *x*. vecad_sparsity ************** The set with index *i_v* in *vecad_sparsity is the sparsity pattern for the vector *v*. {xrst_end var_store_rev_jac} */ // BEGIN_STORE_REV_JAC template inline void store_rev_jac( op_code_var op_code , size_t num_vecad_ind , const addr_t* arg , bool dependency , const pod_vector& vecad_ind , Vector_set& var_sparsity , const Vector_set& vecad_sparsity ) // END_STORE_REV_JAC { // // CPPAD_ASSERT_NARG_NRES(op_code, 3, 0); CPPAD_ASSERT_UNKNOWN( 0 < arg[0] ); CPPAD_ASSERT_UNKNOWN( num_vecad_ind == vecad_ind.size() ); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_vecad_ind ); // // i_v size_t i_v = vecad_ind[ arg[0] - 1 ]; CPPAD_ASSERT_UNKNOWN( i_v < vecad_sparsity.n_set() ); // // i_x, i_y size_t i_x = size_t( arg[1] ); size_t i_y = size_t( arg[2] ); // switch(op_code) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // case StppOp: break; // case StpvOp: var_sparsity.binary_union(i_y, i_y, i_v, vecad_sparsity); break; // case StvpOp: if( dependency ) var_sparsity.binary_union(i_x, i_x, i_v, vecad_sparsity); break; // case StvvOp: if( dependency ) var_sparsity.binary_union(i_x, i_x, i_v, vecad_sparsity); var_sparsity.binary_union(i_y, i_y, i_v, vecad_sparsity); break; } return; } /* ------------------------------------------------------------------------------ {xrst_begin var_store_rev_hes dev} Reverse Hessian Sparsity for Store a VecAD Element ################################################## v, x, y ******* see :ref:`var_store_op@v` , :ref:`var_store_op@x` , :ref:`var_store_op@y` Prototype ********* {xrst_literal // BEGIN_STORE_REV_HES // END_STORE_REV_HES } op_code, num_vecad_ind, arg *************************** see :ref:`var_store_op@op_code` , :ref:`var_store_op@num_vecad_ind` , :ref:`var_store_op@arg` . Vector_set ********** is the type used for vectors of sets. It must satisfy the :ref:`SetVector-name` concept. vecad_ind ********* is a vector with size *num_vec_ind* . We use the notation *i_v* defined by |tab| *i_v* = vecad_ind[ arg[0] - 1 ] This is the index of the VecAD vector and is less than the number of VecAD vectors in the recording. It is also the index of the hessian sparsity pattern for *v* in *vecad_sparsity*. var_sparsity ************ If :ref:`var_store_op@y` is a variable, the hessian sparsity pattern for *v* is added to the hessian sparsity pattern for *y*. vecad_sparsity ************** The set with index *i_v* in *vecad_sparsity is the hessian sparsity pattern for the vector *v*. var_rev_jac *********** If the scalar function we are computing the Hessian sparsity of has a non-zero partial w.r.t. *v*, and *y* is a variable, *var_rev_jac* [ *i_y* ] is set to true. This is because the scalar function has non-zero partial w.r.t. *y* . vecad_rev_jac ************* the *i_v* component of this vector is true , if the scalar function has non-zero partial w.r.t *v*. {xrst_end var_store_rev_hes} */ // BEGIN_STORE_REV_HES template inline void store_rev_hes( op_code_var op_code , const addr_t* arg , size_t num_vecad_ind , const pod_vector& vecad_ind , Vector_set& var_sparsity , const Vector_set& vecad_sparsity , bool* var_rev_jac , const pod_vector& vecad_rev_jac ) // END_STORE_REV_HES { // // CPPAD_ASSERT_NARG_NRES(op_code, 3, 0); CPPAD_ASSERT_UNKNOWN( 0 < arg[0] ); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_vecad_ind ); CPPAD_ASSERT_UNKNOWN( vecad_ind.size() == num_vecad_ind ); // // i_v size_t i_v = vecad_ind[ arg[0] - 1 ]; CPPAD_ASSERT_UNKNOWN( i_v < vecad_sparsity.n_set() ); // // i_y size_t i_y = size_t( arg[2] ); // switch(op_code) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // case StpvOp: case StvvOp: var_sparsity.binary_union(i_y, i_y, i_v, vecad_sparsity); var_rev_jac[i_y] |= vecad_rev_jac[i_v]; break; // case StppOp: case StvpOp: break; } // return; } /* ------------------------------------------------------------------------------ {xrst_begin var_store_for_hes dev} Forward Hessian Sparsity for Store a VecAD Element ################################################## v, x, y ******* see :ref:`var_store_op@v` , :ref:`var_store_op@x` , :ref:`var_store_op@y` Prototype ********* {xrst_literal // BEGIN_STORE_FOR_HES // END_STORE_FOR_HES } op_code, num_vecad_ind, arg *************************** see :ref:`var_store_op@op_code` , :ref:`var_store_op@num_vecad_ind` , :ref:`var_store_op@arg` . Vector_set ********** is the type used for vectors of sets. It must satisfy the :ref:`SetVector-name` concept. vecad_ind ********* is a vector with size *num_vec_ind* . We use the notation *i_v* defined by |tab| *i_v* = vecad_ind[ arg[0] - 1 ] This is the index of the VecAD vector and is less than the number of VecAD vectors in the recording. It is also the index of the hessian sparsity pattern for *v* in *vecad_sparsity*. var_sparsity ************ If :ref:`var_store_op@y` is a variable, the hessian sparsity pattern for *v* is added to the hessian sparsity pattern for *y*. vecad_sparsity ************** The set with index *i_v* in *vecad_sparsity is the hessian sparsity pattern for the vector *v*. var_rev_jac *********** If the scalar function we are computing the Hessian sparsity of has a non-zero partial w.r.t. *v*, and *y* is a variable, *var_rev_jac* [ *i_y* ] is set to true. This is because the scalar function has non-zero partial w.r.t. *y* . vecad_rev_jac ************* the *i_v* component of this vector is true , if the scalar function has non-zero partial w.r.t *v*. {xrst_end var_store_for_hes} */ // BEGIN_STORE_FOR_HES template inline void store_for_hes( op_code_var op_code , const addr_t* arg , size_t num_vecad_ind , size_t n , const pod_vector& vecad_ind , Vector_set& vecad_sparsity , const Vector_set& for_hes_sparse ) // END_STORE_FOR_HES { // // CPPAD_ASSERT_NARG_NRES(op_code, 3, 0); CPPAD_ASSERT_UNKNOWN( 0 < arg[0] ); CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_vecad_ind ); CPPAD_ASSERT_UNKNOWN( vecad_ind.size() == num_vecad_ind ); // // np1 size_t np1 = n + 1; CPPAD_ASSERT_UNKNOWN( for_hes_sparse.end() == np1 ); // // i_v size_t i_v = vecad_ind[ arg[0] - 1 ]; CPPAD_ASSERT_UNKNOWN( i_v < vecad_sparsity.n_set() ); // // i_y size_t i_y = size_t( arg[2] ); // switch(op_code) { // default: CPPAD_ASSERT_UNKNOWN(false); break; // // vecad_sparsity // set Jacobian sparsity for vector with index i_v case StpvOp: case StvvOp: vecad_sparsity.binary_union(i_v, i_v, np1 + i_y, for_hes_sparse); break; // case StppOp: case StvpOp: break; } // return; } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/sub_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_SUB_OP_HPP # define CPPAD_LOCAL_VAR_OP_SUB_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // --------------------------- Subvv ----------------------------------------- // See dev documentation: forward_binary_op template inline void subvv_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SubvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(SubvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to arguments and result Base* x = taylor + size_t(arg[0]) * cap_order; Base* y = taylor + size_t(arg[1]) * cap_order; Base* z = taylor + i_z * cap_order; for(size_t d = p; d <= q; d++) z[d] = x[d] - y[d]; } // See dev documentation: forward_binary_op template inline void subvv_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SubvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(SubvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to arguments and result size_t num_taylor_per_var = (cap_order-1) * r + 1; size_t m = (q-1) * r + 1; Base* x = taylor + size_t(arg[0]) * num_taylor_per_var + m; Base* y = taylor + size_t(arg[1]) * num_taylor_per_var + m; Base* z = taylor + i_z * num_taylor_per_var + m; for(size_t ell = 0; ell < r; ell++) z[ell] = x[ell] - y[ell]; } // See dev documentation: forward_binary_op template inline void subvv_forward_0( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SubvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(SubvvOp) == 1 ); // Taylor coefficients corresponding to arguments and result Base* x = taylor + size_t(arg[0]) * cap_order; Base* y = taylor + size_t(arg[1]) * cap_order; Base* z = taylor + i_z * cap_order; z[0] = x[0] - y[0]; } // See dev documentation: reverse_binary_op template inline void subvv_reverse( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SubvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(SubvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Partial derivatives corresponding to arguments and result Base* px = partial + size_t(arg[0]) * n_order; Base* py = partial + size_t(arg[1]) * n_order; Base* pz = partial + i_z * n_order; // number of indices to access size_t i = d + 1; while(i) { --i; px[i] += pz[i]; py[i] -= pz[i]; } } // --------------------------- Subpv ----------------------------------------- // See dev documentation: forward_binary_op template inline void subpv_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SubpvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(SubpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to arguments and result Base* y = taylor + size_t(arg[1]) * cap_order; Base* z = taylor + i_z * cap_order; // Paraemter value Base x = parameter[ arg[0] ]; if( p == 0 ) { z[0] = x - y[0]; p++; } for(size_t d = p; d <= q; d++) z[d] = - y[d]; } // See dev documentation: forward_binary_op template inline void subpv_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SubpvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(SubpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to arguments and result size_t num_taylor_per_var = (cap_order-1) * r + 1; size_t m = (q-1) * r + 1; Base* y = taylor + size_t(arg[1]) * num_taylor_per_var + m; Base* z = taylor + i_z * num_taylor_per_var + m; // Paraemter value for(size_t ell = 0; ell < r; ell++) z[ell] = - y[ell]; } // See dev documentation: forward_binary_op template inline void subpv_forward_0( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SubpvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(SubpvOp) == 1 ); // Paraemter value Base x = parameter[ arg[0] ]; // Taylor coefficients corresponding to arguments and result Base* y = taylor + size_t(arg[1]) * cap_order; Base* z = taylor + i_z * cap_order; z[0] = x - y[0]; } // See dev documentation: reverse_binary_op template inline void subpv_reverse( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SubvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(SubvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Partial derivatives corresponding to arguments and result Base* py = partial + size_t(arg[1]) * n_order; Base* pz = partial + i_z * n_order; // number of indices to access size_t i = d + 1; while(i) { --i; py[i] -= pz[i]; } } // --------------------------- Subvp ----------------------------------------- // See dev documentation: forward_binary_op template inline void subvp_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SubvpOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(SubvpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to arguments and result Base* x = taylor + size_t(arg[0]) * cap_order; Base* z = taylor + i_z * cap_order; // Parameter value Base y = parameter[ arg[1] ]; if( p == 0 ) { z[0] = x[0] - y; p++; } for(size_t d = p; d <= q; d++) z[d] = x[d]; } // See dev documentation: forward_binary_op template inline void subvp_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SubvpOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(SubvpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to arguments and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + size_t(arg[0]) * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; // Parameter value size_t m = (q-1) * r + 1; for(size_t ell = 0; ell < r; ell++) z[m+ell] = x[m+ell]; } // See dev documentation: forward_binary_op template inline void subvp_forward_0( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SubvpOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(SubvpOp) == 1 ); // Parameter value Base y = parameter[ arg[1] ]; // Taylor coefficients corresponding to arguments and result Base* x = taylor + size_t(arg[0]) * cap_order; Base* z = taylor + i_z * cap_order; z[0] = x[0] - y; } // See dev documentation: reverse_binary_op template inline void subvp_reverse( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(SubvpOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(SubvpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Partial derivatives corresponding to arguments and result Base* px = partial + size_t(arg[0]) * n_order; Base* pz = partial + i_z * n_order; // number of indices to access size_t i = d + 1; while(i) { --i; px[i] += pz[i]; } } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/tan_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_TAN_OP_HPP # define CPPAD_LOCAL_VAR_OP_TAN_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // See dev documentation: forward_unary_op template inline void tan_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(TanOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(TanOp) == 2 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; Base* y = z - cap_order; size_t k; if( p == 0 ) { z[0] = tan( x[0] ); y[0] = z[0] * z[0]; p++; } for(size_t j = p; j <= q; j++) { Base base_j = static_cast(double(j)); z[j] = x[j]; for(k = 1; k <= j; k++) z[j] += Base(double(k)) * x[k] * y[j-k] / base_j; y[j] = z[0] * z[j]; for(k = 1; k <= j; k++) y[j] += z[k] * z[j-k]; } } // See dev documentation: forward_unary_op template inline void tan_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(TanOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(TanOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + i_x * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; Base* y = z - num_taylor_per_var; size_t k; size_t m = (q-1) * r + 1; for(size_t ell = 0; ell < r; ell++) { z[m+ell] = Base(double(q)) * ( x[m+ell] + x[m+ell] * y[0]); for(k = 1; k < q; k++) z[m+ell] += Base(double(k)) * x[(k-1)*r+1+ell] * y[(q-k-1)*r+1+ell]; z[m+ell] /= Base(double(q)); // y[m+ell] = Base(2.0) * z[m+ell] * z[0]; for(k = 1; k < q; k++) y[m+ell] += z[(k-1)*r+1+ell] * z[(q-k-1)*r+1+ell]; } } // See dev documentation: forward_unary_op template inline void tan_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(TanOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(TanOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; // called z in documentation Base* y = z - cap_order; // called y in documentation z[0] = tan( x[0] ); y[0] = z[0] * z[0]; } // See dev documentation: reverse_unary_op template inline void tan_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // // // i_x size_t i_x = size_t(arg[0]); // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(TanOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(TanOp) == 2 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Taylor coefficients and partials corresponding to argument const Base* x = taylor + i_x * cap_order; Base* px = partial + i_x * n_order; // Taylor coefficients and partials corresponding to first result const Base* z = taylor + i_z * cap_order; // called z in doc Base* pz = partial + i_z * n_order; // Taylor coefficients and partials corresponding to auxiliary result const Base* y = z - cap_order; // called y in documentation Base* py = pz - n_order; size_t j = d; size_t k; Base base_two(2); while(j) { px[j] += pz[j]; pz[j] /= Base(double(j)); for(k = 1; k <= j; k++) { px[k] += azmul(pz[j], y[j-k]) * Base(double(k)); py[j-k] += azmul(pz[j], x[k]) * Base(double(k)); } for(k = 0; k < j; k++) pz[k] += azmul(py[j-1], z[j-k-1]) * base_two; --j; } px[0] += azmul(pz[0], Base(1.0) + y[0]); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/tanh_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_TANH_OP_HPP # define CPPAD_LOCAL_VAR_OP_TANH_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // See dev documentation: forward_unary_op template inline void tanh_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(TanOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(TanOp) == 2 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; Base* y = z - cap_order; size_t k; if( p == 0 ) { z[0] = tanh( x[0] ); y[0] = z[0] * z[0]; p++; } for(size_t j = p; j <= q; j++) { Base base_j = static_cast(double(j)); z[j] = x[j]; for(k = 1; k <= j; k++) z[j] -= Base(double(k)) * x[k] * y[j-k] / base_j; y[j] = z[0] * z[j]; for(k = 1; k <= j; k++) y[j] += z[k] * z[j-k]; } } // See dev documentation: forward_unary_op template inline void tanh_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(TanOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(TanOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to argument and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + i_x * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; Base* y = z - num_taylor_per_var; size_t k; size_t m = (q-1) * r + 1; for(size_t ell = 0; ell < r; ell++) { z[m+ell] = Base(double(q)) * ( x[m+ell] - x[m+ell] * y[0] ); for(k = 1; k < q; k++) z[m+ell] -= Base(double(k)) * x[(k-1)*r+1+ell] * y[(q-k-1)*r+1+ell]; z[m+ell] /= Base(double(q)); // y[m+ell] = Base(2.0) * z[m+ell] * z[0]; for(k = 1; k < q; k++) y[m+ell] += z[(k-1)*r+1+ell] * z[(q-k-1)*r+1+ell]; } } // See dev documentation: forward_unary_op template inline void tanh_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) { // // // i_x size_t i_x = size_t(arg[0]); // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(TanOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(TanOp) == 2 ); CPPAD_ASSERT_UNKNOWN( 0 < cap_order ); // Taylor coefficients corresponding to argument and result Base* x = taylor + i_x * cap_order; Base* z = taylor + i_z * cap_order; // called z in documentation Base* y = z - cap_order; // called y in documentation z[0] = tanh( x[0] ); y[0] = z[0] * z[0]; } // See dev documentation: reverse_unary_op template inline void tanh_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // // // i_x size_t i_x = size_t(arg[0]); // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(TanOp) == 1 ); CPPAD_ASSERT_UNKNOWN( NumRes(TanOp) == 2 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Taylor coefficients and partials corresponding to argument const Base* x = taylor + i_x * cap_order; Base* px = partial + i_x * n_order; // Taylor coefficients and partials corresponding to first result const Base* z = taylor + i_z * cap_order; // called z in doc Base* pz = partial + i_z * n_order; // Taylor coefficients and partials corresponding to auxiliary result const Base* y = z - cap_order; // called y in documentation Base* py = pz - n_order; size_t j = d; size_t k; Base base_two(2); while(j) { px[j] += pz[j]; pz[j] /= Base(double(j)); for(k = 1; k <= j; k++) { px[k] -= azmul(pz[j], y[j-k]) * Base(double(k)); py[j-k] -= azmul(pz[j], x[k]) * Base(double(k)); } for(k = 0; k < j; k++) pz[k] += azmul(py[j-1], z[j-k-1]) * base_two; --j; } px[0] += azmul(pz[0], Base(1.0) - y[0]); } } } } // END namespace # endif ================================================ FILE: include/cppad/local/var_op/template/atomic_op.xrst ================================================ {xrst_comment SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later SPDX-FileCopyrightText: Bradley M. Bell SPDX-FileContributor: 2024 Bradley M. Bell This file is intended to help document of all the atomic_op routines. It has the following xrst template comments: headers: n_res, i_z, itr, play, parameter, trace, work headers if @mode@ is reverse: G and H It has the following xrst template replacements: @mode@ : Must be either forward or reverse. } Notation ******** n_res ===== We use *n_res* to denote the number of variables created by this function call. Which is also equal to the number of {xrst_spell_off} ``FunrvOp`` {xrst_spell_on} operators in function call. i_z === We use *i_z* to denote the index of the last variable created by this function call. {xrst_suspend @mode@ != reverse} G and H ======= We use :math:`y(x)` to denote the atomic function call as a mapping from the vector *x* to the vector *y* . We use :math:`G( y, x, \ldots )` to denote a scalar valued function of the taylor coefficients of the variables with index less than or equal *i_z* ; i.e., the last variable in the vector *y* . We define :math:`H(x, \ldots )` by .. math:: H(x, \cdots ) = G [ y(x), x, \cdots ] {xrst_resume} itr *** is an iterator for the recording in *play* . {xrst_suspend @mode@ != forward} On input (output), the operator corresponding to *itr* is the first (second) :ref:`var_atomic_op@AfunOp` for this function call. {xrst_resume} {xrst_suspend @mode@ != reverse} On input (output), the operator corresponding to *itr* is the second (first) :ref:`var_atomic_op@AfunOp` for this function call. {xrst_resume} play **** is a player for the recording that contains the operation sequence that contains this atomic function call. parameter ********* is the parameter vector for this operation sequence. trace ***** if *trace* is true (false) a trace of the evaluation of this atomic function call is (is not) printed. work **** is unspecified work space. It passed as an argument to reduce memory allocations. ================================================ FILE: include/cppad/local/var_op/template/forward_dir.xrst ================================================ {xrst_comment SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later SPDX-FileCopyrightText: Bradley M. Bell SPDX-FileContributor: 2024 Bradley M. Bell This file is intended to help document multiple direction forward Taylor computation for all operators. It has the following xrst template comments: headers: n_dir, cap_order, order_up, taylor It does not have any xrst template replacements: } n_dir ***** number of directions that we are computing the Taylor coefficient for. cap_order ********* is the maximum number of orders that can fit in *taylor* . order_up ******** is the order of the Taylor coefficients that are computed by this call. taylor ****** per_variable ============ For each variable there is one Taylor coefficient of order zero and *n_dir* coefficients for orders greater than zero. The taylor coefficients capacity per variable is:: per_variable = (cap_order - 1) * n_dir + 1 (j, k, ell) =========== For variable index j, order k, and direction index ell:: if k == 0 (j, k, ell) = j * per_variable else (j, k, ell) = j * per_variable + (k-1) * n_dir + 1 + ell The value taylor[ (j, k, ell) ] is the Taylor coefficient corresponding to the variable with index j, the order k, and the direction with index ell. n_dir = 1 ========= If *n_dir* is equal to one then *ell* is zero and:: (j, k, ell) = j * cap_order + k n_res ===== is the number of results (that are variables) for this operator. Input ===== :: for j = 0, ..., i_z - n_res, for k = 0 , ... , order_up for ell = 0 , ... , n_dir - 1 taylor [ (j, k, ell) ] is an input for j = i_z - n_res + 1, ..., i_z for k = 0 , ... , order_up - 1 for ell = 0 , ... , n_dir - 1 taylor [ (j, k, ell) ] is an input Output ====== :: for j = i_z - n_res + 1, ..., i_z for ell = 0 , ... , n_dir - 1 taylor [ (j, order_up, ell) ] is an output ================================================ FILE: include/cppad/local/var_op/template/forward_op.xrst ================================================ {xrst_comment SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later SPDX-FileCopyrightText: Bradley M. Bell SPDX-FileContributor: 2024 Bradley M. Bell This file is intended to help document the multiple order forward Taylor computation for all operators. It has the following xrst template comments: headers: cap_order, order_low, order_up, taylor It does not have any xrst template replacements: } cap_order ********* is the maximum number of orders that can fit in *taylor* . order_low ********* is the lowest order Taylor coefficient that we are computing. order_up ******** is the highest order Taylor coefficient that we are computing. taylor ****** The Taylor coefficient corresponding to variable *j* and order *k* is *taylor* [ *j* * *cap_order* + *k* ] n_res ===== is the number of results (that are variables) for this operator. Input ===== :: for j = 0, ..., i_z - n_res for k = 0 , ... , order_up taylor [ j * cap_order + k ] is an input for j = i_z - n_res + 1 , ... , i_z for k = 0 , ... , order_up - 1 taylor [ j * cap_order + k ] is an input Output ====== :: for j = i_z - n_res + 1 , ... , i_z for k = order_low , ... , order_up taylor [ i_z * cap_order + k ] is an output ================================================ FILE: include/cppad/local/var_op/template/reverse_op.xrst ================================================ {xrst_comment SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later SPDX-FileCopyrightText: Bradley M. Bell SPDX-FileContributor: 2024 Bradley M. Bell This file is intended to help document reverse computation of Taylor partials for all operators. It has the following xrst template comments: headers: n_order, partial It has the following xrst template replacements: @x, y@ : The arguments, without parenthesis, in function for this operator; e.g., if @x, y@ is replaced by 'x , y', the function is 'z(x, y)' . } n_order ******* is the number of Taylor coefficient orders that we are computing the partial derivatives with respect to. partial ******* The partial derivative with respect to the order *k* Taylor coefficient for the variable with index *j* is:: partial[ j * n_order + k ] We use :math:`G( z, @x, y@, \ldots )` to denote a scalar valued function of the taylor coefficients of the variables with index less than or equal the variable index for *z* . We define :math:`H( @x, y@, \ldots )` by .. math:: H( @x, y@, \cdots ) = G [ z(@x, y@), @x, y@, \cdots ] On input, *partial* contains the partial derivatives of *G* with respect to the Taylor coefficients of the arguments to *G* . On output, *partial* contains the partial derivatives of *H* with respect to the Taylor coefficients of the arguments to *H* . We only compute partials with respect to variables; i.e., no partials are computed with respect to the parameters in @x, y@ . ================================================ FILE: include/cppad/local/var_op/two_var.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_TWO_VAR_HPP # define CPPAD_LOCAL_VAR_OP_TWO_VAR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN_CPPAD_LOCAL_SPARSE_NAMESPACE namespace CppAD { namespace local { namespace var_op { // END_DECLARE_NAMESPACE /* {xrst_begin_parent var_two_var dev} Sparsity Calculations for Operators With Two Variables ###################################################### Binary Operators **************** | *z* = *fun* ( *x* , *y* ) | *z* = *x* *Op* *y* See :ref:`var_binary_op@Fun` for binary operators, restricted to the case where both *x* and *y* are variables, for some of the possible values of *Fun* and the corresponding operators. x * is the first operand for this operator . y * is the second operand for this operator z * is the primary result for this operator. Vector_set ********** is the type used for vectors of sets. It must satisfy the :ref:`SetVector-name` concept. i_z *** is the variable index corresponding to *z* . arg *** arg[0] ====== is the variable index corresponding to x. arg[1] ====== is the variable index corresponding to y. {xrst_end var_two_var} ------------------------------------------------------------------------------ {xrst_begin var_two_var_for_jac dev} Forward Jacobian Sparsity for Two Variable Argument Operators ############################################################# x, y, z ******* see :ref:`var_two_var@x` , :ref:`var_two_var@y` , :ref:`var_two_var@z` Prototype ********* {xrst_literal // BEGIN_TWO_VAR_FOR_JAC // END_TWO_VAR_FOR_JAC } Vector_set, i_z, arg ******************** see :ref:`var_two_var@Vector_set` , :ref:`var_two_var@i_z` , :ref:`var_two_var@arg` sparsity ******** Input ===== for *j* < *i_z* and *j* not an auxiliary result, the set with index *j* in *sparsity* is the Jacobian sparsity for the variable with index *j* . Output ====== The set with index *i_z* in *sparsity* is the Jacobian sparsity for the variable *z* . {xrst_end var_two_var_for_jac} */ // BEGIN_TWO_VAR_FOR_JAC template void two_var_for_jac( size_t i_z , const addr_t* arg , Vector_set& sparsity ) // END_TWO_VAR_FOR_JAC { // check assumptions CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < i_z ); CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < i_z ); sparsity.binary_union(i_z, size_t(arg[0]), size_t(arg[1]), sparsity); return; } /* ------------------------------------------------------------------------------ {xrst_begin var_two_var_rev_jac dev} Reverse Jacobian Sparsity for Two Variable Argument Operators ############################################################# x, y, z ******* see :ref:`var_two_var@x` , :ref:`var_two_var@y` , :ref:`var_two_var@z` Prototype ********* {xrst_literal // BEGIN_TWO_VAR_REV_JAC // END_TWO_VAR_REV_JAC } Vector_set, i_z, arg ******************** see :ref:`var_two_var@Vector_set` , :ref:`var_two_var@i_z` , :ref:`var_two_var@arg` sparsity ******** Use z(x, y) to denote the variable *z* as a function of the variables *x* , *y* , and define H in terms of G by:: H( x, y, ... ) = G[ z(x, y) , x, y, ... ] On input, *sparsity* is a sparsity pattern for the Jacobian of *G* . Upon return, *sparsity* is a sparsity pattern for the Jacobian of *H* . {xrst_end var_two_var_rev_jac} */ // BEGIN_TWO_VAR_REV_JAC template void two_var_rev_jac( size_t i_z , const addr_t* arg , Vector_set& sparsity ) // END_TWO_VAR_REV_JAC { // check assumptions CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < i_z ); CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < i_z ); sparsity.binary_union( size_t(arg[0]), size_t(arg[0]), i_z, sparsity); sparsity.binary_union( size_t(arg[1]), size_t(arg[1]), i_z, sparsity); return; } // --------------------------------------------------------------------------- /* {xrst_begin var_two_var_for_hes dev} Forward Hessian Sparsity for Two Variable Argument Operators ############################################################ x, y, z ******* see :ref:`var_two_var@x` , :ref:`var_two_var@y` , :ref:`var_two_var@z` Prototype ********* {xrst_literal // BEGIN_TWO_VAR_FOR_HES // END_TWO_VAR_FOR_HES } Vector_set, i_z, arg ******************** see :ref:`var_two_var@Vector_set` , :ref:`var_two_var@i_z` , :ref:`var_two_var@arg` linear ****** linear[0] ========= This value is true (false) if the :math:`z(x, y)` must have zero second partial derivative with respect to *x* (may have non-zero second partial). linear[1] ========= This value is true (false) if the :math:`z(x, y)` must have zero second partial derivative with respect to *y* (may have non-zero second partial). linear[2] ========= This value is true (false) if the :math:`z(x, y)` must have zero cross partial derivative (may have non-zero cross partial). n_independent_p1 **************** is the number of independent variables (in the tape) plus one. num_var ******* This is the total number of variables in the tape (counting the phantom variable at index zero). for_sparsity ************ On input, all the linear and nonlinear interactions up to the arguments to the atomic function have been take into account. Upon return, the linear and nonlinear interactions in the atomic function have been take into account. Hessian Sparsity ================ For *j* equal 1 to *n_independent_p1* - 1, if *i* is in set with index *j* , the Hessian may have a non-zero partial with respect to the independent variables with indices ( *i* - 1, *j* - 1 ) . Note that the index zero is not used because it corresponds to the phantom variable on the tape. Jacobian Sparsity ================= If *i* is in the set with index *n_independent_p1* + *j* , the variable with index *j* may have a non-zero partial with resect to the independent variable with index *i* - 1 . {xrst_end var_two_var_for_hes} */ // BEGIN_TWO_VAR_FOR_HES template void two_var_for_hes( size_t n_independent_p1 , size_t num_var , size_t i_z , const addr_t* arg , bool* linear , Vector_set& for_sparsity ) // END_TWO_VAR_FOR_HES { // // np1, i_x, i_y, linear_x, linear_y, linear_xy size_t np1 = n_independent_p1; size_t i_x = size_t( arg[0] ); size_t i_y = size_t( arg[1] ); bool linear_x = linear[0]; bool linear_y = linear[1]; bool linear_xy = linear[2]; // CPPAD_ASSERT_UNKNOWN( i_x < i_z && i_y < i_z ); CPPAD_ASSERT_UNKNOWN( i_z < num_var ); CPPAD_ASSERT_UNKNOWN( for_sparsity.end() == np1 ); CPPAD_ASSERT_UNKNOWN( for_sparsity.n_set() == np1 + num_var ); CPPAD_ASSERT_UNKNOWN( for_sparsity.number_elements(np1) == 0 ); // // // for_sparsity // Jacobian sparsity for z for_sparsity.binary_union(np1 + i_z, np1 + i_x, np1 + i_y, for_sparsity); // // if( ! linear_x ) { // // itr_x, i_u typename Vector_set::const_iterator itr_x(for_sparsity, i_x + np1); size_t i_u = *itr_x; while( i_u < np1 ) { // x depends on the independent variable u // // for_sparsity // update Hessian term with one partial w.r.t u if( ! linear_xy ) { // other independent variables that z depends on for_sparsity.binary_union(i_u, i_u, i_z + np1, for_sparsity); } else { // other independent variables that x depends on for_sparsity.binary_union(i_u, i_u, i_x + np1, for_sparsity); } // // i_u i_u = *(++itr_x); } } if( ! linear_y ) { // // itr_y, i_u typename Vector_set::const_iterator itr_y(for_sparsity, i_y + np1); size_t i_u = *itr_y; while( i_u < np1 ) { // y depends on the independent variable u // // for_sparsity // update Hessian term with one partial w.r.t u if( ! linear_xy ) { // other independent variables that z depends on for_sparsity.binary_union(i_u, i_u, i_z + np1, for_sparsity); } else { // other independent variables that y depends on for_sparsity.binary_union(i_u, i_u, i_y + np1, for_sparsity); } // // i_u i_u = *(++itr_y); } } if( (! linear_xy) && linear_x ) { // // itr_x, i_u typename Vector_set::const_iterator itr_x(for_sparsity, i_x + np1); size_t i_u = *itr_x; while( i_u < np1 ) { // x depends on the independent variable u // // for_sparsity // update Hessian term with one partial w.r.t u other w.r.t // independent variables that y depends on for_sparsity.binary_union(i_u, i_u, i_y + np1, for_sparsity); // // i_u i_u = *(++itr_x); } } if( (! linear_xy) && linear_y ) { // // itr_y, i_u typename Vector_set::const_iterator itr_y(for_sparsity, i_y + np1); size_t i_u = *itr_y; while( i_u < np1 ) { // y depends on the independent variable u // // for_sparsity // update Hessian term with one partial w.r.t u other w.r.t // independent variables that x depends on for_sparsity.binary_union(i_u, i_u, i_x + np1, for_sparsity); // // i_u i_u = *(++itr_y); } } } // --------------------------------------------------------------------------- /* {xrst_begin var_two_var_rev_hes dev} Reverse Jacobian Sparsity for Two Variable Argument Operators ############################################################# x, y, z ******* see :ref:`var_two_var@x` , :ref:`var_two_var@y` , :ref:`var_two_var@z` G and H ******* Use z(x, y) to denote the variable *z* as a function of the variables *x* , *y* , and define H in terms of G by:: H( x, y, ... ) = G[ z(x, y) , x, y, ... ] Prototype ********* {xrst_literal // BEGIN_TWO_VAR_REV_HES // END_TWO_VAR_REV_HES } Vector_set, i_z, arg ******************** see :ref:`var_two_var@Vector_set` , :ref:`var_two_var@i_z` , :ref:`var_two_var@arg` linear ****** linear[0] ========= This value is true (false) if the :math:`z(x, y)` must have zero second partial derivative with respect to *x* (may have non-zero second partial). linear[1] ========= This value is true (false) if the :math:`z(x, y)` must have zero second partial derivative with respect to *y* (may have non-zero second partial). linear[2] ========= This value is true (false) if the :math:`z(x, y)` must have zero cross partial derivative (may have non-zero cross partial). n_independent_p1 **************** is the number of independent variables (in the tape) plus one. num_var ******* This is the total number of variables in the tape (counting the phantom variable at index zero). for_jac_sparsity **************** The set with index *j* is the forward Jacobian sparsity pattern for the variable with index *j*. rev_jac_include *************** If the *j* element of this vector is true, the variable with index *j* is included in the Hessian, or affects the value of a variable that is included in the Hessian. Input ===== :: for j = num_var, ... , i_z + 1 rev_jac_include[j] is an input Output ====== rev_jac_include[i_z] is an output rev_hes_sparsity **************** On input (output), this is the sparsity pattern for *G* ( *H* ). For each variable index *j* , *rev_hes_sparsity* [ *j* ] is the set of indices that may have non-zero cross partials with variable index *j* . Example ======= If the indices in the sets correspond to the independent variables, then *rev_hes_sparsity* ``.end()`` is the number of independent variables. For *i* a variable index between 1 and the number of independent variables, *i* - 1 is the corresponding independent variable index. (The index *i* = 0 corresponds to the phantom variable at the beginning of the tape. ) {xrst_end var_two_var_rev_hes} */ // BEGIN_TWO_VAR_REV_HES template void two_var_rev_hes( size_t i_z , const addr_t* arg , bool* linear , bool* rev_jacobian , const Vector_set& for_jac_sparsity , Vector_set& rev_hes_sparsity ) // END_TWO_VAR_REV_HES { // // check for nothing to do if( ! rev_jacobian[i_z] ) return; // // i_x, i_y, linear_x, linear_y, linear_xy size_t i_x = size_t( arg[0] ); size_t i_y = size_t( arg[1] ); bool linear_x = linear[0]; bool linear_y = linear[1]; bool linear_xy = linear[2]; // CPPAD_ASSERT_UNKNOWN( i_x < i_z ); CPPAD_ASSERT_UNKNOWN( i_y < i_z ); // // rev_hes_sparsity // propagate form z to x and y rev_hes_sparsity.binary_union(i_x, i_x, i_z, rev_hes_sparsity); rev_hes_sparsity.binary_union(i_y, i_y, i_z, rev_hes_sparsity); // if( ! linear_x ) { rev_hes_sparsity.binary_union(i_x, i_x, i_x, for_jac_sparsity); rev_hes_sparsity.binary_union(i_y, i_y, i_x, for_jac_sparsity); } if( ! linear_y ) { rev_hes_sparsity.binary_union(i_x, i_x, i_y, for_jac_sparsity); rev_hes_sparsity.binary_union(i_y, i_y, i_y, for_jac_sparsity); } if( ! linear_xy ) { rev_hes_sparsity.binary_union(i_x, i_x, i_y, for_jac_sparsity); rev_hes_sparsity.binary_union(i_y, i_y, i_x, for_jac_sparsity); } // // rev_jacobian rev_jacobian[i_x] = true; rev_jacobian[i_y] = true; } // --------------------------------------------------------------------------- } } } // END_CPPAD_LOCAL_SPARSE_NAMESPACE # endif ================================================ FILE: include/cppad/local/var_op/unary_op.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin_parent var_unary_op dev} {xrst_spell acosh asinh erfc expm neg } Unary Variable Operators ######################## All these operators have the same prototype and could be implemented as virtual functions for a unary base case. Compilers can not optimize across virtual function calls and testing indicates that a virtual function implementation is significant slower. User Syntax *********** | *z* = *Fun* ( *x* ) x * is the argument to this operator which must be a variable. z * is the primary result for this operator which is also a variable. Some unary operators have an auxiliary result; see :ref:`var_unary_op@n_res` below. Base **** base type for the operator; i.e., this operation was recorded using AD and computations by these operators done using type Base. Fun *** .. csv-table:: :widths: auto :header-rows: 1 op_code, Fun, z AbsOp, fabs, absolute value of *x* AcosOp, acos, inverse cosine of *x* AcoshOp, acosh, inverse hyperbolic cosine of *x* AsinOp, asin, inverse sine of *x* AsinhOp, asinh, inverse hyperbolic sine of *x* AtanOp, atan, inverse tangent of *x* AtanhOp, atanh, inverse hyperbolic tangent of *x* CosOp, cos, cosine of *x* CoshOp, cosh, hyperbolic cosine of *x* ErfOp, erf, error function of *x* ErfcOp, erfc, complementary error function of *x* ExpOp, exp, exponential of *x* Expm1Op, expm1, exponential of *x* minus one Log1pOp, log1p, logarithm of 1 + *x* LogOp, log, logarithm of *x* NegOp, -, minus *x* SignOp, sign, sign of *x* SinOp, sin, sine of *x* SinhOp, sinh, hyperbolic sine of *x* SqrtOp, sqrt, square root of *x* TanOp, tan, tangent of *x* TanhOp, tanh, hyperbolic tangent of *x* i_z *** is the variable index corresponding to *z* . n_res ***** is the number of results that are variables. This is 2 (1) for unary operators that have (do not have) an auxiliary result. If an operator has an auxiliary result, the variable index for the auxiliary result is *i_z* - 1 . arg *** *arg* [0] is the variable index corresponding to *x* . {xrst_end var_unary_op} ------------------------------------------------------------------------------ {xrst_begin var_unary_forward_0 dev} Zero Order Forward Unary Variable Operators ########################################### x, z, n_res *********** see :ref:`var_unary_op@x` , :ref:`var_unary_op@z` , :ref:`var_unary_op@n_res` Prototype ********* {xrst_code cpp} template inline void Fun_forward_0( size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) {xrst_code} Base, Fun, i_z, arg ******************* see :ref:`var_unary_op@Base` , :ref:`var_unary_op@Fun` , :ref:`var_unary_op@i_z` , :ref:`var_unary_op@arg` cap_order ********* is the maximum number of orders that can fit in *taylor* . taylor ****** The Taylor coefficient corresponding to variable *i* and order *k* is *taylor* [ *i* * *cap_order* + *k* ] Input ===== The zero order Taylor coefficients for variables with index *i* less than or equal *i_z* - *n_res* . Output ====== The zero order Taylor coefficients for variables with index *i_z* and *i_z* - *n_res* + 1. {xrst_end var_unary_forward_0} ------------------------------------------------------------------------------ {xrst_begin var_unary_forward_any dev} Any Order Forward Unary Variable Operators ########################################## x, z, n_res *********** see :ref:`var_unary_op@x` , :ref:`var_unary_op@z` , :ref:`var_unary_op@n_res` Prototype ********* {xrst_code cpp} template inline void Fun_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) {xrst_code} Base, Fun, i_z, arg ******************* see :ref:`var_unary_op@Base` , :ref:`var_unary_op@Fun` , :ref:`var_unary_op@i_z` , :ref:`var_unary_op@arg` {xrst_template ; include/cppad/local/var_op/template/forward_op.xrst headers: cap_order, order_low, order_up, taylor } {xrst_end var_unary_forward_any} ------------------------------------------------------------------------------ {xrst_begin var_unary_forward_dir dev} Multiple Direction Forward Unary Operators ########################################## x, z, n_res *********** see :ref:`var_unary_op@x` , :ref:`var_unary_op@z` , :ref:`var_unary_op@n_res` Prototype ********* {xrst_code cpp} template inline void Fun_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , size_t cap_order , Base* taylor ) {xrst_code} Base, Fun, i_z, arg ******************* see :ref:`var_unary_op@Base` , :ref:`var_unary_op@Fun` , :ref:`var_unary_op@i_z` , :ref:`var_unary_op@arg` {xrst_template ; include/cppad/local/var_op/template/forward_dir.xrst headers: n_dir, cap_order, order_up, taylor } {xrst_end var_unary_forward_dir} ------------------------------------------------------------------------------ {xrst_begin var_unary_reverse dev} Reverse Mode Unary Operators ############################ x, z, n_res *********** see :ref:`var_unary_op@x` , :ref:`var_unary_op@z` , :ref:`var_unary_op@n_res` Prototype ********* {xrst_code cpp} template inline void Fun_reverse( size_t i_z , const addr_t* arg , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) {xrst_code} Base, Fun, i_z, arg ******************* see :ref:`var_unary_op@Base` , :ref:`var_unary_op@Fun` , :ref:`var_unary_op@i_z` , :ref:`var_unary_op@arg` cap_order ********* is the maximum number of orders that can fit in *taylor* . taylor ****** The Taylor coefficient corresponding to variable *i* and order *k* is *taylor* [ *i* * *cap_order* + *k* ] {xrst_template ; include/cppad/local/var_op/template/reverse_op.xrst headers: n_order, partial @x, y@ ; x } {xrst_end var_unary_reverse} ------------------------------------------------------------------------------ ================================================ FILE: include/cppad/local/var_op/var_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_VAR_OP_HPP # define CPPAD_LOCAL_VAR_OP_VAR_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- // used by the sparse operators # include // operations # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # endif ================================================ FILE: include/cppad/local/var_op/zmul_op.hpp ================================================ # ifndef CPPAD_LOCAL_VAR_OP_ZMUL_OP_HPP # define CPPAD_LOCAL_VAR_OP_ZMUL_OP_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- namespace CppAD { namespace local { namespace var_op { // --------------------------- Zmulvv ----------------------------------------- // See dev documentation: forward_binary_op template inline void zmulvv_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(ZmulvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(ZmulvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to arguments and result Base* x = taylor + size_t(arg[0]) * cap_order; Base* y = taylor + size_t(arg[1]) * cap_order; Base* z = taylor + i_z * cap_order; size_t k; for(size_t d = p; d <= q; d++) { z[d] = Base(0.0); for(k = 0; k <= d; k++) z[d] += azmul(x[d-k], y[k]); } } // See dev documentation: forward_binary_op template inline void zmulvv_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(ZmulvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(ZmulvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to arguments and result size_t num_taylor_per_var = (cap_order-1) * r + 1; Base* x = taylor + size_t(arg[0]) * num_taylor_per_var; Base* y = taylor + size_t(arg[1]) * num_taylor_per_var; Base* z = taylor + i_z * num_taylor_per_var; size_t k, ell, m; for(ell = 0; ell < r; ell++) { m = (q-1)*r + ell + 1; z[m] = azmul(x[0], y[m]) + azmul(x[m], y[0]); for(k = 1; k < q; k++) z[m] += azmul(x[(q-k-1)*r + ell + 1], y[(k-1)*r + ell + 1]); } } // See dev documentation: forward_binary_op template inline void zmulvv_forward_0( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(ZmulvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(ZmulvvOp) == 1 ); // Taylor coefficients corresponding to arguments and result Base* x = taylor + size_t(arg[0]) * cap_order; Base* y = taylor + size_t(arg[1]) * cap_order; Base* z = taylor + i_z * cap_order; z[0] = azmul(x[0], y[0]); } // See dev documentation: reverse_binary_op template inline void zmulvv_reverse( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(ZmulvvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(ZmulvvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Arguments const Base* x = taylor + size_t(arg[0]) * cap_order; const Base* y = taylor + size_t(arg[1]) * cap_order; // Partial derivatives corresponding to arguments and result Base* px = partial + size_t(arg[0]) * n_order; Base* py = partial + size_t(arg[1]) * n_order; Base* pz = partial + i_z * n_order; // number of indices to access size_t j = d + 1; size_t k; while(j) { --j; for(k = 0; k <= j; k++) { px[j-k] += azmul(pz[j], y[k]); py[k] += azmul(pz[j], x[j-k]); } } } // --------------------------- Zmulpv ----------------------------------------- // See dev documentation: forward_binary_op template inline void zmulpv_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(ZmulpvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(ZmulpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to arguments and result Base* y = taylor + size_t(arg[1]) * cap_order; Base* z = taylor + i_z * cap_order; // Paraemter value Base x = parameter[ arg[0] ]; for(size_t d = p; d <= q; d++) z[d] = azmul(x, y[d]); } // See dev documentation: forward_binary_op template inline void zmulpv_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(ZmulpvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(ZmulpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to arguments and result size_t num_taylor_per_var = (cap_order-1) * r + 1; size_t m = (q-1) * r + 1; Base* y = taylor + size_t(arg[1]) * num_taylor_per_var + m; Base* z = taylor + i_z * num_taylor_per_var + m; // Paraemter value Base x = parameter[ arg[0] ]; for(size_t ell = 0; ell < r; ell++) z[ell] = azmul(x, y[ell]); } // See dev documentation: forward_binary_op template inline void zmulpv_forward_0( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(ZmulpvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(ZmulpvOp) == 1 ); // Paraemter value Base x = parameter[ arg[0] ]; // Taylor coefficients corresponding to arguments and result Base* y = taylor + size_t(arg[1]) * cap_order; Base* z = taylor + i_z * cap_order; z[0] = azmul(x, y[0]); } // See dev documentation: reverse_binary_op template inline void zmulpv_reverse( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(ZmulpvOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(ZmulpvOp) == 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Arguments Base x = parameter[ arg[0] ]; // Partial derivatives corresponding to arguments and result Base* py = partial + size_t(arg[1]) * n_order; Base* pz = partial + i_z * n_order; // number of indices to access size_t j = d + 1; while(j) { --j; py[j] += azmul(pz[j], x); } } // --------------------------- Zmulvp ----------------------------------------- // See dev documentation: forward_binary_op template inline void zmulvp_forward_any( size_t order_low , size_t order_up , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // p, q size_t p = order_low; size_t q = order_up; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(ZmulvpOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(ZmulvpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); CPPAD_ASSERT_UNKNOWN( p <= q ); // Taylor coefficients corresponding to arguments and result Base* x = taylor + size_t(arg[0]) * cap_order; Base* z = taylor + i_z * cap_order; // Paraemter value Base y = parameter[ arg[1] ]; for(size_t d = p; d <= q; d++) z[d] = azmul(x[d], y); } // See dev documentation: forward_binary_op template inline void zmulvp_forward_dir( size_t order_up , size_t n_dir , size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // q, r size_t q = order_up; size_t r = n_dir; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(ZmulvpOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(ZmulvpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( 0 < q ); CPPAD_ASSERT_UNKNOWN( q < cap_order ); // Taylor coefficients corresponding to arguments and result size_t num_taylor_per_var = (cap_order-1) * r + 1; size_t m = (q-1) * r + 1; Base* x = taylor + size_t(arg[0]) * num_taylor_per_var + m; Base* z = taylor + i_z * num_taylor_per_var + m; // Paraemter value Base y = parameter[ arg[1] ]; for(size_t ell = 0; ell < r; ell++) z[ell] = azmul(x[ell], y); } // See dev documentation: forward_binary_op template inline void zmulvp_forward_0( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , Base* taylor ) { // // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(ZmulvpOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(ZmulvpOp) == 1 ); // Paraemter value Base y = parameter[ arg[1] ]; // Taylor coefficients corresponding to arguments and result Base* x = taylor + size_t(arg[0]) * cap_order; Base* z = taylor + i_z * cap_order; z[0] = azmul(x[0], y); } // See dev documentation: reverse_binary_op template inline void zmulvp_reverse( size_t i_z , const addr_t* arg , const Base* parameter , size_t cap_order , const Base* taylor , size_t n_order , Base* partial ) { // d // size_t d = n_order - 1; // // check assumptions CPPAD_ASSERT_UNKNOWN( NumArg(ZmulvpOp) == 2 ); CPPAD_ASSERT_UNKNOWN( NumRes(ZmulvpOp) == 1 ); CPPAD_ASSERT_UNKNOWN( n_order <= cap_order ); // Arguments Base y = parameter[ arg[1] ]; // Partial derivatives corresponding to arguments and result Base* px = partial + size_t(arg[0]) * n_order; Base* pz = partial + i_z * n_order; // number of indices to access size_t j = d + 1; while(j) { --j; px[j] += azmul(pz[j], y); } } } } } // END namespace # endif ================================================ FILE: include/cppad/speed/det_33.hpp ================================================ # ifndef CPPAD_SPEED_DET_33_HPP # define CPPAD_SPEED_DET_33_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin det_33} Check Determinant of 3 by 3 matrix ################################## Syntax ****** | # ``include `` | *ok* = ``det_33`` ( *x* , *d* ) Purpose ******* This routine can be used to check a method for computing the determinant of a matrix. Inclusion ********* The template function ``det_33`` is defined in the ``CppAD`` namespace by including the file ``cppad/speed/det_33.hpp`` (relative to the CppAD distribution directory). x * The argument *x* has prototype ``const`` *Vector* & *x* . It contains the elements of the matrix :math:`X` in row major order; i.e., .. math:: X_{i,j} = x [ i * 3 + j ] d * The argument *d* has prototype ``const`` *Vector* & *d* . It is tested to see if *d* [0] it is equal to :math:`\det ( X )`. Vector ****** If *y* is a *Vector* object, it must support the syntax *y* [ *i* ] where *i* has type ``size_t`` with value less than 9. This must return a ``double`` value corresponding to the *i*-th element of the vector *y* . This is the only requirement of the type *Vector* . (Note that only the first element of the vector *d* is used.) ok ** The return value *ok* has prototype ``bool`` *ok* It is true, if the determinant *d* [0] passes the test and false otherwise. {xrst_toc_hidden xrst/det_33_hpp.xrst } Source Code *********** The file :ref:`det_33.hpp-name` contains the source code for this template function. {xrst_end det_33} ------------------------------------------------------------------------------ */ // BEGIN C++ # include namespace CppAD { template bool det_33(const Vector &x, const Vector &d) { bool ok = true; double eps99 = 99.0 * std::numeric_limits::epsilon(); // use expansion by minors to compute the determinant by hand double check = 0.; check += x[0] * ( x[4] * x[8] - x[5] * x[7] ); check -= x[1] * ( x[3] * x[8] - x[5] * x[6] ); check += x[2] * ( x[3] * x[7] - x[4] * x[6] ); ok &= CppAD::NearEqual(check, d[0], eps99, eps99); return ok; } } // END C++ # endif ================================================ FILE: include/cppad/speed/det_by_lu.hpp ================================================ # ifndef CPPAD_SPEED_DET_BY_LU_HPP # define CPPAD_SPEED_DET_BY_LU_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin det_by_lu} Determinant Using Expansion by Lu Factorization ############################################### Syntax ****** | # ``include `` | ``det_by_lu`` < *Scalar* > *det* ( *n* ) | *d* = *det* ( *a* ) Inclusion ********* The template class ``det_by_lu`` is defined in the ``CppAD`` namespace by including the file ``cppad/speed/det_by_lu.hpp`` (relative to the CppAD distribution directory). Constructor *********** The syntax ``det_by_lu`` < *Scalar* > *det* ( *n* ) constructs the object *det* which can be used for evaluating the determinant of *n* by *n* matrices using LU factorization. Scalar ****** The type *Scalar* can be any :ref:`NumericType-name` n * The argument *n* has prototype ``size_t`` *n* det *** The syntax *d* = *det* ( *a* ) returns the determinant of the matrix :math:`A` using LU factorization. a = The argument *a* has prototype ``const`` *Vector* & *a* It must be a *Vector* with length :math:`n * n` and with It must be a *Vector* with length :math:`n * n` and with elements of type *Scalar* . The elements of the :math:`n \times n` matrix :math:`A` are defined, for :math:`i = 0 , \ldots , n-1` and :math:`j = 0 , \ldots , n-1`, by .. math:: A_{i,j} = a[ i * m + j] d = The return value *d* has prototype *Scalar* *d* Vector ****** If *y* is a *Vector* object, it must support the syntax *y* [ *i* ] where *i* has type ``size_t`` with value less than :math:`n * n`. This must return a *Scalar* value corresponding to the *i*-th element of the vector *y* . This is the only requirement of the type *Vector* . {xrst_toc_hidden speed/example/det_by_lu.cpp xrst/det_by_lu_hpp.xrst } Example ******* The file :ref:`det_by_lu.cpp-name` contains an example and test of ``det_by_lu.hpp`` . Source Code *********** The file :ref:`det_by_lu.hpp-name` contains the source for this template function. {xrst_end det_by_lu} --------------------------------------------------------------------------- */ // BEGIN C++ # include # include // BEGIN CppAD namespace namespace CppAD { template class det_by_lu { private: const size_t m_; const size_t n_; CppAD::vector A_; CppAD::vector B_; CppAD::vector X_; public: det_by_lu(size_t n) : m_(0), n_(n), A_(n * n) { } template Scalar operator()(const Vector &x) { Scalar logdet; Scalar det; int signdet; size_t i; // copy matrix so it is not overwritten for(i = 0; i < n_ * n_; i++) A_[i] = x[i]; // compute log determinant signdet = CppAD::LuSolve( n_, m_, A_, B_, X_, logdet); /* // Do not do this for speed test because it makes floating // point operation sequence very simple. if( signdet == 0 ) det = 0; else det = Scalar( signdet ) * exp( logdet ); */ // convert to determinant det = Scalar( signdet ) * exp( logdet ); # ifdef FADBAD // Fadbad requires temporaries to be set to constants for(i = 0; i < n_ * n_; i++) A_[i] = 0; # endif return det; } }; } // END CppAD namespace // END C++ # endif ================================================ FILE: include/cppad/speed/det_by_minor.hpp ================================================ # ifndef CPPAD_SPEED_DET_BY_MINOR_HPP # define CPPAD_SPEED_DET_BY_MINOR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin det_by_minor} Determinant Using Expansion by Minors ##################################### Syntax ****** | ``# include `` | ``det_by_minor`` < *Scalar* > *det* ( *n* ) | *d* = *det* ( *a* ) Inclusion ********* The template class ``det_by_minor`` is defined in the ``CppAD`` namespace by including the file ``cppad/speed/det_by_minor.hpp`` . Constructor *********** The syntax ``det_by_minor`` < *Scalar* > *det* ( *n* ) constructs the object *det* which can be used for evaluating the determinant of *n* by *n* matrices using expansion by minors. Scalar ****** The type *Scalar* must satisfy the same conditions as in the function :ref:`det_of_minor` . n * The argument *n* has prototype ``size_t`` *n* det *** The syntax *d* = *det* ( *a* ) returns the determinant of the matrix *A* using expansion by minors. a = The argument *a* has prototype ``const`` *Vector* & *a* It must be a *Vector* with length :math:`n * n` and with elements of type *Scalar* . The elements of the :math:`n \times n` matrix :math:`A` are defined, for :math:`i = 0 , \ldots , n-1` and :math:`j = 0 , \ldots , n-1`, by .. math:: A_{i,j} = a[ i * m + j] d = The return value *d* has prototype *Scalar* *d* It is equal to the determinant of :math:`A`. Vector ****** If *y* is a *Vector* object, it must support the syntax *y* [ *i* ] where *i* has type ``size_t`` with value less than :math:`n * n`. This must return a *Scalar* value corresponding to the *i*-th element of the vector *y* . This is the only requirement of the type *Vector* . {xrst_toc_hidden speed/example/det_by_minor.cpp xrst/det_by_minor_hpp.xrst } Example ******* The file :ref:`det_by_minor.cpp-name` contains an example and test of ``det_by_minor.hpp`` . Source Code *********** The file :ref:`det_by_minor.hpp-name` contains the source for this template function. {xrst_end det_by_minor} --------------------------------------------------------------------------- */ // BEGIN C++ # include # include // BEGIN CppAD namespace namespace CppAD { template class det_by_minor { private: // // m_ // size for the matrix const size_t m_; // // r_, c_ // row and column indices so that minor is entire matrix. std::vector r_; std::vector c_; // // a_ // temporary vector declared here to avoid reallocation for each use std::vector a_; public: det_by_minor(size_t m) : m_(m) , r_(m + 1) , c_(m + 1), a_(m * m) { // // r_, c_ // values that correspond to entire matrix for(size_t i = 0; i < m; i++) { r_[i] = i+1; c_[i] = i+1; } r_[m] = 0; c_[m] = 0; } // // operator() template Scalar operator()(const Vector &x) { // // a_ // copy from type Vector to std::vector for(size_t i = 0; i < m_ * m_; ++i) a_[i] = x[i]; // // det // compute determinant of entire matrix Scalar det = det_of_minor(a_, m_, m_, r_, c_); // # ifndef NDEBUG // r_, c_ // values that correspond to entire matrix // (not const because det_of_minor uses r_, c_ for work space) for(size_t i = 0; i < m_; ++i) { assert( r_[i] == i + 1 ); assert( c_[i] == i + 1 ); } assert( r_[m_] == 0 ); assert( c_[m_] == 0 ); # endif return det; } }; } // END CppAD namespace // END C++ # endif ================================================ FILE: include/cppad/speed/det_grad_33.hpp ================================================ # ifndef CPPAD_SPEED_DET_GRAD_33_HPP # define CPPAD_SPEED_DET_GRAD_33_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin det_grad_33} Check Gradient of Determinant of 3 by 3 matrix ############################################## Syntax ****** | # ``include `` | *ok* = ``det_grad_33`` ( *x* , *g* ) Purpose ******* This routine can be used to check a method for computing the gradient of the determinant of a matrix. Inclusion ********* The template function ``det_grad_33`` is defined in the ``CppAD`` namespace by including the file ``cppad/speed/det_grad_33.hpp`` (relative to the CppAD distribution directory). x * The argument *x* has prototype ``const`` *Vector* & *x* . It contains the elements of the matrix :math:`X` in row major order; i.e., .. math:: X_{i,j} = x [ i * 3 + j ] g * The argument *g* has prototype ``const`` *Vector* & *g* . It contains the elements of the gradient of :math:`\det ( X )` in row major order; i.e., .. math:: \D{\det (X)}{X(i,j)} = g [ i * 3 + j ] Vector ****** If *y* is a *Vector* object, it must support the syntax *y* [ *i* ] where *i* has type ``size_t`` with value less than 9. This must return a ``double`` value corresponding to the *i*-th element of the vector *y* . This is the only requirement of the type *Vector* . ok ** The return value *ok* has prototype ``bool`` *ok* It is true, if the gradient *g* passes the test and false otherwise. {xrst_toc_hidden xrst/det_grad_33_hpp.xrst } Source Code *********** The file :ref:`det_grad_33.hpp-name` contains the source code for this template function. {xrst_end det_grad_33} ------------------------------------------------------------------------------ */ // BEGIN C++ # include # include namespace CppAD { template bool det_grad_33(const Vector &x, const Vector &g) { bool ok = true; typedef typename Vector::value_type Float; Float eps = 10. * Float( std::numeric_limits::epsilon() ); // use expansion by minors to compute the derivative by hand double check[9]; check[0] = + ( x[4] * x[8] - x[5] * x[7] ); check[1] = - ( x[3] * x[8] - x[5] * x[6] ); check[2] = + ( x[3] * x[7] - x[4] * x[6] ); // check[3] = - ( x[1] * x[8] - x[2] * x[7] ); check[4] = + ( x[0] * x[8] - x[2] * x[6] ); check[5] = - ( x[0] * x[7] - x[1] * x[6] ); // check[6] = + ( x[1] * x[5] - x[2] * x[4] ); check[7] = - ( x[0] * x[5] - x[2] * x[3] ); check[8] = + ( x[0] * x[4] - x[1] * x[3] ); // for(size_t i = 0; i < 3 * 3; i++) ok &= CppAD::NearEqual(check[i], g[i], eps, eps); return ok; } } // END C++ # endif ================================================ FILE: include/cppad/speed/det_of_minor.hpp ================================================ # ifndef CPPAD_SPEED_DET_OF_MINOR_HPP # define CPPAD_SPEED_DET_OF_MINOR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin det_of_minor} Determinant of a Minor ###################### Syntax ****** | ``# include `` | *d* = ``det_of_minor`` ( *a* , *m* , *n* , *r* , *c* ) Prototype ********* {xrst_literal // BEGIN_DET_OF_MINOR // END_DET_OF_MINOR } Inclusion ********* The template function ``det_of_minor`` is defined in the ``CppAD`` namespace by including the file ``cppad/speed/det_of_minor.hpp`` . Purpose ******* This template function returns the determinant of a minor of the matrix :math:`A` using expansion by minors. This template function is for example and testing purposes only. Expansion by minors is chosen as an example because it uses a lot of floating point operations yet does not require much source code (on the order of *m* factorial floating point operations and about 100 lines of source code including comments). This is not an efficient method for computing a determinant; for example, using an LU factorization would be faster. Minor ***** The elements of the :math:`n \times n` minor :math:`M` of the matrix :math:`A` are defined, for :math:`i = 0 , \ldots , n-1` and :math:`j = 0 , \ldots , n-1`, by .. math:: M_{i,j} = A_{R(i), C(j)} where the functions :math:`R(i)` is defined by the :ref:`argument r` and :math:`C(j)` is defined by the :ref:`argument c` . Determinant of A **************** If the following conditions hold, the minor is the entire matrix :math:`A` and hence ``det_of_minor`` will return the determinant of :math:`A`: #. :math:`n = m`. #. for :math:`i = 0 , \ldots , m-1`, :math:`r[i] = i+1`, and :math:`r[m] = 0`. #. for :math:`j = 0 , \ldots , m-1`, :math:`c[j] = j+1`, and :math:`c[m] = 0`. Scalar ****** This is the type of the elements of *a* . If *x* and *y* are *Scalar* objects, the type *Scalar* must support the following operations: .. csv-table:: :widths: auto **Syntax**,**Description**,**Result Type** *Scalar* (0),constructor for *Scalar* object equal to zero,*Scalar* *x* = *y*,set value of *x* to current value of *y* *x* + *y*,value of *x* plus *y*,*Scalar* *x* ``-`` *y*,value of *x* minus *y*,*Scalar* *x* * *y*,value of *x* times value of *y*,*Scalar* a * The elements of the :math:`m \times m` matrix :math:`A` are defined, for :math:`i = 0 , \ldots , m-1` and :math:`j = 0 , \ldots , m-1`, by .. math:: A_{i,j} = a[ i * m + j] m * This is the number of rows (and columns) in the square matrix :math:`A`. n * This is the number of rows (and columns) in the square minor :math:`M`. r * This defines the function :math:`R(i)` which specifies the rows of the minor :math:`M`. To be specific, the function :math:`R(i)` for :math:`i = 1, \ldots , n-1` is defined by .. math:: :nowrap: \begin{eqnarray} R(0) & = & r[m] \\ R(i) & = & r[ R(i-1) ] \end{eqnarray} All the elements of *r* have value less than or equal *m* ; :math:`R(i) < m` and :math:`r[ R(n-1) ] = m` . The elements of vector *r* are modified during the computation, and restored to their original value before the return from ``det_of_minor`` . c * This defines the function :math:`C(i)` which specifies the columns of the minor :math:`M`. To be specific, the function :math:`C(i)` for :math:`j = 1, \ldots , n-1` is defined by .. math:: :nowrap: \begin{eqnarray} C(0) & = & c[m] \\ C(j) & = & c[ C(j-1) ] \end{eqnarray} All the elements of *c* must have value less than or equal *m* ; :math:`C(j) < m` and :math:`c[ C(n-1) ] = m` . The elements of vector *c* are modified during the computation, and restored to their original value before the return from ``det_of_minor`` . d * The return value *d* is equal to the determinant of the minor :math:`M`. {xrst_toc_hidden speed/example/det_of_minor.cpp xrst/det_of_minor_hpp.xrst } Example ******* The file :ref:`det_of_minor.cpp-name` contains an example and test of ``det_of_minor.hpp`` . Source Code *********** The file :ref:`det_of_minor.hpp-name` contains the source for this template function. {xrst_end det_of_minor} --------------------------------------------------------------------------- */ // BEGIN C++ # include # include # include namespace CppAD { // BEGIN CppAD namespace // BEGIN_DET_OF_MINOR template Scalar det_of_minor( const std::vector& a , size_t m , size_t n , std::vector& r , std::vector& c ) { assert( a.size() == m * m ); assert( r.size() == m + 1 ); assert( c.size() == m + 1 ); // END_DET_OF_MINOR // // R0 = R(0) size_t R0 = r[m]; assert( R0 < m ); // // Cj = C(0) size_t Cj = c[m]; assert( Cj < m ); // // // check if this is a 1 by 1 minor if( n == 1 ) return a[ R0 * m + Cj ]; // // detM // initialize determinant of the minor M Scalar detM(0); // // sign // initialize sign of factor for next sub-minor int sign = 1; // // r // remove row with index 0 in M from all the sub-minors of M r[m] = r[R0]; // // C(j-1) // initial index in c for previous column of the minor M size_t Cj1 = m; // // for each column of M for(size_t j = 0; j < n; j++) { // M[0,j] = A[ R0, Cj ] // element with index (0, j) in the minor M assert( Cj < m ); Scalar M0j = a[ R0 * m + Cj ]; // // remove column with index j in M to form next sub-minor S of M c[Cj1] = c[Cj]; // // detS // compute determinant of S, the sub-minor of M with // row R(0) and column C(j) removed. Scalar detS = det_of_minor(a, m, n - 1, r, c); // // restore column with index j in representation of M as a minor of A c[Cj1] = Cj; // // detM // include this sub-minor term in the summation if( sign > 0 ) detM = detM + M0j * detS; else detM = detM - M0j * detS; // // advance to next column of M Cj1 = Cj; Cj = c[Cj]; sign = - sign; } // // r // restore row zero to the minor representation for M r[m] = R0; // // return the determinant of the minor M return detM; } } // END CppAD namespace // END C++ # endif ================================================ FILE: include/cppad/speed/mat_sum_sq.hpp ================================================ # ifndef CPPAD_SPEED_MAT_SUM_SQ_HPP # define CPPAD_SPEED_MAT_SUM_SQ_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin mat_sum_sq} Sum Elements of a Matrix Times Itself ##################################### Syntax ****** | # ``include `` | *mat_sum_sq* ( ``n`` , ``x`` , ``y`` , ``z`` ) Purpose ******* This routine is intended for use with the matrix multiply speed tests; to be specific, it computes .. math:: :nowrap: \begin{eqnarray} y_{i,j} & = & \sum_{k=0}^{n-1} x_{i,k} x_{k,j} \\ z_0 & = & \sum_{i=0}^{n-1} \sum_{j=0}^{n-1} y_{i,j} \end{eqnarray} see :ref:`link_mat_mul-name` . Inclusion ********* The template function ``mat_sum_sq`` is defined in the ``CppAD`` namespace by including the file ``cppad/speed/mat_sum_sq.hpp`` (relative to the CppAD distribution directory). n * This argument has prototype ``size_t`` *n* It specifies the size of the matrices. x * The argument *x* has prototype ``const`` *Vector* & *x* and *x* . ``size`` () == *n* * *n* . It contains the elements of :math:`x` in row major order; i.e., .. math:: x_{i,j} = x [ i * n + j ] y * The argument *y* has prototype *Vector* & *y* and *y* . ``size`` () == *n* * *n* . The input value of its elements does not matter. Upon return, .. math:: :nowrap: \begin{eqnarray} y_{i,j} & = & \sum_{k=0}^{n-1} x_{i,k} x_{k,j} \\ y[ i * n + j ] & = & y_{i,j} \end{eqnarray} z * The argument *d* has prototype *Vector* & *z* . The input value of its element does not matter. Upon return .. math:: :nowrap: \begin{eqnarray} z_0 & = & \sum_{i=0}^{n-1} \sum_{j=0}^n y_{i,j} \\ z[0] & = & z_0 \end{eqnarray} Vector ****** The type *Vector* is any :ref:`SimpleVector-name` , or it can be a raw pointer to the vector elements. The element type must support addition, multiplication, and assignment to both its own type and to a double value. {xrst_toc_hidden speed/example/mat_sum_sq.cpp xrst/mat_sum_sq_hpp.xrst } Example ******* The file :ref:`mat_sum_sq.cpp-name` contains an example and test of ``mat_sum_sq.hpp`` . Source Code *********** The file :ref:`mat_sum_sq.hpp-name` contains the source for this template function. {xrst_end mat_sum_sq} ------------------------------------------------------------------------------ */ // BEGIN C++ # include // namespace CppAD { template void mat_sum_sq(size_t n, Vector& x , Vector& y , Vector& z) { size_t i, j, k; // Very simple computation of y = x * x for speed comparison for(i = 0; i < n; i++) { for(j = 0; j < n; j++) { y[i * n + j] = 0.; for(k = 0; k < n; k++) y[i * n + j] += x[i * n + k] * x[k * n + j]; } } z[0] = 0.; for(i = 0; i < n; i++) { for(j = 0; j < n; j++) z[0] += y[i * n + j]; } return; } } // END C++ # endif ================================================ FILE: include/cppad/speed/ode_evaluate.hpp ================================================ # ifndef CPPAD_SPEED_ODE_EVALUATE_HPP # define CPPAD_SPEED_ODE_EVALUATE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ode_evaluate} {xrst_spell fp retaped runge } Evaluate a Function Defined in Terms of an ODE ############################################## Syntax ****** | # ``include `` | ``ode_evaluate`` ( *x* , *p* , *fp* ) Purpose ******* This routine evaluates a function :math:`f : \B{R}^n \rightarrow \B{R}^n` defined by .. math:: f(x) = y(x, 1) where :math:`y(x, t)` solves the ordinary differential equation .. math:: :nowrap: \begin{eqnarray} y(x, 0) & = & x \\ \partial_t y (x, t ) & = & g[ y(x,t) , t ] \end{eqnarray} where :math:`g : \B{R}^n \times \B{R} \rightarrow \B{R}^n` is an unspecified function. Inclusion ********* The template function ``ode_evaluate`` is defined in the ``CppAD`` namespace by including the file ``cppad/speed/ode_evaluate.hpp`` (relative to the CppAD distribution directory). Float ***** Operation Sequence ================== The type *Float* must be a :ref:`NumericType-name` . The *Float* :ref:`operation sequence` for this routine does not depend on the value of the argument *x* , hence it does not need to be retaped for each value of :math:`x`. fabs ==== If *y* and *z* are *Float* objects, the syntax *y* = ``fabs`` ( *z* ) must be supported. Note that it does not matter if the operation sequence for ``fabs`` depends on *z* because the corresponding results are not actually used by ``ode_evaluate`` ; see ``fabs`` in :ref:`Runge45` . x * The argument *x* has prototype ``const CppAD::vector<`` *Float* >& *x* It contains he argument value for which the function, or its derivative, is being evaluated. The value :math:`n` is determined by the size of the vector *x* . p * The argument *p* has prototype ``size_t`` *p* p == 0 ====== In this case a numerical method is used to solve the ode and obtain an accurate approximation for :math:`y(x, 1)`. This numerical method has a fixed that does not depend on *x* . p = 1 ===== In this case an analytic solution for the partial derivative :math:`\partial_x y(x, 1)` is returned. fp ** The argument *fp* has prototype ``CppAD::vector<`` *Float* >& *fp* The input value of the elements of *fp* does not matter. Function ======== If *p* is zero, *fp* has size equal to :math:`n` and contains the value of :math:`y(x, 1)`. Gradient ======== If *p* is one, *fp* has size equal to *n^2* and for :math:`i = 0 , \ldots 1`, :math:`j = 0 , \ldots , n-1` .. math:: \D{y[i]}{x[j]} (x, 1) = fp [ i \cdot n + j ] {xrst_toc_hidden speed/example/ode_evaluate.cpp xrst/ode_evaluate.xrst } Example ******* The file :ref:`ode_evaluate.cpp-name` contains an example and test of ``ode_evaluate.hpp`` . Source Code *********** The file :ref:`ode_evaluate.hpp-name` contains the source code for this template function. {xrst_end ode_evaluate} */ // BEGIN C++ # include # include # include namespace CppAD { template class ode_evaluate_fun { public: // Given that y_i (0) = x_i, // the following y_i (t) satisfy the ODE below: // y_0 (t) = x[0] // y_1 (t) = x[1] + x[0] * t // y_2 (t) = x[2] + x[1] * t + x[0] * t^2/2 // y_3 (t) = x[3] + x[2] * t + x[1] * t^2/2 + x[0] * t^3 / 3! // ... void Ode( const Float& t, const CppAD::vector& y, CppAD::vector& f) { size_t n = y.size(); f[0] = 0.; for(size_t k = 1; k < n; k++) f[k] = y[k-1]; } }; // template void ode_evaluate( const CppAD::vector& x , size_t p , CppAD::vector& fp ) { using CppAD::vector; typedef vector FloatVector; size_t n = x.size(); CPPAD_ASSERT_KNOWN( p == 0 || p == 1, "ode_evaluate: p is not zero or one" ); CPPAD_ASSERT_KNOWN( ((p==0) && (fp.size()==n)) || ((p==1) && (fp.size()==n*n)), "ode_evaluate: the size of fp is not correct" ); if( p == 0 ) { // function that defines the ode ode_evaluate_fun F; // number of Runge45 steps to use size_t M = 10; // initial and final time Float ti = 0.0; Float tf = 1.0; // initial value for y(x, t); i.e. y(x, 0) // (is a reference to x) const FloatVector& yi = x; // final value for y(x, t); i.e., y(x, 1) // (is a reference to fp) FloatVector& yf = fp; // Use fourth order Runge-Kutta to solve ODE yf = CppAD::Runge45(F, M, ti, tf, yi); return; } /* Compute derivaitve of y(x, 1) w.r.t x y_0 (x, t) = x[0] y_1 (x, t) = x[1] + x[0] * t y_2 (x, t) = x[2] + x[1] * t + x[0] * t^2/2 y_3 (x, t) = x[3] + x[2] * t + x[1] * t^2/2 + x[0] * t^3 / 3! ... */ size_t i, j, k; for(i = 0; i < n; i++) { for(j = 0; j < n; j++) fp[ i * n + j ] = 0.0; } size_t factorial = 1; for(k = 0; k < n; k++) { if( k > 1 ) factorial *= k; for(i = k; i < n; i++) { // partial w.r.t x[i-k] of x[i-k] * t^k / k! j = i - k; fp[ i * n + j ] += 1.0 / Float(factorial); } } } } // END C++ # endif ================================================ FILE: include/cppad/speed/sparse_hes_fun.hpp ================================================ # ifndef CPPAD_SPEED_SPARSE_HES_FUN_HPP # define CPPAD_SPEED_SPARSE_HES_FUN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sparse_hes_fun} {xrst_spell fp } Evaluate a Function That Has a Sparse Hessian ############################################# Syntax ****** | # ``include `` | ``sparse_hes_fun`` ( *n* , *x* , *row* , *col* , *p* , *fp* ) Purpose ******* This routine evaluates :math:`f(x)`, :math:`f^{(1)} (x)`, or :math:`f^{(2)} (x)` where the Hessian :math:`f^{(2)} (x)` is sparse. The function :math:`f : \B{R}^n \rightarrow \B{R}` only depends on the size and contents of the index vectors *row* and *col* . The non-zero entries in the Hessian of this function have one of the following forms: .. math:: \DD{f}{x[row[k]]}{x[row[k]]} \; , \; \DD{f}{x[row[k]]}{x[col[k]]} \; , \; \DD{f}{x[col[k]]}{x[row[k]]} \; , \; \DD{f}{x[col[k]]}{x[col[k]]} for some :math:`k` between zero and :math:`K-1`. All the other terms of the Hessian are zero. Inclusion ********* The template function ``sparse_hes_fun`` is defined in the ``CppAD`` namespace by including the file ``cppad/speed/sparse_hes_fun.hpp`` (relative to the CppAD distribution directory). Float ***** The type *Float* must be a :ref:`NumericType-name` . In addition, if *y* and *z* are *Float* objects, *y* = ``exp`` ( *z* ) must set the *y* equal the exponential of *z* , i.e., the derivative of *y* with respect to *z* is equal to *y* . FloatVector *********** The type *FloatVector* is any :ref:`SimpleVector-name` , or it can be a raw pointer, with elements of type *Float* . n * The argument *n* has prototype ``size_t`` *n* It specifies the dimension for the domain space for :math:`f(x)`. x * The argument *x* has prototype ``const`` *FloatVector* & *x* It contains the argument value for which the function, or its derivative, is being evaluated. We use :math:`n` to denote the size of the vector *x* . row *** The argument *row* has prototype ``const CppAD::vector&`` *row* It specifies one of the first index of :math:`x` for each non-zero Hessian term (see :ref:`sparse_hes_fun@Purpose` above). All the elements of *row* must be between zero and *n* ``-1`` . The value :math:`K` is defined by *K* = *row* . ``size`` () . col *** The argument *col* has prototype ``const CppAD::vector&`` *col* and its size must be :math:`K`; i.e., the same as for *col* . It specifies the second index of :math:`x` for the non-zero Hessian terms. All the elements of *col* must be between zero and *n* ``-1`` . There are no duplicated entries requested, to be specific, if *k1* != *k2* then ( *row* [ *k1* ] , *col* [ *k1* ] ) != ( *row* [ *k2* ] , *col* [ *k2* ] ) p * The argument *p* has prototype ``size_t`` *p* It is either zero or two and specifies the order of the derivative of :math:`f` that is being evaluated, i.e., :math:`f^{(p)} (x)` is evaluated. fp ** The argument *fp* has prototype *FloatVector* & *fp* The input value of the elements of *fp* does not matter. Function ======== If *p* is zero, *fp* has size one and *fp* [0] is the value of :math:`f(x)`. Hessian ======= If *p* is two, *fp* has size *K* and for :math:`k = 0 , \ldots , K-1`, .. math:: \DD{f}{ x[ \R{row}[k] ] }{ x[ \R{col}[k] ]} = fp [k] {xrst_toc_hidden speed/example/sparse_hes_fun.cpp xrst/sparse_hes_fun.xrst } Example ******* The file :ref:`sparse_hes_fun.cpp-name` contains an example and test of ``sparse_hes_fun.hpp`` . Source Code *********** The file :ref:`sparse_hes_fun.hpp-name` contains the source code for this template function. {xrst_end sparse_hes_fun} ------------------------------------------------------------------------------ */ // BEGIN C++ # include # include # include // following needed by gcc under fedora 17 so that exp(double) is defined # include namespace CppAD { template void sparse_hes_fun( size_t n , const FloatVector& x , const CppAD::vector& row , const CppAD::vector& col , size_t p , FloatVector& fp ) { // check numeric type specifications CheckNumericType(); // check value of p CPPAD_ASSERT_KNOWN( p == 0 || p == 2, "sparse_hes_fun: p != 0 and p != 2" ); size_t K = row.size(); size_t i, j, k; if( p == 0 ) fp[0] = Float(0); else { for(k = 0; k < K; k++) fp[k] = Float(0); } // determine which diagonal entries are present in row[k], col[k] CppAD::vector diagonal(n); for(i = 0; i < n; i++) diagonal[i] = K; // no diagonal entry for this row for(k = 0; k < K; k++) { if( row[k] == col[k] ) { CPPAD_ASSERT_UNKNOWN( diagonal[row[k]] == K ); // index of the diagonal entry diagonal[ row[k] ] = k; } } // determine which entries must be multiplied by a factor of two CppAD::vector factor(K); for(k = 0; k < K; k++) { factor[k] = Float(1); for(size_t k1 = 0; k1 < K; k1++) { bool reflected = true; reflected &= k != k1; reflected &= row[k] != col[k]; reflected &= row[k] == col[k1]; reflected &= col[k] == row[k1]; if( reflected ) factor[k] = Float(2); } } Float t; for(k = 0; k < K; k++) { i = row[k]; j = col[k]; t = exp( x[i] * x[j] ); switch(p) { case 0: fp[0] += t; break; case 2: if( i == j ) { // second partial of t w.r.t. x[i], x[i] fp[k] += ( Float(2) + Float(4) * x[i] * x[i] ) * t; } else // (i != j) { // // second partial of t w.r.t x[i], x[j] fp[k] += factor[k] * ( Float(1) + x[i] * x[j] ) * t; if( diagonal[i] != K ) { // second partial of t w.r.t x[i], x[i] size_t ki = diagonal[i]; fp[ki] += x[j] * x[j] * t; } if( diagonal[j] != K ) { // second partial of t w.r.t x[j], x[j] size_t kj = diagonal[j]; fp[kj] += x[i] * x[i] * t; } } break; } } } } // END C++ # endif ================================================ FILE: include/cppad/speed/sparse_jac_fun.hpp ================================================ # ifndef CPPAD_SPEED_SPARSE_JAC_FUN_HPP # define CPPAD_SPEED_SPARSE_JAC_FUN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sparse_jac_fun} {xrst_spell fp } Evaluate a Function That Has a Sparse Jacobian ############################################## Syntax ****** | # ``include `` | ``sparse_jac_fun`` ( *m* , *n* , *x* , *row* , *col* , *p* , *fp* ) Purpose ******* This routine evaluates :math:`f(x)` and :math:`f^{(1)} (x)` where the Jacobian :math:`f^{(1)} (x)` is sparse. The function :math:`f : \B{R}^n \rightarrow \B{R}^m` only depends on the size and contents of the index vectors *row* and *col* . The non-zero entries in the Jacobian of this function have one of the following forms: .. math:: \D{ f[row[k]]}{x[col[k]]} for some :math:`k` between zero and :math:`K-1`. All the other terms of the Jacobian are zero. Inclusion ********* The template function ``sparse_jac_fun`` is defined in the ``CppAD`` namespace by including the file ``cppad/speed/sparse_jac_fun.hpp`` (relative to the CppAD distribution directory). Float ***** The type *Float* must be a :ref:`NumericType-name` . In addition, if *y* and *z* are *Float* objects, *y* = ``exp`` ( *z* ) must set the *y* equal the exponential of *z* , i.e., the derivative of *y* with respect to *z* is equal to *y* . FloatVector *********** The type *FloatVector* is any :ref:`SimpleVector-name` , or it can be a raw pointer, with elements of type *Float* . n * The argument *n* has prototype ``size_t`` *n* It specifies the dimension for the domain space for :math:`f(x)`. m * The argument *m* has prototype ``size_t`` *m* It specifies the dimension for the range space for :math:`f(x)`. x * The argument *x* has prototype ``const`` *FloatVector* & *x* It contains the argument value for which the function, or its derivative, is being evaluated. We use :math:`n` to denote the size of the vector *x* . row *** The argument *row* has prototype ``const CppAD::vector&`` *row* It specifies indices in the range of :math:`f(x)` for non-zero components of the Jacobian (see :ref:`sparse_hes_fun@Purpose` above). The value :math:`K` is defined by *K* = *row* . ``size`` () . All the elements of *row* must be between zero and *m* ``-1`` . col *** The argument *col* has prototype ``const CppAD::vector&`` *col* and its size must be :math:`K`; i.e., the same as *row* . It specifies the component of :math:`x` for the non-zero Jacobian terms. All the elements of *col* must be between zero and *n* ``-1`` . p * The argument *p* has prototype ``size_t`` *p* It is either zero or one and specifies the order of the derivative of :math:`f` that is being evaluated, i.e., :math:`f^{(p)} (x)` is evaluated. fp ** The argument *fp* has prototype *FloatVector* & *fp* If *p* = 0 , it size is *m* otherwise its size is *K* . The input value of the elements of *fp* does not matter. Function ======== If *p* is zero, *fp* has size :math:`m` and ( *fp* [0], ... , ``fp`` [ ``m`` *-1* ]) is the value of :math:`f(x)`. Jacobian ======== If *p* is one, *fp* has size *K* and for :math:`k = 0 , \ldots , K-1`, .. math:: \D{f[ \R{row}[i] ]}{x[ \R{col}[j] ]} = fp [k] {xrst_toc_hidden speed/example/sparse_jac_fun.cpp xrst/sparse_jac_fun.xrst } Example ******* The file :ref:`sparse_jac_fun.cpp-name` contains an example and test of ``sparse_jac_fun.hpp`` . Source Code *********** The file :ref:`sparse_jac_fun.hpp-name` contains the source code for this template function. {xrst_end sparse_jac_fun} ------------------------------------------------------------------------------ */ // BEGIN C++ # include # include # include // following needed by gcc under fedora 17 so that exp(double) is defined # include namespace CppAD { template void sparse_jac_fun( size_t m , size_t n , const FloatVector& x , const CppAD::vector& row , const CppAD::vector& col , size_t p , FloatVector& fp ) { // check numeric type specifications CheckNumericType(); // check value of p CPPAD_ASSERT_KNOWN( p == 0 || p == 1, "sparse_jac_fun: p != 0 and p != 1" ); size_t K = row.size(); CPPAD_ASSERT_KNOWN( K >= m, "sparse_jac_fun: row.size() < m" ); size_t i, j, k; if( p == 0 ) for(i = 0; i < m; i++) fp[i] = Float(0); Float t; for(k = 0; k < K; k++) { i = row[k]; j = col[k]; t = exp( x[j] * x[j] / 2.0 ); switch(p) { case 0: fp[i] += t; break; case 1: fp[k] = t * x[j]; break; } } } } // END C++ # endif ================================================ FILE: include/cppad/speed/uniform_01.hpp ================================================ # ifndef CPPAD_SPEED_UNIFORM_01_HPP # define CPPAD_SPEED_UNIFORM_01_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin uniform_01} Simulate a [0,1] Uniform Random Variate ####################################### Syntax ****** | # ``include `` | ``uniform_01`` ( *seed* ) | ``uniform_01`` ( *n* , *x* ) Purpose ******* This routine is used to create random values for speed testing purposes. Inclusion ********* The template function ``uniform_01`` is defined in the ``CppAD`` namespace by including the file ``cppad/speed/uniform_01.hpp`` (relative to the CppAD distribution directory). seed **** The argument *seed* has prototype ``size_t`` *seed* It specifies a seed for the uniform random number generator. n * The argument *n* has prototype ``size_t`` *n* It specifies the number of elements in the random vector *x* . x * The argument *x* has prototype *Vector* & *x* . The input value of the elements of *x* does not matter. Upon return, the elements of *x* are set to values randomly sampled over the interval [0,1]. Vector ****** If *y* is a ``double`` value, the object *x* must support the syntax *x* [ *i* ] = *y* where *i* has type ``size_t`` with value less than or equal :math:`n-1`. This is the only requirement of the type *Vector* . {xrst_toc_hidden xrst/uniform_01_hpp.xrst } Source Code *********** The file :ref:`uniform_01.hpp-name` constraints the source code for this template function. {xrst_end uniform_01} ------------------------------------------------------------------------------ */ // BEGIN C++ # include namespace CppAD { inline void uniform_01(size_t seed) { std::srand( (unsigned int) seed); } template void uniform_01(size_t n, Vector &x) { static double factor = 1. / double(RAND_MAX); while(n--) x[n] = std::rand() * factor; } } // END C++ # endif ================================================ FILE: include/cppad/utility/check_numeric_type.hpp ================================================ # ifndef CPPAD_UTILITY_CHECK_NUMERIC_TYPE_HPP # define CPPAD_UTILITY_CHECK_NUMERIC_TYPE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin CheckNumericType} Check NumericType Class Concept ############################### Syntax ****** | # ``include `` | ``CheckNumericType`` < *NumericType* >() Purpose ******* The syntax ``CheckNumericType`` < *NumericType* >() preforms compile and run time checks that the type specified by *NumericType* satisfies all the requirements for a :ref:`NumericType-name` class. If a requirement is not satisfied, a an error message makes it clear what condition is not satisfied. Include ******* The file ``cppad/utility/check_numeric_type.hpp`` is included by ``cppad/cppad.hpp`` but it can also be included separately with out the rest if the CppAD include files. Parallel Mode ************* The routine :ref:`thread_alloc::parallel_setup` must be called before it can be used in :ref:`parallel` mode. Example ******* {xrst_toc_hidden example/utility/check_numeric_type.cpp } The file :ref:`check_numeric_type.cpp-name` contains an example and test of this function. The comments in this example suggest a way to change the example so an error message occurs. {xrst_end CheckNumericType} --------------------------------------------------------------------------- */ # include # include namespace CppAD { # ifdef NDEBUG template void CheckNumericType(void) { } # else template NumericType CheckNumericType(void) { // Section 3.6.2 of ISO/IEC 14882:1998(E) states: "The storage for // objects with static storage duration (3.7.1) shall be zero- // initialized (8.5) before any other initialization takes place." static size_t count[CPPAD_MAX_NUM_THREADS]; size_t thread = thread_alloc::thread_num(); if( count[thread] > 0 ) return NumericType(0); count[thread]++; /* constructors */ NumericType check_NumericType_default_constructor; NumericType check_NumericType_constructor_from_int(1); const NumericType x(1); NumericType check_NumericType_copy_constructor(x); // assignment NumericType check_NumericType_assignment; check_NumericType_assignment = x; /* unary operators */ const NumericType check_NumericType_unary_plus(1); NumericType check_NumericType_unary_plus_result = + check_NumericType_unary_plus; const NumericType check_NumericType_unary_minus(1); NumericType check_NumericType_unary_minus_result = - check_NumericType_unary_minus; /* binary operators */ const NumericType check_NumericType_binary_addition(1); NumericType check_NumericType_binary_addition_result = check_NumericType_binary_addition + x; const NumericType check_NumericType_binary_subtraction(1); NumericType check_NumericType_binary_subtraction_result = check_NumericType_binary_subtraction - x; const NumericType check_NumericType_binary_multiplication(1); NumericType check_NumericType_binary_multiplication_result = check_NumericType_binary_multiplication * x; const NumericType check_NumericType_binary_division(1); NumericType check_NumericType_binary_division_result = check_NumericType_binary_division / x; /* compound assignment operators */ NumericType check_NumericType_computed_assignment_addition(1); check_NumericType_computed_assignment_addition += x; NumericType check_NumericType_computed_assignment_subtraction(1); check_NumericType_computed_assignment_subtraction -= x; NumericType check_NumericType_computed_assignment_multiplication(1); check_NumericType_computed_assignment_multiplication *= x; NumericType check_NumericType_computed_assignment_division(1); check_NumericType_computed_assignment_division /= x; /* use all values so as to avoid warnings */ check_NumericType_default_constructor = x; return + check_NumericType_default_constructor + check_NumericType_constructor_from_int + check_NumericType_copy_constructor + check_NumericType_assignment + check_NumericType_unary_plus_result + check_NumericType_unary_minus_result + check_NumericType_binary_addition_result + check_NumericType_binary_subtraction_result + check_NumericType_binary_multiplication_result + check_NumericType_binary_division_result + check_NumericType_computed_assignment_addition + check_NumericType_computed_assignment_subtraction + check_NumericType_computed_assignment_multiplication + check_NumericType_computed_assignment_division ; } # endif } // end namespace CppAD # endif ================================================ FILE: include/cppad/utility/check_simple_vector.hpp ================================================ # ifndef CPPAD_UTILITY_CHECK_SIMPLE_VECTOR_HPP # define CPPAD_UTILITY_CHECK_SIMPLE_VECTOR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin CheckSimpleVector} Check Simple Vector Concept ########################### Syntax ****** | # ``include `` | ``CheckSimpleVector`` < *Scalar* , *Vector* >() | ``CheckSimpleVector`` < *Scalar* , *Vector* >( *x* , *y* ) Purpose ******* Preforms compile and run time checks that the type specified by *Vector* satisfies all the requirements for a :ref:`SimpleVector-name` class with :ref:`elements of type` *Scalar* . If a requirement is not satisfied, a an error message makes it clear what condition is not satisfied. Vector ****** is the vector type we are checking. Scalar ****** is the type corresponding to the elements of an *Vector* . x, y **** If the arguments *x* and *y* are present, they have prototype | |tab| ``const`` *Scalar* & *x* | |tab| ``const`` *Scalar* & *y* In addition, the check *x* == *x* will return the boolean value ``true`` , and *x* == *y* will return ``false`` . Restrictions ************ If the arguments *x* and *y* are not present, the following extra assumption is made by ``CheckSimpleVector`` : If *x* is a *Scalar* object | |tab| *x* = 0 | |tab| *y* = 1 assigns values to the objects *x* and *y* . In addition, *x* == *x* would return the boolean value ``true`` and *x* == *y* would return ``false`` . Include ******* The file ``cppad/utility/check_simple_vector.hpp`` is included by ``cppad/cppad.hpp`` but it can also be included separately with out the rest if the CppAD include files. Parallel Mode ************* This routine must be called before entering parallel mode because it has static variables that must be initialized. If it's first call is not in parallel mode, and NDEBUG is not defined, you will get an assertion. Running in the debugger and going to the stack frame where CheckSimpleVector is called may help you determine what the value of *Scalar* and *Vector* need to be initialized. Example ******* {xrst_toc_hidden example/utility/check_simple_vector.cpp } The file :ref:`check_simple_vector.cpp-name` contains an example and test of this function where *S* is the same as *T* . The comments in this example suggest a way to change the example so *S* is not the same as *T* . {xrst_end CheckSimpleVector} --------------------------------------------------------------------------- */ # include # include # include # include // CPPAD_CHECK_SIMPLE_VECTOR # ifndef NDEBUG # define CPPAD_CHECK_SIMPLE_VECTOR 1 # elif CPPAD_DEBUG_AND_RELEASE # define CPPAD_CHECK_SIMPLE_VECTOR 1 # else # define CPPAD_CHECK_SIMPLE_VECTOR 0 # endif namespace CppAD { # if ! CPPAD_CHECK_SIMPLE_VECTOR template inline void CheckSimpleVector(const Scalar& x, const Scalar& y) { } template inline void CheckSimpleVector(void) { } # else template struct ok_if_S_same_as_T { }; template struct ok_if_S_same_as_T { T value; }; template inline void CheckSimpleVector(const Scalar& x, const Scalar& y) { // // count static size_t count = 0; if( count > 0 ) return; CPPAD_ASSERT_KNOWN( ! CppAD::thread_alloc::in_parallel() , "In parallel mode and CheckSimpleVector was not previously called\n" "with this Scalar and Vector type; see the heading\n" "Parallel Mode in the CheckSimpleVector documentation." ); count++; // value_type must be type of elements of Vector typedef typename Vector::value_type value_type; // check that elements of Vector have type Scalar struct ok_if_S_same_as_T x_copy; x_copy.value = x; // check default constructor Vector d; // size member function CPPAD_ASSERT_KNOWN( d.size() == 0, "default constructor result does not have size zero" ); // resize to same size as other vectors in test d.resize(1); // check sizing constructor Vector s(1); // check element assignment s[0] = y; CPPAD_ASSERT_KNOWN( s[0] == y, "element assignment failed" ); // check copy constructor s[0] = x_copy.value; const Vector c(s); s[0] = y; CPPAD_ASSERT_KNOWN( c[0] == x, "copy constructor is shallow" ); // vector assignment operator d[0] = x; s = d; s[0] = y; CPPAD_ASSERT_KNOWN( d[0] == x, "assignment operator is shallow" ); // element access, right side const // element assignment, left side not const d[0] = c[0]; CPPAD_ASSERT_KNOWN( d[0] == x, "element assignment from const failed" ); } template void CheckSimpleVector(void) { Scalar x; Scalar y; // use assignment and not constructor x = Scalar(0); y = Scalar(1); CheckSimpleVector(x, y); } # endif } // end namespace CppAD # undef CPPAD_CHECK_SIMPLE_VECTOR # endif ================================================ FILE: include/cppad/utility/create_dll_lib.hpp ================================================ # ifndef CPPAD_UTILITY_CREATE_DLL_LIB_HPP # define CPPAD_UTILITY_CREATE_DLL_LIB_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin create_dll_lib} {xrst_spell cmd csrc hc hs msg } Create a Dynamic Link Library ############################# Syntax ****** | # ``include `` | *err_msg* = ``create_dll_lib`` ( *dll_file* , *csrc_files* , *options* ) Prototype ********* {xrst_literal // BEGIN_CREATE_DLL_LIB // END_CREATE_DLL_LIB } include ******* As with all the CppAD utilities, ``create_dll_lib.hpp`` is included by ```` , and can also be included separately. dll_file ******** This is the file where the dynamic link library file named *dll_file* . This file name must have the proper extension for a dynamic link library (``.so`` on unix and ``.dll`` on windows). StringVector ************ The type *StringVector* is a simple vector with elements of type ``std::string`` . csrc_files ********** The vector *csrc_files* contains the names of the C source files that are compiled and linked to the library. These files do not have to have a specific extension. options ******* The possible keys in this map are documented below. The default value for each key is used when the key does not appear in *options* . compile ======= This is an abbreviated version of the compile command. It does not include the output file flag or output file name. If :ref:`cmake-name` detects that this is the MSVC compiler, the default value for this option is `cl /EHs /EHc /c /TC`` If cmake detects that this is the Clang or GNU compiler, the default value for this option is *cppad_c_compiler_cmd* ``-c -fPIC`` Here and below *cppad_c_compiler_cmd* is the command used to run the C compiler (which is determined by cmake) . link ==== This is an abbreviated version of the link command. It does not include the output file flag or output file name. In the MSVC case, the default for this option is ``link /DLL`` In the Clang or GNU case, the default for this option is *cppad_c_compiler_cmd* ``-shared`` . err_msg ******* If this string is empty, no error occurred. Otherwise the processing aborted and *err_msg* is the corresponding error message. Example ******* The file :ref:`dll_lib.cpp-name` contains an example and test of ``create_dll_lib`` . {xrst_end create_dll_lib} */ # include # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // // create // BEGIN_CREATE_DLL_LIB template std::string create_dll_lib( const std::string& dll_file , const StringVector& csrc_files , const std::map& options ) // END_CREATE_DLL_LIB { using std::string; // // err_msg string err_msg = ""; // // compile, link string compile = ""; string link = ""; # if CPPAD_C_COMPILER_MSVC_FLAGS compile = CPPAD_C_COMPILER_CMD " /EHs /EHc /c /TC"; link = "link /DLL"; # endif # if CPPAD_C_COMPILER_GNU_FLAGS compile = CPPAD_C_COMPILER_CMD " -c -fPIC"; link = CPPAD_C_COMPILER_CMD " -shared"; # endif for( const auto& pair : options ) { const string& key = pair.first; if( key == "compile" ) compile = pair.second; else if( key == "link" ) link = pair.second; else { err_msg = "options contains following invalid key: " + key; return err_msg; } } // // check if we know how to create a dll with this compiler if( compile == "" ) { err_msg = "Do not know how to create a dll using this C compiler\n"; err_msg += CPPAD_C_COMPILER_CMD; return err_msg; } // // check the std::system function exists int flag = std::system(nullptr); if( flag == 0 ) { err_msg = "C++ std::system function not available\n"; return err_msg; } // // check the file extensions # ifdef _WIN32 string dll_ext = ".dll"; # else string dll_ext = ".so"; # endif size_t last_match = dll_file.rfind(dll_ext); size_t expected = dll_file.size() - dll_ext.size(); if( last_match != expected ) { err_msg += "dll_file = " + dll_file + "\ndoes not end with " + dll_ext; return err_msg; } // // o_file_list, o_file_vec; string o_file_list; StringVector o_file_vec( csrc_files.size() ); // // i_csrc for(size_t i_csrc = 0; i_csrc < csrc_files.size(); ++i_csrc) { // // c_file string c_file = csrc_files[i_csrc]; // // o_file string o_file = local::temp_file(); // // cmd string cmd = compile + " " + c_file; # ifdef _MSC_VER cmd += " /Fo\"" + o_file + "\" 1> nul 2> nul"; # else cmd += " -o " + o_file; # endif // // o_file // compile c_file and put result in o_file flag = std::system( cmd.c_str() ); if( flag != 0 ) { err_msg = "create_dll_lib: following system command failed\n"; err_msg += cmd; return err_msg; } // // o_file_list o_file_list += " " + o_file; // // o_file_vec o_file_vec[i_csrc] = o_file; } string cmd = link + " " + o_file_list; # ifdef _MSC_VER cmd += " /OUT:" + dll_file + " 1> nul 2> nul"; # else cmd += " -o " + dll_file;; # endif flag = std::system( cmd.c_str() ); if( flag != 0 ) { err_msg = "create_dll_lib: following system command failed\n"; err_msg += cmd; return err_msg; } // // remove o_file for(size_t i = 0; i < o_file_vec.size(); ++i) { flag = std::remove( o_file_vec[i].c_str() ); if( flag != 0 ) { err_msg = "create_dll_lib: following system command failed\n"; err_msg += cmd; return err_msg; } } return err_msg; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/utility/elapsed_seconds.hpp ================================================ # ifndef CPPAD_UTILITY_ELAPSED_SECONDS_HPP # define CPPAD_UTILITY_ELAPSED_SECONDS_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin elapsed_seconds} {xrst_spell chrono } Returns Elapsed Number of Seconds ################################# Syntax ****** | # ``include `` | *s* = ``elapsed_seconds`` () Accuracy ******** This routine uses ``std::chrono::steady_clock`` to do its timing. s * is a ``double`` equal to the number of seconds since the first call to ``elapsed_seconds`` . {xrst_toc_hidden speed/example/elapsed_seconds.cpp } Example ******* The routine :ref:`elapsed_seconds.cpp-name` is an example and test of this routine. {xrst_end elapsed_seconds} ----------------------------------------------------------------------- */ # include // needed before one can use CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL # include // c++11 time function # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE inline double elapsed_seconds(void) // -------------------------------------------------------------------------- { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; static bool first_ = true; static std::chrono::time_point start_; if( first_ ) { start_ = std::chrono::steady_clock::now(); first_ = false; return 0.0; } std::chrono::time_point now; now = std::chrono::steady_clock::now(); std::chrono::duration difference = now - start_; return difference.count(); } // -------------------------------------------------------------------------- } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/utility/error_handler.hpp ================================================ # ifndef CPPAD_UTILITY_ERROR_HANDLER_HPP # define CPPAD_UTILITY_ERROR_HANDLER_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ErrorHandler} {xrst_spell msg test test } Replacing the CppAD Error Handler ################################# Syntax ****** | # ``include `` | ``ErrorHandler`` *info* ( *handler* ) | ``ErrorHandler::Call`` ( *known* , *line* , *file* , *exp* , *msg* ) Constructor *********** When you construct a ``ErrorHandler`` object, the current CppAD error handler is replaced by *handler* . When the object is destructed, the previous CppAD error handler is restored. Parallel Mode ============= The ``ErrorHandler`` constructor and destructor cannot be called in :ref:`parallel` execution mode. If this rule is not abided by, a raw C++ ``assert`` , instead of one that uses this error handler, will be generated. Call **** When ``ErrorHandler::Call`` is called, the current CppAD error handler is used to report an error. This starts out as a default error handler and can be replaced using the ``ErrorHandler`` constructor. info **** The object *info* is used to store information that is necessary to restore the previous CppAD error handler. This restoration is done when the destructor for *info* is called. handler ******* The argument *handler* has prototype | |tab| ``void`` (* *handler* ) | |tab| |tab| ( ``bool`` , ``int`` , ``const char`` * , ``const char`` * , ``const char`` * ); When an error is detected, it is called with the syntax *handler* ( *known* , *line* , *file* , *exp* , *msg* ) This routine should not return; i.e., upon detection of the error, the routine calling *handler* does not know how to proceed. known ***** The *handler* argument *known* has prototype ``bool`` *known* If it is true, the error being reported is from a know problem. line **** The *handler* argument *line* has prototype ``int`` *line* It reports the source code line number where the error is detected. file **** The *handler* argument *file* has prototype ``const char`` * *file* and is a ``'\0'`` terminated character vector. It reports the source code file where the error is detected. exp *** The *handler* argument *exp* has prototype ``const char`` * *exp* and is a ``'\0'`` terminated character vector. It is a source code boolean expression that should have been true, but is false, and thereby causes this call to *handler* . msg *** The *handler* argument *msg* has prototype ``const char`` * *msg* and is a ``'\0'`` terminated character vector. It reports the meaning of the error from the C++ programmers point of view. {xrst_toc_hidden example/utility/error_handler.cpp include/cppad/core/cppad_assert.hpp } Example ******* The file :ref:`error_handler.cpp-name` contains an example and test a test of using this routine. {xrst_end ErrorHandler} --------------------------------------------------------------------------- */ # include # include # include # include # include namespace CppAD { // BEGIN CppAD namespace class ErrorHandler { template friend void parallel_ad(void); public: typedef void (*Handler) (bool, int, const char *, const char *, const char *); // construct a new handler ErrorHandler(Handler handler) : previous( Current() ) { if( local::set_get_in_parallel() ) { bool known = true; int line = __LINE__; const char* file = __FILE__; const char* exp = "! local::set_get_in_parallel()"; const char* msg = "Using ErrorHandler constructor in parallel mode."; Call(known, line, file, exp, msg); } Current() = handler; } // destructor for an error handler ~ErrorHandler(void) { if( local::set_get_in_parallel() ) { bool known = true; int line = __LINE__; const char* file = __FILE__; const char* exp = "! local::set_get_in_parallel()"; const char* msg = "Using ErrorHandler destructor in parallel mode."; Call(known, line, file, exp, msg); } Current() = previous; } // report an error static void Call( bool known, int line , const char *file , const char *exp , const char *msg ) { Handler handler = Current(); handler(known, line, file, exp, msg); } private: const Handler previous; // The default error handler static void Default( bool known, int line , const char *file , const char *exp , const char *msg ) { using std::cerr; using std::endl; cerr << CPPAD_PACKAGE_STRING; if( known ) cerr << " error from a known source:" << endl; else cerr << " error from unknown source" << endl; if( msg[0] != '\0' ) cerr << msg << endl; cerr << "Error detected by false result for" << endl; cerr << " " << exp << endl; cerr << "at line " << line << " in the file " << endl; cerr << " " << file << endl; // terminate program execution assert(false); // termination when NDEBUG is defined std::exit(1); } // current error handler static Handler &Current(void) { static bool first_call = true; static Handler current = Default; // CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL // code below is like macro above but works when NDEBUG defined if( first_call ) { if( local::set_get_in_parallel() ) { bool known = false; int line = __LINE__; const char* file = __FILE__; const char* exp = ""; const char* msg = ""; Call(known, line, file, exp, msg); } first_call = false; } return current; } }; } // END CppAD namespace # endif ================================================ FILE: include/cppad/utility/index_sort.hpp ================================================ # ifndef CPPAD_UTILITY_INDEX_SORT_HPP # define CPPAD_UTILITY_INDEX_SORT_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin index_sort} Returns Indices that Sort a Vector ################################## Syntax ****** | # ``include `` | ``index_sort`` ( *keys* , *ind* ) keys **** The argument *keys* has prototype ``const`` *KeyVector* & *keys* where *KeyVector* is a :ref:`SimpleVector-name` class with elements that support the ``<`` operation. ind *** The argument *ind* has prototype *SizeVector* & *ind* where *SizeVector* is a :ref:`SimpleVector-name` class with elements of type *Size_t* , where *Size_t* is an integral type. The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Input ===== The size of *ind* must be the same as the size of *keys* and the value of its input elements does not matter. Return ====== Upon return, *ind* is a permutation of the set of indices that yields increasing order for *keys* . In other words, for all *i* != *j* , *ind* [ *i* ] != *ind* [ *j* ] and for *i* = 0 , ... , *size* ``-2`` , ( *keys* [ *ind* [ *i* +1] ] < *keys* [ *ind* [ *i* ] ] ) == ``false`` Example ******* {xrst_toc_hidden example/utility/index_sort.cpp } The file :ref:`index_sort.cpp-name` contains an example and test of this routine. It return true if it succeeds and false otherwise. {xrst_end index_sort} */ # include # include # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file index_sort.hpp File used to implement the CppAD index sort utility */ /*! Helper class used by index_sort */ template class index_sort_element { private: /// key used to determine position of this element Compare key_; /// index value corresponding to this key Size_t index_; public: /// operator required by std::sort bool operator<(const index_sort_element& other) const { return key_ < other.key_; } /// set the key for this element void set_key(const Compare& value) { key_ = value; } /// set the index for this element void set_index(const Size_t& index) { index_ = index; } /// get the key for this element Compare get_key(void) const { return key_; } /// get the index for this element Size_t get_index(void) const { return index_; } }; /*! Compute the indices that sort a vector of keys \tparam KeyVector Simple vector type that deterimene the sorting order by < operator on its elements. \tparam SizeVector Simple vector type with elements of size_t that is used to return index values. \param keys [in] values that determine the sorting order. \param ind [out] must have the same size as keys. The input value of its elements does not matter. The output value of its elements satisfy \code ( keys[ ind[i] ] < keys[ ind[i+1] ] ) == false \endcode */ template void index_sort(const KeyVector& keys, SizeVector& ind) { typedef typename KeyVector::value_type Compare; typedef typename SizeVector::value_type Size_t; typedef index_sort_element Element; // CheckSimpleVector(); // CPPAD_ASSERT_KNOWN( keys.size() == ind.size(), "index_sort: vector sizes do not match" ); size_t size_work = keys.size(); size_t size_out; Element* work = thread_alloc::create_array(size_work, size_out); // copy initial order into work size_t i; for(i = 0; i < size_work; i++) { work[i].set_key( keys[i] ); work[i].set_index( Size_t(i) ); } // sort the work array std::sort(work, work+size_work); // copy the indices to the output vector for(i = 0; i < size_work; i++) ind[i] = work[i].get_index(); // we are done with this work array thread_alloc::delete_array(work); return; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/utility/link_dll_lib.hpp ================================================ # ifndef CPPAD_UTILITY_LINK_DLL_LIB_HPP # define CPPAD_UTILITY_LINK_DLL_LIB_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin link_dll_lib} {xrst_spell msg } Link a Dynamic Link Library ########################### Syntax ****** | # ``include `` | ``link_dll_lib`` *dll_linker* ( *dll_file* , *err_msg* ) | *fun_ptr* = *dll_linker* ( *function_name* , *err_msg* ) | Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } dll_linker ********** Is the dynamic link object that holds an in memory version of the library, err_msg ******* If *err_msg* is non-empty, it contains an error message for the corresponding operation. dll_file ******** Is the file containing the dynamic link library. function_name ************* Is the name of an external entry point in the dll. fun_ptr ******* Is a ``void*`` version of a pointer the function corresponding to *function_name* . Warning !! ========== *fun_ptr* becomes invalid when the *dll_linker* destructor is called. {xrst_toc_hidden example/utility/dll_lib.cpp } Example ******* The file :ref:`dll_lib.cpp-name` contains an example and test of ``link_dll_lib`` . {xrst_end link_dll_lib} */ # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE class link_dll_lib { private: // pointer to the dll object void* handle_; // // error message during constructor std::string ctor_err_msg_; // # ifdef _WIN32 static void* dlopen(const char *filename, int flag); static void* dlsym(void* handle, const char* symbol); static int dlclose(void* handle); static const char* dlerror(void); # endif // public: // BEGIN_PROTOTYPE link_dll_lib(const std::string& dll_file, std::string& err_msg); ~link_dll_lib(void); void* operator() (const std::string& function_name, std::string& err_msg) const; // END_PROTOTYPE }; } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/utility/lu_factor.hpp ================================================ # ifndef CPPAD_UTILITY_LU_FACTOR_HPP # define CPPAD_UTILITY_LU_FACTOR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin LuFactor} {xrst_spell geq ip jp } LU Factorization of A Square Matrix ################################### Syntax ****** | # ``include `` | *sign* = ``LuFactor`` ( *ip* , *jp* , *LU* ) Description *********** Computes an LU factorization of the matrix *A* where *A* is a square matrix. Include ******* The file ``cppad/utility/lu_factor.hpp`` is included by ``cppad/cppad.hpp`` but it can also be included separately with out the rest of the ``CppAD`` routines. Matrix Storage ************** All matrices are stored in row major order. To be specific, if :math:`Y` is a vector that contains a :math:`p` by :math:`q` matrix, the size of :math:`Y` must be equal to :math:`p * q` and for :math:`i = 0 , \ldots , p-1`, :math:`j = 0 , \ldots , q-1`, .. math:: Y_{i,j} = Y[ i * q + j ] sign **** The return value *sign* has prototype ``int`` *sign* If *A* is invertible, *sign* is plus or minus one and is the sign of the permutation corresponding to the row ordering *ip* and column ordering *jp* . If *A* is not invertible, *sign* is zero. ip ** The argument *ip* has prototype *SizeVector* & *ip* (see description of :ref:`LuFactor@SizeVector` below). The size of *ip* is referred to as *n* in the specifications below. The input value of the elements of *ip* does not matter. The output value of the elements of *ip* determine the order of the rows in the permuted matrix. jp ** The argument *jp* has prototype *SizeVector* & *jp* (see description of :ref:`LuFactor@SizeVector` below). The size of *jp* must be equal to *n* . The input value of the elements of *jp* does not matter. The output value of the elements of *jp* determine the order of the columns in the permuted matrix. LU ** The argument *LU* has the prototype *FloatVector* & *LU* and the size of *LU* must equal :math:`n * n` (see description of :ref:`LuFactor@FloatVector` below). A = We define *A* as the matrix corresponding to the input value of *LU* . P = We define the permuted matrix *P* in terms of *A* by *P* ( *i* , *j* ) = *A* [ *ip* [ *i* ] * *n* + *jp* [ *j* ] ] L = We define the lower triangular matrix *L* in terms of the output value of *LU* . The matrix *L* is zero above the diagonal and the rest of the elements are defined by *L* ( *i* , *j* ) = *LU* [ *ip* [ *i* ] * *n* + *jp* [ *j* ] ] for :math:`i = 0 , \ldots , n-1` and :math:`j = 0 , \ldots , i`. U = We define the upper triangular matrix *U* in terms of the output value of *LU* . The matrix *U* is zero below the diagonal, one on the diagonal, and the rest of the elements are defined by *U* ( *i* , *j* ) = *LU* [ *ip* [ *i* ] * *n* + *jp* [ *j* ] ] for :math:`i = 0 , \ldots , n-2` and :math:`j = i+1 , \ldots , n-1`. Factor ====== If the return value *sign* is non-zero, *L* * *U* = *P* If the return value of *sign* is zero, the contents of *L* and *U* are not defined. Determinant =========== If the return value *sign* is zero, the determinant of *A* is zero. If *sign* is non-zero, using the output value of *LU* the determinant of the matrix *A* is equal to *sign* * *LU* [ *ip* [0], *jp* [0]] * ... * *LU* [ *ip* [ *n* ``-1`` ], *jp* [ *n* ``-1`` ]] SizeVector ********** The type *SizeVector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type size_t` . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. FloatVector *********** The type *FloatVector* must be a :ref:`simple vector class` . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Float ***** This notation is used to denote the type corresponding to the elements of a *FloatVector* . The type *Float* must satisfy the conditions for a :ref:`NumericType-name` . The routine :ref:`CheckNumericType-name` will generate an error message if this is not the case. In addition, the following operations must be defined for any pair of *Float* objects *x* and *y* : .. list-table:: :widths: auto * - **Operation** - **Description** * - ``log`` ( *x* ) - returns the logarithm of *x* as a *Float* object AbsGeq ****** Including the file ``lu_factor.hpp`` defines the template function | |tab| ``template | |tab| ``bool AbsGeq`` < *Float* >( ``const`` *Float* & *x* , ``const`` *Float* & *y* ) in the ``CppAD`` namespace. This function returns true if the absolute value of *x* is greater than or equal the absolute value of *y* . It is used by ``LuFactor`` to choose the pivot elements. This template function definition uses the operator ``<=`` to obtain the absolute value for *Float* objects. If this operator is not defined for your use of *Float* , you will need to specialize this template so that it works for your use of ``LuFactor`` . Complex numbers do not have the operation ``<=`` defined. The specializations | ``bool AbsGeq< std::complex >`` | |tab| ( ``const std::complex &`` *x* , ``const std::complex &`` *y* ) | ``bool AbsGeq< std::complex >`` | |tab| ( ``const std::complex &`` *x* , ``const std::complex &`` *y* ) are define by including ``lu_factor.hpp`` These return true if the sum of the square of the real and imaginary parts of *x* is greater than or equal the sum of the square of the real and imaginary parts of *y* . {xrst_toc_hidden example/utility/lu_factor.cpp xrst/lu_factor_hpp.xrst } Example ******* The file :ref:`lu_factor.cpp-name` contains an example and test of using ``LuFactor`` by itself. The file :ref:`lu_solve.hpp-name` provides a useful example usage of ``LuFactor`` with ``LuInvert`` . Source ****** The file :ref:`lu_factor.hpp-name` contains the current source code that implements these specifications. {xrst_end LuFactor} -------------------------------------------------------------------------- */ // BEGIN C++ # include # include # include # include # include namespace CppAD { // BEGIN CppAD namespace // AbsGeq template bool AbsGeq(const Float &x, const Float &y) { Float xabs = x; if( xabs <= Float(0) ) xabs = - xabs; Float yabs = y; if( yabs <= Float(0) ) yabs = - yabs; return xabs >= yabs; } inline bool AbsGeq( const std::complex &x, const std::complex &y) { double xsq = x.real() * x.real() + x.imag() * x.imag(); double ysq = y.real() * y.real() + y.imag() * y.imag(); return xsq >= ysq; } inline bool AbsGeq( const std::complex &x, const std::complex &y) { float xsq = x.real() * x.real() + x.imag() * x.imag(); float ysq = y.real() * y.real() + y.imag() * y.imag(); return xsq >= ysq; } // Lines that are different from code in cppad/core/lu_ratio.hpp end with // template // int LuFactor(SizeVector &ip, SizeVector &jp, FloatVector &LU) // { // type of the elements of LU // typedef typename FloatVector::value_type Float; // // check numeric type specifications CheckNumericType(); // check simple vector class specifications CheckSimpleVector(); CheckSimpleVector(); size_t i, j; // some temporary indices const Float zero( 0 ); // the value zero as a Float object size_t imax; // row index of maximum element size_t jmax; // column index of maximum element Float emax; // maximum absolute value size_t p; // count pivots int sign; // sign of the permutation Float etmp; // temporary element Float pivot; // pivot element // ------------------------------------------------------- size_t n = ip.size(); CPPAD_ASSERT_KNOWN( size_t(jp.size()) == n, "Error in LuFactor: jp must have size equal to n" ); CPPAD_ASSERT_KNOWN( size_t(LU.size()) == n * n, "Error in LuFactor: LU must have size equal to n * m" ); // ------------------------------------------------------- // initialize row and column order in matrix not yet pivoted for(i = 0; i < n; i++) { ip[i] = i; jp[i] = i; } // initialize the sign of the permutation sign = 1; // --------------------------------------------------------- // Reduce the matrix P to L * U using n pivots for(p = 0; p < n; p++) { // determine row and column corresponding to element of // maximum absolute value in remaining part of P imax = jmax = n; emax = zero; for(i = p; i < n; i++) { for(j = p; j < n; j++) { CPPAD_ASSERT_UNKNOWN( (ip[i] < n) && (jp[j] < n) ); etmp = LU[ ip[i] * n + jp[j] ]; // check if maximum absolute value so far if( AbsGeq (etmp, emax) ) { imax = i; jmax = j; emax = etmp; } } } CPPAD_ASSERT_KNOWN( (imax < n) && (jmax < n) , "LuFactor can't determine an element with " "maximum absolute value.\n" "Perhaps original matrix contains not a number or infinity.\n" "Perhaps your specialization of AbsGeq is not correct." ); if( imax != p ) { // switch rows so max absolute element is in row p i = ip[p]; ip[p] = ip[imax]; ip[imax] = i; sign = -sign; } if( jmax != p ) { // switch columns so max absolute element is in column p j = jp[p]; jp[p] = jp[jmax]; jp[jmax] = j; sign = -sign; } // pivot using the max absolute element pivot = LU[ ip[p] * n + jp[p] ]; // check for determinant equal to zero if( pivot == zero ) { // abort the mission return 0; } // Reduce U by the elementary transformations that maps // LU( ip[p], jp[p] ) to one. Only need transform elements // above the diagonal in U and LU( ip[p] , jp[p] ) is // corresponding value below diagonal in L. for(j = p+1; j < n; j++) LU[ ip[p] * n + jp[j] ] /= pivot; // Reduce U by the elementary transformations that maps // LU( ip[i], jp[p] ) to zero. Only need transform elements // above the diagonal in U and LU( ip[i], jp[p] ) is // corresponding value below diagonal in L. for(i = p+1; i < n; i++ ) { etmp = LU[ ip[i] * n + jp[p] ]; for(j = p+1; j < n; j++) { LU[ ip[i] * n + jp[j] ] -= etmp * LU[ ip[p] * n + jp[j] ]; } } } return sign; } } // END CppAD namespace // END C++ # endif ================================================ FILE: include/cppad/utility/lu_invert.hpp ================================================ # ifndef CPPAD_UTILITY_LU_INVERT_HPP # define CPPAD_UTILITY_LU_INVERT_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin LuInvert} {xrst_spell ip jp } Invert an LU Factored Equation ############################## Syntax ****** | # ``include `` | ``LuInvert`` ( *ip* , *jp* , *LU* , *X* ) Description *********** Solves the matrix equation *A* * *X* = *B* using an LU factorization computed by :ref:`LuFactor-name` . Include ******* The file ``cppad/utility/lu_invert.hpp`` is included by ``cppad/cppad.hpp`` but it can also be included separately with out the rest of the ``CppAD`` routines. Matrix Storage ************** All matrices are stored in row major order. To be specific, if :math:`Y` is a vector that contains a :math:`p` by :math:`q` matrix, the size of :math:`Y` must be equal to :math:`p * q` and for :math:`i = 0 , \ldots , p-1`, :math:`j = 0 , \ldots , q-1`, .. math:: Y_{i,j} = Y[ i * q + j ] ip ** The argument *ip* has prototype ``const`` *SizeVector* & *ip* (see description for *SizeVector* in :ref:`LuFactor` specifications). The size of *ip* is referred to as *n* in the specifications below. The elements of *ip* determine the order of the rows in the permuted matrix. jp ** The argument *jp* has prototype ``const`` *SizeVector* & *jp* (see description for *SizeVector* in :ref:`LuFactor` specifications). The size of *jp* must be equal to *n* . The elements of *jp* determine the order of the columns in the permuted matrix. LU ** The argument *LU* has the prototype ``const`` *FloatVector* & *LU* and the size of *LU* must equal :math:`n * n` (see description for *FloatVector* in :ref:`LuFactor` specifications). L = We define the lower triangular matrix *L* in terms of *LU* . The matrix *L* is zero above the diagonal and the rest of the elements are defined by *L* ( *i* , *j* ) = *LU* [ *ip* [ *i* ] * *n* + *jp* [ *j* ] ] for :math:`i = 0 , \ldots , n-1` and :math:`j = 0 , \ldots , i`. U = We define the upper triangular matrix *U* in terms of *LU* . The matrix *U* is zero below the diagonal, one on the diagonal, and the rest of the elements are defined by *U* ( *i* , *j* ) = *LU* [ *ip* [ *i* ] * *n* + *jp* [ *j* ] ] for :math:`i = 0 , \ldots , n-2` and :math:`j = i+1 , \ldots , n-1`. P = We define the permuted matrix *P* in terms of the matrix *L* and the matrix *U* by *P* = *L* * *U* . A = The matrix *A* , which defines the linear equations that we are solving, is given by *P* ( *i* , *j* ) = *A* [ *ip* [ *i* ] * *n* + *jp* [ *j* ] ] (Hence *LU* contains a permuted factorization of the matrix *A* .) X * The argument *X* has prototype *FloatVector* & *X* (see description for *FloatVector* in :ref:`LuFactor` specifications). The matrix *X* must have the same number of rows as the matrix *A* . The input value of *X* is the matrix *B* and the output value solves the matrix equation *A* * *X* = *B* . {xrst_toc_hidden example/utility/lu_invert.cpp xrst/lu_invert_hpp.xrst } Example ******* The file :ref:`lu_solve.hpp-name` is a good example usage of ``LuFactor`` with ``LuInvert`` . The file :ref:`lu_invert.cpp-name` contains an example and test of using ``LuInvert`` by itself. Source ****** The file :ref:`lu_invert.hpp-name` contains the current source code that implements these specifications. {xrst_end LuInvert} -------------------------------------------------------------------------- */ // BEGIN C++ # include # include # include namespace CppAD { // BEGIN CppAD namespace // LuInvert template void LuInvert( const SizeVector &ip, const SizeVector &jp, const FloatVector &LU, FloatVector &B ) { size_t k; // column index in X size_t p; // index along diagonal in LU size_t i; // row index in LU and X typedef typename FloatVector::value_type Float; // check numeric type specifications CheckNumericType(); // check simple vector class specifications CheckSimpleVector(); CheckSimpleVector(); Float etmp; size_t n = ip.size(); CPPAD_ASSERT_KNOWN( size_t(jp.size()) == n, "Error in LuInvert: jp must have size equal to n * n" ); CPPAD_ASSERT_KNOWN( size_t(LU.size()) == n * n, "Error in LuInvert: Lu must have size equal to n * m" ); size_t m = size_t(B.size()) / n; CPPAD_ASSERT_KNOWN( size_t(B.size()) == n * m, "Error in LuSolve: B must have size equal to a multiple of n" ); // temporary storage for reordered solution FloatVector x(n); // loop over equations for(k = 0; k < m; k++) { // invert the equation c = L * b for(p = 0; p < n; p++) { // solve for c[p] etmp = B[ ip[p] * m + k ] / LU[ ip[p] * n + jp[p] ]; B[ ip[p] * m + k ] = etmp; // subtract off effect on other variables for(i = p+1; i < n; i++) B[ ip[i] * m + k ] -= etmp * LU[ ip[i] * n + jp[p] ]; } // invert the equation x = U * c p = n; while( p > 0 ) { --p; etmp = B[ ip[p] * m + k ]; x[ jp[p] ] = etmp; for(i = 0; i < p; i++ ) B[ ip[i] * m + k ] -= etmp * LU[ ip[i] * n + jp[p] ]; } // copy reordered solution into B for(i = 0; i < n; i++) B[i * m + k] = x[i]; } return; } } // END CppAD namespace // END C++ # endif ================================================ FILE: include/cppad/utility/lu_solve.hpp ================================================ # ifndef CPPAD_UTILITY_LU_SOLVE_HPP # define CPPAD_UTILITY_LU_SOLVE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin LuSolve} {xrst_spell geq leq logdet signdet } Compute Determinant and Solve Linear Equations ############################################## Syntax ****** | # ``include `` | *signdet* = ``LuSolve`` ( *n* , *m* , *A* , *B* , *X* , *logdet* ) Description *********** Use an LU factorization of the matrix *A* to compute its determinant and solve for *X* in the linear of equation .. math:: A * X = B where *A* is an *n* by *n* matrix, *X* is an *n* by *m* matrix, and *B* is an :math:`n x m` matrix. Include ******* The file ``cppad/utility/lu_solve.hpp`` is included by ``cppad/cppad.hpp`` but it can also be included separately with out the rest of the ``CppAD`` routines. Factor and Invert ***************** This routine is an easy to user interface to :ref:`LuFactor-name` and :ref:`LuInvert-name` for computing determinants and solutions of linear equations. These separate routines should be used if one right hand side *B* depends on the solution corresponding to another right hand side (with the same value of *A* ). In this case only one call to ``LuFactor`` is required but there will be multiple calls to ``LuInvert`` . Matrix Storage ************** All matrices are stored in row major order. To be specific, if :math:`Y` is a vector that contains a :math:`p` by :math:`q` matrix, the size of :math:`Y` must be equal to :math:`p * q` and for :math:`i = 0 , \ldots , p-1`, :math:`j = 0 , \ldots , q-1`, .. math:: Y_{i,j} = Y[ i * q + j ] signdet ******* The return value *signdet* is a ``int`` value that specifies the sign factor for the determinant of *A* . This determinant of *A* is zero if and only if *signdet* is zero. n * The argument *n* has type ``size_t`` and specifies the number of rows in the matrices *A* , *X* , and *B* . The number of columns in *A* is also equal to *n* . m * The argument *m* has type ``size_t`` and specifies the number of columns in the matrices *X* and *B* . If *m* is zero, only the determinant of *A* is computed and the matrices *X* and *B* are not used. A * The argument *A* has the prototype ``const`` *FloatVector* & *A* and the size of *A* must equal :math:`n * n` (see description of :ref:`LuSolve@FloatVector` below). This is the :math:`n` by *n* matrix that we are computing the determinant of and that defines the linear equation. B * The argument *B* has the prototype ``const`` *FloatVector* & *B* and the size of *B* must equal :math:`n * m` (see description of :ref:`LuSolve@FloatVector` below). This is the :math:`n` by *m* matrix that defines the right hand side of the linear equations. If *m* is zero, *B* is not used. X * The argument *X* has the prototype *FloatVector* & *X* and the size of *X* must equal :math:`n * m` (see description of :ref:`LuSolve@FloatVector` below). The input value of *X* does not matter. On output, the elements of *X* contain the solution of the equation we wish to solve (unless *signdet* is equal to zero). If *m* is zero, *X* is not used. logdet ****** The argument *logdet* has prototype *Float* & *logdet* On input, the value of *logdet* does not matter. On output, it has been set to the log of the determinant of *A* (but not quite). To be more specific, the determinant of *A* is given by the formula *det* = *signdet* * ``exp`` ( *logdet* ) This enables ``LuSolve`` to use logs of absolute values in the case where *Float* corresponds to a real number. Float ***** The type *Float* must satisfy the conditions for a :ref:`NumericType-name` . The routine :ref:`CheckNumericType-name` will generate an error message if this is not the case. In addition, the following operations must be defined for any pair of *Float* objects *x* and *y* : .. list-table:: :widths: auto * - **Operation** - **Description** * - ``log`` ( *x* ) - returns the logarithm of *x* as a *Float* object FloatVector *********** The type *FloatVector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type Float` . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. LeqZero ******* Including the file ``lu_solve.hpp`` defines the template function | |tab| ``template | |tab| ``bool LeqZero`` < *Float* >( ``const`` *Float* & *x* ) in the ``CppAD`` namespace. This function returns true if *x* is less than or equal to zero and false otherwise. It is used by ``LuSolve`` to avoid taking the log of zero (or a negative number if *Float* corresponds to real numbers). This template function definition assumes that the operator ``<=`` is defined for *Float* objects. If this operator is not defined for your use of *Float* , you will need to specialize this template so that it works for your use of ``LuSolve`` . Complex numbers do not have the operation or ``<=`` defined. In addition, in the complex case, one can take the log of a negative number. The specializations | |tab| ``bool LeqZero< std::complex >`` ( ``const std::complex &`` *x* ) | |tab| ``bool LeqZero< std::complex >`` ( ``const std::complex &`` *x* ) are defined by including ``lu_solve.hpp`` . These return true if *x* is zero and false otherwise. AbsGeq ****** Including the file ``lu_solve.hpp`` defines the template function | |tab| ``template | |tab| ``bool AbsGeq`` < *Float* >( ``const`` *Float* & *x* , ``const`` *Float* & *y* ) If the type *Float* does not support the ``<=`` operation and it is not ``std::complex`` or ``std::complex`` , see the documentation for ``AbsGeq`` in :ref:`LuFactor` . {xrst_toc_hidden example/utility/lu_solve.cpp xrst/lu_solve_hpp.xrst } Example ******* The file :ref:`lu_solve.cpp-name` contains an example and test of using this routine. Source ****** The file :ref:`lu_solve.hpp-name` contains the current source code that implements these specifications. {xrst_end LuSolve} -------------------------------------------------------------------------- */ // BEGIN C++ # include # include // link exp for float and double cases # include # include # include # include # include # include namespace CppAD { // BEGIN CppAD namespace // LeqZero template bool LeqZero(const Float &x) { return x <= Float(0); } inline bool LeqZero( const std::complex &x ) { return x == std::complex(0); } inline bool LeqZero( const std::complex &x ) { return x == std::complex(0); } // LuSolve template int LuSolve( size_t n , size_t m , const FloatVector &A , const FloatVector &B , FloatVector &X , Float &logdet ) { // check numeric type specifications CheckNumericType(); // check simple vector class specifications CheckSimpleVector(); size_t p; // index of pivot element (diagonal of L) int signdet; // sign of the determinant Float pivot; // pivot element // the value zero const Float zero(0); // pivot row and column order in the matrix std::vector ip(n); std::vector jp(n); // ------------------------------------------------------- CPPAD_ASSERT_KNOWN( size_t(A.size()) == n * n, "Error in LuSolve: A must have size equal to n * n" ); CPPAD_ASSERT_KNOWN( size_t(B.size()) == n * m, "Error in LuSolve: B must have size equal to n * m" ); CPPAD_ASSERT_KNOWN( size_t(X.size()) == n * m, "Error in LuSolve: X must have size equal to n * m" ); // ------------------------------------------------------- // copy A so that it does not change FloatVector Lu(A); // copy B so that it does not change X = B; // Lu factor the matrix A signdet = LuFactor(ip, jp, Lu); // compute the log of the determinant logdet = Float(0); for(p = 0; p < n; p++) { // pivot using the max absolute element pivot = Lu[ ip[p] * n + jp[p] ]; // check for determinant equal to zero if( pivot == zero ) { // abort the mission logdet = Float(0); return 0; } // update the determinant if( LeqZero ( pivot ) ) { logdet += log( - pivot ); signdet = - signdet; } else logdet += log( pivot ); } // solve the linear equations LuInvert(ip, jp, Lu, X); // return the sign factor for the determinant return signdet; } } // END CppAD namespace // END C++ # endif ================================================ FILE: include/cppad/utility/memory_leak.hpp ================================================ # ifndef CPPAD_UTILITY_MEMORY_LEAK_HPP # define CPPAD_UTILITY_MEMORY_LEAK_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin memory_leak app} Memory Leak Detection ##################### Deprecated 2012-04-06 ********************* This routine has been deprecated. You should instead use the routine :ref:`ta_free_all-name` . Syntax ****** | # ``include `` | ``flag`` = ``memory_leak`` () | *flag* = *memory_leak* ( *add_static* ) Purpose ******* This routine checks that the are no memory leaks caused by improper use of :ref:`thread_alloc-name` memory allocator. The deprecated memory allocator :ref:`track_new_del-name` is also checked. Memory errors in the deprecated :ref:`omp_alloc-name` allocator are reported as being in ``thread_alloc`` . thread ****** It is assumed that :ref:`in_parallel()` is false and :ref:`thread_num` is zero when ``memory_leak`` is called. add_static ********** This argument has prototype ``size_t`` *add_static* and its default value is zero. Static variables hold onto memory forever. If the argument *add_static* is present (and non-zero), ``memory_leak`` adds this amount of memory to the :ref:`inuse` sum that corresponds to static variables in the program. A call with *add_static* should be make after a routine that has static variables which use :ref:`get_memory` to allocate memory. The value of *add_static* should be the difference of ``thread_alloc::inuse`` (0) before and after the call. Since multiple statics may be allocated in different places in the program, it is expected that there will be multiple calls that use this option. flag **** The return value *flag* has prototype ``bool`` *flag* If *add_static* is non-zero, the return value for ``memory_leak`` is false. Otherwise, the return value for ``memory_leak`` should be false (indicating that the only allocated memory corresponds to static variables). inuse ***** It is assumed that, when ``memory_leak`` is called, there should not be any memory :ref:`inuse` or :ref:`omp_inuse-name` for any thread (except for inuse memory corresponding to static variables). If there is, a message is printed and ``memory_leak`` returns false. available ********* It is assumed that, when ``memory_leak`` is called, there should not be any memory :ref:`available` or :ref:`omp_available-name` for any thread; i.e., it all has been returned to the system. If there is memory still available for any thread, ``memory_leak`` returns false. TRACK_COUNT *********** It is assumed that, when ``memory_leak`` is called, :ref:`track_new_del@TrackCount` will return a zero value. If it returns a non-zero value, ``memory_leak`` returns false. Error Message ************* If this is the first call to ``memory_leak`` , no message is printed. Otherwise, if it returns true, an error message is printed to standard output describing the memory leak that was detected. {xrst_end memory_leak} */ # include # include # include # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file memory_leak.hpp File that implements a memory check at end of a CppAD program */ /*! Function that checks allocator thread_alloc for misuse that results in memory leaks. Deprecated routines in track_new_del.hpp and omp_alloc.hpp are also checked. \param add_static [in] The amount specified by add_static is added to the amount of memory that is expected to be used by thread zero for static variables. \return If add_static is non-zero, the return value is false. Otherwise, if one of the following errors is detected, the return value is true: \li Thread zero does not have the expected amount of inuse memory (for static variables). \li A thread, other than thread zero, has any inuse memory. \li Any thread has available memory. \par If an error is detected, diagnostic information is printed to standard output. */ inline bool memory_leak(size_t add_static = 0) { // CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL not necessary given asserts below static size_t thread_zero_static_inuse = 0; using std::cout; using std::endl; using CppAD::thread_alloc; using CppAD::omp_alloc; // -------------------------------------------------------------------- CPPAD_ASSERT_KNOWN( ! thread_alloc::in_parallel(), "memory_leak: in_parallel() is true." ); CPPAD_ASSERT_KNOWN( thread_alloc::thread_num() == 0, "memory_leak: thread_num() is not zero." ); if( add_static != 0 ) { thread_zero_static_inuse += add_static; return false; } bool leak = false; size_t thread = 0; // check that memory in use for thread zero corresponds to statics size_t num_bytes = thread_alloc::inuse(thread); if( num_bytes != thread_zero_static_inuse ) { leak = true; cout << "thread zero: static inuse = " << thread_zero_static_inuse; cout << ", current inuse(0)= " << num_bytes << endl; } // check that no memory is currently available for this thread num_bytes = thread_alloc::available(thread); if( num_bytes != 0 ) { leak = true; cout << "thread zero: available = "; cout << num_bytes << endl; } for(thread = 1; thread < CPPAD_MAX_NUM_THREADS; thread++) { // check that no memory is currently in use for this thread num_bytes = thread_alloc::inuse(thread); if( num_bytes != 0 ) { leak = true; cout << "thread " << thread << ": inuse(thread) = "; cout << num_bytes << endl; } // check that no memory is currently available for this thread num_bytes = thread_alloc::available(thread); if( num_bytes != 0 ) { leak = true; cout << "thread " << thread << ": available(thread) = "; cout << num_bytes << endl; } } // ---------------------------------------------------------------------- // check track_new_del if( CPPAD_TRACK_COUNT() != 0 ) { leak = true; CppAD::TrackElement::Print(); } return leak; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/utility/nan.hpp ================================================ # ifndef CPPAD_UTILITY_NAN_HPP # define CPPAD_UTILITY_NAN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin nan} {xrst_spell hasnan isnan } Obtain Nan or Determine if a Value is Nan ######################################### Syntax ****** | # ``include `` | *b* = ``CppAD::isnan`` ( *s* ) | *b* = ``hasnan`` ( *v* ) Purpose ******* Check for the value not a number ``nan`` . The IEEE standard specifies that a floating point value *a* is ``nan`` if and only if the following returns true *a* != *a* std::isnan ********** Some compilers; e.g. Visual Studio, result in an ambiguous error between ``CppAD::isnan`` and ``std::isnan`` unless you include the ``CppAD`` before ``isnan`` (even when inside the CppAD namespace). Include ******* The file ``cppad/utility/nan.hpp`` is included by ``cppad/cppad.hpp`` but it can also be included separately with out the rest of the ``CppAD`` routines. Macros ====== Some C++ compilers use preprocessor symbols called ``nan`` and ``isnan`` . These preprocessor symbols will no longer be defined after this file is included. isnan ***** This routine determines if a scalar value is ``nan`` . s = The argument *s* has prototype ``const`` *Scalar* *s* b = The return value *b* has prototype ``bool`` *b* It is true if the value *s* is ``nan`` . hasnan ****** This routine determines if a :ref:`SimpleVector-name` has an element that is ``nan`` . v = The argument *v* has prototype ``const`` *Vector* & *v* (see :ref:`nan@Vector` for the definition of *Vector* ). b = The return value *b* has prototype ``bool`` *b* It is true if the vector *v* has a ``nan`` . nan(zero) ********* Deprecated 2015-10-04 ===================== This routine has been deprecated, use CppAD numeric limits :ref:`numeric_limits@quiet_NaN` in its place. Syntax ====== *s* = ``nan`` ( *z* ) z = The argument *z* has prototype ``const`` *Scalar* & *z* and its value is zero (see :ref:`nan@Scalar` for the definition of *Scalar* ). s = The return value *s* has prototype *Scalar* *s* It is the value ``nan`` for this floating point type. Scalar ****** The type *Scalar* must support the following operations; .. list-table:: :widths: auto * - **Operation** - **Description** * - *a* / *b* - division operator (returns a *Scalar* object) * - *a* == *b* - equality operator (returns a ``bool`` object) * - *a* != *b* - not equality operator (returns a ``bool`` object) Note that the division operator will be used with *a* and *b* equal to zero. For some types (e.g. ``int`` ) this may generate an exception. No attempt is made to catch any such exception. Vector ****** The type *Vector* must be a :ref:`SimpleVector-name` class with elements of type *Scalar* . {xrst_toc_hidden example/utility/nan.cpp } Example ******* The file :ref:`nan.cpp-name` contains an example and test of this routine. {xrst_end nan} */ # include # include /* # define nan There must be a define for every CppAD undef */ # ifdef nan # undef nan # endif /* # define isnan There must be a define for every CppAD undef */ # ifdef isnan # undef isnan # endif namespace CppAD { // BEGIN CppAD namespace template bool isnan(const Scalar &s) { return (s != s); } template bool hasnan(const Vector &v) { bool found_nan; size_t i; i = v.size(); found_nan = false; // on MS Visual Studio 2012, CppAD required in front of isnan ? while(i--) found_nan |= CppAD::isnan(v[i]); return found_nan; } template Scalar nan(const Scalar &zero) { return zero / zero; } } // End CppAD namespace # endif ================================================ FILE: include/cppad/utility/near_equal.hpp ================================================ # ifndef CPPAD_UTILITY_NEAR_EQUAL_HPP # define CPPAD_UTILITY_NEAR_EQUAL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin NearEqual} {xrst_spell cout endl } Determine if Two Values Are Nearly Equal ######################################## Syntax ****** | # ``include `` | *b* = ``NearEqual`` ( *x* , *y* , *r* , *a* ) Purpose ******* Returns true, if *x* and *y* are nearly equal, and false otherwise. x * The argument *x* has one of the following possible prototypes | |tab| ``const`` *Type* & *x* , | |tab| ``const std::complex<`` *Type* > & *x* , y * The argument *y* has one of the following possible prototypes | |tab| ``const`` *Type* & *y* , | |tab| ``const std::complex<`` *Type* > & *y* , r * The relative error criteria *r* has prototype ``const`` *Type* & *r* It must be greater than or equal to zero. The relative error condition is defined as: .. math:: | x - y | \leq r ( |x| + |y| ) a * The absolute error criteria *a* has prototype ``const`` *Type* & *a* It must be greater than or equal to zero. The absolute error condition is defined as: .. math:: | x - y | \leq a b * The return value *b* has prototype ``bool`` *b* If either *x* or *y* is infinite or not a number, the return value is false. Otherwise, if either the relative or absolute error condition (defined above) is satisfied, the return value is true. Otherwise, the return value is false. Type **** The type *Type* must be a :ref:`NumericType-name` . The routine :ref:`CheckNumericType-name` will generate an error message if this is not the case. In addition, the following operations must be defined objects *a* and *b* of type *Type* : .. list-table:: :widths: auto * - **Operation** - **Description** * - *a* <= *b* - less that or equal operator (returns a ``bool`` object) Include Files ************* The file ``cppad/utility/near_equal.hpp`` is included by ``cppad/cppad.hpp`` but it can also be included separately with out the rest of the ``CppAD`` routines. Example ******* {xrst_toc_hidden example/utility/near_equal.cpp } The file :ref:`near_equal.cpp-name` contains an example and test of ``NearEqual`` . It return true if it succeeds and false otherwise. Exercise ******** Create and run a program that contains the following code: :: using std::complex; using std::cout; using std::endl; complex one(1., 0), i(0., 1); complex x = one / i; complex y = - i; double r = 1e-12; double a = 0; bool ok = CppAD::NearEqual(x, y, r, a); if( ok ) cout << "Ok" << endl; else cout << "Error" << endl; {xrst_end NearEqual} */ # include # include # include # include namespace CppAD { // Begin CppAD namespace // determine if both x and y are finite values template bool near_equal_isfinite(const Type &x , const Type &y) { Type infinity = Type( std::numeric_limits::infinity() ); // handle bug where some compilers return true for nan == nan bool xNan = x != x; bool yNan = y != y; // infinite cases bool xInf = (x == infinity || x == - infinity); bool yInf = (x == infinity || x == - infinity); return ! (xNan | yNan | xInf | yInf); } template bool NearEqual(const Type &x, const Type &y, const Type &r, const Type &a) { CheckNumericType(); Type zero(0); CPPAD_ASSERT_KNOWN( zero <= r, "Error in NearEqual: relative error is less than zero" ); CPPAD_ASSERT_KNOWN( zero <= a, "Error in NearEqual: absolute error is less than zero" ); // check for special cases if( ! CppAD::near_equal_isfinite(x, y) ) return false; Type ax = x; if( ax <= zero ) ax = - ax; Type ay = y; if( ay <= zero ) ay = - ay; Type ad = x - y; if( ad <= zero ) ad = - ad; if( ad <= a ) return true; if( ad <= r * (ax + ay) ) return true; return false; } template bool NearEqual( const std::complex &x , const std::complex &y , const Type &r , const Type & a ) { CheckNumericType(); # ifndef NDEBUG Type zero(0); # endif CPPAD_ASSERT_KNOWN( zero <= r, "Error in NearEqual: relative error is less than zero" ); CPPAD_ASSERT_KNOWN( zero <= a, "Error in NearEqual: absolute error is less than zero" ); // check for special cases if( ! CppAD::near_equal_isfinite(x.real(), x.imag()) ) return false; if( ! CppAD::near_equal_isfinite(y.real(), y.imag()) ) return false; std::complex d = x - y; Type ad = std::abs(d); if( ad <= a ) return true; Type ax = std::abs(x); Type ay = std::abs(y); if( ad <= r * (ax + ay) ) return true; return false; } template bool NearEqual( const std::complex &x , const Type &y , const Type &r , const Type & a ) { return NearEqual(x, std::complex(y, Type(0)), r, a); } template bool NearEqual( const Type &x , const std::complex &y , const Type &r , const Type & a ) { return NearEqual(std::complex(x, Type(0)), y, r, a); } } // END CppAD namespace # endif ================================================ FILE: include/cppad/utility/ode_err_control.hpp ================================================ # ifndef CPPAD_UTILITY_ODE_ERR_CONTROL_HPP # define CPPAD_UTILITY_ODE_ERR_CONTROL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin OdeErrControl} {xrst_spell eabs eb ef erel nstep scur smax smin tf xa xb xf } An Error Controller for ODE Solvers ################################### Syntax ****** | # ``include `` | *xf* = ``OdeErrControl`` ( *method* , *ti* , *tf* , *xi* , | |tab| ``smin`` , ``smax`` , ``scur`` , ``eabs`` , ``erel`` , ``ef`` , ``maxabs`` , ``nstep`` ) Description *********** Let :math:`\B{R}` denote the real numbers and let :math:`F : \B{R} \times \B{R}^n \rightarrow \B{R}^n` be a smooth function. We define :math:`X : [ti , tf] \rightarrow \B{R}^n` by the following initial value problem: .. math:: :nowrap: \begin{eqnarray} X(ti) & = & xi \\ X'(t) & = & F[t , X(t)] \end{eqnarray} The routine ``OdeErrControl`` can be used to adjust the step size used an arbitrary integration methods in order to be as fast as possible and still with in a requested error bound. Include ******* The file ``cppad/utility/ode_err_control.hpp`` is included by ``cppad/cppad.hpp`` but it can also be included separately with out the rest of the ``CppAD`` routines. Notation ******** The template parameter types :ref:`OdeErrControl@Scalar` and :ref:`OdeErrControl@Vector` are documented below. xf ** The return value *xf* has the prototype *Vector* *xf* (see description of :ref:`OdeErrControl@Vector` below). and the size of *xf* is equal to *n* . If *xf* contains not a number :ref:`nan-name` , see the discussion of :ref:`step` . Method ****** The class *Method* and the object *method* satisfy the following syntax *Method* & *method* The object *method* must support ``step`` and ``order`` member functions defined below: step ==== The syntax *method* . ``step`` ( *ta* , *tb* , *xa* , *xb* , *eb* ) executes one step of the integration method. *ta* The argument *ta* has prototype ``const`` *Scalar* & *ta* It specifies the initial time for this step in the ODE integration. (see description of :ref:`OdeErrControl@Scalar` below). *tb* The argument *tb* has prototype ``const`` *Scalar* & *tb* It specifies the final time for this step in the ODE integration. *xa* The argument *xa* has prototype ``const`` *Vector* & *xa* and size *n* . It specifies the value of :math:`X(ta)`. (see description of :ref:`OdeErrControl@Vector` below). *xb* The argument value *xb* has prototype *Vector* & *xb* and size *n* . The input value of its elements does not matter. On output, it contains the approximation for :math:`X(tb)` that the method obtains. *eb* The argument value *eb* has prototype *Vector* & *eb* and size *n* . The input value of its elements does not matter. On output, it contains an estimate for the error in the approximation *xb* . It is assumed (locally) that the error bound in this approximation nearly equal to :math:`K (tb - ta)^m` where *K* is a fixed constant and *m* is the corresponding argument to ``CodeControl`` . Nan === If any element of the vector *eb* or *xb* are not a number ``nan`` , the current step is considered to large. If this happens with the current step size equal to *smin* , ``OdeErrControl`` returns with *xf* and *ef* as vectors of ``nan`` . order ===== If *m* is ``size_t`` , the object *method* must also support the following syntax *m* = *method* . ``order`` () The return value *m* is the order of the error estimate; i.e., there is a constant K such that if :math:`ti \leq ta \leq tb \leq tf`, .. math:: | eb(tb) | \leq K | tb - ta |^m where *ta* , *tb* , and *eb* are as in *method* . ``step`` ( *ta* , *tb* , *xa* , *xb* , *eb* ) ti ** The argument *ti* has prototype ``const`` *Scalar* & *ti* It specifies the initial time for the integration of the differential equation. tf ** The argument *tf* has prototype ``const`` *Scalar* & *tf* It specifies the final time for the integration of the differential equation. xi ** The argument *xi* has prototype ``const`` *Vector* & *xi* and size *n* . It specifies value of :math:`X(ti)`. smin **** The argument *smin* has prototype ``const`` *Scalar* & *smin* The step size during a call to *method* is defined as the corresponding value of :math:`tb - ta`. If :math:`tf - ti \leq smin`, the integration will be done in one step of size *tf - ti* . Otherwise, the minimum value of *tb - ta* will be :math:`smin` except for the last two calls to *method* where it may be as small as :math:`smin / 2`. smax **** The argument *smax* has prototype ``const`` *Scalar* & *smax* It specifies the maximum step size to use during the integration; i.e., the maximum value for :math:`tb - ta` in a call to *method* . The value of *smax* must be greater than or equal *smin* . scur **** The argument *scur* has prototype *Scalar* & *scur* The value of *scur* is the suggested next step size, based on error criteria, to try in the next call to *method* . On input it corresponds to the first call to *method* , in this call to ``OdeErrControl`` (where :math:`ta = ti`). On output it corresponds to the next call to *method* , in a subsequent call to ``OdeErrControl`` (where *ta* = *tf* ). eabs **** The argument *eabs* has prototype ``const`` *Vector* & *eabs* and size *n* . Each of the elements of *eabs* must be greater than or equal zero. It specifies a bound for the absolute error in the return value *xf* as an approximation for :math:`X(tf)`. (see the :ref:`OdeErrControl@Error Criteria Discussion` below). erel **** The argument *erel* has prototype ``const`` *Scalar* & *erel* and is greater than or equal zero. It specifies a bound for the relative error in the return value *xf* as an approximation for :math:`X(tf)` (see the :ref:`OdeErrControl@Error Criteria Discussion` below). ef ** The argument value *ef* has prototype *Vector* & *ef* and size *n* . The input value of its elements does not matter. On output, it contains an estimated bound for the absolute error in the approximation *xf* ; i.e., .. math:: ef_i > | X( tf )_i - xf_i | If on output *ef* contains not a number ``nan`` , see the discussion of :ref:`step` . maxabs ****** The argument *maxabs* is optional in the call to ``OdeErrControl`` . If it is present, it has the prototype *Vector* & *maxabs* and size *n* . The input value of its elements does not matter. On output, it contains an estimate for the maximum absolute value of :math:`X(t)`; i.e., .. math:: maxabs[i] \approx \max \left\{ | X( t )_i | \; : \; t \in [ti, tf] \right\} nstep ***** The argument *nstep* is optional in the call to ``OdeErrControl`` . If it is present, it has the prototype *size_t* & *nstep* Its input value does not matter and its output value is the number of calls to *method* . ``step`` used by ``OdeErrControl`` . Error Criteria Discussion ************************* The relative error criteria *erel* and absolute error criteria *eabs* are enforced during each step of the integration of the ordinary differential equations. In addition, they are inversely scaled by the step size so that the total error bound is less than the sum of the error bounds. To be specific, if :math:`\tilde{X} (t)` is the approximate solution at time :math:`t`, *ta* is the initial step time, and *tb* is the final step time, .. math:: \left| \tilde{X} (tb)_j - X (tb)_j \right| \leq \frac{tf - ti}{tb - ta} \left[ eabs[j] + erel \; | \tilde{X} (tb)_j | \right] If :math:`X(tb)_j` is near zero for some :math:`tb \in [ti , tf]`, and one uses an absolute error criteria :math:`eabs[j]` of zero, the error criteria above will force ``OdeErrControl`` to use step sizes equal to :ref:`OdeErrControl@smin` for steps ending near :math:`tb`. In this case, the error relative to *maxabs* can be judged after ``OdeErrControl`` returns. If *ef* is to large relative to *maxabs* , ``OdeErrControl`` can be called again with a smaller value of *smin* . Scalar ****** The type *Scalar* must satisfy the conditions for a :ref:`NumericType-name` . The routine :ref:`CheckNumericType-name` will generate an error message if this is not the case. In addition, the following operations must be defined for *Scalar* objects *a* and *b* : .. list-table:: :widths: auto * - **Operation** - **Description** * - *a* <= *b* - returns true (false) if *a* is less than or equal (greater than) *b* . * - *a* == *b* - returns true (false) if *a* is equal to *b* . * - ``log`` ( *a* ) - returns a *Scalar* equal to the logarithm of *a* * - ``exp`` ( *a* ) - returns a *Scalar* equal to the exponential of *a* Vector ****** The type *Vector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type Scalar` . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Example ******* {xrst_toc_hidden example/utility/ode_err_control.cpp example/utility/ode_err_maxabs.cpp } The files :ref:`ode_err_control.cpp-name` and :ref:`ode_err_maxabs.cpp-name` contain examples and tests of using this routine. They return true if they succeed and false otherwise. Theory ****** Let :math:`e(s)` be the error as a function of the step size :math:`s` and suppose that there is a constant :math:`K` such that :math:`e(s) = K s^m`. Let :math:`a` be our error bound. Given the value of :math:`e(s)`, a step of size :math:`\lambda s` would be ok provided that .. math:: :nowrap: \begin{eqnarray} a & \geq & e( \lambda s ) (tf - ti) / ( \lambda s ) \\ a & \geq & K \lambda^m s^m (tf - ti) / ( \lambda s ) \\ a & \geq & \lambda^{m-1} s^{m-1} (tf - ti) e(s) / s^m \\ a & \geq & \lambda^{m-1} (tf - ti) e(s) / s \\ \lambda^{m-1} & \leq & \frac{a}{e(s)} \frac{s}{tf - ti} \end{eqnarray} Thus if the right hand side of the last inequality is greater than or equal to one, the step of size :math:`s` is ok. Source Code *********** The source code for this routine is in the file ``cppad/ode_err_control.hpp`` . {xrst_end OdeErrControl} -------------------------------------------------------------------------- */ // link exp and log for float and double # include # include # include # include namespace CppAD { // Begin CppAD namespace template Vector OdeErrControl( Method &method, const Scalar &ti , const Scalar &tf , const Vector &xi , const Scalar &smin , const Scalar &smax , Scalar &scur , const Vector &eabs , const Scalar &erel , Vector &ef , Vector &maxabs, size_t &nstep ) { // check simple vector class specifications CheckSimpleVector(); size_t n = size_t(xi.size()); CPPAD_ASSERT_KNOWN( smin <= smax, "Error in OdeErrControl: smin > smax" ); CPPAD_ASSERT_KNOWN( size_t(eabs.size()) == n, "Error in OdeErrControl: size of eabs is not equal to n" ); CPPAD_ASSERT_KNOWN( size_t(maxabs.size()) == n, "Error in OdeErrControl: size of maxabs is not equal to n" ); size_t m = method.order(); CPPAD_ASSERT_KNOWN( m > 1, "Error in OdeErrControl: m is less than or equal one" ); bool ok; bool minimum_step; size_t i; Vector xa(n), xb(n), eb(n), nan_vec(n); // initialization Scalar zero(0.0); Scalar one(1.0); Scalar two(2.0); Scalar three(3.0); Scalar m1(double(m-1)); Scalar ta = ti; for(i = 0; i < n; i++) { nan_vec[i] = nan(zero); ef[i] = zero; xa[i] = xi[i]; if( zero <= xi[i] ) maxabs[i] = xi[i]; else maxabs[i] = - xi[i]; } nstep = 0; Scalar tb, step, lambda, axbi, a, r, root; while( ! (ta == tf) ) { // start with value suggested by error criteria step = scur; // check maximum if( smax <= step ) step = smax; // check minimum minimum_step = step <= smin; if( minimum_step ) step = smin; // check if near the end if( tf <= ta + step * three / two ) tb = tf; else tb = ta + step; // try using this step size nstep++; method.step(ta, tb, xa, xb, eb); step = tb - ta; // check if this steps error estimate is ok ok = ! (hasnan(xb) || hasnan(eb)); if( (! ok) && minimum_step ) { ef = nan_vec; return nan_vec; } // compute value of lambda for this step lambda = Scalar(10) * scur / step; for(i = 0; i < n; i++) { if( zero <= xb[i] ) axbi = xb[i]; else axbi = - xb[i]; a = eabs[i] + erel * axbi; if( ! (eb[i] == zero) ) { r = ( a / eb[i] ) * step / (tf - ti); root = exp( log(r) / m1 ); if( root <= lambda ) lambda = root; } } if( ok && ( one <= lambda || step <= smin * three / two) ) { // this step is within error limits or // close to the minimum size ta = tb; for(i = 0; i < n; i++) { xa[i] = xb[i]; ef[i] = ef[i] + eb[i]; if( zero <= xb[i] ) axbi = xb[i]; else axbi = - xb[i]; if( axbi > maxabs[i] ) maxabs[i] = axbi; } } if( ! ok ) { // decrease step an see if method will work this time scur = step / two; } else if( ! (ta == tf) ) { // step suggested by the error criteria is not used // on the last step because it may be very small. scur = lambda * step / two; } } return xa; } template Vector OdeErrControl( Method &method, const Scalar &ti , const Scalar &tf , const Vector &xi , const Scalar &smin , const Scalar &smax , Scalar &scur , const Vector &eabs , const Scalar &erel , Vector &ef ) { Vector maxabs(xi.size()); size_t nstep; return OdeErrControl( method, ti, tf, xi, smin, smax, scur, eabs, erel, ef, maxabs, nstep ); } template Vector OdeErrControl( Method &method, const Scalar &ti , const Scalar &tf , const Vector &xi , const Scalar &smin , const Scalar &smax , Scalar &scur , const Vector &eabs , const Scalar &erel , Vector &ef , Vector &maxabs) { size_t nstep; return OdeErrControl( method, ti, tf, xi, smin, smax, scur, eabs, erel, ef, maxabs, nstep ); } } // End CppAD namespace # endif ================================================ FILE: include/cppad/utility/ode_gear.hpp ================================================ # ifndef CPPAD_UTILITY_ODE_GEAR_HPP # define CPPAD_UTILITY_ODE_GEAR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin OdeGear} {xrst_spell dep pp test test vol } An Arbitrary Order Gear Method ############################## Syntax ****** | # ``include `` | ``OdeGear`` ( *F* , *m* , *n* , *T* , *X* , *e* ) Purpose ******* This routine applies :ref:`OdeGear@Gear's Method` to solve an explicit set of ordinary differential equations. We are given :math:`f : \B{R} \times \B{R}^n \rightarrow \B{R}^n` be a smooth function. This routine solves the following initial value problem .. math:: :nowrap: \begin{eqnarray} x( t_{m-1} ) & = & x^0 \\ x^\prime (t) & = & f[t , x(t)] \end{eqnarray} for the value of :math:`x( t_m )`. If your set of ordinary differential equations are not stiff an explicit method may be better (perhaps :ref:`Runge45-name` .) Include ******* The file ``cppad/utility/ode_gear.hpp`` is included by ``cppad/cppad.hpp`` but it can also be included separately with out the rest of the ``CppAD`` routines. Fun *** The class *Fun* and the object *F* satisfy the prototype *Fun* & *F* This must support the following set of calls | |tab| *F* . ``Ode`` ( *t* , *x* , *f* ) | |tab| *F* . ``Ode_dep`` ( *t* , *x* , *f_x* ) t = The argument *t* has prototype ``const`` *Scalar* & *t* (see description of :ref:`OdeGear@Scalar` below). x = The argument *x* has prototype ``const`` *Vector* & *x* and has size *n* (see description of :ref:`OdeGear@Vector` below). f = The argument *f* to *F* . ``Ode`` has prototype *Vector* & *f* On input and output, *f* is a vector of size *n* and the input values of the elements of *f* do not matter. On output, *f* is set equal to :math:`f(t, x)` (see *f* ( *t* , *x* ) in :ref:`OdeGear@Purpose` ). f_x === The argument *f_x* has prototype *Vector* & *f_x* On input and output, *f_x* is a vector of size :math:`n * n` and the input values of the elements of *f_x* do not matter. On output, .. math:: f\_x [i * n + j] = \partial_{x(j)} f_i ( t , x ) Warning ======= The arguments *f* , and *f_x* must have a call by reference in their prototypes; i.e., do not forget the ``&`` in the prototype for *f* and *f_x* . m * The argument *m* has prototype ``size_t`` *m* It specifies the order (highest power of :math:`t`) used to represent the function :math:`x(t)` in the multi-step method. Upon return from ``OdeGear`` , the *i*-th component of the polynomial is defined by .. math:: p_i ( t_j ) = X[ j * n + i ] for :math:`j = 0 , \ldots , m` (where :math:`0 \leq i < n`). The value of :math:`m` must be greater than or equal one. n * The argument *n* has prototype ``size_t`` *n* It specifies the range space dimension of the vector valued function :math:`x(t)`. T * The argument *T* has prototype ``const`` *Vector* & *T* and size greater than or equal to :math:`m+1`. For :math:`j = 0 , \ldots m`, :math:`T[j]` is the time corresponding to time corresponding to a previous point in the multi-step method. The value :math:`T[m]` is the time of the next point in the multi-step method. The array :math:`T` must be monotone increasing; i.e., :math:`T[j] < T[j+1]`. Above and below we often use the shorthand :math:`t_j` for :math:`T[j]`. X * The argument *X* has the prototype *Vector* & *X* and size greater than or equal to :math:`(m+1) * n`. On input to ``OdeGear`` , for :math:`j = 0 , \ldots , m-1`, and :math:`i = 0 , \ldots , n-1` .. math:: X[ j * n + i ] = x_i ( t_j ) Upon return from ``OdeGear`` , for :math:`i = 0 , \ldots , n-1` .. math:: X[ m * n + i ] \approx x_i ( t_m ) e * The vector *e* is an approximate error bound for the result; i.e., .. math:: e[i] \geq | X[ m * n + i ] - x_i ( t_m ) | The order of this approximation is one less than the order of the solution; i.e., .. math:: e = O ( h^m ) where :math:`h` is the maximum of :math:`t_{j+1} - t_j`. Scalar ****** The type *Scalar* must satisfy the conditions for a :ref:`NumericType-name` . The routine :ref:`CheckNumericType-name` will generate an error message if this is not the case. In addition, the following operations must be defined for *Scalar* objects *a* and *b* : .. list-table:: :widths: auto * - **Operation** - **Description** * - *a* < *b* - less than operator (returns a ``bool`` object) Vector ****** The type *Vector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type Scalar` . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Example ******* {xrst_toc_hidden example/utility/ode_gear.cpp } The file :ref:`ode_gear.cpp-name` contains an example and test a test of using this routine. Source Code *********** The source code for this routine is in the file ``cppad/ode_gear.hpp`` . Theory ****** For this discussion we use the shorthand :math:`x_j` for the value :math:`x ( t_j ) \in \B{R}^n` which is not to be confused with :math:`x_i (t) \in \B{R}` in the notation above. The interpolating polynomial :math:`p(t)` is given by .. math:: p(t) = \sum_{j=0}^m x_j \frac{ \prod_{i \neq j} ( t - t_i ) }{ \prod_{i \neq j} ( t_j - t_i ) } The derivative :math:`p^\prime (t)` is given by .. math:: p^\prime (t) = \sum_{j=0}^m x_j \frac{ \sum_{i \neq j} \prod_{k \neq i,j} ( t - t_k ) }{ \prod_{k \neq j} ( t_j - t_k ) } Evaluating the derivative at the point :math:`t_\ell` we have .. math:: :nowrap: \begin{eqnarray} p^\prime ( t_\ell ) & = & x_\ell \frac{ \sum_{i \neq \ell} \prod_{k \neq i,\ell} ( t_\ell - t_k ) }{ \prod_{k \neq \ell} ( t_\ell - t_k ) } + \sum_{j \neq \ell} x_j \frac{ \sum_{i \neq j} \prod_{k \neq i,j} ( t_\ell - t_k ) }{ \prod_{k \neq j} ( t_j - t_k ) } \\ & = & x_\ell \sum_{i \neq \ell} \frac{ 1 }{ t_\ell - t_i } + \sum_{j \neq \ell} x_j \frac{ \prod_{k \neq \ell,j} ( t_\ell - t_k ) }{ \prod_{k \neq j} ( t_j - t_k ) } \\ & = & x_\ell \sum_{k \neq \ell} ( t_\ell - t_k )^{-1} + \sum_{j \neq \ell} x_j ( t_j - t_\ell )^{-1} \prod_{k \neq \ell ,j} ( t_\ell - t_k ) / ( t_j - t_k ) \end{eqnarray} We define the vector :math:`\alpha \in \B{R}^{m+1}` by .. math:: \alpha_j = \left\{ \begin{array}{ll} \sum_{k \neq m} ( t_m - t_k )^{-1} & {\rm if} \; j = m \\ ( t_j - t_m )^{-1} \prod_{k \neq m,j} ( t_m - t_k ) / ( t_j - t_k ) & {\rm otherwise} \end{array} \right. It follows that .. math:: p^\prime ( t_m ) = \alpha_0 x_0 + \cdots + \alpha_m x_m Gear's method determines :math:`x_m` by solving the following nonlinear equation .. math:: f( t_m , x_m ) = \alpha_0 x_0 + \cdots + \alpha_m x_m Newton's method for solving this equation determines iterates, which we denote by :math:`x_m^k`, by solving the following affine approximation of the equation above .. math:: :nowrap: \begin{eqnarray} f( t_m , x_m^{k-1} ) + \partial_x f( t_m , x_m^{k-1} ) ( x_m^k - x_m^{k-1} ) & = & \alpha_0 x_0^k + \alpha_1 x_1 + \cdots + \alpha_m x_m \\ \left[ \alpha_m I - \partial_x f( t_m , x_m^{k-1} ) \right] x_m & = & \left[ f( t_m , x_m^{k-1} ) - \partial_x f( t_m , x_m^{k-1} ) x_m^{k-1} - \alpha_0 x_0 - \cdots - \alpha_{m-1} x_{m-1} \right] \end{eqnarray} In order to initialize Newton's method; i.e. choose :math:`x_m^0` we define the vector :math:`\beta \in \B{R}^{m+1}` by .. math:: \beta_j = \left\{ \begin{array}{ll} \sum_{k \neq m-1} ( t_{m-1} - t_k )^{-1} & {\rm if} \; j = m-1 \\ ( t_j - t_{m-1} )^{-1} \prod_{k \neq m-1,j} ( t_{m-1} - t_k ) / ( t_j - t_k ) & {\rm otherwise} \end{array} \right. It follows that .. math:: p^\prime ( t_{m-1} ) = \beta_0 x_0 + \cdots + \beta_m x_m We solve the following approximation of the equation above to determine :math:`x_m^0`: .. math:: f( t_{m-1} , x_{m-1} ) = \beta_0 x_0 + \cdots + \beta_{m-1} x_{m-1} + \beta_m x_m^0 Gear's Method ************* C. W. Gear, *Simultaneous Numerical Solution of Differential-Algebraic Equations* , IEEE Transactions on Circuit Theory, vol. 18, no. 1, pp. 89-95, Jan. 1971. {xrst_end OdeGear} -------------------------------------------------------------------------- */ # include # include # include # include # include # include # include namespace CppAD { // BEGIN CppAD namespace template void OdeGear( Fun &F , size_t m , size_t n , const Vector &T , Vector &X , Vector &e ) { // temporary indices size_t i, j, k; typedef typename Vector::value_type Scalar; // check numeric type specifications CheckNumericType(); // check simple vector class specifications CheckSimpleVector(); CPPAD_ASSERT_KNOWN( m >= 1, "OdeGear: m is less than one" ); CPPAD_ASSERT_KNOWN( n > 0, "OdeGear: n is equal to zero" ); CPPAD_ASSERT_KNOWN( size_t(T.size()) >= (m+1), "OdeGear: size of T is not greater than or equal (m+1)" ); CPPAD_ASSERT_KNOWN( size_t(X.size()) >= (m+1) * n, "OdeGear: size of X is not greater than or equal (m+1) * n" ); for(j = 0; j < m; j++) CPPAD_ASSERT_KNOWN( T[j] < T[j+1], "OdeGear: the array T is not monotone increasing" ); // some constants Scalar zero(0); Scalar one(1); // vectors required by method Vector alpha(m + 1); Vector beta(m + 1); Vector f(n); Vector f_x(n * n); Vector x_m0(n); Vector x_m(n); Vector b(n); Vector A(n * n); // compute alpha[m] alpha[m] = zero; for(k = 0; k < m; k++) alpha[m] += one / (T[m] - T[k]); // compute beta[m-1] beta[m-1] = one / (T[m-1] - T[m]); for(k = 0; k < m-1; k++) beta[m-1] += one / (T[m-1] - T[k]); // compute other components of alpha for(j = 0; j < m; j++) { // compute alpha[j] alpha[j] = one / (T[j] - T[m]); for(k = 0; k < m; k++) { if( k != j ) { alpha[j] *= (T[m] - T[k]); alpha[j] /= (T[j] - T[k]); } } } // compute other components of beta for(j = 0; j <= m; j++) { if( j != m-1 ) { // compute beta[j] beta[j] = one / (T[j] - T[m-1]); for(k = 0; k <= m; k++) { if( k != j && k != m-1 ) { beta[j] *= (T[m-1] - T[k]); beta[j] /= (T[j] - T[k]); } } } } // evaluate f(T[m-1], x_{m-1} ) for(i = 0; i < n; i++) x_m[i] = X[(m-1) * n + i]; F.Ode(T[m-1], x_m, f); // solve for x_m^0 for(i = 0; i < n; i++) { x_m[i] = f[i]; for(j = 0; j < m; j++) x_m[i] -= beta[j] * X[j * n + i]; x_m[i] /= beta[m]; } x_m0 = x_m; // evaluate partial w.r.t x of f(T[m], x_m^0) F.Ode_dep(T[m], x_m, f_x); // compute the matrix A = ( alpha[m] * I - f_x ) for(i = 0; i < n; i++) { for(j = 0; j < n; j++) A[i * n + j] = - f_x[i * n + j]; A[i * n + i] += alpha[m]; } // LU factor (and overwrite) the matrix A CppAD::vector ip(n) , jp(n); # ifndef NDEBUG int sign = # endif LuFactor(ip, jp, A); CPPAD_ASSERT_KNOWN( sign != 0, "OdeGear: step size is to large" ); // Iterations of Newton's method for(k = 0; k < 3; k++) { // only evaluate f( T[m] , x_m ) keep f_x during iteration F.Ode(T[m], x_m, f); // b = f + f_x x_m - alpha[0] x_0 - ... - alpha[m-1] x_{m-1} for(i = 0; i < n; i++) { b[i] = f[i]; for(j = 0; j < n; j++) b[i] -= f_x[i * n + j] * x_m[j]; for(j = 0; j < m; j++) b[i] -= alpha[j] * X[ j * n + i ]; } LuInvert(ip, jp, A, b); x_m = b; } // return estimate for x( t[k] ) and the estimated error bound for(i = 0; i < n; i++) { X[m * n + i] = x_m[i]; e[i] = x_m[i] - x_m0[i]; if( e[i] < zero ) e[i] = - e[i]; } } } // End CppAD namespace # endif ================================================ FILE: include/cppad/utility/ode_gear_control.hpp ================================================ # ifndef CPPAD_UTILITY_ODE_GEAR_CONTROL_HPP # define CPPAD_UTILITY_ODE_GEAR_CONTROL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin OdeGearControl} {xrst_spell dep eabs ef erel maxabs nstep sini smax smin test test tf xf } An Error Controller for Gear's Ode Solvers ########################################## Syntax ****** | # ``include `` | *xf* = ``OdeGearControl`` ( *F* , *M* , *ti* , *tf* , *xi* , | |tab| ``smin`` , ``smax`` , ``sini`` , ``eabs`` , ``erel`` , ``ef`` , ``maxabs`` , ``nstep`` ) Purpose ******* Let :math:`\B{R}` denote the real numbers and let :math:`f : \B{R} \times \B{R}^n \rightarrow \B{R}^n` be a smooth function. We define :math:`X : [ti , tf] \rightarrow \B{R}^n` by the following initial value problem: .. math:: :nowrap: \begin{eqnarray} X(ti) & = & xi \\ X'(t) & = & f[t , X(t)] \end{eqnarray} The routine :ref:`OdeGear-name` is a stiff multi-step method that can be used to approximate the solution to this equation. The routine ``OdeGearControl`` sets up this multi-step method and controls the error during such an approximation. Include ******* The file ``cppad/utility/ode_gear_control.hpp`` is included by ``cppad/cppad.hpp`` but it can also be included separately with out the rest of the ``CppAD`` routines. Notation ******** The template parameter types :ref:`OdeGearControl@Scalar` and :ref:`OdeGearControl@Vector` are documented below. xf ** The return value *xf* has the prototype *Vector* *xf* and the size of *xf* is equal to *n* (see description of :ref:`OdeGear@Vector` below). It is the approximation for :math:`X(tf)`. Fun *** The class *Fun* and the object *F* satisfy the prototype *Fun* & *F* This must support the following set of calls | |tab| *F* . ``Ode`` ( *t* , *x* , *f* ) | |tab| *F* . ``Ode_dep`` ( *t* , *x* , *f_x* ) t = The argument *t* has prototype ``const`` *Scalar* & *t* (see description of :ref:`OdeGear@Scalar` below). x = The argument *x* has prototype ``const`` *Vector* & *x* and has size *N* (see description of :ref:`OdeGear@Vector` below). f = The argument *f* to *F* . ``Ode`` has prototype *Vector* & *f* On input and output, *f* is a vector of size *N* and the input values of the elements of *f* do not matter. On output, *f* is set equal to :math:`f(t, x)` (see *f* ( *t* , *x* ) in :ref:`OdeGear@Purpose` ). f_x === The argument *f_x* has prototype *Vector* & *f_x* On input and output, *f_x* is a vector of size :math:`N * N` and the input values of the elements of *f_x* do not matter. On output, .. math:: f\_x [i * n + j] = \partial_{x(j)} f_i ( t , x ) Warning ======= The arguments *f* , and *f_x* must have a call by reference in their prototypes; i.e., do not forget the ``&`` in the prototype for *f* and *f_x* . M * The argument *M* has prototype ``size_t`` *M* It specifies the order of the multi-step method; i.e., the order of the approximating polynomial (after the initialization process). The argument *M* must greater than or equal one. ti ** The argument *ti* has prototype ``const`` *Scalar* & *ti* It specifies the initial time for the integration of the differential equation. tf ** The argument *tf* has prototype ``const`` *Scalar* & *tf* It specifies the final time for the integration of the differential equation. xi ** The argument *xi* has prototype ``const`` *Vector* & *xi* and size *n* . It specifies value of :math:`X(ti)`. smin **** The argument *smin* has prototype ``const`` *Scalar* & *smin* The minimum value of :math:`T[M] - T[M-1]` in a call to ``OdeGear`` will be :math:`smin` except for the last two calls where it may be as small as :math:`smin / 2`. The value of *smin* must be less than or equal *smax* . smax **** The argument *smax* has prototype ``const`` *Scalar* & *smax* It specifies the maximum step size to use during the integration; i.e., the maximum value for :math:`T[M] - T[M-1]` in a call to ``OdeGear`` . sini **** The argument *sini* has prototype *Scalar* & *sini* The value of *sini* is the minimum step size to use during initialization of the multi-step method; i.e., for calls to ``OdeGear`` where :math:`m < M`. The value of *sini* must be less than or equal *smax* (and can also be less than *smin* ). eabs **** The argument *eabs* has prototype ``const`` *Vector* & *eabs* and size *n* . Each of the elements of *eabs* must be greater than or equal zero. It specifies a bound for the absolute error in the return value *xf* as an approximation for :math:`X(tf)`. (see the :ref:`OdeGearControl@Error Criteria Discussion` below). erel **** The argument *erel* has prototype ``const`` *Scalar* & *erel* and is greater than or equal zero. It specifies a bound for the relative error in the return value *xf* as an approximation for :math:`X(tf)` (see the :ref:`OdeGearControl@Error Criteria Discussion` below). ef ** The argument value *ef* has prototype *Vector* & *ef* and size *n* . The input value of its elements does not matter. On output, it contains an estimated bound for the absolute error in the approximation *xf* ; i.e., .. math:: ef_i > | X( tf )_i - xf_i | maxabs ****** The argument *maxabs* is optional in the call to ``OdeGearControl`` . If it is present, it has the prototype *Vector* & *maxabs* and size *n* . The input value of its elements does not matter. On output, it contains an estimate for the maximum absolute value of :math:`X(t)`; i.e., .. math:: maxabs[i] \approx \max \left\{ | X( t )_i | \; : \; t \in [ti, tf] \right\} nstep ***** The argument *nstep* has the prototype *size_t* & *nstep* Its input value does not matter and its output value is the number of calls to :ref:`OdeGear-name` used by ``OdeGearControl`` . Error Criteria Discussion ************************* The relative error criteria *erel* and absolute error criteria *eabs* are enforced during each step of the integration of the ordinary differential equations. In addition, they are inversely scaled by the step size so that the total error bound is less than the sum of the error bounds. To be specific, if :math:`\tilde{X} (t)` is the approximate solution at time :math:`t`, *ta* is the initial step time, and *tb* is the final step time, .. math:: \left| \tilde{X} (tb)_j - X (tb)_j \right| \leq \frac{tf - ti}{tb - ta} \left[ eabs[j] + erel \; | \tilde{X} (tb)_j | \right] If :math:`X(tb)_j` is near zero for some :math:`tb \in [ti , tf]`, and one uses an absolute error criteria :math:`eabs[j]` of zero, the error criteria above will force ``OdeGearControl`` to use step sizes equal to :ref:`OdeGearControl@smin` for steps ending near :math:`tb`. In this case, the error relative to *maxabs* can be judged after ``OdeGearControl`` returns. If *ef* is to large relative to *maxabs* , ``OdeGearControl`` can be called again with a smaller value of *smin* . Scalar ****** The type *Scalar* must satisfy the conditions for a :ref:`NumericType-name` . The routine :ref:`CheckNumericType-name` will generate an error message if this is not the case. In addition, the following operations must be defined for *Scalar* objects *a* and *b* : .. list-table:: :widths: auto * - **Operation** - **Description** * - *a* <= *b* - returns true (false) if *a* is less than or equal (greater than) *b* . * - *a* == *b* - returns true (false) if *a* is equal to *b* . * - ``log`` ( *a* ) - returns a *Scalar* equal to the logarithm of *a* * - ``exp`` ( *a* ) - returns a *Scalar* equal to the exponential of *a* Vector ****** The type *Vector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type Scalar` . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Example ******* {xrst_toc_hidden example/utility/ode_gear_control.cpp } The file :ref:`ode_gear_control.cpp-name` contains an example and test a test of using this routine. Theory ****** Let :math:`e(s)` be the error as a function of the step size :math:`s` and suppose that there is a constant :math:`K` such that :math:`e(s) = K s^m`. Let :math:`a` be our error bound. Given the value of :math:`e(s)`, a step of size :math:`\lambda s` would be ok provided that .. math:: :nowrap: \begin{eqnarray} a & \geq & e( \lambda s ) (tf - ti) / ( \lambda s ) \\ a & \geq & K \lambda^m s^m (tf - ti) / ( \lambda s ) \\ a & \geq & \lambda^{m-1} s^{m-1} (tf - ti) e(s) / s^m \\ a & \geq & \lambda^{m-1} (tf - ti) e(s) / s \\ \lambda^{m-1} & \leq & \frac{a}{e(s)} \frac{s}{tf - ti} \end{eqnarray} Thus if the right hand side of the last inequality is greater than or equal to one, the step of size :math:`s` is ok. Source Code *********** The source code for this routine is in the file ``cppad/ode_gear_control.hpp`` . {xrst_end OdeGearControl} -------------------------------------------------------------------------- */ // link exp and log for float and double # include # include namespace CppAD { // Begin CppAD namespace template Vector OdeGearControl( Fun &F , size_t M , const Scalar &ti , const Scalar &tf , const Vector &xi , const Scalar &smin , const Scalar &smax , Scalar &sini , const Vector &eabs , const Scalar &erel , Vector &ef , Vector &maxabs, size_t &nstep ) { // check simple vector class specifications CheckSimpleVector(); // dimension of the state space size_t n = size_t(xi.size()); CPPAD_ASSERT_KNOWN( M >= 1, "Error in OdeGearControl: M is less than one" ); CPPAD_ASSERT_KNOWN( smin <= smax, "Error in OdeGearControl: smin is greater than smax" ); CPPAD_ASSERT_KNOWN( sini <= smax, "Error in OdeGearControl: sini is greater than smax" ); CPPAD_ASSERT_KNOWN( size_t(eabs.size()) == n, "Error in OdeGearControl: size of eabs is not equal to n" ); CPPAD_ASSERT_KNOWN( size_t(maxabs.size()) == n, "Error in OdeGearControl: size of maxabs is not equal to n" ); // some constants const Scalar zero(0); const Scalar one(1); const Scalar one_plus( Scalar(3) / Scalar(2) ); const Scalar two(2); const Scalar ten(10); // temporary indices size_t i, k; // temporary Scalars Scalar step, sprevious, lambda, axi, a, root, r; // vectors of Scalars Vector T (M + 1); Vector X( (M + 1) * n ); Vector e(n); Vector xf(n); // initial integer values size_t m = 1; nstep = 0; // initialize T T[0] = ti; // initialize X, ef, maxabs for(i = 0; i < n; i++) for(i = 0; i < n; i++) { X[i] = xi[i]; ef[i] = zero; X[i] = xi[i]; if( zero <= xi[i] ) maxabs[i] = xi[i]; else maxabs[i] = - xi[i]; } // initial step size step = smin; while( T[m-1] < tf ) { sprevious = step; // check maximum if( smax <= step ) step = smax; // check minimum if( m < M ) { if( step <= sini ) step = sini; } else if( step <= smin ) step = smin; // check if near the end if( tf <= T[m-1] + one_plus * step ) T[m] = tf; else T[m] = T[m-1] + step; // try using this step size nstep++; OdeGear(F, m, n, T, X, e); step = T[m] - T[m-1]; // compute value of lambda for this step lambda = Scalar(10) * sprevious / step; for(i = 0; i < n; i++) { axi = X[m * n + i]; if( axi <= zero ) axi = - axi; a = eabs[i] + erel * axi; if( e[i] > zero ) { if( m == 1 ) root = (a / e[i]) / ten; else { r = ( a / e[i] ) * step / (tf - ti); root = exp( log(r) / Scalar(m-1) ); } if( root <= lambda ) lambda = root; } } bool advance; if( m == M ) advance = one <= lambda || step <= one_plus * smin; else advance = one <= lambda || step <= one_plus * sini; if( advance ) { // accept the results of this time step CPPAD_ASSERT_UNKNOWN( m <= M ); if( m == M ) { // shift for next step for(k = 0; k < m; k++) { T[k] = T[k+1]; for(i = 0; i < n; i++) X[k*n + i] = X[(k+1)*n + i]; } } // update ef and maxabs for(i = 0; i < n; i++) { ef[i] = ef[i] + e[i]; axi = X[m * n + i]; if( axi <= zero ) axi = - axi; if( axi > maxabs[i] ) maxabs[i] = axi; } if( m != M ) m++; // all we need do in this case } // new step suggested by error criteria step = std::min(lambda , ten) * step / two; } for(i = 0; i < n; i++) xf[i] = X[(m-1) * n + i]; return xf; } } // End CppAD namespace # endif ================================================ FILE: include/cppad/utility/omp_alloc.hpp ================================================ # ifndef CPPAD_UTILITY_OMP_ALLOC_HPP # define CPPAD_UTILITY_OMP_ALLOC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # ifdef _OPENMP # include # endif namespace CppAD { // BEGIN_CPPAD_NAMESPACE class omp_alloc{ // ============================================================================ public: /* {xrst_begin omp_max_num_threads app} Set and Get Maximum Number of Threads for omp_alloc Allocator ############################################################# Deprecated 2011-08-31 ********************* Use the functions :ref:`thread_alloc::parallel_setup` and :ref:`thread_alloc:num_threads` instead. Syntax ****** | # ``include `` | ``omp_alloc::set_max_num_threads`` ( *number* ) | *number* = ``omp_alloc::get_max_num_threads`` () Purpose ******* By default there is only one thread and all execution is in sequential mode (not :ref:`parallel` ). number ****** The argument and return value *number* has prototype ``size_t`` *number* and must be greater than zero. set_max_num_threads ******************* Informs :ref:`omp_alloc-name` of the maximum number of OpenMP threads. get_max_num_threads ******************* Returns the valued used in the previous call to ``set_max_num_threads`` . If there was no such previous call, the value one is returned (and only thread number zero can use :ref:`omp_alloc-name` ). Restrictions ************ The function ``set_max_num_threads`` must be called before the program enters :ref:`parallel` execution mode. In addition, this function cannot be called while in parallel mode. {xrst_end omp_max_num_threads} */ /*! Inform omp_alloc of the maximum number of OpenMP threads and enable parallel execution mode by initializing all statics in this file. \param number [in] maximum number of OpenMP threads. */ static void set_max_num_threads(size_t number) { thread_alloc::parallel_setup( number, omp_alloc::in_parallel, omp_alloc::get_thread_num ); thread_alloc::hold_memory(number > 1); } /*! Get the current maximum number of OpenMP threads that omp_alloc can use. \return maximum number of OpenMP threads. */ static size_t get_max_num_threads(void) { return thread_alloc::num_threads(); } /* ----------------------------------------------------------------------- {xrst_begin omp_in_parallel app} Is The Current Execution in OpenMP Parallel Mode ################################################ Deprecated 2011-08-31 ********************* Use the function :ref:`thread_alloc::in_parallel` instead. Syntax ****** # ``include `` *flag* = ``omp_alloc::in_parallel`` () Purpose ******* Some of the :ref:`omp_alloc-name` allocation routines have different specifications for parallel (not sequential) execution mode. This routine enables you to determine if the current execution mode is sequential or parallel. flag **** The return value has prototype ``bool`` *flag* It is true if the current execution is in parallel mode (possibly multi-threaded) and false otherwise (sequential mode). {xrst_end omp_in_parallel} */ /// Are we in a parallel execution state; i.e., is it possible that /// other threads are currently executing. static bool in_parallel(void) { # ifdef _OPENMP return omp_in_parallel() != 0; # else return false; # endif } /* ----------------------------------------------------------------------- {xrst_begin omp_get_thread_num app} Get the Current OpenMP Thread Number #################################### Deprecated 2011-08-31 ********************* Use the function :ref:`thread_alloc::thread_num` instead. Syntax ****** # ``include `` *thread* = ``omp_alloc::get_thread_num`` () Purpose ******* Some of the :ref:`omp_alloc-name` allocation routines have a thread number. This routine enables you to determine the current thread. thread ****** The return value *thread* has prototype ``size_t`` *thread* and is the currently executing thread number. If ``_OPENMP`` is not defined, *thread* is zero. {xrst_end omp_get_thread_num} */ /// Get current OpenMP thread number (zero if _OpenMP not defined). static size_t get_thread_num(void) { # ifdef _OPENMP size_t thread = static_cast( omp_get_thread_num() ); return thread; # else return 0; # endif } /* ----------------------------------------------------------------------- {xrst_begin omp_get_memory app} Get At Least A Specified Amount of Memory ######################################### Deprecated 2011-08-31 ********************* Use the function :ref:`thread_alloc::get_memory` instead. Syntax ****** # ``include `` *v_ptr* = ``omp_alloc::get_memory`` ( *min_bytes* , *cap_bytes* ) Purpose ******* Use :ref:`omp_alloc-name` to obtain a minimum number of bytes of memory (for use by the :ref:`current thread` ). min_bytes ********* This argument has prototype ``size_t`` *min_bytes* It specifies the minimum number of bytes to allocate. cap_bytes ********* This argument has prototype ``size_t&`` *cap_bytes* It's input value does not matter. Upon return, it is the actual number of bytes (capacity) that have been allocated for use, *min_bytes* <= *cap_bytes* v_ptr ***** The return value *v_ptr* has prototype ``void`` * *v_ptr* It is the location where the *cap_bytes* of memory that have been allocated for use begins. Allocation Speed **************** This allocation should be faster if the following conditions hold: #. The memory allocated by a previous call to ``get_memory`` is currently available for use. #. The current *min_bytes* is between the previous *min_bytes* and previous *cap_bytes* . {xrst_end omp_get_memory} */ /*! Use omp_alloc to get a specified amount of memory. If the memory allocated by a previous call to get_memory is now available, and min_bytes is between its previous value and the previous cap_bytes, this memory allocation will have optimal speed. Otherwise, the memory allocation is more complicated and may have to wait for other threads to complete an allocation. \param min_bytes [in] The minimum number of bytes of memory to be obtained for use. \param cap_bytes [out] The actual number of bytes of memory obtained for use. \return pointer to the beginning of the memory allocated for use. */ static void* get_memory(size_t min_bytes, size_t& cap_bytes) { return thread_alloc::get_memory(min_bytes, cap_bytes); } /* ----------------------------------------------------------------------- {xrst_begin omp_return_memory app} Return Memory to omp_alloc ########################## Deprecated 2011-08-31 ********************* Use the function :ref:`thread_alloc::return_memory` instead. Syntax ****** # ``include `` ``omp_alloc::return_memory`` ( *v_ptr* ) Purpose ******* If :ref:`omp_max_num_threads-name` is one, the memory is returned to the system. Otherwise, the memory is retained by :ref:`omp_alloc-name` for quick future use by the thread that allocated to memory. v_ptr ***** This argument has prototype ``void`` * *v_ptr* . It must be a pointer to memory that is currently in use; i.e. obtained by a previous call to :ref:`omp_get_memory-name` and not yet returned. Thread ****** Either the :ref:`current thread` must be the same as during the corresponding call to :ref:`omp_get_memory-name` , or the current execution mode must be sequential (not :ref:`parallel` ). NDEBUG ****** If ``NDEBUG`` is defined, *v_ptr* is not checked (this is faster). Otherwise, a list of in use pointers is searched to make sure that *v_ptr* is in the list. {xrst_end omp_return_memory} */ /*! Return memory that was obtained by get_memory. If max_num_threads(0) == 1, the memory is returned to the system. Otherwise, it is retained by omp_alloc and available for use by get_memory for this thread. \param v_ptr [in] Value of the pointer returned by get_memory and still in use. After this call, this pointer will available (and not in use). \par We must either be in sequential (not parallel) execution mode, or the current thread must be the same as for the corresponding call to get_memory. */ static void return_memory(void* v_ptr) { thread_alloc::return_memory(v_ptr); } /* ----------------------------------------------------------------------- {xrst_begin omp_free_available app} Free Memory Currently Available for Quick Use by a Thread ######################################################### Deprecated 2011-08-31 ********************* Use the function :ref:`thread_alloc::free_available` instead. Syntax ****** # ``include `` ``omp_alloc::free_available`` ( *thread* ) Purpose ******* Free memory, currently available for quick use by a specific thread, for general future use. thread ****** This argument has prototype ``size_t`` *thread* Either :ref:`omp_get_thread_num-name` must be the same as *thread* , or the current execution mode must be sequential (not :ref:`parallel` ). {xrst_end omp_free_available} */ /*! Return all the memory being held as available for a thread to the system. \param thread [in] this thread that will no longer have any available memory after this call. This must either be the thread currently executing, or we must be in sequential (not parallel) execution mode. */ static void free_available(size_t thread) { thread_alloc::free_available(thread); } /* ----------------------------------------------------------------------- {xrst_begin omp_inuse app} Amount of Memory a Thread is Currently Using ############################################ Deprecated 2011-08-31 ********************* Syntax ****** # ``include `` *num_bytes* = ``omp_alloc::inuse`` ( *thread* ) Use the function :ref:`thread_alloc::inuse` instead. Purpose ******* Memory being managed by :ref:`omp_alloc-name` has two states, currently in use by the specified thread, and quickly available for future use by the specified thread. This function informs the program how much memory is in use. thread ****** This argument has prototype ``size_t`` *thread* Either :ref:`omp_get_thread_num-name` must be the same as *thread* , or the current execution mode must be sequential (not :ref:`parallel` ). num_bytes ********* The return value has prototype ``size_t`` *num_bytes* It is the number of bytes currently in use by the specified thread. {xrst_end omp_inuse} */ /*! Determine the amount of memory that is currently inuse. \param thread [in] Thread for which we are determining the amount of memory (must be < CPPAD_MAX_NUM_THREADS). During parallel execution, this must be the thread that is currently executing. \return The amount of memory in bytes. */ static size_t inuse(size_t thread) { return thread_alloc::inuse(thread); } /* ----------------------------------------------------------------------- {xrst_begin omp_available app} Amount of Memory Available for Quick Use by a Thread #################################################### Deprecated 2011-08-31 ********************* Use the function :ref:`thread_alloc::available` instead. Syntax ****** # ``include `` *num_bytes* = ``omp_alloc::available`` ( *thread* ) Purpose ******* Memory being managed by :ref:`omp_alloc-name` has two states, currently in use by the specified thread, and quickly available for future use by the specified thread. This function informs the program how much memory is available. thread ****** This argument has prototype ``size_t`` *thread* Either :ref:`omp_get_thread_num-name` must be the same as *thread* , or the current execution mode must be sequential (not :ref:`parallel` ). num_bytes ********* The return value has prototype ``size_t`` *num_bytes* It is the number of bytes currently available for use by the specified thread. {xrst_end omp_available} */ /*! Determine the amount of memory that is currently available for use. \copydetails inuse */ static size_t available(size_t thread) { return thread_alloc::available(thread); } /* ----------------------------------------------------------------------- {xrst_begin omp_create_array app} Allocate Memory and Create A Raw Array ###################################### Deprecated 2011-08-31 ********************* Use the function :ref:`thread_alloc::create_array` instead. Syntax ****** # ``include `` *array* = ``omp_alloc::create_array<`` *Type* >( *size_min* , *size_out* ) . Purpose ******* Create a new raw array using :ref:`omp_alloc-name` a fast memory allocator that works well in a multi-threading OpenMP environment. Type **** The type of the elements of the array. size_min ******** This argument has prototype ``size_t`` *size_min* This is the minimum number of elements that there can be in the resulting *array* . size_out ******** This argument has prototype ``size_t&`` *size_out* The input value of this argument does not matter. Upon return, it is the actual number of elements in *array* ( *size_min* <= *size_out* ). array ***** The return value *array* has prototype *Type* * *array* It is array with *size_out* elements. The default constructor for *Type* is used to initialize the elements of *array* . Note that :ref:`omp_delete_array-name` should be used to destroy the array when it is no longer needed. Delta ***** The amount of memory :ref:`omp_inuse-name` by the current thread, will increase *delta* where ``sizeof`` ( *Type* ) * ( *size_out* + 1) > *delta* >= ``sizeof`` ( *Type* ) * *size_out* The :ref:`omp_available-name` memory will decrease by *delta* , (and the allocation will be faster) if a previous allocation with *size_min* between its current value and *size_out* is available. {xrst_end omp_create_array} */ /*! Use omp_alloc to Create a Raw Array. \tparam Type The type of the elements of the array. \param size_min [in] The minimum number of elements in the array. \param size_out [out] The actual number of elements in the array. \return pointer to the first element of the array. The default constructor is used to initialize all the elements of the array. \par The extra_ field, in the omp_alloc node before the return value, is set to size_out. */ template static Type* create_array(size_t size_min, size_t& size_out) { return thread_alloc::create_array(size_min, size_out); } /* ----------------------------------------------------------------------- {xrst_begin omp_delete_array app} Return A Raw Array to The Available Memory for a Thread ####################################################### Deprecated 2011-08-31 ********************* Use the function :ref:`thread_alloc::delete_array` instead. Syntax ****** # ``include `` ``omp_alloc::delete_array`` ( *array* ) . Purpose ******* Returns memory corresponding to a raw array (create by :ref:`omp_create_array-name` ) to the :ref:`omp_available-name` memory pool for the current thread. Type **** The type of the elements of the array. array ***** The argument *array* has prototype *Type* * *array* It is a value returned by :ref:`omp_create_array-name` and not yet deleted. The *Type* destructor is called for each element in the array. Thread ****** The :ref:`current thread` must be the same as when :ref:`omp_create_array-name` returned the value *array* . There is an exception to this rule: when the current execution mode is sequential (not :ref:`parallel` ) the current thread number does not matter. Delta ***** The amount of memory :ref:`omp_inuse-name` will decrease by *delta* , and the :ref:`omp_available-name` memory will increase by *delta* , where :ref:`omp_create_array@Delta` is the same as for the corresponding call to ``create_array`` . {xrst_end omp_delete_array} */ /*! Return Memory Used for a Raw Array to the Available Pool. \tparam Type The type of the elements of the array. \param array [in] A value returned by create_array that has not yet been deleted. The Type destructor is used to destroy each of the elements of the array. \par During parallel execution, the current thread must be the same as during the corresponding call to create_array. */ template static void delete_array(Type* array) { thread_alloc::delete_array(array); } }; /* -------------------------------------------------------------------------- {xrst_begin omp_efficient app} Check If A Memory Allocation is Efficient for Another Use ######################################################### Removed ******* This function has been removed because speed tests seem to indicate it is just as fast, or faster, to free and then reallocate the memory. Syntax ****** # ``include `` *flag* = ``omp_alloc::efficient`` ( *v_ptr* , *num_bytes* ) Purpose ******* Check if memory that is currently in use is an efficient allocation for a specified number of bytes. v_ptr ***** This argument has prototype ``const void`` * *v_ptr* . It must be a pointer to memory that is currently in use; i.e. obtained by a previous call to :ref:`omp_get_memory-name` and not yet returned. num_bytes ********* This argument has prototype ``size_t`` *num_bytes* It specifies the number of bytes of the memory allocated by *v_ptr* that we want to use. flag **** The return value has prototype ``bool`` *flag* It is true, a call to ``get_memory`` with :ref:`omp_get_memory@min_bytes` equal to *num_bytes* would result in a value for :ref:`omp_get_memory@cap_bytes` that is the same as when ``v_ptr`` was returned by ``get_memory`` ; i.e., *v_ptr* is an efficient memory block for *num_bytes* bytes of information. Thread ****** Either the :ref:`current thread` must be the same as during the corresponding call to :ref:`omp_get_memory-name` , or the current execution mode must be sequential (not :ref:`parallel` ). NDEBUG ****** If ``NDEBUG`` is defined, *v_ptr* is not checked (this is faster). Otherwise, a list of in use pointers is searched to make sure that *v_ptr* is in the list. {xrst_end omp_efficient} --------------------------------------------------------------------------- {xrst_begin old_max_num_threads app} Set Maximum Number of Threads for omp_alloc Allocator ##################################################### Removed ******* This function has been removed from the CppAD API. Use the function :ref:`thread_alloc::parallel_setup` in its place. Syntax ****** # ``include `` ``omp_alloc::max_num_threads`` ( *number* ) Purpose ******* By default there is only one thread and all execution is in sequential mode (not :ref:`parallel` ). number ****** The argument *number* has prototype ``size_t`` *number* It must be greater than zero and specifies the maximum number of OpenMP threads that will be active at one time. Restrictions ************ This function must be called before the program enters :ref:`parallel` execution mode. {xrst_end old_max_num_threads} ------------------------------------------------------------------------------- */ } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/utility/poly.hpp ================================================ # ifndef CPPAD_UTILITY_POLY_HPP # define CPPAD_UTILITY_POLY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin Poly} Evaluate a Polynomial or its Derivative ####################################### Syntax ****** | # ``include `` | *p* = ``Poly`` ( *k* , *a* , *z* ) Description *********** Computes the *k*-th derivative of the polynomial .. math:: P(z) = a_0 + a_1 z^1 + \cdots + a_d z^d If *k* is equal to zero, the return value is :math:`P(z)`. Include ******* The file ``cppad/utility/poly.hpp`` is included by ``cppad/cppad.hpp`` but it can also be included separately with out the rest of the ``CppAD`` routines. Including this file defines ``Poly`` within the ``CppAD`` namespace. k * The argument *k* has prototype ``size_t`` *k* It specifies the order of the derivative to calculate. a * The argument *a* has prototype ``const`` *Vector* & *a* (see :ref:`Poly@Vector` below). It specifies the vector corresponding to the polynomial :math:`P(z)`. z * The argument *z* has prototype ``const`` *Type* & *z* (see *Type* below). It specifies the point at which to evaluate the polynomial p * The result *p* has prototype *Type* *p* (see :ref:`Poly@Type` below) and it is equal to the *k*-th derivative of :math:`P(z)`; i.e., .. math:: p = \frac{k !}{0 !} a_k + \frac{(k+1) !}{1 !} a_{k+1} z^1 + \ldots + \frac{d !}{(d - k) !} a_d z^{d - k} If :math:`k > d`, *p* = *Type* (0) . Type **** The type *Type* is determined by the argument *z* . It is assumed that multiplication and addition of *Type* objects are commutative. Operations ========== The following operations must be supported where *x* and *y* are objects of type *Type* and *i* is an ``int`` : .. list-table:: :widths: auto * - *x* = *i* - assignment * - *x* = *y* - assignment * - *x* \\* = *y* - multiplication compound assignment * - *x* += *y* - addition compound assignment Vector ****** The type *Vector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` *Type* . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Operation Sequence ****************** The *Type* operation sequence used to calculate *p* is :ref:`glossary@Operation@Independent` of *z* and the elements of *a* (it does depend on the size of the vector *a* ). {xrst_toc_hidden example/utility/poly.cpp xrst/poly_hpp.xrst } Example ******* The file :ref:`poly.cpp-name` contains an example and test of this routine. Source ****** The file :ref:`poly.hpp-name` contains the current source code that implements these specifications. {xrst_end Poly} ------------------------------------------------------------------------------ */ // BEGIN C++ # include // used to defined size_t # include namespace CppAD { // BEGIN CppAD namespace template Type Poly(size_t k, const Vector &a, const Type &z) { size_t i; size_t d = a.size() - 1; Type tmp; // check Vector is Simple Vector class with Type elements CheckSimpleVector(); // case where derivative order greater than degree of polynomial if( k > d ) { tmp = 0; return tmp; } // case where we are evaluating a derivative if( k > 0 ) { // initialize factor as (k-1) ! size_t factor = 1; for(i = 2; i < k; i++) factor *= i; // set b to coefficient vector corresponding to derivative Vector b(d - k + 1); for(i = k; i <= d; i++) { factor *= i; tmp = double( factor ); b[i - k] = a[i] * tmp; factor /= (i - k + 1); } // value of derivative polynomial return Poly(0, b, z); } // case where we are evaluating the original polynomial Type sum = a[d]; i = d; while(i > 0) { sum *= z; sum += a[--i]; } return sum; } } // END CppAD namespace // END C++ # endif ================================================ FILE: include/cppad/utility/pow_int.hpp ================================================ # ifndef CPPAD_UTILITY_POW_INT_HPP # define CPPAD_UTILITY_POW_INT_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* ------------------------------------------------------------------------------- {xrst_begin pow_int} The Integer Power Function ########################## Syntax ****** | # ``include `` | *z* = ``pow`` ( *x* , *y* ) See Also ******** :ref:`pow-name` Purpose ******* Determines the value of the power function .. math:: {\rm pow} (x, y) = x^y for integer exponents *n* using multiplication and possibly division to compute the value. The other CppAD :ref:`pow-name` function may use logarithms and exponentiation to compute derivatives of the same value (which will not work if *x* is less than or equal zero). Include ******* The file ``cppad/utility/pow_int.hpp`` is included by ``cppad/cppad.hpp`` but it can also be included separately with out the rest of the ``CppAD`` routines. Including this file defines this version of the ``pow`` within the ``CppAD`` namespace. x * The argument *x* has prototype ``const`` *Type* & *x* y * The argument *y* has prototype ``const int&`` *y* z * The result *z* has prototype *Type* *z* Type **** The type *Type* must support the following operations where *a* and *b* are *Type* objects and *i* is an ``int`` : .. csv-table:: :widths: auto **Operation**,**Description**,**Result Type** *Type* *a* ( *i* ),construction of a *Type* object from an ``int``,*Type* *a* * *b*,binary multiplication of *Type* objects,*Type* *a* / *b*,binary division of *Type* objects,*Type* Operation Sequence ****************** The *Type* operation sequence used to calculate *z* is :ref:`glossary@Operation@Independent` of *x* . Example ******* {xrst_toc_hidden example/utility/pow_int.cpp } The file :ref:`pow_int.cpp-name` is an example and test of this function. {xrst_end pow_int} ------------------------------------------------------------------------------- */ namespace CppAD { template inline Type pow (const Type& x, const int& n) { Type p(1); int n2 = n / 2; if( n == 0 ) return p; if( n < 0 ) return p / pow(x, -n); if( n == 1 ) return x; // p = (x^2)^(n/2) p = pow( x * x , n2 ); // n is even case if( n % 2 == 0 ) return p; // n is odd case return p * x; } } # endif ================================================ FILE: include/cppad/utility/romberg_mul.hpp ================================================ # ifndef CPPAD_UTILITY_ROMBERG_MUL_HPP # define CPPAD_UTILITY_ROMBERG_MUL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin RombergMul} {xrst_spell test test } Multi-dimensional Romberg Integration ##################################### Syntax ****** | # ``include `` | ``RombergMul`` < *Fun* , *SizeVector* , *FloatVector* , *m* > *R* | *r* = *R* ( *F* , *a* , *b* , *n* , *p* , *e* ) Description *********** Returns the Romberg integration estimate :math:`r` for the multi-dimensional integral .. math:: r = \int_{a[0]}^{b[0]} \cdots \int_{a[m-1]}^{b[m-1]} \; F(x) \; {\bf d} x_0 \cdots {\bf d} x_{m-1} \; + \; \sum_{i=0}^{m-1} O \left[ ( b[i] - a[i] ) / 2^{n[i]-1} \right]^{2(p[i]+1)} Include ******* The file ``cppad/utility/romberg_mul.hpp`` is included by ``cppad/cppad.hpp`` but it can also be included separately with out the rest of the ``CppAD`` routines. m * The template parameter *m* must be convertible to a ``size_t`` object with a value that can be determined at compile time; for example ``2`` . It determines the dimension of the domain space for the integration. r * The return value *r* has prototype *Float* *r* It is the estimate computed by ``RombergMul`` for the integral above (see description of :ref:`RombergMul@Float` below). F * The object *F* has the prototype *Fun* & *F* It must support the operation *F* ( *x* ) The argument *x* to *F* has prototype ``const`` *Float* & *x* The return value of *F* is a *Float* object a * The argument *a* has prototype ``const`` *FloatVector* & *a* It specifies the lower limit for the integration (see description of :ref:`RombergMul@FloatVector` below). b * The argument *b* has prototype ``const`` *FloatVector* & *b* It specifies the upper limit for the integration. n * The argument *n* has prototype ``const`` *SizeVector* & *n* A total number of :math:`2^{n[i]-1} + 1` evaluations of *F* ( *x* ) are used to estimate the integral with respect to :math:`{\bf d} x_i`. p * The argument *p* has prototype ``const`` *SizeVector* & *p* For :math:`i = 0 , \ldots , m-1`, :math:`n[i]` determines the accuracy order in the approximation for the integral that is returned by ``RombergMul`` . The values in *p* must be less than or equal *n* ; i.e., *p* [ *i* ] <= *n* [ *i* ] . e * The argument *e* has prototype *Float* & *e* The input value of *e* does not matter and its output value is an approximation for the absolute error in the integral estimate. Float ***** The type *Float* is defined as the type of the elements of :ref:`RombergMul@FloatVector` . The type *Float* must satisfy the conditions for a :ref:`NumericType-name` . The routine :ref:`CheckNumericType-name` will generate an error message if this is not the case. In addition, if *x* and *y* are *Float* objects, *x* < *y* returns the ``bool`` value true if *x* is less than *y* and false otherwise. FloatVector *********** The type *FloatVector* must be a :ref:`SimpleVector-name` class. The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. {xrst_toc_hidden example/utility/romberg_mul.cpp } Example ******* The file :ref:`romberg_mul.cpp-name` contains an example and test a test of using this routine. Source Code *********** The source code for this routine is in the file ``cppad/romberg_mul.hpp`` . {xrst_end RombergMul} */ # include # include # include namespace CppAD { // BEGIN CppAD namespace template class SliceLast { typedef typename FloatVector::value_type Float; private: Fun *F; size_t last; FloatVector x; public: SliceLast( Fun *F_, size_t last_, const FloatVector &x_ ) : F(F_) , last(last_), x(last + 1) { size_t i; for(i = 0; i < last; i++) x[i] = x_[i]; } double operator()(const Float &xlast) { x[last] = xlast; return (*F)(x); } }; template class IntegrateLast { private: Fun *F; const size_t last; const FloatVector a; const FloatVector b; const SizeVector n; const SizeVector p; Float esum; size_t ecount; public: IntegrateLast( Fun *F_ , size_t last_ , const FloatVector &a_ , const FloatVector &b_ , const SizeVector &n_ , const SizeVector &p_ ) : F(F_) , last(last_), a(a_) , b(b_) , n(n_) , p(p_) { } Float operator()(const FloatVector &x) { Float r, e; SliceLast S(F, last, x); r = CppAD::RombergOne( S, a[last], b[last], n[last], p[last], e ); esum = esum + e; ecount++; return r; } void ClearEsum(void) { esum = 0.; } Float GetEsum(void) { return esum; } void ClearEcount(void) { ecount = 0; } size_t GetEcount(void) { return ecount; } }; template class RombergMul { typedef typename FloatVector::value_type Float; public: RombergMul(void) { } Float operator() ( Fun &F , const FloatVector &a , const FloatVector &b , const SizeVector &n , const SizeVector &p , Float &e ) { Float r; typedef IntegrateLast< Fun , SizeVector , FloatVector , Float > IntegrateOne; IntegrateOne Fm1(&F, m-1, a, b, n, p); RombergMul< IntegrateOne, SizeVector , FloatVector , m-1 > RombergMulM1; Fm1.ClearEsum(); Fm1.ClearEcount(); r = RombergMulM1(Fm1, a, b, n, p, e); Float prod = 1; for(size_t i = 0; i < m-1; i++) prod *= (b[i] - a[i]); # ifndef NDEBUG size_t pow2 = 1; for(size_t i = 0; i < m-1; i++) for(size_t j = 0; j < (n[i] - 1); j++) pow2 *= 2; assert( Fm1.GetEcount() == (pow2+1) ); # endif e = e + Fm1.GetEsum() * prod / Float( double(Fm1.GetEcount()) ); return r; } }; template class RombergMul { typedef typename FloatVector::value_type Float; public: Float operator() ( Fun &F , const FloatVector &a , const FloatVector &b , const SizeVector &n , const SizeVector &p , Float &e ) { Float r; typedef IntegrateLast< Fun , SizeVector , FloatVector , Float > IntegrateOne; // check simple vector class specifications CheckSimpleVector(); // check numeric type specifications CheckNumericType(); IntegrateOne F0(&F, 0, a, b, n, p); F0.ClearEsum(); F0.ClearEcount(); r = F0(a); assert( F0.GetEcount() == 1 ); e = F0.GetEsum(); return r; } }; } // END CppAD namespace # endif ================================================ FILE: include/cppad/utility/romberg_one.hpp ================================================ # ifndef CPPAD_UTILITY_ROMBERG_ONE_HPP # define CPPAD_UTILITY_ROMBERG_ONE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin RombergOne} {xrst_spell test test } One DimensionalRomberg Integration ################################## Syntax ****** | # ``include `` | *r* = ``RombergOne`` ( *F* , *a* , *b* , *n* , *e* ) Description *********** Returns the Romberg integration estimate :math:`r` for a one dimensional integral .. math:: r = \int_a^b F(x) {\bf d} x + O \left[ (b - a) / 2^{n-1} \right]^{2(p+1)} Include ******* The file ``cppad/utility/romberg_one.hpp`` is included by ``cppad/cppad.hpp`` but it can also be included separately with out the rest of the ``CppAD`` routines. r * The return value *r* has prototype *Float* *r* It is the estimate computed by ``RombergOne`` for the integral above. F * The object *F* can be of any type, but it must support the operation *F* ( *x* ) The argument *x* to *F* has prototype ``const`` *Float* & *x* The return value of *F* is a *Float* object (see description of :ref:`RombergOne@Float` below). a * The argument *a* has prototype ``const`` *Float* & *a* It specifies the lower limit for the integration. b * The argument *b* has prototype ``const`` *Float* & *b* It specifies the upper limit for the integration. n * The argument *n* has prototype ``size_t`` *n* A total number of :math:`2^{n-1} + 1` evaluations of *F* ( *x* ) are used to estimate the integral. p * The argument *p* has prototype ``size_t`` *p* It must be less than or equal :math:`n` and determines the accuracy order in the approximation for the integral that is returned by ``RombergOne`` . To be specific .. math:: r = \int_a^b F(x) {\bf d} x + O \left[ (b - a) / 2^{n-1} \right]^{2(p+1)} e * The argument *e* has prototype *Float* & *e* The input value of *e* does not matter and its output value is an approximation for the error in the integral estimates; i.e., .. math:: e \approx \left| r - \int_a^b F(x) {\bf d} x \right| Float ***** The type *Float* must satisfy the conditions for a :ref:`NumericType-name` . The routine :ref:`CheckNumericType-name` will generate an error message if this is not the case. In addition, if *x* and *y* are *Float* objects, *x* < *y* returns the ``bool`` value true if *x* is less than *y* and false otherwise. {xrst_toc_hidden example/utility/romberg_one.cpp } Example ******* The file :ref:`romberg_one.cpp-name` contains an example and test a test of using this routine. Source Code *********** The source code for this routine is in the file ``cppad/romberg_one.hpp`` . {xrst_end RombergOne} */ # include # include # include namespace CppAD { // BEGIN CppAD namespace template Float RombergOne( Fun &F , const Float &a , const Float &b , size_t n , size_t p , Float &e ) { size_t ipow2 = 1; size_t k, i; Float pow2, sum, x; Float zero = Float(0); Float two = Float(2); // check specifications for a NumericType CheckNumericType(); CPPAD_ASSERT_KNOWN( n >= 2, "RombergOne: n must be greater than or equal 2" ); CppAD::vector r(n); // set r[i] = trapazoidal rule with 2^i intervals in [a, b] r[0] = ( F(a) + F(b) ) * (b - a) / two; for(i = 1; i < n; i++) { ipow2 *= 2; // there must be a conversion from int to any numeric type pow2 = Float(int(ipow2)); sum = zero; for(k = 1; k < ipow2; k += 2) { // start = a + (b-a)/pow2, increment = 2*(b-a)/pow2 x = ( (pow2 - Float(double(k))) * a + double(k) * b ) / pow2; sum = sum + F(x); } // combine function evaluations in sum with those in T[i-1] r[i] = r[i-1] / two + sum * (b - a) / pow2; } // now compute the higher order estimates size_t ipow4 = 1; // order of accuract for previous estimate Float pow4, pow4minus; for(i = 0; i < p; i++) { // compute estimate accurate to O[ step^(2*(i+1)) ] // put results in r[n-1], r[n-2], ... , r[n-i+1] ipow4 *= 4; pow4 = Float(int(ipow4)); pow4minus = Float(ipow4-1); for(k = n-1; k > i; k--) r[k] = ( pow4 * r[k] - r[k-1] ) / pow4minus; } // error estimate for r[n] e = r[n-1] - r[n-2]; if( e < zero ) e = - e; return r[n-1]; } } // END CppAD namespace # endif ================================================ FILE: include/cppad/utility/rosen_34.hpp ================================================ # ifndef CPPAD_UTILITY_ROSEN_34_HPP # define CPPAD_UTILITY_ROSEN_34_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin Rosen34} {xrst_spell dep rosenbrock test test tf xf } A 3rd and 4th Order Rosenbrock ODE Solver ######################################### Syntax ****** | # ``include `` | *xf* = ``Rosen34`` ( *F* , *M* , *ti* , *tf* , *xi* ) | *xf* = ``Rosen34`` ( *F* , *M* , *ti* , *tf* , *xi* , *e* ) Description *********** This is an embedded 3rd and 4th order Rosenbrock ODE solver (see Section 16.6 of :ref:`Bib@Numerical Recipes` for a description of Rosenbrock ODE solvers). In particular, we use the formulas taken from page 100 of :ref:`Bib@Shampine, L.F.` (except that the fraction 98/108 has been correction to be 97/108). We use :math:`n` for the size of the vector *xi* . Let :math:`\B{R}` denote the real numbers and let :math:`F : \B{R} \times \B{R}^n \rightarrow \B{R}^n` be a smooth function. The return value *xf* contains a 5th order approximation for the value :math:`X(tf)` where :math:`X : [ti , tf] \rightarrow \B{R}^n` is defined by the following initial value problem: .. math:: :nowrap: \begin{eqnarray} X(ti) & = & xi \\ X'(t) & = & F[t , X(t)] \end{eqnarray} If your set of ordinary differential equations are not stiff an explicit method may be better (perhaps :ref:`Runge45-name` .) Include ******* The file ``cppad/utility/rosen_34.hpp`` is included by ``cppad/cppad.hpp`` but it can also be included separately with out the rest of the ``CppAD`` routines. xf ** The return value *xf* has the prototype *Vector* *xf* and the size of *xf* is equal to *n* (see description of :ref:`Rosen34@Vector` below). .. math:: X(tf) = xf + O( h^5 ) where :math:`h = (tf - ti) / M` is the step size. If *xf* contains not a number :ref:`nan-name` , see the discussion of :ref:`f` . Fun *** The class *Fun* and the object *F* satisfy the prototype *Fun* & *F* This must support the following set of calls | |tab| *F* . ``Ode`` ( *t* , *x* , *f* ) | |tab| *F* . ``Ode_ind`` ( *t* , *x* , *f_t* ) | |tab| *F* . ``Ode_dep`` ( *t* , *x* , *f_x* ) t = In all three cases, the argument *t* has prototype ``const`` *Scalar* & *t* (see description of :ref:`Rosen34@Scalar` below). x = In all three cases, the argument *x* has prototype ``const`` *Vector* & *x* and has size *n* (see description of :ref:`Rosen34@Vector` below). f = The argument *f* to *F* . ``Ode`` has prototype *Vector* & *f* On input and output, *f* is a vector of size *n* and the input values of the elements of *f* do not matter. On output, *f* is set equal to :math:`F(t, x)` (see *F* ( *t* , *x* ) in :ref:`Rosen34@Description` ). f_t === The argument *f_t* to *F* . ``Ode_ind`` has prototype *Vector* & *f_t* On input and output, *f_t* is a vector of size *n* and the input values of the elements of *f_t* do not matter. On output, the *i*-th element of *f_t* is set equal to :math:`\partial_t F_i (t, x)` (see *F* ( *t* , *x* ) in :ref:`Rosen34@Description` ). f_x === The argument *f_x* to *F* . ``Ode_dep`` has prototype *Vector* & *f_x* On input and output, *f_x* is a vector of size *n* * *n* and the input values of the elements of *f_x* do not matter. On output, the [ *i* * *n* + *j* ] element of *f_x* is set equal to :math:`\partial_{x(j)} F_i (t, x)` (see *F* ( *t* , *x* ) in :ref:`Rosen34@Description` ). Nan === If any of the elements of *f* , *f_t* , or *f_x* have the value not a number ``nan`` , the routine ``Rosen34`` returns with all the elements of *xf* and *e* equal to ``nan`` . Warning ======= The arguments *f* , *f_t* , and *f_x* must have a call by reference in their prototypes; i.e., do not forget the ``&`` in the prototype for *f* , *f_t* and *f_x* . Optimization ============ Every call of the form *F* . ``Ode_ind`` ( *t* , *x* , *f_t* ) is directly followed by a call of the form *F* . ``Ode_dep`` ( *t* , *x* , *f_x* ) where the arguments *t* and *x* have not changed between calls. In many cases it is faster to compute the values of *f_t* and *f_x* together and then pass them back one at a time. M * The argument *M* has prototype ``size_t`` *M* It specifies the number of steps to use when solving the differential equation. This must be greater than or equal one. The step size is given by :math:`h = (tf - ti) / M`, thus the larger *M* , the more accurate the return value *xf* is as an approximation for :math:`X(tf)`. ti ** The argument *ti* has prototype ``const`` *Scalar* & *ti* (see description of :ref:`Rosen34@Scalar` below). It specifies the initial time for *t* in the differential equation; i.e., the time corresponding to the value *xi* . tf ** The argument *tf* has prototype ``const`` *Scalar* & *tf* It specifies the final time for *t* in the differential equation; i.e., the time corresponding to the value *xf* . xi ** The argument *xi* has the prototype ``const`` *Vector* & *xi* and the size of *xi* is equal to *n* . It specifies the value of :math:`X(ti)` e * The argument *e* is optional and has the prototype *Vector* & *e* If *e* is present, the size of *e* must be equal to *n* . The input value of the elements of *e* does not matter. On output it contains an element by element estimated bound for the absolute value of the error in *xf* .. math:: e = O( h^4 ) where :math:`h = (tf - ti) / M` is the step size. Scalar ****** The type *Scalar* must satisfy the conditions for a :ref:`NumericType-name` . The routine :ref:`CheckNumericType-name` will generate an error message if this is not the case. In addition, the following operations must be defined for *Scalar* objects *a* and *b* : .. list-table:: :widths: auto * - **Operation** - **Description** * - *a* < *b* - less than operator (returns a ``bool`` object) Vector ****** The type *Vector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type Scalar` . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Parallel Mode ************* For each set of types :ref:`Rosen34@Scalar` , :ref:`Rosen34@Vector` , and :ref:`Rosen34@Fun` , the first call to ``Rosen34`` must not be :ref:`parallel` execution mode. Example ******* {xrst_toc_hidden example/utility/rosen_34.cpp } The file :ref:`rosen_34.cpp-name` contains an example and test a test of using this routine. Source Code *********** The source code for this routine is in the file ``cppad/rosen_34.hpp`` . {xrst_end Rosen34} -------------------------------------------------------------------------- */ # include # include # include # include # include # include # include // needed before one can use CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL # include namespace CppAD { // BEGIN CppAD namespace template Vector Rosen34( Fun &F , size_t M , const Scalar &ti , const Scalar &tf , const Vector &xi ) { Vector e( xi.size() ); return Rosen34(F, M, ti, tf, xi, e); } template Vector Rosen34( Fun &F , size_t M , const Scalar &ti , const Scalar &tf , const Vector &xi , Vector &e ) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; // check numeric type specifications CheckNumericType(); // check simple vector class specifications CheckSimpleVector(); // Parameters for Shampine's Rosenbrock method // are static to avoid recalculation on each call and // do not use Vector to avoid possible memory leak static Scalar a[3] = { Scalar(0), Scalar(1), Scalar(3) / Scalar(5) }; static Scalar b[2 * 2] = { Scalar(1), Scalar(0), Scalar(24) / Scalar(25), Scalar(3) / Scalar(25) }; static Scalar ct[4] = { Scalar(1) / Scalar(2), - Scalar(3) / Scalar(2), Scalar(121) / Scalar(50), Scalar(29) / Scalar(250) }; static Scalar cg[3 * 3] = { - Scalar(4), Scalar(0), Scalar(0), Scalar(186) / Scalar(25), Scalar(6) / Scalar(5), Scalar(0), - Scalar(56) / Scalar(125), - Scalar(27) / Scalar(125), - Scalar(1) / Scalar(5) }; static Scalar d3[3] = { Scalar(97) / Scalar(108), Scalar(11) / Scalar(72), Scalar(25) / Scalar(216) }; static Scalar d4[4] = { Scalar(19) / Scalar(18), Scalar(1) / Scalar(4), Scalar(25) / Scalar(216), Scalar(125) / Scalar(216) }; CPPAD_ASSERT_KNOWN( M >= 1, "Error in Rosen34: the number of steps is less than one" ); CPPAD_ASSERT_KNOWN( e.size() == xi.size(), "Error in Rosen34: size of e not equal to size of xi" ); size_t i, j, k, l, m; // indices size_t n = xi.size(); // number of components in X(t) Scalar ns = Scalar(double(M)); // number of steps as Scalar object Scalar h = (tf - ti) / ns; // step size Scalar zero = Scalar(0); // some constants Scalar one = Scalar(1); Scalar two = Scalar(2); // permutation vectors needed for LU factorization routine CppAD::vector ip(n), jp(n); // vectors used to store values returned by F Vector E(n * n), Eg(n), f_t(n); Vector g(n * 3), x3(n), x4(n), xf(n), ftmp(n), xtmp(n), nan_vec(n); // initialize e = 0, nan_vec = nan for(i = 0; i < n; i++) { e[i] = zero; nan_vec[i] = nan(zero); } xf = xi; // initialize solution for(m = 0; m < M; m++) { // time at beginning of this interval Scalar t = ti * (Scalar(int(M - m)) / ns) + tf * (Scalar(int(m)) / ns); // value of x at beginning of this interval x3 = x4 = xf; // evaluate partial derivatives at beginning of this interval F.Ode_ind(t, xf, f_t); F.Ode_dep(t, xf, E); // E = f_x if( hasnan(f_t) || hasnan(E) ) { e = nan_vec; return nan_vec; } // E = I - f_x * h / 2 for(i = 0; i < n; i++) { for(j = 0; j < n; j++) E[i * n + j] = - E[i * n + j] * h / two; E[i * n + i] += one; } // LU factor the matrix E # ifndef NDEBUG int sign = LuFactor(ip, jp, E); # else LuFactor(ip, jp, E); # endif CPPAD_ASSERT_KNOWN( sign != 0, "Error in Rosen34: I - f_x * h / 2 not invertible" ); // loop over integration steps for(k = 0; k < 3; k++) { // set location for next function evaluation xtmp = xf; for(l = 0; l < k; l++) { // loop over previous function evaluations Scalar bkl = b[(k-1)*2 + l]; for(i = 0; i < n; i++) { // loop over elements of x xtmp[i] += bkl * g[i*3 + l] * h; } } // ftmp = F(t + a[k] * h, xtmp) F.Ode(t + a[k] * h, xtmp, ftmp); if( hasnan(ftmp) ) { e = nan_vec; return nan_vec; } // Form Eg for this integration step for(i = 0; i < n; i++) Eg[i] = ftmp[i] + ct[k] * f_t[i] * h; for(l = 0; l < k; l++) { for(i = 0; i < n; i++) Eg[i] += cg[(k-1)*3 + l] * g[i*3 + l]; } // Solve the equation E * g = Eg LuInvert(ip, jp, E, Eg); // save solution and advance x3, x4 for(i = 0; i < n; i++) { g[i*3 + k] = Eg[i]; x3[i] += h * d3[k] * Eg[i]; x4[i] += h * d4[k] * Eg[i]; } } // Form Eg for last update to x4 only for(i = 0; i < n; i++) Eg[i] = ftmp[i] + ct[3] * f_t[i] * h; for(l = 0; l < 3; l++) { for(i = 0; i < n; i++) Eg[i] += cg[2*3 + l] * g[i*3 + l]; } // Solve the equation E * g = Eg LuInvert(ip, jp, E, Eg); // advance x4 and accumulate error bound for(i = 0; i < n; i++) { x4[i] += h * d4[3] * Eg[i]; // cant use abs because cppad.hpp may not be included Scalar diff = x4[i] - x3[i]; if( diff < zero ) e[i] -= diff; else e[i] += diff; } // advance xf for this step using x4 xf = x4; } return xf; } } // End CppAD namespace # endif ================================================ FILE: include/cppad/utility/runge_45.hpp ================================================ # ifndef CPPAD_UTILITY_RUNGE_45_HPP # define CPPAD_UTILITY_RUNGE_45_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin Runge45} {xrst_spell karp kutta tf xf } An Embedded 4th and 5th Order Runge-Kutta ODE Solver #################################################### Syntax ****** | # ``include `` | *xf* = ``Runge45`` ( *F* , *M* , *ti* , *tf* , *xi* ) | *xf* = ``Runge45`` ( *F* , *M* , *ti* , *tf* , *xi* , *e* ) Purpose ******* This is an implementation of the Cash-Karp embedded 4th and 5th order Runge-Kutta ODE solver described in Section 16.2 of :ref:`Bib@Numerical Recipes` . We use :math:`n` for the size of the vector *xi* . Let :math:`\B{R}` denote the real numbers and let :math:`F : \B{R} \times \B{R}^n \rightarrow \B{R}^n` be a smooth function. The return value *xf* contains a 5th order approximation for the value :math:`X(tf)` where :math:`X : [ti , tf] \rightarrow \B{R}^n` is defined by the following initial value problem: .. math:: :nowrap: \begin{eqnarray} X(ti) & = & xi \\ X'(t) & = & F[t , X(t)] \end{eqnarray} If your set of ordinary differential equations are stiff, an implicit method may be better (perhaps :ref:`Rosen34-name` .) Operation Sequence ****************** The :ref:`operation sequence` for *Runge* does not depend on any of its *Scalar* input values provided that the operation sequence for *F* . ``Ode`` ( *t* , *x* , *f* ) does not on any of its *Scalar* inputs (see below). Include ******* The file ``cppad/utility/runge_45.hpp`` is included by ``cppad/cppad.hpp`` but it can also be included separately with out the rest of the ``CppAD`` routines. xf ** The return value *xf* has the prototype *Vector* *xf* and the size of *xf* is equal to *n* (see description of :ref:`Runge45@Vector` below). .. math:: X(tf) = xf + O( h^6 ) where :math:`h = (tf - ti) / M` is the step size. If *xf* contains not a number :ref:`nan-name` , see the discussion for :ref:`Runge45@Fun@f` . Fun *** The class *Fun* and the object *F* satisfy the prototype *Fun* & *F* The object *F* (and the class *Fun* ) must have a member function named ``Ode`` that supports the syntax *F* . ``Ode`` ( *t* , *x* , *f* ) t = The argument *t* to *F* . ``Ode`` has prototype ``const`` *Scalar* & *t* (see description of :ref:`Runge45@Scalar` below). x = The argument *x* to *F* . ``Ode`` has prototype ``const`` *Vector* & *x* and has size *n* (see description of :ref:`Runge45@Vector` below). f = The argument *f* to *F* . ``Ode`` has prototype *Vector* & *f* On input and output, *f* is a vector of size *n* and the input values of the elements of *f* do not matter. On output, *f* is set equal to :math:`F(t, x)` in the differential equation. If any of the elements of *f* have the value not a number ``nan`` the routine ``Runge45`` returns with all the elements of *xf* and *e* equal to ``nan`` . Warning ======= The argument *f* to *F* . ``Ode`` must have a call by reference in its prototype; i.e., do not forget the ``&`` in the prototype for *f* . M * The argument *M* has prototype ``size_t`` *M* It specifies the number of steps to use when solving the differential equation. This must be greater than or equal one. The step size is given by :math:`h = (tf - ti) / M`, thus the larger *M* , the more accurate the return value *xf* is as an approximation for :math:`X(tf)`. ti ** The argument *ti* has prototype ``const`` *Scalar* & *ti* (see description of :ref:`Runge45@Scalar` below). It specifies the initial time for *t* in the differential equation; i.e., the time corresponding to the value *xi* . tf ** The argument *tf* has prototype ``const`` *Scalar* & *tf* It specifies the final time for *t* in the differential equation; i.e., the time corresponding to the value *xf* . xi ** The argument *xi* has the prototype ``const`` *Vector* & *xi* and the size of *xi* is equal to *n* . It specifies the value of :math:`X(ti)` e * The argument *e* is optional and has the prototype *Vector* & *e* If *e* is present, the size of *e* must be equal to *n* . The input value of the elements of *e* does not matter. On output it contains an element by element estimated bound for the absolute value of the error in *xf* .. math:: e = O( h^5 ) where :math:`h = (tf - ti) / M` is the step size. If on output, *e* contains not a number ``nan`` , see the discussion for :ref:`Runge45@Fun@f` . Scalar ****** The type *Scalar* must satisfy the conditions for a :ref:`NumericType-name` . The routine :ref:`CheckNumericType-name` will generate an error message if this is not the case. fabs ==== In addition, the following function must be defined for *Scalar* objects *a* and *b* *a* = ``fabs`` ( *b* ) Note that this operation is only used for computing *e* ; hence the operation sequence for *xf* can still be independent of the arguments to ``Runge45`` even if ``fabs`` ( *b* ) = ``std::max`` ( ``-`` *b* , *b* ) . Vector ****** The type *Vector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type Scalar` . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Parallel Mode ************* For each set of types :ref:`Runge45@Scalar` , :ref:`Runge45@Vector` , and :ref:`Runge45@Fun` , the first call to ``Runge45`` must not be :ref:`parallel` execution mode. Example ******* {xrst_toc_hidden example/utility/runge45_1.cpp example/utility/runge_45.cpp } The file :ref:`runge45_1.cpp-name` contains a simple example and test of ``Runge45`` . The file :ref:`runge_45.cpp-name` contains an example using ``Runge45`` in the context of algorithmic differentiation. It also returns true if it succeeds and false otherwise. Source Code *********** The source code for this routine is in the file ``cppad/runge_45.hpp`` . {xrst_end Runge45} -------------------------------------------------------------------------- */ # include # include # include # include # include // needed before one can use CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL # include namespace CppAD { // BEGIN CppAD namespace template Vector Runge45( Fun &F , size_t M , const Scalar &ti , const Scalar &tf , const Vector &xi ) { Vector e( xi.size() ); return Runge45(F, M, ti, tf, xi, e); } template Vector Runge45( Fun &F , size_t M , const Scalar &ti , const Scalar &tf , const Vector &xi , Vector &e ) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; // check numeric type specifications CheckNumericType(); // check simple vector class specifications CheckSimpleVector(); // Cash-Karp parameters for embedded Runge-Kutta method // are static to avoid recalculation on each call and // do not use Vector to avoid possible memory leak static Scalar a[6] = { Scalar(0), Scalar(1) / Scalar(5), Scalar(3) / Scalar(10), Scalar(3) / Scalar(5), Scalar(1), Scalar(7) / Scalar(8) }; static Scalar b[5 * 5] = { Scalar(1) / Scalar(5), Scalar(0), Scalar(0), Scalar(0), Scalar(0), Scalar(3) / Scalar(40), Scalar(9) / Scalar(40), Scalar(0), Scalar(0), Scalar(0), Scalar(3) / Scalar(10), -Scalar(9) / Scalar(10), Scalar(6) / Scalar(5), Scalar(0), Scalar(0), -Scalar(11) / Scalar(54), Scalar(5) / Scalar(2), -Scalar(70) / Scalar(27), Scalar(35) / Scalar(27), Scalar(0), Scalar(1631) / Scalar(55296), Scalar(175) / Scalar(512), Scalar(575) / Scalar(13824), Scalar(44275) / Scalar(110592), Scalar(253) / Scalar(4096) }; static Scalar c4[6] = { Scalar(2825) / Scalar(27648), Scalar(0), Scalar(18575) / Scalar(48384), Scalar(13525) / Scalar(55296), Scalar(277) / Scalar(14336), Scalar(1) / Scalar(4), }; static Scalar c5[6] = { Scalar(37) / Scalar(378), Scalar(0), Scalar(250) / Scalar(621), Scalar(125) / Scalar(594), Scalar(0), Scalar(512) / Scalar(1771) }; CPPAD_ASSERT_KNOWN( M >= 1, "Error in Runge45: the number of steps is less than one" ); CPPAD_ASSERT_KNOWN( e.size() == xi.size(), "Error in Runge45: size of e not equal to size of xi" ); size_t i, j, k, m; // indices size_t n = xi.size(); // number of components in X(t) Scalar ns = Scalar(int(M)); // number of steps as Scalar object Scalar h = (tf - ti) / ns; // step size Scalar zero_or_nan = Scalar(0); // zero (nan if Ode returns has a nan) for(i = 0; i < n; i++) // initialize e = 0 e[i] = zero_or_nan; // vectors used to store values returned by F Vector fh(6 * n), xtmp(n), ftmp(n), x4(n), x5(n), xf(n); xf = xi; // initialize solution for(m = 0; m < M; m++) { // time at beginning of this interval // (convert to int to avoid MS compiler warning) Scalar t = ti * (Scalar(int(M - m)) / ns) + tf * (Scalar(int(m)) / ns); // loop over integration steps x4 = x5 = xf; // start x4 and x5 at same point for each step for(j = 0; j < 6; j++) { // loop over function evaluations for this step xtmp = xf; // location for next function evaluation for(k = 0; k < j; k++) { // loop over previous function evaluations Scalar bjk = b[ (j-1) * 5 + k ]; for(i = 0; i < n; i++) { // loop over elements of x xtmp[i] += bjk * fh[i * 6 + k]; } } // ftmp = F(t + a[j] * h, xtmp) F.Ode(t + a[j] * h, xtmp, ftmp); // if ftmp has a nan, set zero_or_nan to nan for(i = 0; i < n; i++) zero_or_nan *= ftmp[i]; for(i = 0; i < n; i++) { // loop over elements of x Scalar fhi = ftmp[i] * h; fh[i * 6 + j] = fhi; x4[i] += c4[j] * fhi; x5[i] += c5[j] * fhi; x5[i] += zero_or_nan; } } // accumulate error bound for(i = 0; i < n; i++) { // cant use abs because cppad.hpp may not be included Scalar diff = x5[i] - x4[i]; e[i] += fabs(diff); e[i] += zero_or_nan; } // advance xf for this step using x5 xf = x5; } return xf; } } // End CppAD namespace # endif ================================================ FILE: include/cppad/utility/set_union.hpp ================================================ # ifndef CPPAD_UTILITY_SET_UNION_HPP # define CPPAD_UTILITY_SET_UNION_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin set_union} Union of Standard Sets ###################### Syntax ****** | # ``include `` | *result* = ``set_union`` ( *left* , *right* ) Purpose ******* This is a simplified (and restricted) interface to the ``std::union`` operation. Element ******* This is the type of the elements of the sets. left **** This argument has prototype ``const std::set<`` *Element* >& *left* right ***** This argument has prototype ``const std::set<`` *Element* >& *right* result ****** The return value has prototype ``std::set<`` *Element* >& *result* It contains the union of *left* and *right* . Note that C++11 detects that the return value is a temporary and uses it for the result instead of making a separate copy. {xrst_toc_hidden example/utility/set_union.cpp } Example ******* The file :ref:`set_union.cpp-name` contains an example and test of this {xrst_end set_union} */ # include # include # include namespace CppAD { template std::set set_union( const std::set& left , const std::set& right ) { std::set result; std::set_union( left.begin() , left.end() , right.begin() , right.end() , std::inserter(result, result.begin()) ); return result; } } # endif ================================================ FILE: include/cppad/utility/sparse2eigen.hpp ================================================ # ifndef CPPAD_UTILITY_SPARSE2EIGEN_HPP # define CPPAD_UTILITY_SPARSE2EIGEN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sparse2eigen} {xrst_spell nnz } Convert A CppAD Sparse Matrix to an Eigen Sparse Matrix ####################################################### Syntax ****** | # ``include `` | ``sparse2eigen`` ( *source* , *destination* ) Prototype ********* {xrst_literal // BEGIN_PROTOTYPE // END_PROTOTYPE } Include ******* If :ref:`cmake@Eigen` is found (and c++14 is supported), the file ``cppad/utility/sparse2eigen.hpp`` is included by ``cppad/cppad.hpp`` . In any case, it can also be included separately with out the rest of the ``CppAD`` routines. Including this file defines this version of the ``sparse2eigen`` within the ``CppAD`` namespace. SizeVector ********** We use :ref:`sparse_rc@SizeVector` to denote a :ref:`SimpleVector-name` class with elements of ``size_t`` . ValueVector *********** We use *ValueVector* to denote a :ref:`SimpleVector-name` class with elements of type *value_type* . Options ******* We use *Options* to denote either ``Eigen::RowMajor`` of ``Eigen::ColMajor`` . value_type ********** The type of elements of elements in *source* and *destination* must be the same. We use *value_type* to denote this type. source ****** This is the CppAD sparse matrix that is being converted to eigen format. destination *********** This is the Eigen sparse matrix that is the result of the conversion. Compressed ********** The result matrix *destination* is in compressed format. For example, let | |tab| ``size_t`` *nnz* = *source* . ``nnz`` (); | |tab| ``const`` *s_vector* & *s_value* = *source* . ``val`` (); | |tab| ``const`` *value_type* * *d_value* = *destination* . ``valuePtr`` (); | |tab| ``const`` *s_vector* & *row_major* = *source* . ``row_major`` (); | |tab| ``const`` *s_vector* & *col_major* = *source* . ``col_major`` (); It follows that, for *k* = 0 , ..., *nnz* : If *Options* is ``Eigen::RowMajor`` , *d_value* [ *k* ] == *s_value* [ *row_major* [ *k* ] ] If *Options* is ``Eigen::ColMajor`` , *d_value* [ *k* ] == *s_value* [ *col_major* [ *k* ] ] {xrst_toc_hidden example/sparse/sparse2eigen.cpp } Example ******* The file :ref:`sparse2eigen.cpp-name` contains an example and test of ``sparse2eigen.cpp`` It return true if the test passes and false otherwise. {xrst_end sparse2eigen} */ # include # include # include # include namespace CppAD { // BEGIN CPPAD_NAMESPACE // BEGIN_PROTOTYPE template void sparse2eigen( const CppAD::sparse_rcv& source , Eigen::SparseMatrix& destination ) // END_PROTOTYPE { using Eigen::Index; typedef typename ValueVector::value_type value_type; typedef Eigen::Triplet triplet; std::vector vec( source.nnz() ); // const SizeVector& row = source.row(); const SizeVector& col = source.col(); const ValueVector& val = source.val(); // for(size_t k = 0; k < source.nnz(); k++) vec[k] = triplet( int(row[k]), int(col[k]), val[k] ); // size_t nr = source.nr(); size_t nc = source.nc(); destination.resize( Index(nr), Index(nc) ); destination.setFromTriplets(vec.begin(), vec.end()); // CPPAD_ASSERT_UNKNOWN( destination.isCompressed() ); // return; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/utility/sparse_rc.hpp ================================================ # ifndef CPPAD_UTILITY_SPARSE_RC_HPP # define CPPAD_UTILITY_SPARSE_RC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sparse_rc} {xrst_spell nnz nr ostream } Row and Column Index Sparsity Patterns ###################################### Syntax ****** include ======= # ``include `` Constructor =========== | ``sparse_rc`` < *SizeVector* > *empty* | ``sparse_rc`` < *SizeVector* > *pattern* ( *nr* , *nc* , *nnz* ) | ``sparse_rc`` < *SizeVector* > *pattern* ( *other* ) Assignment ========== | *pattern* = *other* | *pattern* . ``swap`` ( *other* ) Equality ======== *equal* = *pattern* == *other* . Setting ======= | *resize* ( *nr* , *nc* , *nnz* ) | *pattern* . ``set`` ( *k* , *r* , *c* ) | *pattern* . ``push_back`` ( *r* , *c* ) | *pattern* . ``set_row_major`` () | *pattern* . ``set_col_major`` () Scalars ======= | *pattern* . ``nr`` () | *pattern* . ``nc`` () | *pattern* . ``nnz`` () Vectors ======= | ``const`` *SizeVector* & *row* ( *pattern* . ``row`` () ) | ``const`` *SizeVector* & *col* ( *pattern* . ``col`` () ) | ``const`` *SizeVector* & *row_major* ( *pattern* . ``get_row_major`` () ) | ``const`` *SizeVector* & *col_major* ( *pattern* . ``get_col_major`` () ) | *row_major* = *pattern* . ``row_major`` () | *col_major* = *pattern* . ``col_major`` () Output ====== *os* << *pattern* SizeVector ********** We use *SizeVector* to denote :ref:`SimpleVector-name` class :ref:`with elements of type` ``size_t`` . In addition, *SimpleVector* must support the ``swap`` operation between two of its vectors. empty ***** This is an empty sparsity pattern. To be specific, the corresponding number of rows *nr* , number of columns *nc* , and number of possibly non-zero values *nnz* , are all zero. pattern ******* This object is used to hold a sparsity pattern for a matrix. The sparsity *pattern* is ``const`` except during its constructor, ``resize`` , and ``set`` . other ***** Assignment and Constructor ========================== In the assignment and constructor, *other* has prototype ``const sparse_rc`` < *SizeVector* >& *other* After the assignment and constructor, *pattern* is an independent copy of *other* ; i.e. it has all the same values as *other* and changes to *pattern* do not affect *other* . Move Semantics Assignment and Constructor ========================================= In the assignment and constructor, if *other* has prototype ``sparse_rc`` < *SizeVector* >&& *other* A move semantics version of the assignment or constructor is used; e.g., when *other* is a function return value. swap ==== In the swap operation, *other* has prototype ``sparse_rc`` < *SizeVector* >& *other* After the swap operation *other* ( *pattern* ) is equivalent to *pattern* ( *other* ) before the operation. Equality ======== In the equality operation, *other* has prototype ``const sparse_rc`` < *SizeVector* >& *other* The two sparsity patterns are equal if the following conditions hold: #. The number of rows *pattern* . ``nr`` () and *other* . ``nr`` () are equal. #. The number of columns *pattern* . ``nc`` () and *other* . ``nc`` () are equal. #. The number of non-zero values *pattern* . ``nnz`` () and *other* . ``nnz`` () are equal. #. The set of (row, column) pairs corresponding to *pattern* and *other* , are equal. Determining equality requires sorting both patterns nr ** This argument has prototype ``size_t`` *nr* It specifies the number of rows in the sparsity pattern. The function call ``nr()`` returns the value of *nr* . nc ** This argument has prototype ``size_t`` *nc* It specifies the number of columns in the sparsity pattern. The function call ``nc()`` returns the value of *nc* . nnz *** This argument has prototype ``size_t`` *nnz* It specifies the number of possibly non-zero index pairs in the sparsity pattern. The function call ``nnz()`` returns the value of *nnz* . resize ****** The current sparsity pattern is lost and a new one is started with the specified parameters. The elements in the *row* and *col* vectors should be assigned using ``set`` . set *** This function sets the values | |tab| *row* [ *k* ] = *r* | |tab| *col* [ *k* ] = *c* push_back ********* This function the value *r* to the back of *row* , the value *c* to the back of *col* , and increases *nnz* by one. This operation requires *SizeVector* to support the ``push_back`` operation (which is not part of the SimpleVector requirements). k = This argument has type ``size_t`` *k* and must be less than *nnz* . r = This argument has type ``size_t`` *r* It specifies the value assigned to *row* [ *k* ] and must be less than *nr* . c = This argument has type ``size_t`` *c* It specifies the value assigned to *col* [ *k* ] and must be less than *nc* . row *** This vector has size *nnz* and *row* [ *k* ] is the row index of the *k*-th possibly non-zero index pair in the sparsity pattern. col *** This vector has size *nnz* and *col* [ *k* ] is the column index of the *k*-th possibly non-zero index pair in the sparsity pattern. row_major ********* This vector has prototype *SizeVector* *row_major* and its size *nnz* . It sorts the sparsity pattern in row-major order. To be specific, *col* [ *row_major* [ *k* ] ] <= *col* [ *row_major* [ *k* +1] ] and if *col* [ *row_major* [ *k* ] ] == *col* [ *row_major* [ *k* +1] ] , *row* [ *row_major* [ *k* ] ] < *row* [ *row_major* [ *k* +1] ] This routine generates an assert if there are two entries with the same row and column values (if ``NDEBUG`` is not defined). set_row_major ************* Store the current row major order in *pattern* . This can be used by the row_major function and the equality function to avoid re-sorting the pattern each time. get_row_major ************* Retrieve the row major order stored in *pattern* by the previous ``set_row_major`` . If this order is no longer valid, the return value *row_major* has size zero. col_major ********* This vector has prototype *SizeVector* *col_major* and its size *nnz* . It sorts the sparsity pattern in column-major order. To be specific, *row* [ *col_major* [ *k* ] ] <= *row* [ *col_major* [ *k* +1] ] and if *row* [ *col_major* [ *k* ] ] == *row* [ *col_major* [ *k* +1] ] , *col* [ *col_major* [ *k* ] ] < *col* [ *col_major* [ *k* +1] ] This routine generates an assert if there are two entries with the same row and column values (if ``NDEBUG`` is not defined). set_col_major ************* Store the current row major order in *pattern* . This can be used by the col_major function and the equality function to avoid re-sorting the pattern each time. get_col_major ************* Retrieve the row major order stored in *pattern* by the previous ``set_col_major`` . If this order is no longer valid, the return value *col_major* has size zero. {xrst_toc_hidden example/utility/sparse_rc.cpp } Example ******* The file :ref:`sparse_rc.cpp-name` contains an example and test of this class. os ** If *os* is an ``std::ostream`` , the operation *os* << *pattern* outputs *pattern* to the *os* stream. The output begins with a left brace ``{`` and ends with a right brace ``}`` . The output is in row major order and has one line for each row. The row index is output at the beginning of a line and the column indices follow. {xrst_end sparse_rc} */ # include // for size_t # include // for CPPAD_ASSERT # include // for row and column major ordering namespace CppAD { // BEGIN CPPAD_NAMESPACE // sparsity pattern for a matrix with indices of type size_t template class sparse_rc { private: // number of rows in the sparsity pattern size_t nr_; // // number of columns in the sparsity pattern size_t nc_; // // number of possibly non-zero index pairs size_t nnz_; // // row_[k] is the row index for the k-th possibly non-zero entry SizeVector row_; // // col_[k] is the column index for the k-th possibly non-zero entry SizeVector col_; // // if row_major_.size() != 0, row_major_[k] is index of k-th non-zero // entry in row major order. SizeVector row_major_; // // if col_major_.size() != 0, col_major_[k] is index of k-th non-zero // entry in column major order. SizeVector col_major_; // // simple_vector_assign static void simple_vector_assign( SizeVector& destination, const SizeVector& source ) { // resize to zero first so do not copy any values destination.resize(0); // size agreement required for simple vector destination.resize( source.size() ); // assignment destination = source; } public: // default constructor // Eigen vector is ambiguous for row_(0), col_(0) so use default ctor sparse_rc(void) : nr_(0), nc_(0), nnz_(0) { } // // move semantics constructor // (none of the default constructor values are used by destructor) sparse_rc(sparse_rc&& other) { swap(other); } // // destructor ~sparse_rc(void) { } // // sizing constructor // Eigen vector is ambiguous for row_(0), col_(0) so use default ctor sparse_rc(size_t nr, size_t nc, size_t nnz) : nr_(nr), nc_(nc), nnz_(nnz) { row_.resize(nnz); col_.resize(nnz); } // // copy constructor sparse_rc(const sparse_rc& other) : nr_(other.nr_) , nc_(other.nc_) , nnz_(other.nnz_) , row_(other.row_) , col_(other.col_) , row_major_(other.row_major_) , col_major_(other.col_major_) { } // // assignment void operator=(const sparse_rc& other) { nr_ = other.nr_; nc_ = other.nc_; nnz_ = other.nnz_; // simple_vector_assign(row_, other.row_); simple_vector_assign(col_, other.col_); simple_vector_assign(row_major_, other.row_major_); simple_vector_assign(col_major_, other.col_major_); } // // swap void swap(sparse_rc& other) { std::swap( nr_ , other.nr_ ); std::swap( nc_ , other.nc_ ); std::swap( nnz_ , other.nnz_ ); // row_.swap( other.row_ ); col_.swap( other.col_ ); row_major_.swap( other.row_major_ ); col_major_.swap( other.col_major_ ); } // // move semantics assignment void operator=(sparse_rc&& other) { swap(other); } // // equality bool operator==(const sparse_rc& other) const { // result bool result = true; result &= nr_ == other.nr_; result &= nc_ == other.nc_; result &= nnz_ == other.nnz_; if( ! result ) return result; // // this_order, other_order, this_order_ptr, other_order_ptr SizeVector this_order; SizeVector other_order; const SizeVector* this_order_ptr = &this_order; const SizeVector* other_order_ptr = &other_order; bool this_row_ok = row_major_.size() > 0; bool this_col_ok = col_major_.size() > 0; bool other_row_ok = other.row_major_.size() > 0; bool other_col_ok = other.col_major_.size() > 0; if( this_row_ok && this_row_ok ) { this_order_ptr = &row_major_; other_order_ptr = &(other.row_major_); } else if( this_col_ok && this_col_ok ) { this_order_ptr = &col_major_; other_order_ptr = &(other.col_major_); } else if( this_row_ok ) { this_order_ptr = &row_major_; other_order = other.row_major(); } else if( this_col_ok ) { this_order_ptr = &col_major_; other_order = other.col_major(); } else if( other_row_ok ) { other_order_ptr = &(other.row_major_); this_order = row_major(); } else if( other_col_ok ) { other_order_ptr = &(other.col_major_); this_order = col_major(); } else { this_order = row_major(); other_order = other.row_major(); } // // result for(size_t k = 0; k < nnz_; ++k) { size_t this_k = (*this_order_ptr)[k]; size_t other_k = (*other_order_ptr)[k]; result &= row_[this_k] == other.row_[other_k]; result &= col_[this_k] == other.col_[other_k]; } return result; } // // resize void resize(size_t nr, size_t nc, size_t nnz) { nr_ = nr; nc_ = nc; nnz_ = nnz; row_.resize(nnz); col_.resize(nnz); row_major_.resize(0); col_major_.resize(0); } // // set row and column for a possibly non-zero element void set(size_t k, size_t r, size_t c) { CPPAD_ASSERT_KNOWN( k < nnz_, "The index k is not less than nnz in sparse_rc::set" ); CPPAD_ASSERT_KNOWN( r < nr_, "The index r is not less than nr in sparse_rc::set" ); CPPAD_ASSERT_KNOWN( c < nc_, "The index c is to not less than nc in sparse_rc::set" ); row_[k] = r; col_[k] = c; // row_major_.resize(0); col_major_.resize(0); } // // push_back void push_back(size_t r, size_t c) { CPPAD_ASSERT_KNOWN( r < nr_, "The index r is not less than nr in sparse_rc::push_back" ); CPPAD_ASSERT_KNOWN( c < nc_, "The index c is to not less than nc in sparse_rc::push_back" ); row_.push_back(r); col_.push_back(c); ++nnz_; CPPAD_ASSERT_UNKNOWN( row_.size() == nnz_ ); CPPAD_ASSERT_UNKNOWN( col_.size() == nnz_ ); // row_major_.resize(0); col_major_.resize(0); } // // number of rows in matrix size_t nr(void) const { return nr_; } // // number of columns in matrix size_t nc(void) const { return nc_; } // // number of possibly non-zero elements in matrix size_t nnz(void) const { return nnz_; } // // row indices const SizeVector& row(void) const { return row_; } // // column indices const SizeVector& col(void) const { return col_; } // // row-major order SizeVector row_major(void) const { if( row_major_.size() > 0 ) return row_major_; // SizeVector keys(nnz_), row_major(nnz_); for(size_t k = 0; k < nnz_; k++) { CPPAD_ASSERT_UNKNOWN( row_[k] < nr_ ); keys[k] = row_[k] * nc_ + col_[k]; } index_sort(keys, row_major); # ifndef NDEBUG for(size_t ell = 0; ell + 1 < nnz_; ell++) { size_t k = row_major[ ell ]; size_t kp = row_major[ ell + 1 ]; CPPAD_ASSERT_KNOWN( row_[k] != row_[kp] || col_[k] != col_[kp], "sparse_rc: row_major: duplicate entry in this pattern" ); CPPAD_ASSERT_UNKNOWN( row_[k] 0 ) return col_major_; SizeVector keys(nnz_), col_major(nnz_); for(size_t k = 0; k < nnz_; k++) { CPPAD_ASSERT_UNKNOWN( col_[k] < nc_ ); keys[k] = col_[k] * nr_ + row_[k]; } index_sort(keys, col_major); # ifndef NDEBUG for(size_t ell = 0; ell + 1 < nnz_; ell++) { size_t k = col_major[ ell ]; size_t kp = col_major[ ell + 1 ]; CPPAD_ASSERT_KNOWN( col_[k] != col_[kp] || row_[k] != row_[kp], "sparse_rc: col_major: duplicate entry in this pattern" ); CPPAD_ASSERT_UNKNOWN( col_[k] std::ostream& operator << ( std::ostream& os , const CppAD::sparse_rc& pattern ) { size_t nnz = pattern.nnz(); if( nnz == 0 ) { os << "{ }"; return os; } const SizeVector& row = pattern.row(); const SizeVector& col = pattern.col(); SizeVector row_major = pattern.row_major(); // // k, r, c size_t k = 0; size_t r = row[ row_major[k] ]; size_t c = col[ row_major[k] ]; // // os os << "{\nrow = " << r << ", col = " << c; while(++k < nnz ) { bool new_row = r != row[ row_major[k] ]; r = row[ row_major[k] ]; c = col[ row_major[k] ]; if( new_row ) os << "\nrow = " << r << ", col = " << c; else os << ", " << c; } os << "\n}"; // return os; } } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/utility/sparse_rcv.hpp ================================================ # ifndef CPPAD_UTILITY_SPARSE_RCV_HPP # define CPPAD_UTILITY_SPARSE_RCV_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* ------------------------------------------------------------------------------ {xrst_begin sparse_rcv} {xrst_spell nnz nr rc } Sparse Matrix Row, Column, Value Representation ############################################### Syntax ****** | # ``include `` | ``sparse_rcv`` < *SizeVector* , *ValueVector* > *empty* | ``sparse_rcv`` < *SizeVector* , *ValueVector* > *matrix* ( *pattern* ) | *matrix* = *other* | *matrix* . ``swap`` ( *other* ) | *matrix* . ``set`` ( *k* , *v* ) | *nr* = *matrix* . ``nr`` () | *nc* = *matrix* . ``nc`` () | *nnz* = *matrix* . ``nnz`` () | ``const`` *SizeVector* & *row* ( *matrix* . ``row`` () ) | ``const`` *SizeVector* & *col* ( *matrix* . ``col`` () ) | ``const`` *ValueVector* & *val* ( *matrix* . ``val`` () ) | ``const sparse_rc`` < *SizeVector* >& *pat* ( *matrix* . ``pat`` () ) | *row_major* = *matrix* . ``row_major`` () | *col_major* = *matrix* . ``col_major`` () SizeVector ********** We use :ref:`sparse_rc@SizeVector` to denote the :ref:`SimpleVector-name` class corresponding to *pattern* . ValueVector *********** We use *ValueVector* to denote the :ref:`SimpleVector-name` class corresponding to *val* . empty ***** This is an empty sparse matrix object. To be specific, the corresponding number of rows *nr* , number of columns *nc* , and number of possibly non-zero values *nnz* , are all zero. pattern ******* This constructor argument has prototype ``const sparse_rc`` < *SizeVector* >& *pattern* It specifies the number of rows, number of columns and the possibly non-zero entries in the *matrix* . matrix ****** This is a sparse matrix object with the sparsity specified by *pattern* . Only the *val* vector can be changed. All other values returned by *matrix* are fixed during the constructor and constant there after. The *val* vector is only changed by the constructor and the ``set`` function. There are two exceptions to this rule, where *other* appears in the assignment and swap syntax. other ***** Assignment and Constructor ========================== In the assignment and constructor, *other* has prototype ``const sparse_rcv`` < *SizeVector* , *ValueVector* >& *other* After this assignment and constructor, *other* is an independent copy of *matrix* ; i.e. it has all the same values as *matrix* and changes to *matrix* do not affect *other* . Move Semantics Assignment and Constructor ========================================= In the assignment and constructor, if *other* has prototype ``sparse_rcv`` < *SizeVector* , *ValueVector* >&& *other* A move semantics version of the assignment operator is used; e.g., when *other* is a function return value; swap ==== After the swap operation *other* ( *matrix* ) is equivalent to *matrix* ( *other* ) before the operation. nr ** This return value has prototype ``size_t`` *nr* and is the number of rows in *matrix* . nc ** This argument and return value has prototype ``size_t`` *nc* and is the number of columns in *matrix* . nnz *** We use the notation *nnz* to denote the number of possibly non-zero entries in *matrix* . set *** This function sets the value *val* [ *k* ] = *v* k = This argument has type ``size_t`` *k* and must be less than *nnz* . v = This argument has type ``const`` *ValueVector* :: ``value_type&`` *v* It specifies the value assigned to *val* [ *k* ] . row *** This vector has size *nnz* and *row* [ *k* ] is the row index of the *k*-th possibly non-zero element in *matrix* . col *** This vector has size *nnz* and *col* [ ``k`` ] is the column index of the *k*-th possibly non-zero element in *matrix* val *** This vector has size *nnz* and *val* [ ``k`` ] is value of the *k*-th possibly non-zero entry in the sparse matrix (the value may be zero). pat *** This is equal to the sparsity pattern; i.e., *pattern* in the constructor. row_major ********* This vector has prototype *SizeVector* *row_major* and its size *nnz* . It sorts the sparsity pattern in row-major order. To be specific, *col* [ *row_major* [ *k* ] ] <= *col* [ *row_major* [ *k* +1] ] and if *col* [ *row_major* [ *k* ] ] == *col* [ *row_major* [ *k* +1] ] , *row* [ *row_major* [ *k* ] ] < *row* [ *row_major* [ *k* +1] ] This routine generates an assert if there are two entries with the same row and column values (if ``NDEBUG`` is not defined). col_major ********* This vector has prototype *SizeVector* *col_major* and its size *nnz* . It sorts the sparsity pattern in column-major order. To be specific, *row* [ *col_major* [ *k* ] ] <= *row* [ *col_major* [ *k* +1] ] and if *row* [ *col_major* [ *k* ] ] == *row* [ *col_major* [ *k* +1] ] , *col* [ *col_major* [ *k* ] ] < *col* [ *col_major* [ *k* +1] ] This routine generates an assert if there are two entries with the same row and column values (if ``NDEBUG`` is not defined). Eigen Matrix ************ If you have the :ref:`eigen package` in your include path, you can use :ref:`sparse2eigen-name` to convert a sparse matrix to eigen format. {xrst_toc_hidden example/utility/sparse_rcv.cpp } Example ******* The file :ref:`sparse_rcv.cpp-name` contains an example and test of this class. {xrst_end sparse_rcv} */ /*! \file sparse_rcv.hpp A sparse matrix class. */ # include namespace CppAD { // BEGIN CPPAD_NAMESPACE /// Sparse matrices with elements of type Scalar template class sparse_rcv { private: /// sparsity pattern sparse_rc pattern_; /// value_type typedef typename ValueVector::value_type value_type; /// val_[k] is the value for the k-th possibly non-zero entry in the matrix ValueVector val_; public: // ------------------------------------------------------------------------ /// default constructor sparse_rcv(void) : pattern_(0, 0, 0) { } /// copy constructor sparse_rcv(const sparse_rcv& other) : pattern_( other.pat() ) , val_( other.val() ) { } /// move semantics constructor /// (none of the default constructor values are used by destructor) sparse_rcv(sparse_rcv&& other) { swap(other); } /// destructor ~sparse_rcv(void) { } /// constructor sparse_rcv(const sparse_rc& pattern ) : pattern_(pattern) , val_(pattern_.nnz()) { } /// assignment void operator=(const sparse_rcv& other) { pattern_ = other.pattern_; // simple vector assignment requires vectors to have same size val_.resize( other.nnz() ); val_ = other.val(); } /// swap void swap(sparse_rcv& other) { pattern_.swap( other.pattern_ ); val_.swap( other.val_ ); } /// move semantics assignment void operator=(sparse_rcv&& other) { swap(other); } // ------------------------------------------------------------------------ void set(size_t k, const value_type& v) { CPPAD_ASSERT_KNOWN( pattern_.nnz(), "The index k is not less than nnz in sparse_rcv::set" ); val_[k] = v; } /// number of rows in matrix size_t nr(void) const { return pattern_.nr(); } /// number of columns in matrix size_t nc(void) const { return pattern_.nc(); } /// number of possibly non-zero elements in matrix size_t nnz(void) const { return pattern_.nnz(); } /// row indices const SizeVector& row(void) const { return pattern_.row(); } /// column indices const SizeVector& col(void) const { return pattern_.col(); } /// value for possibly non-zero elements const ValueVector& val(void) const { return val_; } /// sparsity pattern const sparse_rc& pat(void) const { return pattern_; } /// row-major order SizeVector row_major(void) const { return pattern_.row_major(); } /// column-major indices SizeVector col_major(void) const { return pattern_.col_major(); } }; } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/utility/speed_test.hpp ================================================ # ifndef CPPAD_UTILITY_SPEED_TEST_HPP # define CPPAD_UTILITY_SPEED_TEST_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin speed_test} {xrst_spell ctime gettimeofday } Run One Speed Test and Return Results ##################################### Syntax ****** | # ``include `` | *rate_vec* = ``speed_test`` ( *test* , *size_vec* , *time_min* ) See Also ******** :ref:`time_test-name` Purpose ******* The ``speed_test`` function executes a speed test for various sized problems and reports the rate of execution. Motivation ********** It is important to separate small calculation units and test them individually. This way individual changes can be tested in the context of the routine that they are in. On many machines, accurate timing of a very short execution sequences is not possible. In addition, there may be set up and tear down time for a test that we do not really want included in the timing. For this reason ``speed_test`` automatically determines how many times to repeat the section of the test that we wish to time. Include ******* The file ``cppad/utility/speed_test.hpp`` defines the ``speed_test`` function. This file is included by ``cppad/cppad.hpp`` and it can also be included separately with out the rest of the ``CppAD`` routines. Vector ****** We use *Vector* to denote a :ref:`simple vector class` with elements of type ``size_t`` . test **** The ``speed_test`` argument *test* is a function with the syntax *test* ( *size* , *repeat* ) and its return value is ``void`` . size ==== The *test* argument *size* has prototype ``size_t`` *size* It specifies the size for this test. repeat ====== The *test* argument *repeat* has prototype ``size_t`` *repeat* It specifies the number of times to repeat the test. size_vec ******** The ``speed_test`` argument *size_vec* has prototype ``const`` *Vector* & *size_vec* This vector determines the size for each of the tests problems. time_min ******** The argument *time_min* has prototype ``double`` *time_min* It specifies the minimum amount of time in seconds that the *test* routine should take. The *repeat* argument to *test* is increased until this amount of execution time is reached. rate_vec ******** The return value *rate_vec* has prototype *Vector* & *rate_vec* We use :math:`n` to denote its size which is the same as the vector *size_vec* . For :math:`i = 0 , \ldots , n-1`, *rate_vec* [ *i* ] is the ratio of *repeat* divided by time in seconds for the problem with size *size_vec* [ *i* ] . Timing ****** If your system supports the unix ``gettimeofday`` function, it will be used to measure time. Otherwise, time is measured by the difference in :: (double) clock() / (double) CLOCKS_PER_SEC in the context of the standard ```` definitions. {xrst_toc_hidden speed/example/speed_test.cpp } Example ******* The routine :ref:`speed_test.cpp-name` is an example and test of ``speed_test`` . {xrst_end speed_test} ----------------------------------------------------------------------- */ # include # include # include # include namespace CppAD { // BEGIN CppAD namespace // implemented as an inline so that can include in multiple link modules // with this same file template Vector speed_test( void test(size_t size, size_t repeat), const Vector& size_vec , double time_min ) { // check that size_vec is a simple vector with size_t elements CheckSimpleVector(); size_t n = size_vec.size(); Vector rate_vec(n); size_t i; for(i = 0; i < n; i++) { size_t size = size_vec[i]; size_t repeat = 1; double s0 = elapsed_seconds(); double s1 = elapsed_seconds(); while( s1 - s0 < time_min ) { if( 2 * repeat < repeat ) { // Can't use an assert here because this happens // in release mode first. std::cerr << "speed_test: test function is too fast to time\n"; std::exit(1); } repeat = 2 * repeat; s0 = elapsed_seconds(); test(size, repeat); s1 = elapsed_seconds(); } double rate = .5 + double(repeat) / (s1 - s0); // first convert to float to avoid warning with g++ -Wconversion rate_vec[i] = static_cast( static_cast(rate) ); } return rate_vec; } } // END CppAD namespace /* {xrst_begin SpeedTest} {xrst_spell cout ctime } Run One Speed Test and Print Results #################################### Syntax ****** # ``include `` ``SpeedTest`` ( *Test* , *first* , *inc* , *last* ) See Also ******** :ref:`time_test-name` Purpose ******* The ``SpeedTest`` function executes a speed test for various sized problems and reports the results on standard output; i.e. ``std::cout`` . The size of each test problem is included in its report (unless *first* is equal to *last* ). Motivation ********** It is important to separate small calculation units and test them individually. This way individual changes can be tested in the context of the routine that they are in. On many machines, accurate timing of a very short execution sequences is not possible. In addition, there may be set up time for a test that we do not really want included in the timing. For this reason ``SpeedTest`` automatically determines how many times to repeat the section of the test that we wish to time. Include ******* The file ``speed_test.hpp`` contains the ``SpeedTest`` function. This file is included by ``cppad/utility/cppad.hpp`` but it can also be included separately with out the rest of the ``CppAD`` routines. Test **** The ``SpeedTest`` argument *Test* is a function with the syntax *name* = *Test* ( *size* , *repeat* ) size ==== The *Test* argument *size* has prototype ``size_t`` *size* It specifies the size for this test. repeat ====== The *Test* argument *repeat* has prototype ``size_t`` *repeat* It specifies the number of times to repeat the test. name ==== The *Test* result *name* has prototype ``std::string`` *name* The results for this test are reported on ``std::cout`` with *name* as an identifier for the test. It is assumed that, for the duration of this call to ``SpeedTest`` , *Test* will always return the same value for *name* . If *name* is the empty string, no test name is reported by ``SpeedTest`` . first ***** The ``SpeedTest`` argument *first* has prototype ``size_t`` *first* It specifies the size of the first test problem reported by this call to ``SpeedTest`` . last **** The ``SpeedTest`` argument *last* has prototype ``size_t`` *last* It specifies the size of the last test problem reported by this call to ``SpeedTest`` . inc *** The ``SpeedTest`` argument *inc* has prototype ``int`` *inc* It specifies the increment between problem sizes; i.e., all values of *size* in calls to *Test* are given by *size* = *first* + *j* * *inc* where *j* is a positive integer. The increment can be positive or negative but it cannot be zero. The values *first* , *last* and *inc* must satisfy the relation .. math:: inc * ( last - first ) \geq 0 rate **** The value displayed in the ``rate`` column on ``std::cout`` is defined as the value of *repeat* divided by the corresponding elapsed execution time in seconds. The elapsed execution time is measured by the difference in :: (double) clock() / (double) CLOCKS_PER_SEC in the context of the standard ```` definitions. Errors ****** If one of the restrictions above is violated, the CppAD error handler is used to report the error. You can redefine this action using the instructions in :ref:`ErrorHandler-name` Example ******* {xrst_toc_hidden speed/example/speed_program.cpp } The program :ref:`speed_program.cpp-name` is an example usage of ``SpeedTest`` . {xrst_end SpeedTest} ----------------------------------------------------------------------- */ // BEGIN C++ # include # include # include # include namespace CppAD { // BEGIN CppAD namespace inline void SpeedTestNdigit(size_t value, size_t &ndigit, size_t &pow10) { pow10 = 10; ndigit = 1; while( pow10 <= value ) { pow10 *= 10; ndigit += 1; } } // implemented as an inline so that can include in multiple link modules // with this same file inline void SpeedTest( std::string Test(size_t size, size_t repeat), size_t first, int inc, size_t last ) { using std::cout; using std::endl; size_t size; size_t repeat; size_t rate; size_t digit; size_t ndigit; size_t pow10; size_t maxSize; size_t maxSizeDigit; double s0; double s1; std::string name; CPPAD_ASSERT_KNOWN( inc != 0 && first != 0 && last != 0, "inc, first, or last is zero in call to SpeedTest" ); CPPAD_ASSERT_KNOWN( (inc > 0 && first <= last) || (inc < 0 && first >= last), "SpeedTest: increment is positive and first > last or " "increment is negative and first < last" ); // compute maxSize maxSize = size = first; while( (inc > 0 && size <= last) || (inc < 0 && size >= last) ) { if( size > maxSize ) maxSize = size; // next size if( int(size) + inc > 0 ) size = size_t( int(size) + inc ); else size = 0; } SpeedTestNdigit(maxSize, maxSizeDigit, pow10); size = first; while( (inc > 0 && size <= last) || (inc < 0 && size >= last) ) { repeat = 1; s0 = elapsed_seconds(); s1 = elapsed_seconds(); while( s1 - s0 < 1. ) { if( 2 * repeat < repeat ) { // Can't use an assert here because this happens // in release mode first. std::cerr << "SpeedTest: test function is too fast to time\n"; std::exit(1); } repeat = 2 * repeat; s0 = elapsed_seconds(); name = Test(size, repeat); s1 = elapsed_seconds(); } double r = .5 + double(repeat) / (s1 - s0); // first convert to float to avoid warning with g++ -Wconversion rate = static_cast( static_cast( r ) ); if( size == first && name != "" ) cout << name << endl; if( first != last ) { // convert int(size_t) to avoid warning on _MSC_VER sys std::cout << "size = " << int(size); SpeedTestNdigit(size, ndigit, pow10); while( ndigit < maxSizeDigit ) { cout << " "; ndigit++; } cout << " "; } cout << "rate = "; SpeedTestNdigit(rate, ndigit, pow10); while( ndigit > 0 ) { pow10 /= 10; digit = rate / pow10; // convert int(size_t) to avoid warning on _MSC_VER sys std::cout << int(digit); rate = rate % pow10; ndigit -= 1; if( (ndigit > 0) && (ndigit % 3 == 0) ) cout << ","; } cout << endl; // next size if( int(size) + inc > 0 ) size = size_t( int(size) + inc ); else size = 0; } return; } } // END CppAD namespace // END C++ # endif ================================================ FILE: include/cppad/utility/test_boolofvoid.hpp ================================================ # ifndef CPPAD_UTILITY_TEST_BOOLOFVOID_HPP # define CPPAD_UTILITY_TEST_BOOLOFVOID_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin test_boolofvoid} {xrst_spell ipopt } Object that Runs a Group of Tests ################################# Syntax ****** | # ``include `` | ``test_boolofvoid`` *Run* ( *group* , *width* ) | *Run* ( *test* , *name* ) | *ok* = *Run* . ``summary`` ( *memory_ok* ) Purpose ******* The object *Run* is used to run a group of tests functions and report the results on standard output. group ***** The argument has prototype ``const std::string&`` *group* It is the name for this group of tests. width ***** The argument has prototype ``size_t`` *width* It is the number of columns used to display the name of each test. It must be greater than the maximum number of characters in a test name. test **** The argument has prototype ``bool`` *test* ( ``void`` ) It is a function that returns true (when the test passes) and false otherwise. name **** The argument has prototype ``const std::string&`` *name* It is the name for the corresponding *test* . memory_ok ********* The argument has prototype ``bool`` *memory_ok* It is false if a memory leak is detected (and true otherwise). ok ** This is true if all of the tests pass (including the memory leak test), otherwise it is false. Example ******* See any of the main programs in the example directory; e.g., ``example/ipopt_solve.cpp`` . {xrst_end test_boolofvoid} */ # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /// One class object is used to run a group of tests class test_boolofvoid { private: /// name for the group of test this object will run const std::string group_; /// number of characters used to display the name for each individual test /// (must be larger than the number of characters in name for each test) const size_t width_; /// number of tests that have passed size_t n_ok_; /// number of tests that have failed size_t n_error_; public: /// ctor test_boolofvoid(const std::string& group, size_t width) : group_(group) , width_(width) , n_ok_(0) , n_error_(0) { std::cout << "Begin test group " << group_ << std::endl; } /// destructor ~test_boolofvoid(void) { std::cout << "End test group " << group_ << std::endl; } /// run one test bool operator()(bool test(void), const std::string& name) { CPPAD_ASSERT_KNOWN( name.size() < width_ , "test_boolofvoid: name does not have less characters than width" ); std::cout.width( int(width_) ); std::cout.setf( std::ios_base::left ); std::cout << name; // bool ok = test(); if( ok ) { std::cout << "OK" << std::endl; n_ok_++; } else { std::cout << "Error" << std::endl; n_error_++; } return ok; } /// number of tests that passed size_t n_ok(void) const { return n_ok_; } /// number of tests that failed size_t n_error(void) const { return n_error_; } /// summary bool summary(bool memory_ok ) { std::cout.width( int(width_) ); std::cout.setf( std::ios_base::left ); std::cout << "memory_leak"; // if( memory_ok ) { std::cout << "OK" << std::endl; n_ok_++; } else { std::cout << "Error" << std::endl; n_error_++; } if( n_error_ == 0 ) std::cout << "All " << n_ok_ << " tests passed." << std::endl; else std::cout << n_error_ << " tests failed." << std::endl; // return n_error_ == 0; } }; } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/utility/thread_alloc.hpp ================================================ # ifndef CPPAD_UTILITY_THREAD_ALLOC_HPP # define CPPAD_UTILITY_THREAD_ALLOC_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # ifdef _MSC_VER // Suppress warning that Microsoft compiler changed its behavior and is now // doing the correct thing at the statement: // new(array + i) Type(); # pragma warning(disable:4345) # endif # include # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file thread_alloc.hpp File used to define the CppAD multi-threading allocator class */ /*! \def CPPAD_MAX_NUM_CAPACITY Maximum number of different capacities the allocator will attempt. This must be larger than the log base two of numeric_limit::max(). */ # define CPPAD_MAX_NUM_CAPACITY 100 /*! \def CPPAD_MIN_DOUBLE_CAPACITY Minimum number of double values that will fit in an allocation. */ # define CPPAD_MIN_DOUBLE_CAPACITY 16 /*! \def CPPAD_TRACE_CAPACITY If NDEBUG is not defined, print all calls to get_memory and return_memory that correspond to this capacity and thread CPPAD_TRACE_THREAD. (Note that if CPPAD_TRACE_CAPACITY is zero, or any other value not in the list of capacities, no tracing will be done.) */ # define CPPAD_TRACE_CAPACITY 0 /*! \def CPPAD_TRACE_THREAD If NDEBUG is not defined, print all calls to get_memory and return_memory that correspond to this thead and capacity CPPAD_TRACE_CAPACITY. */ # define CPPAD_TRACE_THREAD 0 /* Note that Section 3.6.2 of ISO/IEC 14882:1998(E) states: "The storage for objects with static storage duration (3.7.1) shall be zero-initialized (8.5) before any other initialization takes place." */ /*! Capacity vector for memory allocation block sizes. Only one of these objects should be created and used as a static variable inside of the thread_alloc::capacity_info function. */ /*! Allocator class that works well with an multi-threading environment. */ class thread_alloc{ // ============================================================================ private: class capacity_t { public: /// number of capacity values actually used size_t number; /// the different capacity values size_t value[CPPAD_MAX_NUM_CAPACITY]; /// ctor capacity_t(void) { // Cannot figure out how to call thread_alloc::in_parallel here. // CPPAD_ASSERT_UNKNOWN( // ! thread_alloc::in_parallel() , "thread_alloc: " // "parallel mode and parallel_setup not yet called." // ); number = 0; size_t capacity = CPPAD_MIN_DOUBLE_CAPACITY * sizeof(double); while( capacity < std::numeric_limits::max() / 2 ) { CPPAD_ASSERT_UNKNOWN( number < CPPAD_MAX_NUM_CAPACITY ); value[number++] = capacity; // next capactiy is 3/2 times the current one capacity = 3 * ( (capacity + 1) / 2 ); } CPPAD_ASSERT_UNKNOWN( number > 0 ); } }; class block_t { public: /// extra information (currently used by create and delete array) size_t extra_; /// an index that uniquely identifies both thread and capacity size_t tc_index_; /// pointer to the next memory allocation with the same tc_index_ void* next_; /// /// Calculated by include/cppad/CMakeLists.txt CPPAD_PADDING_BLOCK_T // ----------------------------------------------------------------- /// make default constructor private. It is only used by constructor /// for `root arrays below. block_t(void) : extra_(0), tc_index_(0), next_(nullptr) { } }; // --------------------------------------------------------------------- /// Vector of fixed capacity values for this allocator static const capacity_t* capacity_info(void) { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; static const capacity_t capacity; return &capacity; } // --------------------------------------------------------------------- /// Structure of information for each thread struct thread_alloc_info { /// count of available bytes for this thread size_t count_inuse_; /// count of inuse bytes for this thread size_t count_available_; /// root of available list for this thread and each capacity block_t root_available_[CPPAD_MAX_NUM_CAPACITY]; /*! root of inuse list for this thread and each capacity If NDEBUG is defined or CPPAD_DEBUG_AND_RELEASE is true, this memory is not used, but it still helps to separate this structure from the structure for the next thread. */ block_t root_inuse_[CPPAD_MAX_NUM_CAPACITY]; }; // --------------------------------------------------------------------- /*! Set and Get hold available memory flag. \param set [in] if true, the value returned by this return is changed. \param new_value [in] if set is true, this is the new value returned by this routine. Otherwise, new_value is ignored. \return the current setting for this routine (which is initially false). */ static bool set_get_hold_memory(bool set, bool new_value = false) { static bool value = false; if( set ) value = new_value; return value; } // --------------------------------------------------------------------- /*! Get pointer to the information for this thread. \param thread [in] Is the thread number for this information pointer. \param clear If clear is true, then the information pointer for this thread is deleted and the nullptr pointer is returned. There must be no memory currently in either the inuse or available lists when this routine is called. \return is the current information pointer for this thread. If clear is false, and the current pointer is nullptr, a new information record is allocated and its pointer returned. In this case, if info is the returned pointer, info->count_inuse == 0 and info->count_available == 0. In addition, for c = 0 , ... , CPPAD_MAX_NUM_CAPACITY-1 info->root_inuse_[c].next_ == nullptr and info->root_available_[c].next_ == nullptr. */ static thread_alloc_info* thread_info( size_t thread , bool clear = false ) { static thread_alloc_info* all_info[CPPAD_MAX_NUM_THREADS]; static thread_alloc_info zero_info; CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; CPPAD_ASSERT_UNKNOWN( thread < CPPAD_MAX_NUM_THREADS ); thread_alloc_info* info = all_info[thread]; if( clear ) { if( info != nullptr ) { # ifndef NDEBUG CPPAD_ASSERT_UNKNOWN( info->count_inuse_ == 0 && info->count_available_ == 0 ); for(size_t c = 0; c < CPPAD_MAX_NUM_CAPACITY; c++) { CPPAD_ASSERT_UNKNOWN( info->root_inuse_[c].next_ == nullptr && info->root_available_[c].next_ == nullptr ); } # endif if( thread != 0 ) ::operator delete( reinterpret_cast(info) ); info = nullptr; all_info[thread] = info; } } else if( info == nullptr ) { if( thread == 0 ) info = &zero_info; else { size_t size = sizeof(thread_alloc_info); void* v_ptr = ::operator new(size); info = reinterpret_cast(v_ptr); } all_info[thread] = info; // initialize the information record for(size_t c = 0; c < CPPAD_MAX_NUM_CAPACITY; c++) { info->root_inuse_[c].next_ = nullptr; info->root_available_[c].next_ = nullptr; } info->count_inuse_ = 0; info->count_available_ = 0; } return info; } // ----------------------------------------------------------------------- /*! Increase the number of bytes of memory that are currently in use; i.e., that been obtained with get_memory and not yet returned. \param inc [in] amount to increase memory in use. \param thread [in] Thread for which we are increasing the number of bytes in use (must be less than num_threads). During parallel execution, this must be the thread that is currently executing. */ static void inc_inuse(size_t inc, size_t thread) { CPPAD_ASSERT_UNKNOWN( thread < num_threads() ); CPPAD_ASSERT_UNKNOWN( thread == thread_num() || (! in_parallel()) ); thread_alloc_info* info = thread_info(thread); // do the addition size_t result = info->count_inuse_ + inc; CPPAD_ASSERT_UNKNOWN( result >= info->count_inuse_ ); info->count_inuse_ = result; } // ----------------------------------------------------------------------- /*! Increase the number of bytes of memory that are currently available; i.e., have been obtained obtained from the system and are being held future use. \copydetails inc_inuse */ static void inc_available(size_t inc, size_t thread) { CPPAD_ASSERT_UNKNOWN( thread < CPPAD_MAX_NUM_THREADS); CPPAD_ASSERT_UNKNOWN( thread == thread_num() || (! in_parallel()) ); thread_alloc_info* info = thread_info(thread); // do the addition size_t result = info->count_available_ + inc; CPPAD_ASSERT_UNKNOWN( result >= info->count_available_ ); info->count_available_ = result; } // ----------------------------------------------------------------------- /*! Decrease the number of bytes of memory that are currently in use; i.e., that been obtained with get_memory and not yet returned. \param dec [in] amount to decrease number of bytes in use. \param thread [in] Thread for which we are decreasing the number of bytes in use (must be less than num_threads). During parallel execution, this must be the thread that is currently executing. */ static void dec_inuse(size_t dec, size_t thread) { CPPAD_ASSERT_UNKNOWN( thread < num_threads() || (! in_parallel()) ); CPPAD_ASSERT_UNKNOWN( thread == thread_num() || (! in_parallel()) ); thread_alloc_info* info = thread_info(thread); // do the subtraction CPPAD_ASSERT_UNKNOWN( info->count_inuse_ >= dec ); info->count_inuse_ = info->count_inuse_ - dec; } // ----------------------------------------------------------------------- /*! Decrease the number of bytes of memory that are currently available; i.e., have been obtained obtained from the system and are being held future use. \copydetails dec_inuse */ static void dec_available(size_t dec, size_t thread) { CPPAD_ASSERT_UNKNOWN( thread < CPPAD_MAX_NUM_THREADS); CPPAD_ASSERT_UNKNOWN( thread == thread_num() || (! in_parallel()) ); thread_alloc_info* info = thread_info(thread); // do the subtraction CPPAD_ASSERT_UNKNOWN( info->count_available_ >= dec ); info->count_available_ = info->count_available_ - dec; } // ---------------------------------------------------------------------- /*! Set and get the number of threads that are sharing memory. \param number_new If number is zero, we are only retrieving the current maximum number of threads. Otherwise, we are setting and retrieving maximum number of threads. \return the number of threads that are sharing memory. If number_new is non-zero, the return value is equal to number_new. */ static size_t set_get_num_threads(size_t number_new) { static size_t number_user = 1; CPPAD_ASSERT_UNKNOWN( number_new <= CPPAD_MAX_NUM_THREADS ); CPPAD_ASSERT_UNKNOWN( ! in_parallel() || (number_new == 0) ); // case where we are changing the number of threads if( number_new != 0 ) number_user = number_new; return number_user; } /*! Set and call the routine that determine the current thread number. \return returns value for the most recent setting for thread_num_new. If set is true, or the most recent setting is nullptr (its initial value), the return value is zero. Otherwise the routine corresponding to the most recent setting is called and its value returned by set_get_thread_num. \param thread_num_new [in] If set is false, thread_num_new it is not used. Otherwise, the current value of thread_num_new becomes the most recent setting for thread_num. \param set If set is true, then thread_num_new is becomes the most recent setting for this set_get_thread_num. */ static size_t set_get_thread_num( size_t (*thread_num_new)(void) , bool set = false ) { static size_t (*thread_num_user)(void) = nullptr; if( set ) { thread_num_user = thread_num_new; return 0; } if( thread_num_user == nullptr ) return 0; size_t thread = thread_num_user(); CPPAD_ASSERT_KNOWN( thread < set_get_num_threads(0) , "parallel_setup: thread_num() >= num_threads" ); return thread; } // ============================================================================ public: /* {xrst_begin ta_parallel_setup} Setup thread_alloc For Use in Multi-Threading Environment ######################################################### Syntax ****** | ``thread_alloc::parallel_setup`` ( *num_threads* , *in_parallel* , *thread_num* ) Purpose ******* By default there is only one thread and all execution is in sequential mode, i.e., multiple threads are not sharing the same memory; i.e. not in parallel mode. Speed ***** It should be faster, even when *num_thread* is equal to one, for ``thread_alloc`` to hold onto memory. This can be accomplished using the function call ``thread_alloc::hold_memory`` ( ``true`` ) see :ref:`hold_memory` . num_threads *********** This argument has prototype ``size_t`` *num_threads* and must be greater than zero. It specifies the number of threads that are sharing memory. The case *num_threads* == 1 is a special case that is used to terminate a multi-threading environment. in_parallel *********** This function has prototype ``bool`` *in_parallel* ( ``void`` ) It must return ``true`` if there is more than one thread currently executing. Otherwise it can return false. In the special case where *num_threads* == 1 , the routine *in_parallel* is not used. thread_num ********** This function has prototype ``size_t`` *thread_num* ( ``void`` ) It must return a thread number that uniquely identifies the currently executing thread. Furthermore 0 <= *thread_num* () < *num_threads* . In the special case where *num_threads* == 1 , the routine *thread_num* is not used. Note that this function is called by other routines so, as soon as a new thread is executing, one must be certain that *thread_num* () will work for that thread. Restrictions ************ The function ``parallel_setup`` must be called before the program enters :ref:`parallel` execution mode. In addition, this function cannot be called while in parallel mode. Example ******* The files :ref:`openmp_get_started.cpp-name` , :ref:`bthread_get_started.cpp-name` , and :ref:`pthread_get_started.cpp-name` , contain examples and tests that use this function. {xrst_end ta_parallel_setup} */ /*! Set thread_alloc up for parallel mode usage. \param num_threads [in] Is the number of thread that may be executing at the same time. \param in_parallel [in] Is the routine that determines if we are in parallel mode or not. \param thread_num [in] Is the routine that determines the current thread number (between zero and num_threads minus one). */ static void parallel_setup( size_t num_threads , bool (*in_parallel)(void) , size_t (*thread_num)(void) ) { // Special case where we go back to single thread mode right away // (previous settings may no longer be valid) if( num_threads == 1 ) { bool set = true; set_get_num_threads(num_threads); // emphasize that this routine is outside thread_alloc class CppAD::local::set_get_in_parallel(set, nullptr); set_get_thread_num(nullptr, set); return; } CPPAD_ASSERT_KNOWN( num_threads <= CPPAD_MAX_NUM_THREADS , "parallel_setup: num_threads is too large" ); CPPAD_ASSERT_KNOWN( num_threads != 0 , "parallel_setup: num_threads == zero" ); CPPAD_ASSERT_KNOWN( in_parallel != nullptr , "parallel_setup: num_threads != 1 and in_parallel == nullptr" ); CPPAD_ASSERT_KNOWN( thread_num != nullptr , "parallel_setup: num_threads != 1 and thread_num == nullptr" ); // Make sure that constructors for all static variables in this file // are called in sequential mode. for(size_t thread = 0; thread < num_threads; thread++) thread_info(thread); capacity_info(); size_t cap_bytes; void* v_ptr = get_memory(0, cap_bytes); // free memory allocated by call to get_memory above return_memory(v_ptr); free_available( set_get_thread_num(nullptr) ); // delay this so thread_num() call above is in previous mode // (current settings may not yet be valid) if( num_threads > 1 ) { bool set = true; set_get_num_threads(num_threads); // emphasize that this routine is outside thread_alloc class CppAD::local::set_get_in_parallel(set, in_parallel); set_get_thread_num(thread_num, set); } } /* {xrst_begin ta_num_threads} Get Number of Threads ##################### Syntax ****** *number* = ``thread_alloc::num_threads`` () Purpose ******* Determine the number of threads as set during :ref:`parallel_setup` . number ****** The return value *number* has prototype ``size_t`` *number* and is equal to the value of :ref:`ta_parallel_setup@num_threads` in the previous call to *parallel_setup* . If there was no such previous call, the value one is returned. Example ******* The example and test :ref:`thread_alloc.cpp-name` uses this routine. {xrst_end ta_num_threads} */ /*! Get the current number of threads that thread_alloc can use. */ static size_t num_threads(void) { return set_get_num_threads(0); } /* ----------------------------------------------------------------------- {xrst_begin ta_in_parallel} Is The Current Execution in Parallel Mode ######################################### Syntax ****** *flag* = ``thread_alloc::in_parallel`` () Purpose ******* Some of the :ref:`thread_alloc-name` allocation routines have different specifications for parallel (not sequential) execution mode. This routine enables you to determine if the current execution mode is sequential or parallel. flag **** The return value has prototype ``bool`` *flag* It is true if the current execution is in parallel mode (possibly multi-threaded) and false otherwise (sequential mode). Example ******* :ref:`thread_alloc.cpp-name` {xrst_end ta_in_parallel} */ /// Are we in a parallel execution state; i.e., is it possible that /// other threads are currently executing. static bool in_parallel(void) { // emphasize that this routine is outside thread_alloc class return CppAD::local::set_get_in_parallel(); } /* ----------------------------------------------------------------------- {xrst_begin ta_thread_num} Get the Current Thread Number ############################# Syntax ****** *thread* = ``thread_alloc::thread_num`` () Purpose ******* Some of the :ref:`thread_alloc-name` allocation routines have a thread number. This routine enables you to determine the current thread. thread ****** The return value *thread* has prototype ``size_t`` *thread* and is the currently executing thread number. Example ******* :ref:`thread_alloc.cpp-name` {xrst_end ta_thread_num} */ /// Get current thread number static size_t thread_num(void) { return set_get_thread_num(nullptr); } /* ----------------------------------------------------------------------- {xrst_begin ta_get_memory} Get At Least A Specified Amount of Memory ######################################### Syntax ****** *v_ptr* = ``thread_alloc::get_memory`` ( *min_bytes* , *cap_bytes* ) Purpose ******* Use :ref:`thread_alloc-name` to obtain a minimum number of bytes of memory (for use by the :ref:`current thread` ). min_bytes ********* This argument has prototype ``size_t`` *min_bytes* It specifies the minimum number of bytes to allocate. This value must be less than :: std::numeric_limits::max() / 2 cap_bytes ********* This argument has prototype ``size_t&`` *cap_bytes* It's input value does not matter. Upon return, it is the actual number of bytes (capacity) that have been allocated for use, *min_bytes* <= *cap_bytes* v_ptr ***** The return value *v_ptr* has prototype ``void`` * *v_ptr* It is the location where the *cap_bytes* of memory that have been allocated for use begins. Allocation Speed **************** This allocation should be faster if the following conditions hold: #. The memory allocated by a previous call to ``get_memory`` is currently available for use. #. The current *min_bytes* is between the previous *min_bytes* and previous *cap_bytes* . Alignment ********* We call a memory allocation aligned if the address is a multiple of the number of bytes in a ``size_t`` value. If the system ``new`` allocator is aligned, then *v_ptr* pointer is also aligned. Example ******* :ref:`thread_alloc.cpp-name` {xrst_end ta_get_memory} */ /*! Use thread_alloc to get a specified amount of memory. If the memory allocated by a previous call to get_memory is now available, and min_bytes is between its previous value and the previous cap_bytes, this memory allocation will have optimal speed. Otherwise, the memory allocation is more complicated and may have to wait for other threads to complete an allocation. \param min_bytes [in] The minimum number of bytes of memory to be obtained for use. \param cap_bytes [out] The actual number of bytes of memory obtained for use. \return pointer to the beginning of the memory allocated for use. */ static void* get_memory(size_t min_bytes, size_t& cap_bytes) { // see first_trace below CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; // check that number of requested bytes is not to large CPPAD_ASSERT_KNOWN( min_bytes < std::numeric_limits::max() / 2 , "get_memory(min_bytes, cap_bytes): min_bytes is too large" ); size_t num_cap = capacity_info()->number; using std::cout; using std::endl; // determine the capacity for this request size_t c_index = 0; const size_t* capacity_vec = capacity_info()->value; while( capacity_vec[c_index] < min_bytes ) { ++c_index; CPPAD_ASSERT_UNKNOWN(c_index < num_cap ); } cap_bytes = capacity_vec[c_index]; // determine the thread, capacity, and info for this thread size_t thread = thread_num(); size_t tc_index = thread * num_cap + c_index; thread_alloc_info* info = thread_info(thread); # ifndef NDEBUG // trace allocation static bool first_trace = true; if( cap_bytes == CPPAD_TRACE_CAPACITY && thread == CPPAD_TRACE_THREAD && first_trace ) { cout << endl; cout << "thread_alloc: Trace for Thread = " << thread; cout << " and capacity = " << cap_bytes << endl; if( first_trace ) first_trace = false; } # if ! CPPAD_DEBUG_AND_RELEASE // Root nodes for both lists. Note these are different for different // threads because tc_index is different for different threads. block_t* inuse_root = info->root_inuse_ + c_index; # endif # endif block_t* available_root = info->root_available_ + c_index; // check if we already have a node we can use void* v_node = available_root->next_; block_t* node = reinterpret_cast(v_node); if( node != nullptr ) { CPPAD_ASSERT_UNKNOWN( node->tc_index_ == tc_index ); // remove node from available list available_root->next_ = node->next_; // return value for get_memory void* v_ptr = reinterpret_cast(node + 1); # ifndef NDEBUG # if ! CPPAD_DEBUG_AND_RELEASE // add node to inuse list node->next_ = inuse_root->next_; inuse_root->next_ = v_node; # endif // trace allocation if( cap_bytes == CPPAD_TRACE_CAPACITY && thread == CPPAD_TRACE_THREAD ) { cout << "get_memory: v_ptr = " << v_ptr << endl; } # endif // adjust counts inc_inuse(cap_bytes, thread); dec_available(cap_bytes, thread); # ifndef NDEBUG // check that pointers and doubles are aligned std::uintptr_t i_ptr = reinterpret_cast(v_ptr); CPPAD_ASSERT_UNKNOWN( i_ptr % sizeof(v_ptr) == 0 ); CPPAD_ASSERT_UNKNOWN( i_ptr % sizeof(double) == 0 ); # endif // return pointer to memory, do not include block_t at beginning return v_ptr; } // Create a new node with thread_alloc information at front. // This uses the system allocator, which is thread safe, but slower, // because the thread might wait for a lock on the allocator. v_node = ::operator new(sizeof(block_t) + cap_bytes); CPPAD_ASSERT_UNKNOWN( v_node != nullptr ); node = reinterpret_cast(v_node); node->tc_index_ = tc_index; void* v_ptr = reinterpret_cast(node + 1); # ifndef NDEBUG # if ! CPPAD_DEBUG_AND_RELEASE // add node to inuse list node->next_ = inuse_root->next_; inuse_root->next_ = v_node; # endif // trace allocation if( cap_bytes == CPPAD_TRACE_CAPACITY && thread == CPPAD_TRACE_THREAD ) { cout << "get_memory: v_ptr = " << v_ptr << endl; } # endif // adjust counts inc_inuse(cap_bytes, thread); return v_ptr; } /* ----------------------------------------------------------------------- {xrst_begin ta_return_memory} Return Memory to thread_alloc ############################# Syntax ****** ``thread_alloc::return_memory`` ( *v_ptr* ) Purpose ******* If :ref:`hold_memory` is false, the memory is returned to the system. Otherwise, the memory is retained by :ref:`thread_alloc-name` for quick future use by the thread that allocated to memory. v_ptr ***** This argument has prototype ``void`` * *v_ptr* . It must be a pointer to memory that is currently in use; i.e. obtained by a previous call to :ref:`get_memory` and not yet returned. Thread ****** Either the :ref:`current thread` must be the same as during the corresponding call to :ref:`get_memory` , or the current execution mode must be sequential (not :ref:`parallel` ). NDEBUG ****** If ``NDEBUG`` is defined, *v_ptr* is not checked (this is faster). Otherwise, a list of in use pointers is searched to make sure that *v_ptr* is in the list. Example ******* :ref:`thread_alloc.cpp-name` {xrst_end ta_return_memory} */ /*! Return memory that was obtained by get_memory. If num_threads() == 1, the memory is returned to the system. Otherwise, it is retained by thread_alloc and available for use by get_memory for this thread. \param v_ptr [in] Value of the pointer returned by get_memory and still in use. After this call, this pointer will available (and not in use). \par We must either be in sequential (not parallel) execution mode, or the current thread must be the same as for the corresponding call to get_memory. */ static void return_memory(void* v_ptr) { size_t num_cap = capacity_info()->number; block_t* node = reinterpret_cast(v_ptr) - 1; size_t tc_index = node->tc_index_; size_t thread = tc_index / num_cap; size_t c_index = tc_index % num_cap; size_t capacity = capacity_info()->value[c_index]; CPPAD_ASSERT_UNKNOWN( thread < CPPAD_MAX_NUM_THREADS ); CPPAD_ASSERT_KNOWN( thread == thread_num() || (! in_parallel()), "Attempt to return memory for a different thread " "while in parallel mode" ); thread_alloc_info* info = thread_info(thread); # ifndef NDEBUG # if ! CPPAD_DEBUG_AND_RELEASE // remove node from inuse list void* v_node = reinterpret_cast(node); block_t* inuse_root = info->root_inuse_ + c_index; block_t* previous = inuse_root; while( (previous->next_ != nullptr) && (previous->next_ != v_node) ) previous = reinterpret_cast(previous->next_); // check that v_ptr is valid if( previous->next_ != v_node ) { using std::endl; std::ostringstream oss; oss << "return_memory: attempt to return memory not in use"; oss << endl; oss << "v_ptr = " << v_ptr << endl; oss << "thread = " << thread << endl; oss << "capacity = " << capacity << endl; oss << "See CPPAD_TRACE_THREAD & CPPAD_TRACE_CAPACITY in"; oss << endl << "# include " << endl; // oss.str() returns a string object with a copy of the current // contents in the stream buffer. std::string msg_str = oss.str(); // msg_str.c_str() returns a pointer to the c-string // representation of the string object's value. const char* msg_char_star = msg_str.c_str(); CPPAD_ASSERT_KNOWN(false, msg_char_star ); } // remove v_ptr from inuse list previous->next_ = node->next_; # endif // trace option if( capacity==CPPAD_TRACE_CAPACITY && thread==CPPAD_TRACE_THREAD ) { std::cout << "return_memory: v_ptr = " << v_ptr << std::endl; } # endif // capacity bytes are removed from the inuse pool dec_inuse(capacity, thread); // check for case where we just return the memory to the system if( ! set_get_hold_memory(false) ) { ::operator delete( reinterpret_cast(node) ); return; } // add this node to available list for this thread and capacity block_t* available_root = info->root_available_ + c_index; node->next_ = available_root->next_; available_root->next_ = reinterpret_cast(node); // capacity bytes are added to the available pool inc_available(capacity, thread); } /* ----------------------------------------------------------------------- {xrst_begin ta_free_available} {xrst_spell inuse } Free Memory Currently Available for Quick Use by a Thread ######################################################### Syntax ****** ``thread_alloc::free_available`` ( *thread* ) Purpose ******* Return to the system all the memory that is currently being :ref:`held` for quick use by the specified thread. Extra Memory ============ In the case where *thread* > 0 , some extra memory is used to track allocations by the specified thread. If ``thread_alloc::inuse`` ( *thread* ) == 0 the extra memory is also returned to the system. thread ****** This argument has prototype ``size_t`` *thread* Either :ref:`thread_num` must be the same as *thread* , or the current execution mode must be sequential (not :ref:`parallel` ). Example ******* :ref:`thread_alloc.cpp-name` {xrst_end ta_free_available} */ /*! Return all the memory being held as available for a thread to the system. \param thread [in] this thread that will no longer have any available memory after this call. This must either be the thread currently executing, or we must be in sequential (not parallel) execution mode. */ static void free_available(size_t thread) { CPPAD_ASSERT_KNOWN( thread < CPPAD_MAX_NUM_THREADS, "Attempt to free memory for a thread >= CPPAD_MAX_NUM_THREADS" ); CPPAD_ASSERT_KNOWN( thread == thread_num() || (! in_parallel()), "Attempt to free memory for a different thread " "while in parallel mode" ); size_t num_cap = capacity_info()->number; if( num_cap == 0 ) return; const size_t* capacity_vec = capacity_info()->value; size_t c_index; thread_alloc_info* info = thread_info(thread); for(c_index = 0; c_index < num_cap; c_index++) { size_t capacity = capacity_vec[c_index]; block_t* available_root = info->root_available_ + c_index; void* v_ptr = available_root->next_; while( v_ptr != nullptr ) { block_t* node = reinterpret_cast(v_ptr); void* next = node->next_; ::operator delete(v_ptr); v_ptr = next; dec_available(capacity, thread); } available_root->next_ = nullptr; } CPPAD_ASSERT_UNKNOWN( available(thread) == 0 ); if( inuse(thread) == 0 ) { // clear the information for this thread thread_info(thread, true); } } /* ----------------------------------------------------------------------- {xrst_begin ta_hold_memory} Control When Thread Alloc Retains Memory For Future Use ####################################################### Syntax ****** ``thread_alloc::hold_memory`` ( *value* ) Purpose ******* It should be faster, even when *num_thread* is equal to one, for ``thread_alloc`` to hold onto memory. Calling *hold_memory* with *value* equal to true, instructs ``thread_alloc`` to hold onto memory, and put it in the :ref:`available` pool, after each call to :ref:`return_memory` . value ***** If *value* is true, ``thread_alloc`` with hold onto memory for future quick use. If it is false, future calls to :ref:`return_memory` will return the corresponding memory to the system. By default (when ``hold_memory`` has not been called) ``thread_alloc`` does not hold onto memory. free_available ************** Memory that is being held by ``thread_alloc`` can be returned to the system using :ref:`free_available` . {xrst_end ta_hold_memory} */ /*! Change the thread_alloc hold memory setting. \param value [in] New value for the thread_alloc hold memory setting. */ static void hold_memory(bool value) { bool set = true; set_get_hold_memory(set, value); } /* ----------------------------------------------------------------------- {xrst_begin ta_inuse} Amount of Memory a Thread is Currently Using ############################################ Syntax ****** *num_bytes* = ``thread_alloc::inuse`` ( *thread* ) Purpose ******* Memory being managed by :ref:`thread_alloc-name` has two states, currently in use by the specified thread, and quickly available for future use by the specified thread. This function informs the program how much memory is in use. thread ****** This argument has prototype ``size_t`` *thread* Either :ref:`thread_num` must be the same as *thread* , or the current execution mode must be sequential (not :ref:`parallel` ). num_bytes ********* The return value has prototype ``size_t`` *num_bytes* It is the number of bytes currently in use by the specified thread. Example ******* :ref:`thread_alloc.cpp-name` {xrst_end ta_inuse} */ /*! Determine the amount of memory that is currently inuse. \param thread [in] Thread for which we are determining the amount of memory (must be < CPPAD_MAX_NUM_THREADS). During parallel execution, this must be the thread that is currently executing. \return The amount of memory in bytes. */ static size_t inuse(size_t thread) { CPPAD_ASSERT_UNKNOWN( thread < CPPAD_MAX_NUM_THREADS); CPPAD_ASSERT_UNKNOWN( thread == thread_num() || (! in_parallel()) ); thread_alloc_info* info = thread_info(thread); return info->count_inuse_; } /* ----------------------------------------------------------------------- {xrst_begin ta_available} Amount of Memory Available for Quick Use by a Thread #################################################### Syntax ****** *num_bytes* = ``thread_alloc::available`` ( *thread* ) Purpose ******* Memory being managed by :ref:`thread_alloc-name` has two states, currently in use by the specified thread, and quickly available for future use by the specified thread. This function informs the program how much memory is available. thread ****** This argument has prototype ``size_t`` *thread* Either :ref:`thread_num` must be the same as *thread* , or the current execution mode must be sequential (not :ref:`parallel` ). num_bytes ********* The return value has prototype ``size_t`` *num_bytes* It is the number of bytes currently available for use by the specified thread. Example ******* :ref:`thread_alloc.cpp-name` {xrst_end ta_available} */ /*! Determine the amount of memory that is currently available for use. \copydetails inuse */ static size_t available(size_t thread) { CPPAD_ASSERT_UNKNOWN( thread < CPPAD_MAX_NUM_THREADS); CPPAD_ASSERT_UNKNOWN( thread == thread_num() || (! in_parallel()) ); thread_alloc_info* info = thread_info(thread); return info->count_available_; } /* ----------------------------------------------------------------------- {xrst_begin ta_create_array} Allocate An Array and Call Default Constructor for its Elements ############################################################### Syntax ****** *array* = ``thread_alloc::create_array<`` *Type* >( *size_min* , *size_out* ) . Purpose ******* Create a new raw array using :ref:`thread_alloc-name` memory allocator (works well in a multi-threading environment) and call default constructor for each element. Type **** The type of the elements of the array. size_min ******** This argument has prototype ``size_t`` *size_min* This is the minimum number of elements that there can be in the resulting *array* . size_out ******** This argument has prototype ``size_t&`` *size_out* The input value of this argument does not matter. Upon return, it is the actual number of elements in *array* ( *size_min* <= *size_out* ). array ***** The return value *array* has prototype *Type* * *array* It is array with *size_out* elements. The default constructor for *Type* is used to initialize the elements of *array* . Note that :ref:`delete_array` should be used to destroy the array when it is no longer needed. Delta ***** The amount of memory :ref:`inuse` by the current thread, will increase *delta* where ``sizeof`` ( *Type* ) * ( *size_out* + 1) > *delta* >= ``sizeof`` ( *Type* ) * *size_out* The :ref:`available` memory will decrease by *delta* , (and the allocation will be faster) if a previous allocation with *size_min* between its current value and *size_out* is available. Alignment ********* We call a memory allocation aligned if the address is a multiple of the number of bytes in a ``size_t`` value. If the system ``new`` allocator is aligned, then *array* pointer is also aligned. Example ******* :ref:`thread_alloc.cpp-name` {xrst_end ta_create_array} */ /*! Use thread_alloc to allocate an array, then call default constructor for each element. \tparam Type The type of the elements of the array. \param size_min [in] The minimum number of elements in the array. \param size_out [out] The actual number of elements in the array. \return pointer to the first element of the array. The default constructor is used to initialize all the elements of the array. \par The extra_ field, in the thread_alloc node before the return value, is set to size_out. */ template static Type* create_array(size_t size_min, size_t& size_out) { // minimum number of bytes to allocate size_t min_bytes = size_min * sizeof(Type); // do the allocation size_t num_bytes; void* v_ptr = get_memory(min_bytes, num_bytes); // This is where the array starts Type* array = reinterpret_cast(v_ptr); // number of Type values in the allocation size_out = num_bytes / sizeof(Type); // store this number in the extra field block_t* node = reinterpret_cast(v_ptr) - 1; node->extra_ = size_out; // call default constructor for each element size_t i; for(i = 0; i < size_out; i++) new(array + i) Type(); return array; } /* ----------------------------------------------------------------------- {xrst_begin ta_delete_array} {xrst_spell deallocate } Deallocate An Array and Call Destructor for its Elements ######################################################## Syntax ****** ``thread_alloc::delete_array`` ( *array* ) . Purpose ******* Returns memory corresponding to an array created by (create by :ref:`create_array` ) to the :ref:`available` memory pool for the current thread. Type **** The type of the elements of the array. array ***** The argument *array* has prototype *Type* * *array* It is a value returned by :ref:`create_array` and not yet deleted. The *Type* destructor is called for each element in the array. Thread ****** The :ref:`current thread` must be the same as when :ref:`create_array` returned the value *array* . There is an exception to this rule: when the current execution mode is sequential (not :ref:`parallel` ) the current thread number does not matter. Delta ***** The amount of memory :ref:`inuse` will decrease by *delta* , and the :ref:`available` memory will increase by *delta* , where :ref:`ta_create_array@Delta` is the same as for the corresponding call to ``create_array`` . Example ******* :ref:`thread_alloc.cpp-name` {xrst_end ta_delete_array} */ /*! Return Memory Used for an Array to the Available Pool (include destructor call for each element). \tparam Type The type of the elements of the array. \param array [in] A value returned by create_array that has not yet been deleted. The Type destructor is used to destroy each of the elements of the array. \par During parallel execution, the current thread must be the same as during the corresponding call to create_array. */ template static void delete_array(Type* array) { // determine the number of values in the array block_t* node = reinterpret_cast(array) - 1; size_t size = node->extra_; // call destructor for each element size_t i; for(i = 0; i < size; i++) (array + i)->~Type(); // return the memory to the available pool for this thread thread_alloc::return_memory( reinterpret_cast(array) ); } /* ----------------------------------------------------------------------- {xrst_begin ta_free_all} Free All Memory That Was Allocated for Use by thread_alloc ########################################################## Syntax ****** *ok* = ``thread_alloc::free_all`` () . Purpose ******* Returns all memory that was used by ``thread_alloc`` to the system. ok ** The return value *ok* has prototype ``bool`` *ok* Its value will be ``true`` if all the memory can be freed. This requires that for all *thread* indices, there is no memory :ref:`inuse` ; i.e., 0 == ``thread_alloc::inuse`` ( *thread* ) Otherwise, the return value will be false. Restrictions ************ This function cannot be called while in parallel mode. Example ******* :ref:`thread_alloc.cpp-name` {xrst_end ta_free_all} */ /*! Return to the system all thread_alloc memory that is not currently inuse. \return If no thread_alloc memory is currently inuse, all memory is returned to the system and the return value is true. Otherwise the return value is false. */ static bool free_all(void) { CPPAD_ASSERT_KNOWN( ! in_parallel(), "free_all cannot be used while in parallel execution" ); bool ok = true; size_t thread = CPPAD_MAX_NUM_THREADS; while(thread--) { ok &= inuse(thread) == 0; free_available(thread); } return ok; } }; } // END_CPPAD_NAMESPACE // preprocessor symbols local to this file # undef CPPAD_MAX_NUM_CAPACITY # undef CPPAD_MIN_DOUBLE_CAPACITY # undef CPPAD_TRACE_CAPACITY # undef CPPAD_TRACE_THREAD # endif ================================================ FILE: include/cppad/utility/time_test.hpp ================================================ # ifndef CPPAD_UTILITY_TIME_TEST_HPP # define CPPAD_UTILITY_TIME_TEST_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin time_test} Determine Amount of Time to Execute a Test ########################################## Syntax ****** | # ``include `` | *time* = ``time_test`` ( *test_fun* , *time_min* ) | *time* = ``time_test`` ( *test_fun* , *time_min* , *test_size* ) | *time* = ``time_test`` ( *test_fun* , *time_min* , *test_size* , *repeat_out* ) Purpose ******* The ``time_test`` function executes a timing test and reports the amount of wall clock time for execution. Motivation ********** It is important to separate small calculation units and test them individually. This way individual changes can be tested in the context of the routine that they are in. On many machines, accurate timing of a very short execution sequences is not possible. In addition, there may be set up and tear down time for a test that we do not really want included in the timing. For this reason ``time_test`` automatically determines how many times to repeat the section of the test that we wish to time. Include ******* The file ``cppad/utility/time_test.hpp`` defines the ``time_test`` function. This file is included by ``cppad/cppad.hpp`` and it can also be included separately with out the rest of the ``CppAD`` routines. test_fun ******** The ``time_test`` argument *test_fun* is a function, or function object. In the case where *test_size* is not present, *test_fun* supports the syntax *test_fun* ( *repeat* ) In the case where *test_size* is present, *test_fun* supports the syntax *test_fun* ( *size* , *repeat* ) In either case, the return value for *test_fun* is ``void`` . size ==== If the argument *size* is present, it has prototype ``size_t`` *size* and is equal to the *test_size* argument to ``time_test`` . repeat ====== The *test_fun* argument *repeat* has prototype ``size_t`` *repeat* It specifies the number of times to repeat the test. time_min ******** The argument *time_min* has prototype ``double`` *time_min* It specifies the minimum amount of time in seconds that the repeats of *test_fun* routine should take. The *repeat* argument to *test_fun* is increased until this amount of execution time (or more) is reached. test_size ********* If this argument is present, it argument has prototype ``size_t`` *test_size* In this case *test_size* will be present, and have the same value, in each call to *test_fun* . repeat_out ********** If this argument is present, it has prototype ``size_t&`` *repeat_out* This input value of this argument does not matter. Upon return, it is the value of :ref:`time_test@test_fun@repeat` that corresponds to the return value *time* ; i.e., the total time for the repeats of the test is *total_time* = *repeat_out* * *time* time **** The return value *time* has prototype ``double`` *time* and is the number of wall clock seconds that it took to execute the repeats of *test_fun* divided by the value used for *repeat* . Timing ****** The routine :ref:`elapsed_seconds-name` will be used to determine the amount of time it took to execute the test. {xrst_toc_hidden include/cppad/utility/elapsed_seconds.hpp speed/example/time_test.cpp } Example ******* The routine :ref:`time_test.cpp-name` is an example and test of ``time_test`` . {xrst_end time_test} ----------------------------------------------------------------------- */ # include # include # include # include # include # define CPPAD_EXTRA_RUN_BEFORE_TIMING 0 namespace CppAD { // BEGIN_CPPAD_NAMESPACE /*! \file time_test.hpp \brief Function that preforms one timing test (for speed of execution). */ /*! Preform one wall clock execution timing test. \tparam Test Either the type void (*)(size_t) or a function object type that supports the same syntax. \param test The function, or function object, that supports the operation test(repeat) where repeat is the number of times to repeat the tests operation that is being timed. \param time_min is the minimum amount of time that test should take to preform the repetitions of the operation being timed. \return is the time for each execution of the test. */ template double time_test(Test test, double time_min ) { # if CPPAD_EXTRA_RUN_BEFORE_TIMING test(1); # endif size_t repeat = 0; double s0 = elapsed_seconds(); double s1 = s0; while( s1 - s0 < time_min ) { repeat = std::max(size_t(1), 2 * repeat); s0 = elapsed_seconds(); test(repeat); s1 = elapsed_seconds(); } double time = (s1 - s0) / double(repeat); return time; } /*! Preform one wall clock execution timing test. \tparam Test Either the type void (*)(size_t, size_t) or a function object type that supports the same syntax. \param test The function, or function object, that supports the operation test(size, repeat) where size is the size for this test and repeat is the number of times to repeat the tests operation that is being timed. \param time_min is the minimum amount of time that test should take to preform the repetitions of the operation being timed. \param test_size will be used for the value of size in the call to test. \return is the time for each execution of the test. */ template double time_test(Test test, double time_min, size_t test_size) { # if CPPAD_EXTRA_RUN_BEFORE_TIMING test(test_size, 1); # endif size_t repeat = 0; double s0 = elapsed_seconds(); double s1 = s0; while( s1 - s0 < time_min ) { repeat = std::max(size_t(1), 2 * repeat); s0 = elapsed_seconds(); test(test_size, repeat); s1 = elapsed_seconds(); } double time = (s1 - s0) / double(repeat); return time; } /*! Preform one wall clock execution timing test. \tparam Test Either the type void (*)(size_t, size_t) or a function object type that supports the same syntax. \param test The function, or function object, that supports the operation test(size, repeat) where size is the size for this test and repeat is the number of times to repeat the tests operation that is being timed. \param time_min is the minimum amount of time that test should take to preform the repetitions of the operation being timed. \param test_size will be used for the value of size in the call to test. \param repeat_out the return value is the number of times the test was repeated; i.e., the return value is the total time divided by repeat. \return is the time for each execution of the test. */ template double time_test( Test test, double time_min, size_t test_size, size_t& repeat_out ) { # if CPPAD_EXTRA_RUN_BEFORE_TIMING test(test_size, 1); # endif repeat_out = 0; double s0 = elapsed_seconds(); double s1 = s0; while( s1 - s0 < time_min ) { repeat_out = std::max(size_t(1), 2 * repeat_out); s0 = elapsed_seconds(); test(test_size, repeat_out); s1 = elapsed_seconds(); } double time = (s1 - s0) / double(repeat_out); return time; } } // END_CPPAD_NAMESPACE # undef CPPAD_EXTRA_RUN_BEFORE_TIMING // END PROGRAM # endif ================================================ FILE: include/cppad/utility/to_string.hpp ================================================ # ifndef CPPAD_UTILITY_TO_STRING_HPP # define CPPAD_UTILITY_TO_STRING_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin to_string} {xrst_spell long long ostringstream } Convert Certain Types to a String ################################# Syntax ****** | # ``include `` | *s* = ``to_string`` ( *value* ) . See Also ******** :ref:`base_to_string-name` , :ref:`ad_to_string-name` Purpose ******* This routine is similar to the C++11 routine ``std::to_string`` with the following differences: #. It works with C++98. #. It has been extended to the fundamental floating point types. #. It has specifications for extending to an arbitrary type; see :ref:`base_to_string-name` . #. If ```` is included, and it has been extended to a *Base* type, it automatically extends to the :ref:`AD types above Base` . #. For integer types, conversion to a string is exact. For floating point types, conversion to a string yields a value that has relative error within machine epsilon. value ***** Integer ======= The argument *value* can have the following prototype ``const`` *Integer* & *value* where *Integer* is any of the fundamental integer types; e.g., ``short int`` and ``unsigned long`` . Note that if C++11 is supported by this compilation, ``unsigned long long`` is also a fundamental integer type. Float ===== The argument *value* can have the following prototype ``const`` *Float* & *value* where *Float* is any of the fundamental floating point types; i.e., ``float`` , ``double`` , and ``long double`` . s * The return value has prototype ``std::string`` *s* and contains a representation of the specified *value* . Integer ======= If *value* is an ``Integer`` , the representation is equivalent to ``os`` << ``value`` where *os* is an ``std::ostringstream`` . Float ===== If *value* is a ``Float`` , enough digits are used in the representation so that the result is accurate to within round off error. {xrst_toc_hidden example/utility/to_string.cpp } Example ******* The file :ref:`to_string.cpp-name` contains an example and test of this routine. {xrst_end to_string} */ # include # include # include # include # include # define CPPAD_SPECIALIZE_TO_STRING_INTEGER(Type) \ template <> struct to_string_struct\ { std::string operator()(const Type& value) \ { std::stringstream os;\ os << value;\ return os.str();\ }\ }; # define CPPAD_SPECIALIZE_TO_STRING_FLOAT(Float) \ template <> struct to_string_struct\ { std::string operator()(const Float& value) \ { std::stringstream os;\ int n_digits = 1 + std::numeric_limits::digits10;\ os << std::setprecision(n_digits);\ os << value;\ return os.str();\ }\ }; namespace CppAD { // Default implementation, // each type must define its own specialization. template struct to_string_struct { std::string operator()(const Type& value) { CPPAD_ASSERT_KNOWN( false, "to_string is not implemented for this type" ); // return empty string return std::string(""); } }; // specialization for the fundamental integer types CPPAD_SPECIALIZE_TO_STRING_INTEGER(signed short) CPPAD_SPECIALIZE_TO_STRING_INTEGER(unsigned short) // CPPAD_SPECIALIZE_TO_STRING_INTEGER(signed int) CPPAD_SPECIALIZE_TO_STRING_INTEGER(unsigned int) // CPPAD_SPECIALIZE_TO_STRING_INTEGER(signed long) CPPAD_SPECIALIZE_TO_STRING_INTEGER(unsigned long) // CPPAD_SPECIALIZE_TO_STRING_INTEGER(signed long long) CPPAD_SPECIALIZE_TO_STRING_INTEGER(unsigned long long) // specialization for the fundamental floating point types CPPAD_SPECIALIZE_TO_STRING_FLOAT(float) CPPAD_SPECIALIZE_TO_STRING_FLOAT(double) CPPAD_SPECIALIZE_TO_STRING_FLOAT(long double) // link from function to function object in structure template std::string to_string(const Type& value) { to_string_struct to_str; return to_str(value); } } # undef CPPAD_SPECIALIZE_TO_STRING_FLOAT # undef CPPAD_SPECIALIZE_TO_STRING_INTEGER # endif ================================================ FILE: include/cppad/utility/track_new_del.hpp ================================================ # ifndef CPPAD_UTILITY_TRACK_NEW_DEL_HPP # define CPPAD_UTILITY_TRACK_NEW_DEL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin track_new_del app} {xrst_spell ncopy newlen newptr oldptr } Routines That Track Use of New and Delete ######################################### Deprecated 2007-07-23 ********************* All these routines have been deprecated. You should use the :ref:`thread_alloc-name` memory allocator instead (which works better in both a single thread and properly in multi-threading environment). Syntax ****** | # ``include `` | *newptr* = ``TrackNewVec`` ( *file* , *line* , *newlen* , *oldptr* ) | ``TrackDelVec`` ( *file* , *line* , *oldptr* ) | *newptr* = ``TrackExtend`` ( *file* , *line* , *newlen* , *ncopy* , *oldptr* ) | *count* = ``TrackCount`` ( *file* , *line* ) Purpose ******* These routines aid in the use of ``new[]`` and ``delete[]`` during the execution of a C++ program. Include ******* The file ``cppad/utility/track_new_del.hpp`` is included by ``cppad/cppad.hpp`` but it can also be included separately with out the rest of the CppAD include files. file **** The argument *file* has prototype ``const char`` * *file* It should be the source code file name where the call to ``TrackNew`` is located. The best way to accomplish this is the use the preprocessor symbol ``__FILE__`` for this argument. line **** The argument *line* has prototype ``int`` *line* It should be the source code file line number where the call to ``TrackNew`` is located. The best way to accomplish this is the use the preprocessor symbol ``__LINE__`` for this argument. oldptr ****** The argument *oldptr* has prototype *Type* * *oldptr* This argument is used to identify the type *Type* . newlen ****** The argument *newlen* has prototype ``size_t`` *newlen* head newptr *********** The return value *newptr* has prototype *Type* * *newptr* It points to the newly allocated vector of objects that were allocated using ``new Type`` [ *newlen* ] ncopy ***** The argument *ncopy* has prototype ``size_t`` *ncopy* This specifies the number of elements that are copied from the old array to the new array. The value of *ncopy* must be less than or equal *newlen* . TrackNewVec *********** If ``NDEBUG`` is defined, this routine only sets *newptr* = *Type* ``new`` [ *newlen* ] The value of *oldptr* does not matter (except that it is used to identify *Type* ). If ``NDEBUG`` is not defined, ``TrackNewVec`` also tracks the this memory allocation. In this case, if memory cannot be allocated :ref:`ErrorHandler-name` is used to generate a message stating that there was not sufficient memory. Macro ===== The preprocessor macro call ``CPPAD_TRACK_NEW_VEC`` ( *newlen* , *oldptr* ) expands to ``CppAD::TrackNewVec`` (__ ``FILE__`` , __ ``LINE__`` , *newlen* , *oldptr* ) Previously Deprecated ===================== The preprocessor macro ``CppADTrackNewVec`` is the same as ``CPPAD_TRACK_NEW_VEC`` and was previously deprecated. TrackDelVec *********** This routine is used to a vector of objects that have been allocated using ``TrackNew`` or ``TrackExtend`` . If ``NDEBUG`` is defined, this routine only frees memory with ``delete`` [] *oldptr* If ``NDEBUG`` is not defined, ``TrackDelete`` also checks that *oldptr* was allocated by ``TrackNew`` or ``TrackExtend`` and has not yet been freed. If this is not the case, :ref:`ErrorHandler-name` is used to generate an error message. Macro ===== The preprocessor macro call ``CPPAD_TRACK_DEL_VEC`` ( *oldptr* ) expands to ``CppAD::TrackDelVec`` (__ ``FILE__`` , __ ``LINE__`` , *oldptr* ) Previously Deprecated ===================== The preprocessor macro ``CppADTrackDelVec`` is the same as ``CPPAD_TRACK_DEL_VEC`` was previously deprecated. TrackExtend *********** This routine is used to allocate a new vector (using ``TrackNewVec`` ), copy *ncopy* elements from the old vector to the new vector. If *ncopy* is greater than zero, *oldptr* must have been allocated using ``TrackNewVec`` or ``TrackExtend`` . In this case, the vector pointed to by *oldptr* must be have at least *ncopy* elements and it will be deleted (using ``TrackDelVec`` ). Note that the dependence of ``TrackExtend`` on ``NDEBUG`` is indirectly through the routines ``TrackNewVec`` and ``TrackDelVec`` . Macro ===== The preprocessor macro call ``CPPAD_TRACK_EXTEND`` ( *newlen* , *ncopy* , *oldptr* ) expands to ``CppAD::TrackExtend`` (__ ``FILE__`` , __ ``LINE__`` , *newlen* , *ncopy* , *oldptr* ) Previously Deprecated ===================== The preprocessor macro ``CppADTrackExtend`` is the same as ``CPPAD_TRACK_EXTEND`` and was previously deprecated. TrackCount ********** The return value *count* has prototype ``size_t`` *count* If ``NDEBUG`` is defined, *count* will be zero. Otherwise, it will be the number of vectors that have been allocated (by ``TrackNewVec`` or ``TrackExtend`` ) and not yet freed (by ``TrackDelete`` ). Macro ===== The preprocessor macro call ``CPPAD_TRACK_COUNT`` () expands to ``CppAD::TrackCount`` (__ ``FILE__`` , __ ``LINE__`` ) Previously Deprecated ===================== The preprocessor macro ``CppADTrackCount`` is the same as ``CPPAD_TRACK_COUNT`` and was previously deprecated. Multi-Threading *************** These routines cannot be used :ref:`in_parallel` execution mode. Use the :ref:`thread_alloc-name` routines instead. {xrst_end track_new_del} ------------------------------------------------------------------------------ */ # include # include # include # include # include # ifndef CPPAD_TRACK_DEBUG # define CPPAD_TRACK_DEBUG 0 # endif // ------------------------------------------------------------------------- # define CPPAD_TRACK_NEW_VEC(newlen, oldptr) \ CppAD::TrackNewVec(__FILE__, __LINE__, newlen, oldptr) # define CPPAD_TRACK_DEL_VEC(oldptr) \ CppAD::TrackDelVec(__FILE__, __LINE__, oldptr) # define CPPAD_TRACK_EXTEND(newlen, ncopy, oldptr) \ CppAD::TrackExtend(__FILE__, __LINE__, newlen, ncopy, oldptr) # define CPPAD_TRACK_COUNT() \ CppAD::TrackCount(__FILE__, __LINE__) // ------------------------------------------------------------------------- # define CppADTrackNewVec CPPAD_TRACK_NEW_VEC # define CppADTrackDelVec CPPAD_TRACK_DEL_VEC # define CppADTrackExtend CPPAD_TRACK_EXTEND # define CppADTrackCount CPPAD_TRACK_COUNT // ------------------------------------------------------------------------- namespace CppAD { // Begin CppAD namespace // TrackElement ------------------------------------------------------------ class TrackElement { public: std::string file; // corresponding file name int line; // corresponding line number void *ptr; // value returned by TrackNew TrackElement *next; // next element in linked list // default constructor (used to initialize root) TrackElement(void) : file(""), line(0), ptr(nullptr), next(nullptr) { } TrackElement(const char *f, int l, void *p) : file(f), line(l), ptr(p), next(nullptr) { CPPAD_ASSERT_UNKNOWN( p != nullptr); } // There is only one tracking list and it starts it here static TrackElement *Root(void) { CPPAD_ASSERT_UNKNOWN( ! thread_alloc::in_parallel() ); static TrackElement root; return &root; } // Print one tracking element static void Print(TrackElement* E) { CPPAD_ASSERT_UNKNOWN( ! thread_alloc::in_parallel() ); using std::cout; cout << "E = " << E; cout << ", E->next = " << E->next; cout << ", E->ptr = " << E->ptr; cout << ", E->line = " << E->line; cout << ", E->file = " << E->file; cout << std::endl; } // Print the linked list for a thread static void Print(void) { CPPAD_ASSERT_UNKNOWN( ! thread_alloc::in_parallel() ); using std::cout; using std::endl; TrackElement *E = Root(); // convert int(size_t) to avoid warning on _MSC_VER systems cout << "Begin Track List" << endl; while( E->next != nullptr ) { E = E->next; Print(E); } cout << "End Track List:" << endl; cout << endl; } }; // TrackError ---------------------------------------------------------------- inline void TrackError( const char *routine, const char *file, int line, const char *msg ) { CPPAD_ASSERT_UNKNOWN( ! thread_alloc::in_parallel() ); std::ostringstream buf; buf << routine << ": at line " << line << " in file " << file << std::endl << msg; std::string str = buf.str(); size_t n = str.size(); size_t i; char *message = new char[n + 1]; for(i = 0; i < n; i++) message[i] = str[i]; message[n] = '\0'; CPPAD_ASSERT_KNOWN( false , message); } // TrackNewVec --------------------------------------------------------------- # ifdef NDEBUG template Type *TrackNewVec( const char *file, int line, size_t len, Type * /* oldptr */ ) { # if CPPAD_TRACK_DEBUG static bool first = true; if( first ) { std::cout << "NDEBUG is defined for TrackNewVec" << std::endl; first = false; } # endif return (new Type[len]); } # else template Type *TrackNewVec( const char *file , int line , size_t len , Type * /* oldptr */ ) { CPPAD_ASSERT_KNOWN( ! thread_alloc::in_parallel() , "attempt to use TrackNewVec in parallel execution mode." ); // try to allocate the new memrory Type *newptr = nullptr; try { newptr = new Type[len]; } catch(...) { TrackError("TrackNewVec", file, line, "Cannot allocate sufficient memory" ); } // create tracking element void *vptr = static_cast(newptr); TrackElement *E = new TrackElement(file, line, vptr); // get the root TrackElement *root = TrackElement::Root(); // put this element at the front of linked list E->next = root->next; root->next = E; # if CPPAD_TRACK_DEBUG std::cout << "TrackNewVec: "; TrackElement::Print(E); # endif return newptr; } # endif // TrackDelVec -------------------------------------------------------------- # ifdef NDEBUG template void TrackDelVec(const char *file, int line, Type *oldptr) { # if CPPAD_TRACK_DEBUG static bool first = true; if( first ) { std::cout << "NDEBUG is defined in TrackDelVec" << std::endl; first = false; } # endif delete [] oldptr; } # else template void TrackDelVec( const char *file , int line , Type *oldptr ) { CPPAD_ASSERT_KNOWN( ! thread_alloc::in_parallel() , "attempt to use TrackDelVec in parallel execution mode." ); TrackElement *P; TrackElement *E; // search list for pointer P = TrackElement::Root(); E = P->next; void *vptr = static_cast(oldptr); while(E != nullptr && E->ptr != vptr) { P = E; E = E->next; } // check if pointer was not in list if( E == nullptr || E->ptr != vptr ) TrackError( "TrackDelVec", file, line, "Invalid value for the argument oldptr.\n" "Possible linking of debug and NDEBUG compilations of CppAD." ); # if CPPAD_TRACK_DEBUG std::cout << "TrackDelVec: "; TrackElement::Print(E); # endif // remove tracking element from list P->next = E->next; // delete allocated pointer delete [] oldptr; // delete tracking element delete E; return; } # endif // TrackExtend -------------------------------------------------------------- template Type *TrackExtend( const char *file , int line , size_t newlen , size_t ncopy , Type *oldptr ) { CPPAD_ASSERT_KNOWN( ! thread_alloc::in_parallel() , "attempt to use TrackExtend in parallel execution mode." ); # if CPPAD_TRACK_DEBUG using std::cout; cout << "TrackExtend: file = " << file; cout << ", line = " << line; cout << ", newlen = " << newlen; cout << ", ncopy = " << ncopy; cout << ", oldptr = " << oldptr; cout << std::endl; # endif CPPAD_ASSERT_KNOWN( ncopy <= newlen, "TrackExtend: ncopy is greater than newlen." ); // allocate the new memrory Type *newptr = TrackNewVec(file, line, newlen, oldptr); // copy the data size_t i; for(i = 0; i < ncopy; i++) newptr[i] = oldptr[i]; // delete the old vector if( ncopy > 0 ) TrackDelVec(file, line, oldptr); return newptr; } // TrackCount -------------------------------------------------------------- inline size_t TrackCount(const char *file, int line) { CPPAD_ASSERT_KNOWN( ! thread_alloc::in_parallel() , "attempt to use TrackCount in parallel execution mode." ); size_t count = 0; TrackElement *E = TrackElement::Root(); while( E->next != nullptr ) { ++count; E = E->next; } return count; } // --------------------------------------------------------------------------- } // End CppAD namespace // preprocessor symbols local to this file # undef CPPAD_TRACK_DEBUG # endif ================================================ FILE: include/cppad/utility/vector.hpp ================================================ # ifndef CPPAD_UTILITY_VECTOR_HPP # define CPPAD_UTILITY_VECTOR_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include // Note that CPPAD_CONST is undefined by cppad_vector_itr.hpp # define CPPAD_CONST 0 # include # undef CPPAD_LOCAL_UTILITY_CPPAD_VECTOR_ITR_HPP # define CPPAD_CONST 1 # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // ========================================================================== template class vector { // ========================================================================== /* {xrst_begin cppad_vector_member dev} Vector Class: Member Data ######################### Syntax ****** | *vec* . ``capacity`` () | *vec* . ``size`` () | *vec* . ``data`` () Type **** is the type of the elements in the array. capacity\_ ********** Number of *Type* elements in ``data_`` that have been allocated (and constructor has been called). length\_ ******** Number of *Type* elements currently in this vector. data\_ ****** Pointer to the first element of the vector (not defined and should not be used when ``capacity_`` is 0). Source ****** {xrst_spell_off} {xrst_code hpp} */ private: size_t capacity_; size_t length_; Type* data_; public: size_t capacity(void) const noexcept { return capacity_; } size_t size(void) const noexcept { return length_; } const Type* data(void) const noexcept { return data_; } Type* data(void) noexcept { return data_; } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_vector_member} ----------------------------------------------------------------------------- {xrst_begin cppad_vector_typedef dev} Vector Class: Type Definitions ############################## value_type ********** type corresponding to an element of a vector. iterator ******** type corresponding to an iterator for a vector. const_iterator ************** type corresponding to an iterator for a vector when the vector is ``const`` . {xrst_spell_off} {xrst_code hpp} */ public: typedef Type value_type; typedef local::utility::cppad_vector_itr iterator; typedef local::utility::const_cppad_vector_itr const_iterator; /* {xrst_code} {xrst_spell_on} {xrst_end cppad_vector_typedef} ----------------------------------------------------------------------------- {xrst_begin cppad_vector_ctor dev} {xrst_spell initializer } Vector Class: Constructors and Destructor ######################################### Default ******* ``vector`` < *Type* > *vec* creates an empty vector no elements and capacity zero. Sizing ****** | ``vector`` < *Type* > *vec* ( *n* ) | ``vector`` < *Type* > *vec* ( *n* , *value* ) where *n* is a ``size_t`` , ``unsigned int`` , or ``int`` . This creates the vector *vec* with *n* elements and capacity greater than or equal *n* . Value ===== If *value* is present, it is a *Type* object and all of the elements of the vector are given this value. Initializer List **************** ``vector`` < *Type > *vec* ( { *e_1* , ... , *e_n* } ) Copy **** ``vector`` < *Type* > *vec* ( *other* ) where *other* is a ``vector`` < *Type* > , creates the vector *vec* with *n* = *other* . ``size`` () elements and capacity greater than or equal *n* . Move Semantics ************** A move semantics version of the copy operator is implemented using ``swap`` . Destructor ********** If ``capacity_`` is non-zero, call the destructor for all the corresponding elements and then frees the corresponding memory. delete_data *********** Call destructor and free all the allocated elements (there are ``capacity_`` such elements). Source ****** {xrst_spell_off} {xrst_code hpp} */ public: // default vector(void) noexcept : capacity_(0), length_(0), data_(nullptr) { } // // sizing vector(size_t n) : capacity_(0), length_(0), data_(nullptr) { resize(n); } # if ! CPPAD_IS_SAME_UNSIGNED_INT_SIZE_T vector(unsigned int n) : capacity_(0), length_(0), data_(nullptr) { resize(n); } # endif vector(int n) : capacity_(0), length_(0), data_(nullptr) { resize(n); } // // sizing with value vector(size_t n, const Type& value) : capacity_(0), length_(0), data_(nullptr) { resize(n); for(size_t i = 0; i < length_; ++i) data_[i] = value; } # if ! CPPAD_IS_SAME_UNSIGNED_INT_SIZE_T vector(unsigned int n, const Type& value) : capacity_(0), length_(0), data_(nullptr) { resize(n); for(size_t i = 0; i < length_; ++i) data_[i] = value; } # endif vector(int n, const Type& value) : capacity_(0), length_(0), data_(nullptr) { resize(n); for(size_t i = 0; i < length_; ++i) data_[i] = value; } // // initializer list vector(std::initializer_list list) : capacity_(0), length_(0), data_(nullptr) { for(auto itr = list.begin(); itr != list.end(); ++itr) this->push_back(*itr); } // // copy vector(const vector& other) : capacity_(0), length_(0), data_(nullptr) { resize(other.length_); for(size_t i = 0; i < length_; i++) data_[i] = other.data_[i]; } // capacity_ is only value required to make destructor work for other // after this move semantics constructor vector(vector&& other) : capacity_(0), length_(0), data_(nullptr) { swap(other); } ~vector(void) { if( capacity_ > 0 ) delete_data(data_); } private: void delete_data(Type* data_ptr) { thread_alloc::delete_array(data_ptr); } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_vector_ctor} ----------------------------------------------------------------------------- {xrst_begin cppad_vector_size dev} Vector Class: Change Size ######################### Syntax ****** | |tab| *vec* . ``resize`` ( *n* ) | |tab| *vec* . ``clear`` () n * is a ``size_t`` , ``unsigned int`` , or ``int`` specifying the number of elements in the new version of the vector. resize ****** If *n* is less than or equal the input value of *vec* . ``capacity_`` , the only change is that *vec* . ``length_`` is set to *n* . Otherwise, new memory is allocated for the vector and *vec* . ``length_`` elements are copied from the old vector to the new one. I you do not need the old elements, you can first resize to zero and then the *n* to avoid copying the elements. clear ***** The destructor is called for all the elements of *vec* and then *vec.length_* and *vec* . ``capacity_`` are set to zero. {xrst_end cppad_vector_size} ------------------------------------------------------------------------------ */ public: # if ! CPPAD_IS_SAME_UNSIGNED_INT_SIZE_T void resize(unsigned int n) { resize( size_t(n) ); } # endif void resize(int n) { CPPAD_ASSERT_KNOWN( n >= 0, "CppAD::vector: attempt to create a vector with a negative size." ); resize( size_t(n) ); } void resize(size_t n) { if( capacity_ < n ) { if( capacity_ == 0 ) { // get new memory and set capacity data_ = thread_alloc::create_array(n, capacity_); } else { // save old information Type* old_data = data_; // get new memory and set capacity data_ = thread_alloc::create_array(n, capacity_); // copy old data for(size_t i = 0; i < length_; ++i) data_[i] = old_data[i]; // free old memory thread_alloc::delete_array(old_data); } } length_ = n; } void clear(void) { length_ = 0; // check if there is old memory to be freed if( capacity_ > 0 ) delete_data(data_); capacity_ = 0; } /* ------------------------------------------------------------------------------- {xrst_begin cppad_vector_assign dev} Vector Class: Assignment Operators ################################## Syntax ****** *vec* . ``swap`` ( *other* ) *vec* = *other* Prototype ********* {xrst_literal // BEGIN_SWAP // END_SWAP } {xrst_literal // BEGIN_MOVE_ASSIGN // END_MOVE_ASSIGN } {xrst_literal // BEGIN_ASSIGN // END_ASSIGN } swap **** Swaps ``length_`` , ``capacity_`` and ``data_`` between *vec* and *other* . Assignment ********** see :ref:`user API assignment` Move Semantics ************** The move semantics version of the assignment operator is implemented using ``swap`` . {xrst_end cppad_vector_assign} ------------------------------------------------------------------------------- */ // BEGIN_SWAP public: // swap does not do any allocation and hence is declared noexcept void swap(vector& other) noexcept // END_SWAP { // special case where vec and other are the same vector if( this == &other ) return; // std::swap(length_, other.length_ ); std::swap(capacity_, other.capacity_ ); std::swap(data_, other.data_ ); return; } // BEGIN_MOVE_ASSIGN // move assignment does not doe any allocation and hence is declared noexcept vector& operator=(vector&& other) noexcept // END_MOVE_ASSIGN { swap(other); return *this; } // BEGIN_ASSIGN vector& operator=(const vector& other) // END_ASSIGN { // avoid copying old elements resize(0); // new size for this vector resize( other.length_ ); // copy elements from other for(size_t i = 0; i < length_; i++) data_[i] = other.data_[i]; return *this; } /* ------------------------------------------------------------------------------- {xrst_begin cppad_vector_compare dev} {xrst_spell } Vector Class: Comparison Operators ################################## Syntax ****** | *result* = *vec* *op* *other* where *op* is one of the following: == , != , < , <= , > , >= . Prototype ********* {xrst_literal , // BEGIN_EQUAL, // END_EQUAL // BEGIN_NOT_EQUAL, // END_NOT_EQUAL // BEGIN_LESS_THAN, // END_LESS_THAN // BEGIN_LESS_OR_EQUAL, // END_LESS_OR_EQUAL // BEGIN_GREATER_THAN, // END_GREATER_THAN // BEGIN_GREATER_OR_EQUAL, // END_GREATER_OR_EQUAL } vec *** is the left side for the comparison operation. other ***** is the right side for the comparison operation. This vector must have the same size as *vec* . result ****** The *result* is true if the comparison | *vec* [ *i* ] *op* *other [ *i* ] true for all valid indices *i* . Otherwise, the *result* is false {xrst_end cppad_vector_compare} ------------------------------------------------------------------------------- */ // BEGIN_EQUAL public: bool operator==(const vector& other) const // END_EQUAL { bool result = true; CPPAD_ASSERT_KNOWN( length_ == other.length_ , "CppAD::vector: vec == other: size of vectors is different" ); for(size_t i = 0; i < length_; ++i) result &= data_[i] == other.data_[i]; return result; } // BEGIN_NOT_EQUAL bool operator!=(const vector& other) const // END_NOT_EQUAL { bool result = true; CPPAD_ASSERT_KNOWN( length_ == other.length_ , "CppAD::vector: vec == other: size of vectors is different" ); for(size_t i = 0; i < length_; ++i) result &= data_[i] != other.data_[i]; return result; } // BEGIN_LESS_THAN bool operator<(const vector& other) const // END_LESS_THAN { bool result = true; CPPAD_ASSERT_KNOWN( length_ == other.length_ , "CppAD::vector: vec == other: size of vectors is different" ); for(size_t i = 0; i < length_; ++i) result &= data_[i] < other.data_[i]; return result; } // BEGIN_LESS_OR_EQUAL bool operator<=(const vector& other) const // END_LESS_OR_EQUAL { bool result = true; CPPAD_ASSERT_KNOWN( length_ == other.length_ , "CppAD::vector: vec == other: size of vectors is different" ); for(size_t i = 0; i < length_; ++i) result &= data_[i] <= other.data_[i]; return result; } // BEGIN_GREATER_THAN bool operator>(const vector& other) const // END_GREATER_THAN { bool result = true; CPPAD_ASSERT_KNOWN( length_ == other.length_ , "CppAD::vector: vec == other: size of vectors is different" ); for(size_t i = 0; i < length_; ++i) result &= data_[i] > other.data_[i]; return result; } // BEGIN_GREATER_OR_EQUAL bool operator>=(const vector& other) const // END_GREATER_OR_EQUAL { bool result = true; CPPAD_ASSERT_KNOWN( length_ == other.length_ , "CppAD::vector: vec == other: size of vectors is different" ); for(size_t i = 0; i < length_; ++i) result &= data_[i] >= other.data_[i]; return result; } /* ------------------------------------------------------------------------------- {xrst_begin cppad_vector_subscript dev} Vector Class: Subscript Operator ################################ Syntax ****** | *element* = *vec* [ *i* ] | *vec* [ *i* ] = *element* Source ****** {xrst_spell_off} {xrst_code hpp} */ const Type& operator[]( size_t i) const { CPPAD_ASSERT_KNOWN( i < length_, "vector: index greater than or equal vector size" ); return data_[i]; } Type& operator[](size_t i) { CPPAD_ASSERT_KNOWN(i < length_, "vector: index greater than or equal vector size" ); return data_[i]; } template const Type& operator[]( Index i) const { return (*this)[size_t(i)]; } template Type& operator[](Index i) { return (*this)[size_t(i)]; } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_vector_subscript} ------------------------------------------------------------------------------- {xrst_begin cppad_vector_push_back dev} Vector Class: push_back ####################### Syntax ****** *vec* . ``push_back`` ( *element* ) Prototype ********* {xrst_literal // BEGIN_PUSH_BACK // END_PUSH_BACK } Documentation ************* see :ref:`use API push_back` {xrst_end cppad_vector_push_back} */ // BEGIN_PUSH_BACK void push_back(const Type& element) // END_PUSH_BACK { // case where no allocation is necessary if( length_ < capacity_ ) { data_[length_++] = element; return; } CPPAD_ASSERT_UNKNOWN( length_ == capacity_ ); // create new vector with required size vector vec(length_ + 1); // copy old data values for(size_t i = 0; i < length_; ++i) vec.data_[i] = data_[i]; // put the new element in the new vector CPPAD_ASSERT_UNKNOWN( vec.length_ == length_ + 1); vec.data_[length_] = element; // swap old and new vectors swap(vec); } /* %$$ ------------------------------------------------------------------------------- {xrst_begin cppad_vector_push_vector dev} Vector Class: push_vector ######################### Syntax ****** *vec* . ``push_vector`` ( *other* ) Prototype ********* {xrst_literal // BEGIN_PUSH_VECTOR // END_PUSH_VECTOR } Documentation ************* see :ref:`use API push_vector` {xrst_end cppad_vector_push_vector} */ // BEGIN_PUSH_VECTOR template void push_vector(const Vector& other) // END_PUSH_VECTOR { // can not use push_back because MS V++ 7.1 did not resolve // to non-template member function when scalar is used. // CheckSimpleVector(); size_t m = other.size(); // case where no allocation is necessary if( length_ + m <= capacity_ ) { for(size_t i = 0; i < m; i++) data_[length_++] = other[i]; return; } // create new vector with required size vector vec(length_ + m); // copy old data values for(size_t i = 0; i < length_; ++i) vec.data_[i] = data_[i]; // put the new elements in the new vector CPPAD_ASSERT_UNKNOWN( vec.length_ == length_ + m ); for(size_t i = 0; i < m; i++) vec.data_[length_ + i] = other[i]; // swap old and new vectors swap(vec); } /* ------------------------------------------------------------------------------ {xrst_begin cppad_vector_itr_fun dev} Vector Class: Iterator Functions ################################ Syntax ****** | ``os`` *vec* . ``begin`` () | *os* ``vec`` . *end* () Source ****** {xrst_spell_off} {xrst_code hpp} */ const_iterator begin(void) const noexcept { return const_iterator(&data_, &length_, 0); } const_iterator end(void) const noexcept { typedef typename const_iterator::difference_type difference_type; difference_type index = static_cast(length_); return const_iterator(&data_, &length_, index); } // iterator begin(void) noexcept { return iterator(&data_, &length_, 0); } iterator end(void) noexcept { typedef typename iterator::difference_type difference_type; difference_type index = static_cast(length_); return iterator(&data_, &length_, index); } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_vector_itr_fun} */ // ========================================================================= }; // END_TEMPLATE_CLASS_VECTOR // ========================================================================= /* {xrst_begin cppad_vector_output dev} Vector Class: Output #################### Syntax ****** *os* << ``vec`` Source ****** {xrst_spell_off} {xrst_code hpp} */ template std::ostream& operator << (std::ostream& os , const CppAD::vector& vec ) { os << "{ "; for(size_t i = 0; i < vec.size(); ++i) { os << vec[i]; if( i + 1 < vec.size() ) os << ", "; } os << " }"; return os; } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_vector_output} */ } // END_CPPAD_NAMESPACE // user API specifies that vector_bool.hpp is included by vector.hpp # include # endif ================================================ FILE: include/cppad/utility/vector_bool.hpp ================================================ # ifndef CPPAD_UTILITY_VECTOR_BOOL_HPP # define CPPAD_UTILITY_VECTOR_BOOL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include # include # include namespace CppAD { // BEGIN_CPPAD_NAMESPACE // ============================================================================ class vectorBool { // ============================================================================ /* {xrst_begin vector_bool_member dev} vectorBool: Member Data ####################### Syntax ****** | *vec* . ``unit_min`` () | *vec* . ``bit_per_unit`` () unit_t ****** Type used to pack multiple boolean (bit) values into one unit. Logical operations are preformed one unit at a time. bit_per_unit\_ ************** number of bits packed into each unit value in ``data_`` . n_unit\_ ******** Number of unit values in ``data_`` . length\_ ******** number of bits currently stored in this vector. data\_ ****** pointer to where the bits are stored. unit_min ******** minimum number of ``unit_t`` values that can store ``length_`` bits. Note that this is really a function of ``length_`` . size **** is the number of boolean elements in the vector. capacity ******** is the maximum number of boolean elements that will fit in the current allocation for ``data_`` . Source ****** {xrst_spell_off} {xrst_code hpp} */ private: typedef size_t unit_t; static const size_t bit_per_unit_ = std::numeric_limits::digits; size_t n_unit_; size_t length_; unit_t *data_; // size_t unit_min(void) const { if( length_ == 0 ) return 0; return (length_ - 1) / bit_per_unit_ + 1; } public: static size_t bit_per_unit(void) { return bit_per_unit_; } size_t size(void) const { return length_; } size_t capacity(void) const { return n_unit_ * bit_per_unit_; } /* {xrst_code} {xrst_spell_on} {xrst_end vector_bool_member} ------------------------------------------------------------------------------- {xrst_begin vector_bool_typedef dev} vectorBool Type Definitions ########################### value_type ********** type corresponding to the elements of this vector (note that non-const elements actually use :ref:`vectorBoolElement` ). Source ****** {xrst_spell_off} {xrst_code hpp} */ public: typedef bool value_type; /* {xrst_code} {xrst_spell_on} {xrst_end vector_bool_typedef} ---------------------------------------------------------------------------- {xrst_begin vector_bool_ctor dev} {xrst_spell initializer } vectorBool: Constructors and Destructor ####################################### Default ******* ``vectorBool`` *vec* creates an empty vector with no elements and ``n_unit_`` zero. Sizing ****** ``vectorBool`` *vec* ( *n* ) where *n* is a ``size_t`` , creates the vector *vec* with *n* elements and ``n_unit_`` greater than or equal ``unit_min()`` . Initializer *********** ``vectorBool`` *vec* ( { *b_1* , ... , *b_n* } ) Copy **** ``vector`` < *Type* > *vec* ( *other* ) where *other* is a ``vector`` < *Type* > , creates the vector *vec* with *n* = *other* . ``size`` () elements and ``n_unit_`` greater than or equal ``unit_min()`` . Destructor ********** If ``n_unit_`` is non-zero, the memory corresponding to ``data_`` is returned to thread_alloc. Source ****** {xrst_spell_off} {xrst_code hpp}: */ // default vectorBool(void) : n_unit_(0), length_(0), data_(nullptr) { } // // sizing vectorBool(size_t n) : n_unit_(0), length_(0), data_(nullptr) { resize(n); } vectorBool(int n) : n_unit_(0), length_(0), data_(nullptr) { resize( size_t(n) ); } # if ! CPPAD_IS_SAME_UNSIGNED_INT_SIZE_T vectorBool(unsigned int n) : n_unit_(0), length_(0), data_(nullptr) { resize( size_t(n) ); } # endif // // copy vectorBool(const vectorBool& other) : n_unit_(0), length_(0), data_(nullptr) { resize(other.length_); size_t n_used = unit_min(); CPPAD_ASSERT_UNKNOWN( n_used <= n_unit_ ); for(size_t i = 0; i < n_used; ++i) data_[i] = other.data_[i]; } vectorBool(std::initializer_list list) : n_unit_(0), length_(0), data_(nullptr) { for(auto itr = list.begin(); itr != list.end(); ++itr) this->push_back(*itr); } // n_unit_ is the only value necessary to make destructor work // for other after this move semantics constructor vectorBool(vectorBool&& other) : n_unit_(0), length_(0), data_(nullptr) { swap(other); } ~vectorBool(void) { clear(); } /* {xrst_code} {xrst_spell_on} {xrst_end vector_bool_ctor} ----------------------------------------------------------------------------- {xrst_begin vector_bool_size dev} vectorBool: Change Size ####################### Syntax ****** | |tab| *vec* . ``resize`` ( *n* ) | |tab| *vec* . ``clear`` () n * is a ``size_t`` or ``int`` specifying the number of elements in the new version of the vector. resize ****** If *n* is less than or equal the input value of *vec* . ``n_unit_`` times *vec* . ``bit_per_unit_`` , the only change is that *vec* . ``length_`` is set to *n* . Otherwise the old elements are freed and a new vector is created with *vec* . ``length_`` equal to *n* . clear ***** the memory allocated for this vector is freed and *vec.length_* and *vec* . ``n_unit_`` are set to zero. {xrst_end vector_bool_size} ------------------------------------------------------------------------------ */ // BEGIN_RESIZE public: # if ! CPPAD_IS_SAME_UNSIGNED_INT_SIZE_T void resize(unsigned int n) { resize( size_t(n) ); } # endif void resize(int n) { CPPAD_ASSERT_KNOWN( n >= 0, "CppAD::vector: attempt to create a vector with a negative size." ); resize( size_t(n) ); } void resize(size_t n) // END_RESIZE { length_ = n; // check if we can use the current memory size_t min_unit = unit_min(); if( n_unit_ >= min_unit ) return; // check if there is old memory to be freed if( n_unit_ > 0 ) { void* v_ptr = reinterpret_cast(data_); thread_alloc::return_memory(v_ptr); } // get new memory and set n_unit size_t min_bytes = min_unit * sizeof(unit_t); size_t cap_bytes; void* v_ptr = thread_alloc::get_memory(min_bytes, cap_bytes); data_ = reinterpret_cast(v_ptr); n_unit_ = cap_bytes / sizeof(unit_t); CPPAD_ASSERT_UNKNOWN( n_unit_ >= min_unit ); } // BEGIN_CLEAR void clear(void) // END_CLEAR { // check if there is old memory to be freed if( n_unit_ > 0 ) { void* v_ptr = reinterpret_cast(data_); thread_alloc::return_memory(v_ptr); } length_ = 0; n_unit_ = 0; } /* ------------------------------------------------------------------------------- {xrst_begin vector_bool_assign dev} vectorBool: Assignment Operators ################################ Syntax ****** | |tab| *vec* . ``swap`` ( *other* ) | |tab| *vec* = *other* Prototype ********* {xrst_literal // BEGIN_SWAP // END_SWAP } {xrst_literal // BEGIN_ASSIGN // END_ASSIGN } {xrst_literal // BEGIN_MOVE_SEMANTICS // END_MOVE_SEMANTICS } swap **** Swaps ``n_unit_`` , ``length_`` and ``data_`` between *vec* and *other* . Assignment ********** If the input value of *vec* . ``length_`` is zero, :ref:`vector_bool_size@resize` is used to change its size to be the same as other. The size of *vec* and *other* are then compared and if different, an assert with a know cause is generated. The elements of *vec* are then individually assigned to have the value of the corresponding elements of *other* . Move Semantics ************** A move semantics version of the assignment operator, implemented using ``swap`` , is defined. {xrst_end vector_bool_assign} ------------------------------------------------------------------------------- */ // BEGIN_SWAP void swap(vectorBool& other) // END_SWAP { // swap with self case if( this == &other ) return; std::swap(n_unit_, other.n_unit_ ); std::swap(length_, other.length_ ); std::swap(data_, other.data_ ); return; } // BEGIN_ASSIGN vectorBool& operator=(const vectorBool& other) // END_ASSIGN { // If original length is zero, then resize it to other. // Otherwise a length mismatch is an error. if( length_ == 0 ) resize( other.length_ ); CPPAD_ASSERT_KNOWN( length_ == other.length_ , "vectorBool: size miss match in assignment operation" ); size_t n_used = unit_min(); CPPAD_ASSERT_UNKNOWN( n_used <= n_unit_ ); for(size_t i = 0; i < n_used; i++) data_[i] = other.data_[i]; return *this; } // BEGIN_MOVE_SEMANTICS vectorBool& operator=(vectorBool&& other) // END_MOVE_SEMANTICS { CPPAD_ASSERT_KNOWN( length_ == other.length_ || (length_ == 0), "vectorBool: size miss match in assignment operation" ); swap(other); return *this; } /* ------------------------------------------------------------------------------- {xrst_begin vector_bool_subscript dev} vectorBool: Subscript Operator ############################## Syntax ****** | *target* = *vec* [ *i* ] | *vec* [ *i* ] = *source* target ****** In this syntax *vec* is ``const`` and the value *vec* [ *i* ] is a ``bool`` . source ****** In this syntax *vec* is not ``const`` and the value *vec* [ *i* ] is a :ref:`vectorBoolElement` . Source Code *********** {xrst_spell_off} {xrst_code hpp} */ bool operator[](size_t i) const { CPPAD_ASSERT_KNOWN( i < length_, "vectorBool: index greater than or equal vector size" ); size_t unit_index = i / bit_per_unit_; size_t bit_index = i - unit_index * bit_per_unit_; unit_t unit = data_[unit_index]; unit_t mask = unit_t(1) << bit_index; return (unit & mask) != 0; } local::utility::vectorBoolElement operator[](size_t i) { CPPAD_ASSERT_KNOWN( i < length_, "vectorBool: index greater than or equal vector size" ); size_t unit_index = i / bit_per_unit_; size_t bit_index = i - unit_index * bit_per_unit_; unit_t mask = unit_t(1) << bit_index; return local::utility::vectorBoolElement(data_ + unit_index , mask); } template bool operator[]( Index i) const { return (*this)[size_t(i)]; } template local::utility::vectorBoolElement operator[](Index i) { return (*this)[size_t(i)]; } /* {xrst_code} {xrst_spell_on} {xrst_end vector_bool_subscript} ------------------------------------------------------------------------------- {xrst_begin vector_bool_push_back dev} vectorBool: push_back ##################### Syntax ****** *vec* . ``push_back`` ( *element* ) Prototype ********* {xrst_literal // BEGIN_PUSH_BACK // END_PUSH_BACK } Documentation ************* see :ref:`use API push_back` {xrst_end vector_bool_push_back} */ // BEGIN_PUSH_BACK void push_back(bool element) // END_PUSH_BACK { CPPAD_ASSERT_UNKNOWN( unit_min() <= n_unit_ ); size_t old_length = length_; if( length_ + 1 > n_unit_ * bit_per_unit_ ) { CPPAD_ASSERT_UNKNOWN( unit_min() == n_unit_ ); // create new vector with requuired size vectorBool vec(length_ + 1); // copy old data values size_t n_used = unit_min(); CPPAD_ASSERT_UNKNOWN( n_used <= n_unit_ ); for(size_t i = 0; i < n_used; ++i) vec.data_[i] = data_[i]; // swap old and new vectors swap(vec); } else ++length_; CPPAD_ASSERT_UNKNOWN( length_ <= n_unit_ * bit_per_unit_ ) size_t unit_index = old_length / bit_per_unit_; size_t bit_index = old_length - unit_index * bit_per_unit_; unit_t mask = unit_t(1) << bit_index; if( element ) data_[unit_index] |= mask; else data_[unit_index] &= ~mask; } /* %$$ ------------------------------------------------------------------------------- {xrst_begin vector_bool_push_vector dev} vectorBool: push_vector ####################### Syntax ****** *vec* . ``push_vector`` ( *other* ) Prototype ********* {xrst_literal // BEGIN_PUSH_VECTOR // END_PUSH_VECTOR } Documentation ************* see :ref:`use API push_vector` {xrst_end vector_bool_push_vector} */ // BEGIN_PUSH_VECTOR template void push_vector(const Vector& other) // END_PUSH_VECTOR { CPPAD_ASSERT_UNKNOWN( unit_min() <= n_unit_ ); CheckSimpleVector(); size_t old_length = length_; size_t m = other.size(); if( length_ + m > n_unit_ * bit_per_unit_ ) { // create new vector with requuired size vectorBool vec(length_ + m); // copy old data values size_t n_used = unit_min(); CPPAD_ASSERT_UNKNOWN( n_used <= n_unit_ ); for(size_t i = 0; i < n_used; ++i) vec.data_[i] = data_[i]; // swap old and new vectors swap(vec); } else length_ += m; // // put the new elements in this vector CPPAD_ASSERT_UNKNOWN( length_ <= n_unit_ * bit_per_unit_ ) for(size_t k = 0; k < m; k++) { size_t unit_index = (old_length + k) / bit_per_unit_; size_t bit_index = (old_length + k) - unit_index * bit_per_unit_; unit_t mask = unit_t(1) << bit_index; if( other[k] ) data_[unit_index] |= mask; else data_[unit_index] &= ~mask; } } }; /* {xrst_begin vector_bool_output dev} vectorBool: Output ################## Syntax ****** *os* << ``vec`` Source ****** {xrst_spell_off} {xrst_code hpp} */ inline std::ostream& operator << (std::ostream& os , const vectorBool& vec ) { for(size_t i = 0; i < vec.size(); ++i) os << vec[i]; return os; } /* {xrst_code} {xrst_spell_on} {xrst_end vector_bool_output} */ } // END_CPPAD_NAMESPACE # endif ================================================ FILE: include/cppad/utility/xrst/cppad_vector.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin CppAD_vector} {xrst_spell citr cout dereference endl initializer ostream rvalues typename } The CppAD::vector Template Class ################################ Syntax ****** | # ``include `` | # ``include `` | ``CppAD::vector<`` *Scalar* > *vec* , *other* Description *********** The include file ``cppad/vector.hpp`` defines the vector template class ``CppAD::vector`` . This is a :ref:`SimpleVector-name` template class and in addition it has the features listed below. The purposes for this template vector class are as follows: #. If ``NDEBUG`` is not defined, it checks for all memory accesses to make sure the corresponding index is valid. This includes when using its :ref:`CppAD_vector@Iterators` #. It has a simple set of private member variables that make it easy to understand when viewing its values in a C++ debugger. #. It uses the :ref:`thread_alloc-name` memory allocator which makes it fast in a multi-threading environment; see :ref:`CppAD_vector@Memory and Parallel Mode` . #. The operations it has are like the corresponding ``std::vector`` operation so it is easy to use. Include ******* The files ``cppad/utility/vector.hpp`` and ``cppad/utility/vector_bool.hpp`` are included by ``cppad/cppad.hpp`` . They can also be included separately with out the rest of the CppAD include files. Deprecated 2019-08-19 ===================== The file ``cppad/utility/vector.hpp`` includes the ``cppad/utility/vector_bool.hpp`` because they used to be one file. If you want :ref:`CppAD_vector@vectorBool` , and not the rest of CppAD, you should include ``cppad/utility/vector_bool.hpp`` . Size Constructor **************** The size *n* in the constructor syntax below can be an ``int`` or ``unsigned int`` (all simple vectors support ``size_t`` ): ``CppAD::vector<`` *Scalar* > *vec* ( *n* ) Value ===== The size constructor can specify a *value* to assign to all of the vector elements: ``CppAD::vector<`` *Scalar* > *vec* ( *n* , *value* ) where *value* is a *Scalar* object. Initializer Constructor *********************** If *e_1* , ... , *e_n* are values of type *Scalar*, the syntax below constructs a vector with the corresponding element values. ``CppAD::vector`` < *Scalar* > *vec* ( { *e_1* , ... , *e_n* ) capacity ******** If *cap* is a ``size_t`` object, *cap* = *vec* . ``capacity`` () set *cap* to the number of *Scalar* objects that could fit in the memory currently allocated for *vec* . Note that *vec* . ``size`` () <= *vec* . ``capacity`` () swap **** *vec* . ``swap`` ( *other* ) exchanges the contents of *vec* and *other* . For example :ref:`vec.data()` after the ``swap`` is equal to *other* . ``data`` () before ``swap`` . Assignment ********** *vec* = *other* has all the properties listed for a :ref:`simple vector assignment` plus the following: Check Size ========== It is no longer necessary for *vec* to have the same size as *other* . This makes ``CppAD::vector`` more like ``std::vector`` . Return Reference ================ A reference to the vector *vec* is returned. An example use of this reference is in multiple assignments of the form *vec* = *other* = *another* where *another* is a ``CppAD::vector<`` *Scalar* > object. Move Semantics ============== If the C++ compiler supports move semantic rvalues using the ``&&`` syntax, then it will be used during the vector assignment statement. This means that return values and other temporaries are not be copied, but rather pointers are transferred. Comparison ********** If *op* is == , != , < , <= , > , >= and *result* is a ``bool`` , and *result* = *vec* *op* *other* *result* is true if the comparison *vec* [ *i* ] *op* *other [ *i* ] is true for all valid indices *i* . Otherwise, the *result* is false Element Access ************** If *i* has type ``size_t`` , *vec* [ *i* ] has all the properties listed for a :ref:`simple vector element access` plus the following: i = This operation is defined for any *i* that has a conversion to ``size_t`` . The object *vec* [ *i* ] has type *Scalar* (is not possibly a different type that can be converted to *Scalar* ). Error Checking ============== If *i* is not less than the size of the *vec* , and ``NDEBUUG`` is not defined, ``CppAD::vector`` will use :ref:`ErrorHandler-name` to generate an appropriate error report. push_back ********* If *vec* has size *n* and *scalar* has type *Scalar* , *vec* . ``push_back`` ( *scalar* ) extends the vector so that its new size is *n* +1 and *vec* [ *n* ] is equal to *s* (equal in the sense of the *Scalar* assignment operator). push_vector *********** If *vec* has size *n* and *simple_vec* is a :ref:`simple vector` with elements of type *Scalar* and size *m* , *vec* . ``push_vector`` ( *simple_vec* ) extends the vector *vec* so that its new size is *n* + *m* and *vec* [ *n* + *i* ] is equal to *simple_vec* [ *i* ] for *i* = 1 , ... , *m-1* (equal in the sense of the *Scalar* assignment operator). Output ****** If *os* is an ``std::ostream`` , the operation *os* << *vec* will output *vec* to the standard output stream *os* . The elements of *vec* are enclosed at the beginning by a ``{`` character, they are separated by ``,`` characters, and they are enclosed at the end by ``}`` character. It is assumed by this operation that if *scalar* is an object with type *Scalar* , *os* << *scalar* will output the value *scalar* to *os* . resize ****** If *n* is a ``size_t`` or ``int`` , *vec* . ``resize`` ( *n* ) sets the size of *vec* equal to *n* . data ==== The elements in *vec* before the resize operation are preserved. memory ====== If before the resize, *n* <= *vec* . ``capacity`` () , no memory is freed or allocated and the capacity of *vec* does not change. Otherwise, new memory is allocated and the elements before the resize are copied to the new memory. If you do not need to the elements previously in the vector, you can resize to zero and then to the new size to avoid the copy. clear ***** *vec* . ``clear`` () frees all memory allocated for *vec* and both its size and capacity are set to zero. This can be useful when using very large vectors and when checking for memory leaks (and there are global vectors) see the :ref:`memory` discussion. data **** *vec* . ``data`` () returns a pointer to a *Scalar* object such that for 0 <= *i* < *vec* . ``size`` () , *vec* [ *i* ] and *vec* . ``data`` ()[ *i* ] are the same *Scalar* object. If *vec* is ``const`` , the pointer is ``const`` . If *vec* . ``capacity`` () is zero, the value of the pointer is not defined. The pointer may no longer be valid after the following operations on *vec* : its destructor, ``clear`` , ``resize`` , ``push_back`` , ``push_vector`` , assignment to another vector when original size of *vec* is zero. Iterators ********* Syntax ====== | ``typename CppAD::vector<`` *Scalar* >:: ``iterator`` *itr* | ``typename CppAD::vector<`` *Scalar* >:: ``const_iterator`` *citr* | *vec* . ``begin`` () | *vec* . ``end`` () itr === is a random access iterator type for non ``const`` objects. citr ==== is a random access iterator type for a ``const`` objects. An ``iterator`` can be converted to a ``const_iterator`` , but not the other way around. begin ===== is an iterator corresponding to the first element of the vector. It is a ``const_iterator`` (``iterator`` ) depending on if *vec* is ``const`` (not ``const`` ) end === is an iterator corresponding to just beyond the last element of the vector. It is a ``const_iterator`` (``iterator`` ) depending on if *vec* is ``const`` (not ``const`` ) operator[] ========== The syntax *itr* [ *i* ] and *citr* [ *i* ] is extended (from a normal random access iterator) to include the case where *i* is ``size_t`` object. Error Checking ============== Each element access (dereference of the iterator) does an error check similar to the element access :ref:`CppAD_vector@Element Access@Error Checking` above. The error handler will also be called, if ``NDEBUG`` is not defined and a comparison operator (e.g. ``>`` ) is used between two iterators that correspond to different vectors. vectorBool ********** The file ```` defines the class ``CppAD::vectorBool`` . This has the same specifications as ``CppAD::vector`` with the following exceptions: Memory ====== The class ``vectorBool`` conserves on memory, on the other hand, ``CppAD::vector`` is expected to be faster than ``vectorBool`` . bit_per_unit ============ The static function call *size* = ``vectorBool::bit_per_unit`` () returns the ``size_t`` value *s* which is equal to the number of boolean values (bits) that are packed into one operation unit. Bits are accessed using a mask with the size of an operation unit. data ==== The :ref:`CppAD_vector@data` function is not supported by ``vectorBool`` . Iterators ========= The :ref:`CppAD_vector@Iterators` are not supported by ``vectorBool`` . Output ====== The ``CppAD::vectorBool`` output operator prints each boolean value as a ``0`` for false, a ``1`` for true, and does not print any other output; i.e., the vector is written a long sequence of zeros and ones with no surrounding ``{`` , ``}`` and with no separating commas or spaces. Element Type ============ If *vec_bool* has type ``vectorBool`` and *i* has type ``size_t`` , the element access value *vec_bool* [ *i* ] has an unspecified type, referred to here as *element_t* , that supports the following operations: #. *element_t* can be converted to ``bool`` ; e.g. the following syntax is supported: ``static_cast`` ( *vec_bool* [ *i* ] ) #. *element_t* supports the assignment operator ``=`` where the right hand side is a ``bool`` or an *element_t* object; e.g., if *flag* has type ``bool`` , the following syntax is supported: *vec_bool* [ *i* ] = *flag* #. The result of an assignment to an *element_t* also has type *element_t* . For example, if *other_flag* has type ``bool`` , the following syntax is supported: *other_flag* = *vec_bool* [ *i* ] = *flag* Memory and Parallel Mode ************************ These vectors use the multi-threaded fast memory allocator :ref:`thread_alloc-name` : #. The :ref:`hold_memory` routine can be used to make memory allocation faster. #. The routine :ref:`parallel_setup` must be called before these vectors can be used :ref:`in parallel` . #. Using these vectors affects the amount of memory :ref:`in_use` and :ref:`available` . #. Calling :ref:`CppAD_vector@clear` , makes the corresponding memory available (though ``thread_alloc`` ) to the current thread. #. Available memory can then be completely freed using :ref:`free_available` . Example ******* {xrst_toc_hidden example/utility/cppad_vector.cpp example/utility/vector_bool.cpp } The files :ref:`cppad_vector.cpp-name` and :ref:`vector_bool.cpp-name` each contain an example and test of this template class. They return true if they succeed and false otherwise. Exercise ******** Create and run a program that contains the following code: :: CppAD::vector x(3); size_t i; for(i = 0; i < 3; i++) x[i] = 4. - i; std::cout << "x = " << x << std::endl; {xrst_end CppAD_vector} ================================================ FILE: include/cppad/utility/xrst/dev_cppad_vector.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin dev_cppad_vector dev} The CppAD Vector Template Class Developer Documentation ####################################################### User API ******** :ref:`cppad_vector-name` Contents ******** {xrst_toc_table include/cppad/local/utility/cppad_vector_itr.hpp include/cppad/utility/vector.hpp } {xrst_end dev_cppad_vector} ================================================ FILE: include/cppad/utility/xrst/dev_utility.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin dev_utility dev} General Purpose Utilities Developer Documentation ################################################# Contents ******** {xrst_toc_table include/cppad/utility/xrst/dev_cppad_vector.xrst include/cppad/utility/xrst/dev_vector_bool.xrst } {xrst_end dev_utility} ================================================ FILE: include/cppad/utility/xrst/dev_vector_bool.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin dev_vector_bool dev} vectorBool Developer Documentation ################################## User API ******** :ref:`CppAD_vector@vectorBool` Contents ******** {xrst_toc_table include/cppad/local/utility/vector_bool.hpp include/cppad/utility/vector_bool.hpp } {xrst_end dev_vector_bool} ================================================ FILE: include/cppad/utility/xrst/utility.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin utility} {xrst_spell boolofvoid dll rc rcv rosen runge } Some General Purpose Utilities ############################## These routines can be included individually; for example, :: # include only includes the definitions necessary for the ``CppAD::vector`` class. They can also be included as a group, separate from the rest of CppAD, using :: # include They will also be included, along with the rest of CppAD, using :: # include {xrst_comment BEGIN_SORT_THIS_LINE_PLUS_2} {xrst_toc_hidden include/cppad/utility/check_numeric_type.hpp include/cppad/utility/check_simple_vector.hpp include/cppad/utility/create_dll_lib.hpp include/cppad/utility/error_handler.hpp include/cppad/utility/index_sort.hpp include/cppad/utility/link_dll_lib.hpp include/cppad/utility/nan.hpp include/cppad/utility/near_equal.hpp include/cppad/utility/ode_err_control.hpp include/cppad/utility/ode_gear.hpp include/cppad/utility/ode_gear_control.hpp include/cppad/utility/poly.hpp include/cppad/utility/pow_int.hpp include/cppad/utility/romberg_mul.hpp include/cppad/utility/romberg_one.hpp include/cppad/utility/rosen_34.hpp include/cppad/utility/runge_45.hpp include/cppad/utility/set_union.hpp include/cppad/utility/sparse2eigen.hpp include/cppad/utility/sparse_rc.hpp include/cppad/utility/sparse_rcv.hpp include/cppad/utility/speed_test.hpp include/cppad/utility/test_boolofvoid.hpp include/cppad/utility/time_test.hpp include/cppad/utility/to_string.hpp include/cppad/utility/xrst/cppad_vector.xrst xrst/lu_det_and_solve.xrst xrst/numeric_type.xrst xrst/simple_vector.xrst xrst/thread_alloc.xrst } {xrst_comment END_SORT_THIS_LINE_MINUS_2} Testing ******* The routines listed below support numerical correctness and speed testing: .. csv-table:: :widths: auto NearEqual,:ref:`NearEqual-title` time_test,:ref:`time_test-title` speed_test,:ref:`speed_test-title` SpeedTest,:ref:`SpeedTest-title` test_boolofvoid,:ref:`test_boolofvoid-title` C++ Concepts ************ We refer to a the set of classes that satisfy certain conditions as a C++ concept. The following concepts are used by the CppAD Template library: .. csv-table:: :widths: auto NumericType,:ref:`NumericType-title` CheckNumericType,:ref:`CheckNumericType-title` SimpleVector,:ref:`SimpleVector-title` CheckSimpleVector,:ref:`CheckSimpleVector-title` General Numerical Routines ************************** The routines listed below are general purpose numerical routines written with the floating point type a C++ template parameter. This enables them to be used with algorithmic differentiation types, as well as for other purposes. .. csv-table:: :widths: auto nan,:ref:`nan-title` pow_int,:ref:`pow_int-title` Poly,:ref:`Poly-title` lu_det_and_solve,:ref:`lu_det_and_solve-title` RombergOne,:ref:`RombergOne-title` RombergMul,:ref:`RombergMul-title` Runge45,:ref:`Runge45-title` Rosen34,:ref:`Rosen34-title` OdeErrControl,:ref:`OdeErrControl-title` OdeGear,:ref:`OdeGear-title` OdeGearControl,:ref:`OdeGearControl-title` Miscellaneous ************* Error Handler ============= All of the routines in the CppAD namespace use the following general purpose error handler: .. csv-table:: :widths: auto ErrorHandler,:ref:`ErrorHandler-title` The CppAD Vector Template Class =============================== This is a simple implementation of a template vector class (that is easy to view in a C++ debugger): .. csv-table:: :widths: auto CppAD_vector,:ref:`CppAD_vector-title` Multi-Threading Memory Allocation ================================= .. csv-table:: :widths: auto thread_alloc,:ref:`thread_alloc-title` Sorting Indices =============== .. csv-table:: :widths: auto index_sort,:ref:`index_sort-title` to_string ========= .. csv-table:: :widths: auto to_string,:ref:`to_string-title` set_union ========= .. csv-table:: :widths: auto set_union,:ref:`set_union-title` Sparse Matrices =============== .. csv-table:: :widths: auto sparse_rc,:ref:`sparse_rc-title` sparse_rcv,:ref:`sparse_rcv-title` sparse2eigen,:ref:`sparse2eigen-title` Dynamic Libraries ================= .. csv-table:: :widths: auto create_dll_lib,:ref:`create_dll_lib-title` link_dll_lib,:ref:`link_dll_lib-title` {xrst_end utility} ================================================ FILE: include/cppad/utility.hpp ================================================ # ifndef CPPAD_UTILITY_HPP # define CPPAD_UTILITY_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN_SORT_THIS_LINE_PLUS_1 # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include // END_SORT_THIS_LINE_MINUS_1 # if CPPAD_HAS_EIGEN # include # endif # endif ================================================ FILE: include/cppad/wno_conversion.hpp ================================================ # ifndef CPPAD_WNO_CONVERSION_HPP # define CPPAD_WNO_CONVERSION_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin wno_conversion} Suppress Suspect Implicit Conversion Warnings ############################################# Syntax ****** | # ``include `` Purpose ******* In many cases it is good to have warnings for implicit conversions that may loose range or precision. The include command above, before any other includes, suppresses these warning for a particular compilation unit (which usually corresponds to a * . ``cpp`` file). {xrst_end wno_conversion} */ # include # if CPPAD_COMPILER_HAS_CONVERSION_WARN # pragma GCC diagnostic ignored "-Wfloat-conversion" # pragma GCC diagnostic ignored "-Wconversion" # endif # endif ================================================ FILE: introduction/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the introduction/exp_apx directory tests # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list exp_2.cpp exp_2.hpp exp_2_cppad.cpp exp_2_for0.cpp exp_2_for1.cpp exp_2_for2.cpp exp_2_rev1.cpp exp_2_rev2.cpp exp_eps.cpp exp_eps.hpp exp_eps_cppad.cpp exp_eps_for0.cpp exp_eps_for1.cpp exp_eps_for2.cpp exp_eps_rev1.cpp exp_eps_rev2.cpp introduction.cpp ) # END_SORT_THIS_LINE_MINUS_2 set_compile_flags( introduction "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE( introduction EXCLUDE_FROM_ALL ${source_list} ) # TARGET_LINK_LIBRARIES(introduction ${cppad_lib} ${colpack_libs}) # # check_introduction add_check_executable(check introduction) ================================================ FILE: introduction/exp_2.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN C++ # include // define fabs function # include "exp_2.hpp" // definition of exp_2 algorithm bool exp_2(void) { double x = .5; double check = 1 + x + x * x / 2.; bool ok = std::fabs( exp_2(x) - check ) <= 1e-10; return ok; } // END C++ ================================================ FILE: introduction/exp_2.hpp ================================================ # ifndef CPPAD_INTRODUCTION_EXP_2_HPP # define CPPAD_INTRODUCTION_EXP_2_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin exp_2} {xrst_spell apx yyyymmdd } Second Order Exponential Approximation ###################################### Syntax ****** | # ``include`` ``"exp_2.hpp"`` | *y* = ``exp_2`` ( *x* ) Purpose ******* This is a simple example algorithm that is used to demonstrate Algorithmic Differentiation (see :ref:`exp_eps-name` for a more complex example). Mathematical Form ***************** The exponential function can be defined by .. math:: \exp (x) = 1 + x^1 / 1 ! + x^2 / 2 ! + \cdots The second order approximation for the exponential function is .. math:: {\rm exp\_2} (x) = 1 + x + x^2 / 2 include ******* The include command in the syntax is relative to ``cppad-`` *yyyymmdd* / ``introduction/exp_apx`` where ``cppad-`` *yyyymmdd* is the distribution directory created during the beginning steps of the :ref:`installation` of CppAD. x * The argument *x* has prototype ``const`` *Type* & *x* (see *Type* below). It specifies the point at which to evaluate the approximation for the second order exponential approximation. y * The result *y* has prototype *Type* *y* It is the value of the exponential function approximation defined above. Type **** If *u* and *v* are *Type* objects and *i* is an ``int`` : .. csv-table:: :widths: auto **Operation**,**Result Type**,**Description** *Type* ( *i* ),*Type*,construct object with value equal to *i* *Type u* = *v*,*Type*,construct *u* with value equal to *v* *u* * *v*,*Type*,result is value of :math:`u * v` *u* / *v*,*Type*,result is value of :math:`u / v` *u* + *v*,*Type*,result is value of :math:`u + v` Contents ******** {xrst_toc_table introduction/exp_2.xrst introduction/exp_2_cppad.cpp } Implementation ************** The file :ref:`exp_2.hpp-name` contains a C++ implementation of this function. Test **** The file :ref:`exp_2.cpp-name` contains a test of this implementation. Exercises ********* #. Suppose that we make the call :: double x = .1; double y = exp_2(x); What is the value assigned to ``v1`` , ``v2`` , ... ,``v5`` in :ref:`exp_2.hpp-name` ? #. Extend the routine ``exp_2.hpp`` to a routine ``exp_3.hpp`` that computes .. math:: 1 + x^2 / 2 ! + x^3 / 3 ! Do this in a way that only assigns one value to each variable (as ``exp_2`` does). #. Suppose that we make the call :: double x = .5; double y = exp_3(x); using ``exp_3`` created in the previous problem. What is the value assigned to the new variables in ``exp_3`` (variables that are in ``exp_3`` and not in ``exp_2`` ) ? {xrst_end exp_2} ------------------------------------------------------------------------------ */ // BEGIN C++ template Type exp_2(const Type &x) { Type v1 = x; // v1 = x Type v2 = Type(1) + v1; // v2 = 1 + x Type v3 = v1 * v1; // v3 = x^2 Type v4 = v3 / Type(2); // v4 = x^2 / 2 Type v5 = v2 + v4; // v5 = 1 + x + x^2 / 2 return v5; // exp_2(x) = 1 + x + x^2 / 2 } // END C++ # endif ================================================ FILE: introduction/exp_2.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin exp_2.hpp} exp_2: Implementation ##################### {xrst_literal introduction/exp_2.hpp // BEGIN C++ // END C++ } {xrst_end exp_2.hpp} ----------------------------------------------------------------------------- {xrst_begin exp_2.cpp} exp_2: Test ########### {xrst_literal introduction/exp_2.cpp // BEGIN C++ // END C++ } {xrst_end exp_2.cpp} ----------------------------------------------------------------------------- {xrst_begin exp_2_for0} exp_2: Operation Sequence and Zero Order Forward Mode ##################################################### Mathematical Form ***************** The operation sequence (see below) corresponding to the algorithm :ref:`exp_2.hpp-name` is the same for all values of *x* . The mathematical form for the corresponding function is .. math:: f(x) = 1 + x + x^2 / 2 An algorithmic differentiation package does not operate on the mathematical function :math:`f(x)` but rather on the particular algorithm used to compute the function (in this case :ref:`exp_2.hpp-name` ). Zero Order Expansion ******************** In general, a zero order forward sweep is given a vector :math:`x^{(0)} \in \B{R}^n` and it returns the corresponding vector :math:`y^{(0)} \in \B{R}^m` given by .. math:: y^{(0)} = f( x^{(0)} ) The superscript :math:`(0)` denotes zero order derivative; i.e., it is equal to the value of the corresponding variable. For the example we are considering here, both :math:`n` and :math:`m` are equal to one. Operation Sequence ****************** An atomic *Type* operation is an operation that has a *Type* result and is not made up of other more basic operations. A sequence of atomic *Type* operations is called a *Type* operation sequence. Given an C++ algorithm and its inputs, there is a corresponding *Type* operation sequence for each type. If *Type* is clear from the context, we drop it and just refer to the operation sequence. We consider the case where :ref:`exp_2.hpp-name` is executed with :math:`x^{(0)} = .5`. The table below contains the corresponding operation sequence and the results of a zero order sweep. Index ===== The Index column contains the index in the operation sequence of the corresponding atomic operation and variable. A Forward sweep starts with the first operation and ends with the last. Code ==== The Code column contains the C++ source code corresponding to the corresponding atomic operation in the sequence. Operation ========= The Operation column contains the mathematical function corresponding to each atomic operation in the sequence. Zero Order ========== The Zero Order column contains the zero order derivative for the corresponding variable in the operation sequence. Forward mode refers to the fact that these coefficients are computed in the same order as the original algorithm; i.e, in order of increasing index in the operation sequence. Sweep ===== .. csv-table:: :widths: auto **Index**,,**Code**,,**Operation**,,**Zero Order** 1,,``Type v1 = x;``,,:math:`v_1 = x`,,:math:`v_1^{(0)} = 0.5` 2,,``Type v2 = Type(1) + v1;``,,:math:`v_2 = 1 + v_1`,,:math:`v_2^{(0)} = 1.5` 3,,``Type v3 = v1 * v1;``,,:math:`v_3 = v_1 * v_1`,,:math:`v_3^{(0)} = 0.25` 4,,``Type v4 = v3 / Type(2);``,,:math:`v_4 = v_3 / 2`,,:math:`v_4^{(0)} = 0.125` 5,,``Type v5 = v2 + v4;``,,:math:`v_5 = v_2 + v_4`,,:math:`v_5^{(0)} = 1.625` Return Value ************ The return value for this case is .. math:: 1.625 = v_5^{(0)} = f( x^{(0)} ) {xrst_toc_hidden introduction/exp_2_for0.cpp } Verification ************ The file :ref:`exp_2_for0.cpp-name` contains a routine that verifies the values computed above. Exercises ********* #. Suppose that :math:`x^{(0)} = .2`, what is the result of a zero order forward sweep for the operation sequence above; i.e., what are the corresponding values for .. math:: v_1^{(0)} , v_2^{(0)} , \cdots , v_5^{(0)} #. Create a modified version of :ref:`exp_2_for0.cpp-name` that verifies the values you obtained for the previous exercise. #. Create and run a main program that reports the result of calling the modified version of :ref:`exp_2_for0.cpp-name` in the previous exercise. {xrst_end exp_2_for0} ------------------------------------------------------------------------------ {xrst_begin exp_2_for1} exp_2: First Order Forward Mode ############################### First Order Expansion ********************* We define :math:`x(t)` near :math:`t = 0` by the first order expansion .. math:: x(t) = x^{(0)} + x^{(1)} * t it follows that :math:`x^{(0)}` is the zero, and :math:`x^{(1)}` the first, order derivative of :math:`x(t)` at :math:`t = 0`. Purpose ******* In general, a first order forward sweep is given the :ref:`zero order derivative` for all of the variables in an operation sequence, and the first order derivatives for the independent variables. It uses these to compute the first order derivatives, and thereby obtain the first order expansion, for all the other variables in the operation sequence. Mathematical Form ***************** Suppose that we use the algorithm :ref:`exp_2.hpp-name` to compute .. math:: f(x) = 1 + x + x^2 / 2 The corresponding derivative function is .. math:: \partial_x f (x) = 1 + x An algorithmic differentiation package does not operate on the mathematical form of the function, or its derivative, but rather on the :ref:`exp_2_for0@Operation Sequence` for the for the algorithm that is used to evaluate the function. Operation Sequence ****************** We consider the case where :ref:`exp_2.hpp-name` is executed with :math:`x = .5`. The corresponding operation sequence and zero order forward mode values (see :ref:`zero order sweep` ) are inputs and are used by a first order forward sweep. Index ===== The Index column contains the index in the operation sequence of the corresponding atomic operation. A Forward sweep starts with the first operation and ends with the last. Operation ========= The Operation column contains the mathematical function corresponding to each atomic operation in the sequence. Zero Order ========== The Zero Order column contains the zero order derivatives for the corresponding variable in the operation sequence (see :ref:`zero order sweep` ). Derivative ========== The Derivative column contains the mathematical function corresponding to the derivative with respect to :math:`t`, at :math:`t = 0`, for each variable in the sequence. First Order =========== The First Order column contains the first order derivatives for the corresponding variable in the operation sequence; i.e., .. math:: v_j (t) = v_j^{(0)} + v_j^{(1)} t We use :math:`x^{(1)} = 1` so that differentiation with respect to :math:`t`, at :math:`t = 0`, is the same as partial differentiation with respect to :math:`x` at :math:`x = x^{(0)}`. Sweep ===== .. csv-table:: :widths: auto **Index**,,**Operation**,,**Zero Order**,,**Derivative**,,**First Order** 1,,:math:`v_1 = x`,,0.5,,:math:`v_1^{(1)} = x^{(1)}`,,:math:`v_1^{(1)} = 1` 2,,:math:`v_2 = 1 + v_1`,,1.5,,:math:`v_2^{(1)} = v_1^{(1)}`,,:math:`v_2^{(1)} = 1` 3,,:math:`v_3 = v_1 * v_1`,,0.25,,:math:`v_3^{(1)} = 2 * v_1^{(0)} * v_1^{(1)}`,,:math:`v_3^{(1)} = 1` 4,,:math:`v_4 = v_3 / 2`,,0.125,,:math:`v_4^{(1)} = v_3^{(1)} / 2`,,:math:`v_4^{(1)} = 0.5` 5,,:math:`v_5 = v_2 + v_4`,,1.625,,:math:`v_5^{(1)} = v_2^{(1)} + v_4^{(1)}`,,:math:`v_5^{(1)} = 1.5` Return Value ************ The derivative of the return value for this case is .. math:: :nowrap: \begin{eqnarray} 1.5 & = & v_5^{(1)} = \left[ \D{v_5}{t} \right]_{t=0} = \left[ \D{}{t} f ( x^{(0)} + x^{(1)} t ) \right]_{t=0} \\ & = & f^{(1)} ( x^{(0)} ) * x^{(1)} = f^{(1)} ( x^{(0)} ) \end{eqnarray} (We have used the fact that :math:`x^{(1)} = 1`.) {xrst_toc_hidden introduction/exp_2_for1.cpp } Verification ************ The file :ref:`exp_2_for1.cpp-name` contains a routine which verifies the values computed above. Exercises ********* #. Which statement in the routine defined by :ref:`exp_2_for1.cpp-name` uses the values that are calculated by the routine defined by :ref:`exp_2_for0.cpp-name` ? #. Suppose that :math:`x = .1`, what are the results of a zero and first order forward sweep for the operation sequence above; i.e., what are the corresponding values for :math:`v_1^{(0)}, v_2^{(0)}, \cdots , v_5^{(0)}` and :math:`v_1^{(1)}, v_2^{(1)}, \cdots , v_5^{(1)}` ? #. Create a modified version of :ref:`exp_2_for1.cpp-name` that verifies the derivative values from the previous exercise. Also create and run a main program that reports the result of calling the modified version of :ref:`exp_2_for1.cpp-name` . {xrst_end exp_2_for1} ------------------------------------------------------------------------------ {xrst_begin exp_2_rev1} {xrst_spell rcll } exp_2: First Order Reverse Mode ############################### Purpose ******* First order reverse mode uses the :ref:`exp_2_for0@Operation Sequence` , and zero order forward sweep values, to compute the first order derivative of one dependent variable with respect to all the independent variables. The computations are done in reverse of the order of the computations in the original algorithm. Mathematical Form ***************** Suppose that we use the algorithm :ref:`exp_2.hpp-name` to compute .. math:: f(x) = 1 + x + x^2 / 2 The corresponding derivative function is .. math:: \partial_x f (x) = 1 + x f_5 *** For our example, we chose to compute the derivative of the value returned by :ref:`exp_2.hpp-name` which is equal to the symbol :math:`v_5` in the :ref:`exp_2 operation sequence` . We begin with the function :math:`f_5` where :math:`v_5` is both an argument and the value of the function; i.e., .. math:: :nowrap: \begin{eqnarray} f_5 ( v_1 , v_2 , v_3 , v_4 , v_5 ) & = & v_5 \\ \D{f_5}{v_5} & = & 1 \end{eqnarray} All the other partial derivatives of :math:`f_5` are zero. Index 5: f_4 ************ Reverse mode starts with the last operation in the sequence. For the case in question, this is the operation with index 5, .. math:: v_5 = v_2 + v_4 We define the function :math:`f_4 ( v_1 , v_2 , v_3 , v_4 )` as equal to :math:`f_5` except that :math:`v_5` is eliminated using this operation; i.e. .. math:: f_4 = f_5 [ v_1 , v_2 , v_3 , v_4 , v_5 ( v_2 , v_4 ) ] It follows that .. math:: \begin{array}{rcll} \D{f_4}{v_2} & = & \D{f_5}{v_2} + \D{f_5}{v_5} * \D{v_5}{v_2} & = 1 \\ \D{f_4}{v_4} & = & \D{f_5}{v_4} + \D{f_5}{v_5} * \D{v_5}{v_4} & = 1 \end{array} All the other partial derivatives of :math:`f_4` are zero. Index 4: f_3 ************ The next operation has index 4, .. math:: v_4 = v_3 / 2 We define the function :math:`f_3 ( v_1 , v_2 , v_3 )` as equal to :math:`f_4` except that :math:`v_4` is eliminated using this operation; i.e., .. math:: f_3 = f_4 [ v_1 , v_2 , v_3 , v_4 ( v_3 ) ] It follows that .. math:: \begin{array}{rcll} \D{f_3}{v_1} & = & \D{f_4}{v_1} & = 0 \\ \D{f_3}{v_2} & = & \D{f_4}{v_2} & = 1 \\ \D{f_3}{v_3} & = & \D{f_4}{v_3} + \D{f_4}{v_4} * \D{v_4}{v_3} & = 0.5 \end{array} Index 3: f_2 ************ The next operation has index 3, .. math:: v_3 = v_1 * v_1 We define the function :math:`f_2 ( v_1 , v_2 )` as equal to :math:`f_3` except that :math:`v_3` is eliminated using this operation; i.e., .. math:: f_2 = f_3 [ v_1 , v_2 , v_3 ( v_1 ) ] Note that the value of :math:`v_1` is equal to :math:`x` which is .5 for this evaluation. It follows that .. math:: \begin{array}{rcll} \D{f_2}{v_1} & = & \D{f_3}{v_1} + \D{f_3}{v_3} * \D{v_3}{v_1} & = 0.5 \\ \D{f_2}{v_2} & = & \D{f_3}{v_2} & = 1 \end{array} Index 2: f_1 ************ The next operation has index 2, .. math:: v_2 = 1 + v_1 We define the function :math:`f_1 ( v_1 )` as equal to :math:`f_2` except that :math:`v_2` is eliminated using this operation; i.e., .. math:: f_1 = f_2 [ v_1 , v_2 ( v_1 ) ] It follows that .. math:: \begin{array}{rcll} \D{f_1}{v_1} & = & \D{f_2}{v_1} + \D{f_2}{v_2} * \D{v_2}{v_1} & = 1.5 \end{array} Note that :math:`v_1` is equal to :math:`x`, so the derivative of this is the derivative of the function defined by :ref:`exp_2.hpp-name` at :math:`x = .5`. {xrst_toc_hidden introduction/exp_2_rev1.cpp } Verification ************ The file :ref:`exp_2_rev1.cpp-name` contains a routine which verifies the values computed above. It only tests the partial derivatives of :math:`f_j` that might not be equal to the corresponding partials of :math:`f_{j+1}`; i.e., the other partials of :math:`f_j` must be equal to the corresponding partials of :math:`f_{j+1}`. Exercises ********* #. Which statement in the routine defined by :ref:`exp_2_rev1.cpp-name` uses the values that are calculated by the routine defined by :ref:`exp_2_for0.cpp-name` ? #. Consider the case where :math:`x = .1` and we first preform a zero order forward sweep for the operation sequence used above. What are the results of a first order reverse sweep; i.e., what are the corresponding derivatives of :math:`f_5 , f_4 , \ldots , f_1`. #. Create a modified version of :ref:`exp_2_rev1.cpp-name` that verifies the values you obtained for the previous exercise. Also create and run a main program that reports the result of calling the modified version of :ref:`exp_2_rev1.cpp-name` . {xrst_end exp_2_rev1} ------------------------------------------------------------------------------ {xrst_begin exp_2_for2} exp_2: Second Order Forward Mode ################################ Second Order Expansion ********************** We define :math:`x(t)` near :math:`t = 0` by the second order expansion .. math:: x(t) = x^{(0)} + x^{(1)} * t + x^{(2)} * t^2 / 2 It follows that for :math:`k = 0 , 1 , 2`, .. math:: x^{(k)} = \dpow{k}{t} x (0) Purpose ******* In general, a second order forward sweep is given the :ref:`exp_2_for1@First Order Expansion` for all of the variables in an operation sequence, and the second order derivatives for the independent variables. It uses these to compute the second order derivative, and thereby obtain the second order expansion, for all the variables in the operation sequence. Mathematical Form ***************** Suppose that we use the algorithm :ref:`exp_2.hpp-name` to compute .. math:: f(x) = 1 + x + x^2 / 2 The corresponding second derivative function is .. math:: \Dpow{2}{x} f (x) = 1 Operation Sequence ****************** We consider the case where :ref:`exp_2.hpp-name` is executed with :math:`x = .5`. The corresponding operation sequence, zero order forward sweep values, and first order forward sweep values are inputs and are used by a second order forward sweep. Index ===== The Index column contains the index in the operation sequence of the corresponding atomic operation. A Forward sweep starts with the first operation and ends with the last. Zero ==== The Zero column contains the zero order sweep results for the corresponding variable in the operation sequence (see :ref:`zero order sweep` ). Operation ========= The Operation column contains the first order sweep operation for this variable. First ===== The First column contains the first order sweep results for the corresponding variable in the operation sequence (see :ref:`first order sweep` ). Derivative ========== The Derivative column contains the mathematical function corresponding to the second derivative with respect to :math:`t`, at :math:`t = 0`, for each variable in the sequence. Second ====== The Second column contains the second order derivatives for the corresponding variable in the operation sequence; i.e., the second order expansion for the *i*-th variable is given by .. math:: v_i (t) = v_i^{(0)} + v_i^{(1)} * t + v_i^{(2)} * t^2 / 2 We use :math:`x^{(0)} = 1`, and :math:`x^{(2)} = 0` so that second order differentiation with respect to :math:`t`, at :math:`t = 0`, is the same as the second partial differentiation with respect to :math:`x` at :math:`x = x^{(0)}`. Sweep ===== .. csv-table:: :widths: auto **Index**,,**Zero**,,**Operation**,,**First**,,**Derivative**,,**Second** 1,,0.5,,:math:`v_1^{(1)} = x^{(1)}`,,1,,:math:`v_1^{(2)} = x^{(2)}`,,:math:`v_1^{(2)} = 0` 2,,1.5,,:math:`v_2^{(1)} = v_1^{(1)}`,,1,,:math:`v_2^{(2)} = v_1^{(2)}`,,:math:`v_2^{(2)} = 0` 3,,0.25,,:math:`v_3^{(1)} = 2 * v_1^{(0)} * v_1^{(1)}`,,1,,:math:`v_3^{(2)} = 2 * (v_1^{(1)} * v_1^{(1)} + v_1^{(0)} * v_1^{(2)} )`,,:math:`v_3^{(2)} = 2` 4,,0.125,,:math:`v_4^{(1)} = v_3^{(1)} / 2`,,.5,,:math:`v_4^{(2)} = v_3^{(2)} / 2`,,:math:`v_4^{(2)} = 1` 5,,1.625,,:math:`v_5^{(1)} = v_2^{(1)} + v_4^{(1)}`,,1.5,,:math:`v_5^{(2)} = v_2^{(2)} + v_4^{(2)}`,,:math:`v_5^{(2)} = 1` Return Value ************ The second derivative of the return value for this case is .. math:: :nowrap: \begin{eqnarray} 1 & = & v_5^{(2)} = \left[ \Dpow{2}{t} v_5 \right]_{t=0} = \left[ \Dpow{2}{t} f( x^{(0)} + x^{(1)} * t ) \right]_{t=0} \\ & = & x^{(1)} * \Dpow{2}{x} f ( x^{(0)} ) * x^{(1)} = \Dpow{2}{x} f ( x^{(0)} ) \end{eqnarray} (We have used the fact that :math:`x^{(1)} = 1` and :math:`x^{(2)} = 0`.) {xrst_toc_hidden introduction/exp_2_for2.cpp } Verification ************ The file :ref:`exp_2_for2.cpp-name` contains a routine which verifies the values computed above. Exercises ********* #. Which statement in the routine defined by :ref:`exp_2_for2.cpp-name` uses the values that are calculated by the routine defined by :ref:`exp_2_for1.cpp-name` ? #. Suppose that :math:`x = .1`, what are the results of a zero, first, and second order forward sweep for the operation sequence above; i.e., what are the corresponding values for :math:`v_i^{(k)}` for :math:`i = 1, \ldots , 5` and :math:`k = 0, 1, 2`. #. Create a modified version of :ref:`exp_2_for2.cpp-name` that verifies the derivative values from the previous exercise. Also create and run a main program that reports the result of calling the modified version of :ref:`exp_2_for2.cpp-name` . {xrst_end exp_2_for2} ------------------------------------------------------------------------------ {xrst_begin exp_2_rev2} {xrst_spell rcll } exp_2: Second Order Reverse Mode ################################ Purpose ******* In general, a second order reverse sweep is given the :ref:`exp_2_for1@First Order Expansion` for all of the variables in an operation sequence. Given a choice of a particular variable, it computes the derivative, of that variables first order expansion coefficient, with respect to all of the independent variables. Mathematical Form ***************** Suppose that we use the algorithm :ref:`exp_2.hpp-name` to compute .. math:: f(x) = 1 + x + x^2 / 2 The corresponding second derivative is .. math:: \Dpow{2}{x} f (x) = 1 f_5 *** For our example, we chose to compute the derivative of :math:`v_5^{(1)}` with respect to all the independent variable. For the case computed for the :ref:`first order sweep` , :math:`v_5^{(1)}` is the derivative of the value returned by :ref:`exp_2.hpp-name` . This the value computed will be the second derivative of the value returned by :ref:`exp_2.hpp-name` . We begin with the function :math:`f_5` where :math:`v_5^{(1)}` is both an argument and the value of the function; i.e., .. math:: :nowrap: \begin{eqnarray} f_5 \left( v_1^{(0)}, v_1^{(1)} , \ldots , v_5^{(0)} , v_5^{(1)} \right) & = & v_5^{(1)} \\ \D{f_5}{v_5^{(1)}} & = & 1 \end{eqnarray} All the other partial derivatives of :math:`f_5` are zero. Index 5: f_4 ************ Second order reverse mode starts with the last operation in the sequence. For the case in question, this is the operation with index 5. The zero and first order sweep representations of this operation are .. math:: :nowrap: \begin{eqnarray} v_5^{(0)} & = & v_2^{(0)} + v_4^{(0)} \\ v_5^{(1)} & = & v_2^{(1)} + v_4^{(1)} \end{eqnarray} We define the function :math:`f_4 \left( v_1^{(0)} , \ldots , v_4^{(1)} \right)` as equal to :math:`f_5` except that :math:`v_5^{(0)}` and :math:`v_5^{(1)}` are eliminated using this operation; i.e. .. math:: f_4 = f_5 \left[ v_1^{(0)} , \ldots , v_4^{(1)} , v_5^{(0)} \left( v_2^{(0)} , v_4^{(0)} \right) , v_5^{(1)} \left( v_2^{(1)} , v_4^{(1)} \right) \right] It follows that .. math:: \begin{array}{rcll} \D{f_4}{v_2^{(1)}} & = & \D{f_5}{v_2^{(1)}} + \D{f_5}{v_5^{(1)}} * \D{v_5^{(1)}}{v_2^{(1)}} & = 1 \\ \D{f_4}{v_4^{(1)}} & = & \D{f_5}{v_4^{(1)}} + \D{f_5}{v_5^{(1)}} * \D{v_5}{v_4^{(1)}} & = 1 \end{array} All the other partial derivatives of :math:`f_4` are zero. Index 4: f_3 ************ The next operation has index 4, .. math:: :nowrap: \begin{eqnarray} v_4^{(0)} & = & v_3^{(0)} / 2 \\ v_4^{(1)} & = & v_3^{(1)} / 2 \end{eqnarray} We define the function :math:`f_3 \left( v_1^{(0)} , \ldots , v_3^{(1)} \right)` as equal to :math:`f_4` except that :math:`v_4^{(0)}` and :math:`v_4^{(1)}` are eliminated using this operation; i.e., .. math:: f_3 = f_4 \left[ v_1^{(0)} , \ldots , v_3^{(1)} , v_4^{(0)} \left( v_3^{(0)} \right) , v_4^{(1)} \left( v_3^{(1)} \right) \right] It follows that .. math:: \begin{array}{rcll} \D{f_3}{v_2^{(1)}} & = & \D{f_4}{v_2^{(1)}} & = 1 \\ \D{f_3}{v_3^{(1)}} & = & \D{f_4}{v_3^{(1)}} + \D{f_4}{v_4^{(1)}} * \D{v_4^{(1)}}{v_3^{(1)}} & = 0.5 \end{array} All the other partial derivatives of :math:`f_3` are zero. Index 3: f_2 ************ The next operation has index 3, .. math:: :nowrap: \begin{eqnarray} v_3^{(0)} & = & v_1^{(0)} * v_1^{(0)} \\ v_3^{(1)} & = & 2 * v_1^{(0)} * v_1^{(1)} \end{eqnarray} We define the function :math:`f_2 \left( v_1^{(0)} , \ldots , v_2^{(1)} \right)` as equal to :math:`f_3` except that :math:`v_3^{(0)}` and :math:`v_3^{(1)}` are eliminated using this operation; i.e., .. math:: f_2 = f_3 \left[ v_1^{(0)} , \ldots , v_2^{(1)} , v_3^{(0)} ( v_1^{(0)} ) , v_3^{(1)} ( v_1^{(0)} , v_1^{(1)} ) \right] Note that, from the :ref:`first order forward sweep` , the value of :math:`v_1^{(0)}` is equal to :math:`.5` and :math:`v_1^{(1)}` is equal 1. It follows that .. math:: \begin{array}{rcll} \D{f_2}{v_1^{(0)}} & = & \D{f_3}{v_1^{(0)}} + \D{f_3}{v_3^{(0)}} * \D{v_3^{(0)}}{v_1^{(0)}} + \D{f_3}{v_3^{(1)}} * \D{v_3^{(1)}}{v_1^{(0)}} & = 1 \\ \D{f_2}{v_1^{(1)}} & = & \D{f_3}{v_1^{(1)}} + \D{f_3}{v_3^{(1)}} * \D{v_3^{(1)}}{v_1^{(1)}} & = 0.5 \\ \D{f_2}{v_2^{(0)}} & = & \D{f_3}{v_2^{(0)}} & = 0 \\ \D{f_2}{v_2^{(1)}} & = & \D{f_3}{v_2^{(1)}} & = 1 \end{array} Index 2: f_1 ************ The next operation has index 2, .. math:: :nowrap: \begin{eqnarray} v_2^{(0)} & = & 1 + v_1^{(0)} \\ v_2^{(1)} & = & v_1^{(1)} \end{eqnarray} We define the function :math:`f_1 ( v_1^{(0)} , v_1^{(1)} )` as equal to :math:`f_2` except that :math:`v_2^{(0)}` and :math:`v_2^{(1)}` are eliminated using this operation; i.e., .. math:: f_1 = f_2 \left[ v_1^{(0)} , v_1^{(1)} , v_2^{(0)} ( v_1^{(0)} ) , v_2^{(1)} ( v_1^{(1)} ) \right] It follows that .. math:: \begin{array}{rcll} \D{f_1}{v_1^{(0)}} & = & \D{f_2}{v_1^{(0)}} + \D{f_2}{v_2^{(0)}} * \D{v_2^{(0)}}{v_1^{(0)}} & = 1 \\ \D{f_1}{v_1^{(1)}} & = & \D{f_2}{v_1^{(1)}} + \D{f_2}{v_2^{(1)}} * \D{v_2^{(1)}}{v_1^{(1)}} & = 1.5 \end{array} Note that :math:`v_1` is equal to :math:`x`, so the second derivative of the function defined by :ref:`exp_2.hpp-name` at :math:`x = .5` is given by .. math:: \Dpow{2}{x} v_5^{(0)} = \D{ v_5^{(1)} }{x} = \D{ v_5^{(1)} }{v_1^{(0)}} = \D{f_1}{v_1^{(0)}} = 1 There is a theorem about Algorithmic Differentiation that explains why the other partial of :math:`f_1` is equal to the first derivative of the function defined by :ref:`exp_2.hpp-name` at :math:`x = .5`. {xrst_toc_hidden introduction/exp_2_rev2.cpp } Verification ************ The file :ref:`exp_2_rev2.cpp-name` contains a routine which verifies the values computed above. It only tests the partial derivatives of :math:`f_j` that might not be equal to the corresponding partials of :math:`f_{j+1}`; i.e., the other partials of :math:`f_j` must be equal to the corresponding partials of :math:`f_{j+1}`. Exercises ********* #. Which statement in the routine defined by :ref:`exp_2_rev2.cpp-name` uses the values that are calculated by the routine defined by :ref:`exp_2_for0.cpp-name` ? Which statements use values that are calculate by the routine defined in :ref:`exp_2_for1.cpp-name` ? #. Consider the case where :math:`x = .1` and we first preform a zero order forward sweep, then a first order sweep, for the operation sequence used above. What are the results of a second order reverse sweep; i.e., what are the corresponding derivatives of :math:`f_5 , f_4 , \ldots , f_1`. #. Create a modified version of :ref:`exp_2_rev2.cpp-name` that verifies the values you obtained for the previous exercise. Also create and run a main program that reports the result of calling the modified version of :ref:`exp_2_rev2.cpp-name` . {xrst_end exp_2_rev2} ------------------------------------------------------------------------------ ================================================ FILE: introduction/exp_2_cppad.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin exp_2_cppad} exp_2: CppAD Forward and Reverse Sweeps ####################################### Purpose ******* Use CppAD forward and reverse modes to compute the partial derivative with respect to :math:`x`, at the point :math:`x = .5`, of the function ``exp_2`` ( *x* ) as defined by the :ref:`exp_2.hpp-name` include file. Exercises ********* #. Create and test a modified version of the routine below that computes the same order derivatives with respect to :math:`x`, at the point :math:`x = .1` of the function ``exp_2`` ( *x* ) #. Create a routine called ``exp_3`` ( *x* ) that evaluates the function .. math:: f(x) = 1 + x^2 / 2 + x^3 / 6 Test a modified version of the routine below that computes the derivative of :math:`f(x)` at the point :math:`x = .5`. {xrst_spell_off} {xrst_code cpp} */ # include // https://www.coin-or.org/CppAD/ # include "exp_2.hpp" // second order exponential approximation bool exp_2_cppad(void) { bool ok = true; using CppAD::AD; using CppAD::vector; // can use any simple vector template class using CppAD::NearEqual; // checks if values are nearly equal // domain space vector size_t n = 1; // dimension of the domain space vector< AD > X(n); X[0] = .5; // value of x for this operation sequence // declare independent variables and start recording operation sequence CppAD::Independent(X); // evaluate our exponential approximation AD x = X[0]; AD apx = exp_2(x); // range space vector size_t m = 1; // dimension of the range space vector< AD > Y(m); Y[0] = apx; // variable that represents only range space component // Create f: X -> Y corresponding to this operation sequence // and stop recording. This also executes a zero order forward // sweep using values in X for x. CppAD::ADFun f(X, Y); // first order forward sweep that computes // partial of exp_2(x) with respect to x vector dx(n); // differential in domain space vector dy(m); // differential in range space dx[0] = 1.; // direction for partial derivative dy = f.Forward(1, dx); double check = 1.5; ok &= NearEqual(dy[0], check, 1e-10, 1e-10); // first order reverse sweep that computes the derivative vector w(m); // weights for components of the range vector dw(n); // derivative of the weighted function w[0] = 1.; // there is only one weight dw = f.Reverse(1, w); // derivative of w[0] * exp_2(x) check = 1.5; // partial of exp_2(x) with respect to x ok &= NearEqual(dw[0], check, 1e-10, 1e-10); // second order forward sweep that computes // second partial of exp_2(x) with respect to x vector x2(n); // second order Taylor coefficients vector y2(m); x2[0] = 0.; // evaluate second partial .w.r.t. x y2 = f.Forward(2, x2); check = 0.5 * 1.; // Taylor coef is 1/2 second derivative ok &= NearEqual(y2[0], check, 1e-10, 1e-10); // second order reverse sweep that computes // derivative of partial of exp_2(x) w.r.t. x dw.resize(2 * n); // space for first and second derivatives dw = f.Reverse(2, w); check = 1.; // result should be second derivative ok &= NearEqual(dw[0*2+1], check, 1e-10, 1e-10); return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end exp_2_cppad} */ ================================================ FILE: introduction/exp_2_for0.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin exp_2_for0.cpp} exp_2: Verify Zero Order Forward Sweep ###################################### {xrst_spell_off} {xrst_code cpp} */ # include // for fabs function bool exp_2_for0(double *v0) // double v0[6] { bool ok = true; double x = .5; v0[1] = x; // v1 = x ok &= std::fabs( v0[1] - 0.5) < 1e-10; v0[2] = 1. + v0[1]; // v2 = 1 + v1 ok &= std::fabs( v0[2] - 1.5) < 1e-10; v0[3] = v0[1] * v0[1]; // v3 = v1 * v1 ok &= std::fabs( v0[3] - 0.25) < 1e-10; v0[4] = v0[3] / 2.; // v4 = v3 / 2 ok &= std::fabs( v0[4] - 0.125) < 1e-10; v0[5] = v0[2] + v0[4]; // v5 = v2 + v4 ok &= std::fabs( v0[5] - 1.625) < 1e-10; return ok; } bool exp_2_for0(void) { double v0[6]; return exp_2_for0(v0); } /* {xrst_code} {xrst_spell_on} {xrst_end exp_2_for0.cpp} */ ================================================ FILE: introduction/exp_2_for1.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin exp_2_for1.cpp} exp_2: Verify First Order Forward Sweep ####################################### {xrst_spell_off} {xrst_code cpp} */ # include // prototype for fabs extern bool exp_2_for0(double *v0); // computes zero order forward sweep bool exp_2_for1(double *v1) // double v1[6] { bool ok = true; double v0[6]; // set the value of v0[j] for j = 1 , ... , 5 ok &= exp_2_for0(v0); v1[1] = 1.; // v1 = x ok &= std::fabs( v1[1] - 1. ) <= 1e-10; v1[2] = v1[1]; // v2 = 1 + v1 ok &= std::fabs( v1[2] - 1. ) <= 1e-10; v1[3] = v1[1] * v0[1] + v0[1] * v1[1]; // v3 = v1 * v1 ok &= std::fabs( v1[3] - 1. ) <= 1e-10; v1[4] = v1[3] / 2.; // v4 = v3 / 2 ok &= std::fabs( v1[4] - 0.5) <= 1e-10; v1[5] = v1[2] + v1[4]; // v5 = v2 + v4 ok &= std::fabs( v1[5] - 1.5) <= 1e-10; return ok; } bool exp_2_for1(void) { double v1[6]; return exp_2_for1(v1); } /* {xrst_code} {xrst_spell_on} {xrst_end exp_2_for1.cpp} */ ================================================ FILE: introduction/exp_2_for2.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin exp_2_for2.cpp} exp_2: Verify Second Order Forward Sweep ######################################## {xrst_spell_off} {xrst_code cpp} */ # include // prototype for fabs extern bool exp_2_for0(double *v0); // computes zero order forward sweep extern bool exp_2_for1(double *v1); // computes first order forward sweep bool exp_2_for2(void) { bool ok = true; double v0[6], v1[6], v2[6]; // set the value of v0[j], v1[j], for j = 1 , ... , 5 ok &= exp_2_for0(v0); ok &= exp_2_for1(v1); v2[1] = 0.; // v1 = x ok &= std::fabs( v2[1] - 0. ) <= 1e-10; v2[2] = v2[1]; // v2 = 1 + v1 ok &= std::fabs( v2[2] - 0. ) <= 1e-10; v2[3] = 2.*(v0[1]*v2[1] + v1[1]*v1[1]); // v3 = v1 * v1 ok &= std::fabs( v2[3] - 2. ) <= 1e-10; v2[4] = v2[3] / 2.; // v4 = v3 / 2 ok &= std::fabs( v2[4] - 1. ) <= 1e-10; v2[5] = v2[2] + v2[4]; // v5 = v2 + v4 ok &= std::fabs( v2[5] - 1. ) <= 1e-10; return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end exp_2_for2.cpp} */ ================================================ FILE: introduction/exp_2_rev1.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin exp_2_rev1.cpp} exp_2: Verify First Order Reverse Sweep ####################################### {xrst_spell_off} {xrst_code cpp} */ # include // define size_t # include // prototype for fabs extern bool exp_2_for0(double *v0); // computes zero order forward sweep bool exp_2_rev1(void) { bool ok = true; // set the value of v0[j] for j = 1 , ... , 5 double v0[6]; ok &= exp_2_for0(v0); // initial all partial derivatives as zero double f_v[6]; size_t j; for(j = 0; j < 6; j++) f_v[j] = 0.; // set partial derivative for f5 f_v[5] = 1.; ok &= std::fabs( f_v[5] - 1. ) <= 1e-10; // f5_v5 // f4 = f5( v1 , v2 , v3 , v4 , v2 + v4 ) f_v[2] += f_v[5] * 1.; f_v[4] += f_v[5] * 1.; ok &= std::fabs( f_v[2] - 1. ) <= 1e-10; // f4_v2 ok &= std::fabs( f_v[4] - 1. ) <= 1e-10; // f4_v4 // f3 = f4( v1 , v2 , v3 , v3 / 2 ) f_v[3] += f_v[4] / 2.; ok &= std::fabs( f_v[3] - 0.5) <= 1e-10; // f3_v3 // f2 = f3( v1 , v2 , v1 * v1 ) f_v[1] += f_v[3] * 2. * v0[1]; ok &= std::fabs( f_v[1] - 0.5) <= 1e-10; // f2_v1 // f1 = f2( v1 , 1 + v1 ) f_v[1] += f_v[2] * 1.; ok &= std::fabs( f_v[1] - 1.5) <= 1e-10; // f1_v1 return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end exp_2_rev1.cpp} */ ================================================ FILE: introduction/exp_2_rev2.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin exp_2_rev2.cpp} exp_2: Verify Second Order Reverse Sweep ######################################## {xrst_spell_off} {xrst_code cpp} */ # include // define size_t # include // prototype for fabs extern bool exp_2_for0(double *v0); // computes zero order forward sweep extern bool exp_2_for1(double *v1); // computes first order forward sweep bool exp_2_rev2(void) { bool ok = true; // set the value of v0[j], v1[j] for j = 1 , ... , 5 double v0[6], v1[6]; ok &= exp_2_for0(v0); ok &= exp_2_for1(v1); // initial all partial derivatives as zero double f_v0[6], f_v1[6]; size_t j; for(j = 0; j < 6; j++) { f_v0[j] = 0.; f_v1[j] = 0.; } // set partial derivative for f_5 f_v1[5] = 1.; ok &= std::fabs( f_v1[5] - 1. ) <= 1e-10; // partial f_5 w.r.t v_5^1 // f_4 = f_5( v_1^0 , ... , v_4^1 , v_2^0 + v_4^0 , v_2^1 + v_4^1 ) f_v0[2] += f_v0[5] * 1.; f_v0[4] += f_v0[5] * 1.; f_v1[2] += f_v1[5] * 1.; f_v1[4] += f_v1[5] * 1.; ok &= std::fabs( f_v0[2] - 0. ) <= 1e-10; // partial f_4 w.r.t. v_2^0 ok &= std::fabs( f_v0[4] - 0. ) <= 1e-10; // partial f_4 w.r.t. v_4^0 ok &= std::fabs( f_v1[2] - 1. ) <= 1e-10; // partial f_4 w.r.t. v_2^1 ok &= std::fabs( f_v1[4] - 1. ) <= 1e-10; // partial f_4 w.r.t. v_4^1 // f_3 = f_4( v_1^0 , ... , v_3^1, v_3^0 / 2 , v_3^1 / 2 ) f_v0[3] += f_v0[4] / 2.; f_v1[3] += f_v1[4] / 2.; ok &= std::fabs( f_v0[3] - 0. ) <= 1e-10; // partial f_3 w.r.t. v_3^0 ok &= std::fabs( f_v1[3] - 0.5 ) <= 1e-10; // partial f_3 w.r.t. v_3^1 // f_2 = f_3( v_1^0 , ... , v_2^1, v_1^0 * v_1^0 , 2 * v_1^0 * v_1^1 ) f_v0[1] += f_v0[3] * 2. * v0[1]; f_v0[1] += f_v1[3] * 2. * v1[1]; f_v1[1] += f_v1[3] * 2. * v0[1]; ok &= std::fabs( f_v0[1] - 1. ) <= 1e-10; // partial f_2 w.r.t. v_1^0 ok &= std::fabs( f_v1[1] - 0.5 ) <= 1e-10; // partial f_2 w.r.t. v_1^1 // f_1 = f_2( v_1^0 , v_1^1 , 1 + v_1^0 , v_1^1 ) f_v0[1] += f_v0[2] * 1.; f_v1[1] += f_v1[2] * 1.; ok &= std::fabs( f_v0[1] - 1. ) <= 1e-10; // partial f_1 w.r.t. v_1^0 ok &= std::fabs( f_v1[1] - 1.5) <= 1e-10; // partial f_1 w.r.t. v_1^1 return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end exp_2_rev2.cpp} */ ================================================ FILE: introduction/exp_eps.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN C++ # include // for fabs function # include "exp_eps.hpp" // definition of exp_eps algorithm bool exp_eps(void) { double x = .5; double epsilon = .2; double check = 1 + .5 + .125; // include 1 term less than epsilon bool ok = std::fabs( exp_eps(x, epsilon) - check ) <= 1e-10; return ok; } // END C++ ================================================ FILE: introduction/exp_eps.hpp ================================================ # ifndef CPPAD_INTRODUCTION_EXP_EPS_HPP # define CPPAD_INTRODUCTION_EXP_EPS_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin exp_eps} {xrst_spell apx yyyymmdd } An Epsilon Accurate Exponential Approximation ############################################# Syntax ****** | # ``include`` ``"exp_eps.hpp"`` | *y* = ``exp_eps`` ( *x* , *epsilon* ) Purpose ******* This is a an example algorithm that is used to demonstrate how Algorithmic Differentiation works with loops and boolean decision variables (see :ref:`exp_2-name` for a simpler example). Mathematical Function ********************* The exponential function can be defined by .. math:: \exp (x) = 1 + x^1 / 1 ! + x^2 / 2 ! + \cdots We define :math:`k ( x, \varepsilon )` as the smallest non-negative integer such that :math:`\varepsilon \geq x^k / k !`; i.e., .. math:: k( x, \varepsilon ) = \min \{ k \in {\rm Z}_+ \; | \; \varepsilon \geq x^k / k ! \} The mathematical form for our approximation of the exponential function is .. math:: :nowrap: \begin{eqnarray} {\rm exp\_eps} (x , \varepsilon ) & = & \left\{ \begin{array}{ll} \frac{1}{ {\rm exp\_eps} (-x , \varepsilon ) } & {\rm if} \; x < 0 \\ 1 + x^1 / 1 ! + \cdots + x^{k( x, \varepsilon)} / k( x, \varepsilon ) ! & {\rm otherwise} \end{array} \right. \end{eqnarray} include ******* The include command in the syntax is relative to ``cppad-`` *yyyymmdd* / ``introduction/exp_apx`` where ``cppad-`` *yyyymmdd* is the distribution directory created during the beginning steps of the :ref:`installation` of CppAD. x * The argument *x* has prototype ``const`` *Type* & *x* (see *Type* below). It specifies the point at which to evaluate the approximation for the exponential function. epsilon ******* The argument *epsilon* has prototype ``const`` *Type* & *epsilon* It specifies the accuracy with which to approximate the exponential function value; i.e., it is the value of :math:`\varepsilon` in the exponential function approximation defined above. y * The result *y* has prototype *Type* *y* It is the value of the exponential function approximation defined above. Type **** If *u* and *v* are *Type* objects and *i* is an ``int`` : .. list-table:: :widths: auto * - **Operation** - **Result Type** - **Description** * - *Type* ( *i* ) - *Type* - object with value equal to *i* * - *Type u* = *v* - *Type* - construct *u* with value equal to *v* * - *u* > *v* - ``bool`` - true, if *u* greater than *v* , an false otherwise * - *u* = *v* - *Type* - new *u* (and result) is value of *v* * - *u* * *v* - *Type* - result is value of :math:`u * v` * - *u* / *v* - *Type* - result is value of :math:`u / v` * - *u* + *v* - *Type* - result is value of :math:`u + v` * - ``-`` *u* - *Type* - result is value of :math:`- u` {xrst_toc_hidden introduction/exp_eps.xrst introduction/exp_eps_cppad.cpp } Implementation ************** The file :ref:`exp_eps.hpp-name` contains a C++ implementation of this function. Test **** The file :ref:`exp_eps.cpp-name` contains a test of this implementation. Exercises ********* #. Using the definition of :math:`k( x, \varepsilon )` above, what is the value of :math:`k(.5, 1)`, :math:`k(.5, .1)`, and :math:`k(.5, .01)` ? #. Suppose that we make the following call to ``exp_eps`` : :: double x = 1.; double epsilon = .01; double y = exp_eps(x, epsilon); What is the value assigned to ``k`` , ``temp`` , ``term`` , and ``sum`` the first time through the ``while`` loop in :ref:`exp_eps.hpp-name` ? #. Continuing the previous exercise, what is the value assigned to ``k`` , ``temp`` , ``term`` , and ``sum`` the second time through the ``while`` loop in :ref:`exp_eps.hpp-name` ? {xrst_end exp_eps} ----------------------------------------------------------------------------- */ // BEGIN C++ template Type exp_eps(const Type &x, const Type &epsilon) { // abs_x = |x| Type abs_x = x; if( Type(0) > x ) abs_x = - x; // initialize int k = 0; // initial order Type term = 1.; // term = |x|^k / k ! Type sum = term; // initial sum while(term > epsilon) { k = k + 1; // order for next term Type temp = term * abs_x; // term = |x|^k / (k-1)! term = temp / Type(k); // term = |x|^k / k ! sum = sum + term; // sum = 1 + ... + |x|^k / k ! } // In the case where x is negative, use exp(x) = 1 / exp(-|x|) if( Type(0) > x ) sum = Type(1) / sum; return sum; } // END C++ # endif ================================================ FILE: introduction/exp_eps.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin exp_eps.hpp} exp_eps: Implementation ####################### {xrst_literal introduction/exp_eps.hpp // BEGIN C++ // END C++ } {xrst_end exp_eps.hpp} ----------------------------------------------------------------------------- {xrst_begin exp_eps.cpp} exp_eps: Test of exp_eps ######################## {xrst_literal introduction/exp_eps.cpp // BEGIN C++ // END C++ } {xrst_end exp_eps.cpp} ----------------------------------------------------------------------------- {xrst_begin exp_eps_for0} exp_eps: Operation Sequence and Zero Order Forward Sweep ######################################################## Mathematical Form ***************** Suppose that we use the algorithm :ref:`exp_eps.hpp-name` to compute ``exp_eps`` ( *x* , *epsilon* ) with *x* is equal to .5 and *epsilon* is equal to .2. For this case, the mathematical form for the operation sequence corresponding to the ``exp_eps`` is .. math:: f( x , \varepsilon ) = 1 + x + x^2 / 2 Note that, for these particular values of *x* and *epsilon* , this is the same as the mathematical form for :ref:`exp_2` . Operation Sequence ****************** We consider the :ref:`operation sequence` corresponding to the algorithm :ref:`exp_eps.hpp-name` with the argument *x* is equal to .5 and *epsilon* is equal to .2. Variable ======== We refer to values that depend on the input variables *x* and *epsilon* as variables. Parameter ========= We refer to values that do not depend on the input variables *x* or *epsilon* as parameters. Operations where the result is a parameter are not included in the zero order sweep below. Index ===== The Index column contains the index in the operation sequence of the corresponding atomic operation and variable. A Forward sweep starts with the first operation and ends with the last. Code ==== The Code column contains the C++ source code corresponding to the corresponding atomic operation in the sequence. Operation ========= The Operation column contains the mathematical function corresponding to each atomic operation in the sequence. Zero Order ========== The Zero Order column contains the :ref:`zero order derivative` for the corresponding variable in the operation sequence. Forward mode refers to the fact that these coefficients are computed in the same order as the original algorithm; i.e., in order of increasing index. Sweep ===== .. csv-table:: :widths: auto **Index**,,**Code**,,**Operation**,,**Zero Order** 1,,``abs_x = x;``,,:math:`v_1 = x`,,:math:`v_1^{(0)} = 0.5` 2,,``temp = term * abs_x;``,,:math:`v_2 = 1 * v_1`,,:math:`v_2^{(0)} = 0.5` 3,,``term = temp / Type(k);``,,:math:`v_3 = v_2 / 1`,,:math:`v_3^{(0)} = 0.5` 4,,``sum = sum + term;``,,:math:`v_4 = 1 + v_3`,,:math:`v_4^{(0)} = 1.5` 5,,``temp = term * abs_x;``,,:math:`v_5 = v_3 * v_1`,,:math:`v_5^{(0)} = 0.25` 6,,``term = temp / Type(k);``,,:math:`v_6 = v_5 / 2`,,:math:`v_6^{(0)} = 0.125` 7,,``sum = sum + term;``,,:math:`v_7 = v_4 + v_6`,,:math:`v_7^{(0)} = 1.625` Return Value ************ The return value for this case is .. math:: 1.625 = v_7^{(0)} = f ( x^{(0)} , \varepsilon^{(0)} ) Comparisons *********** If *x* were negative, or if *epsilon* were a much smaller or much larger value, the results of the following comparisons could be different: :: if( Type(0) > x ) while(term > epsilon) This in turn would result in a different operation sequence. Thus the operation sequence above only corresponds to :ref:`exp_eps.hpp-name` for values of *x* and *epsilon* within a certain range. Note that there is a neighborhood of :math:`x = 0.5` for which the comparisons would have the same result and hence the operation sequence would be the same. {xrst_toc_hidden introduction/exp_eps_for0.cpp } Verification ************ The file :ref:`exp_eps_for0.cpp-name` contains a routine that verifies the values computed above. Exercises ********* #. Suppose that :math:`x^{(0)} = .1`, what is the result of a zero order forward sweep for the operation sequence above; i.e., what are the corresponding values for :math:`v_1^{(0)} , v_2^{(0)} , \ldots , v_7^{(0)}`. #. Create a modified version of :ref:`exp_eps_for0.cpp-name` that verifies the values you obtained for the previous exercise. #. Create and run a main program that reports the result of calling the modified version of :ref:`exp_eps_for0.cpp-name` in the previous exercise. {xrst_end exp_eps_for0} ----------------------------------------------------------------------------- {xrst_begin exp_eps_for1} exp_eps: First Order Forward Sweep ################################## First Order Expansion ********************* We define :math:`x(t)` and :math:`\varepsilon(t) ]` near :math:`t = 0` by the first order expansions .. math:: :nowrap: \begin{eqnarray} x(t) & = & x^{(0)} + x^{(1)} * t \\ \varepsilon(t) & = & \varepsilon^{(0)} + \varepsilon^{(1)} * t \end{eqnarray} It follows that :math:`x^{(0)}` (:math:`\varepsilon^{(0)}`) is the zero, and :math:`x^{(1)}` (:math:`\varepsilon^{(1)}`) the first, order derivative of :math:`x(t)` at :math:`t = 0` (:math:`\varepsilon (t)`) at :math:`t = 0`. Mathematical Form ***************** Suppose that we use the algorithm :ref:`exp_eps.hpp-name` to compute ``exp_eps`` ( *x* , *epsilon* ) with *x* is equal to .5 and *epsilon* is equal to .2. For this case, the mathematical function for the operation sequence corresponding to ``exp_eps`` is .. math:: f ( x , \varepsilon ) = 1 + x + x^2 / 2 The corresponding partial derivative with respect to :math:`x`, and the value of the derivative, are .. math:: \partial_x f ( x , \varepsilon ) = 1 + x = 1.5 Operation Sequence ****************** Index ===== The Index column contains the index in the operation sequence of the corresponding atomic operation. A Forward sweep starts with the first operation and ends with the last. Operation ========= The Operation column contains the mathematical function corresponding to each atomic operation in the sequence. Zero Order ========== The Zero Order column contains the zero order derivatives for the corresponding variable in the operation sequence (see :ref:`zero order sweep` ). Derivative ========== The Derivative column contains the mathematical function corresponding to the derivative with respect to :math:`t`, at :math:`t = 0`, for each variable in the sequence. First Order =========== The First Order column contains the first order derivatives for the corresponding variable in the operation sequence; i.e., .. math:: v_j (t) = v_j^{(0)} + v_j^{(1)} t We use :math:`x^{(1)} = 1` and :math:`\varepsilon^{(1)} = 0`, so that differentiation with respect to :math:`t`, at :math:`t = 0`, is the same partial differentiation with respect to :math:`x` at :math:`x = x^{(0)}`. Sweep ===== .. csv-table:: :widths: auto **Index**,,**Operation**,,**Zero Order**,,**Derivative**,,**First Order** 1,,:math:`v_1 = x`,,0.5,,:math:`v_1^{(1)} = x^{(1)}`,,:math:`v_1^{(1)} = 1` 2,,:math:`v_2 = 1 * v_1`,,0.5,,:math:`v_2^{(1)} = 1 * v_1^{(1)}`,,:math:`v_2^{(1)} = 1` 3,,:math:`v_3 = v_2 / 1`,,0.5,,:math:`v_3^{(1)} = v_2^{(1)} / 1`,,:math:`v_3^{(1)} = 1` 4,,:math:`v_4 = 1 + v_3`,,1.5,,:math:`v_4^{(1)} = v_3^{(1)}`,,:math:`v_4^{(1)} = 1` 5,,:math:`v_5 = v_3 * v_1`,,0.25,,:math:`v_5^{(1)} = v_3^{(1)} * v_1^{(0)} + v_3^{(0)} * v_1^{(1)}`,,:math:`v_5^{(1)} = 1` 6,,:math:`v_6 = v_5 / 2`,,0.125,,:math:`v_6^{(1)} = v_5^{(1)} / 2`,,:math:`v_6^{(1)} = 0.5` 7,,:math:`v_7 = v_4 + v_6`,,1.625,,:math:`v_7^{(1)} = v_4^{(1)} + v_6^{(1)}`,,:math:`v_7^{(1)} = 1.5` Return Value ************ The derivative of the return value for this case is .. math:: :nowrap: \begin{eqnarray} 1.5 & = & v_7^{(1)} = \left[ \D{v_7}{t} \right]_{t=0} = \left[ \D{}{t} f( x^{(0)} + x^{(1)} * t , \varepsilon^{(0)} ) \right]_{t=0} \\ & = & \partial_x f ( x^{(0)} , \varepsilon^{(0)} ) * x^{(1)} = \partial_x f ( x^{(0)} , \varepsilon^{(0)} ) \end{eqnarray} (We have used the fact that :math:`x^{(1)} = 1` and :math:`\varepsilon^{(1)} = 0`.) {xrst_toc_hidden introduction/exp_eps_for1.cpp } Verification ************ The file :ref:`exp_eps_for1.cpp-name` contains a routine that verifies the values computed above. Exercises ********* #. Suppose that :math:`x = .1`, what are the results of a zero and first order forward mode sweep for the operation sequence above; i.e., what are the corresponding values for :math:`v_1^{(0)}, v_2^{(0)}, \cdots , v_7^{(0)}` and :math:`v_1^{(1)}, v_2^{(1)}, \cdots , v_7^{(1)}` ? #. Create a modified version of :ref:`exp_eps_for1.cpp-name` that verifies the derivative values from the previous exercise. Also create and run a main program that reports the result of calling the modified version of :ref:`exp_eps_for1.cpp-name` . #. Suppose that :math:`x = .1` and :math:`\epsilon = .2`, what is the operation sequence corresponding to ``exp_eps`` ( *x* , *epsilon* ) {xrst_end exp_eps_for1} ----------------------------------------------------------------------------- {xrst_begin exp_eps_rev1} {xrst_spell rcll } exp_eps: First Order Reverse Sweep ################################## Purpose ******* First order reverse mode uses the :ref:`exp_eps_for0@Operation Sequence` , and zero order forward sweep values, to compute the first order derivative of one dependent variable with respect to all the independent variables. The computations are done in reverse of the order of the computations in the original algorithm. Mathematical Form ***************** Suppose that we use the algorithm :ref:`exp_eps.hpp-name` to compute ``exp_eps`` ( *x* , *epsilon* ) with *x* is equal to .5 and *epsilon* is equal to .2. For this case, the mathematical function for the operation sequence corresponding to ``exp_eps`` is .. math:: f ( x , \varepsilon ) = 1 + x + x^2 / 2 The corresponding partial derivatives, and the value of the derivatives, are .. math:: :nowrap: \begin{eqnarray} \partial_x f ( x , \varepsilon ) & = & 1 + x = 1.5 \\ \partial_\varepsilon f ( x , \varepsilon ) & = & 0 \end{eqnarray} epsilon ******* Since :math:`\varepsilon` is an independent variable, it could included as an argument to all of the :math:`f_j` functions below. The result would be that all the partials with respect to :math:`\varepsilon` would be zero and hence we drop it to simplify the presentation. f_7 *** In reverse mode we choose one dependent variable and compute its derivative with respect to all the independent variables. For our example, we chose the value returned by :ref:`exp_eps.hpp-name` which is :math:`v_7`. We begin with the function :math:`f_7` where :math:`v_7` is both an argument and the value of the function; i.e., .. math:: :nowrap: \begin{eqnarray} f_7 ( v_1 , v_2 , v_3 , v_4 , v_5 , v_6 , v_7 ) & = & v_7 \\ \D{f_7}{v_7} & = & 1 \end{eqnarray} All the other partial derivatives of :math:`f_7` are zero. Index 7: f_6 ************ The last operation has index 7, .. math:: v_7 = v_4 + v_6 We define the function :math:`f_6 ( v_1 , v_2 , v_3 , v_4 , v_5 , v_6 )` as equal to :math:`f_7` except that :math:`v_7` is eliminated using this operation; i.e. .. math:: f_6 = f_7 [ v_1 , v_2 , v_3 , v_4 , v_5 , v_6 , v_7 ( v_4 , v_6 ) ] It follows that .. math:: \begin{array}{rcll} \D{f_6}{v_4} & = & \D{f_7}{v_4} + \D{f_7}{v_7} * \D{v_7}{v_4} & = 1 \\ \D{f_6}{v_6} & = & \D{f_7}{v_6} + \D{f_7}{v_7} * \D{v_7}{v_6} & = 1 \end{array} All the other partial derivatives of :math:`f_6` are zero. Index 6: f_5 ************ The previous operation has index 6, .. math:: v_6 = v_5 / 2 We define the function :math:`f_5 ( v_1 , v_2 , v_3 , v_4 , v_5 )` as equal to :math:`f_6` except that :math:`v_6` is eliminated using this operation; i.e., .. math:: f_5 = f_6 [ v_1 , v_2 , v_3 , v_4 , v_5 , v_6 ( v_5 ) ] It follows that .. math:: \begin{array}{rcll} \D{f_5}{v_4} & = & \D{f_6}{v_4} & = 1 \\ \D{f_5}{v_5} & = & \D{f_6}{v_5} + \D{f_6}{v_6} * \D{v_6}{v_5} & = 0.5 \end{array} All the other partial derivatives of :math:`f_5` are zero. Index 5: f_4 ************ The previous operation has index 5, .. math:: v_5 = v_3 * v_1 We define the function :math:`f_4 ( v_1 , v_2 , v_3 , v_4 )` as equal to :math:`f_5` except that :math:`v_5` is eliminated using this operation; i.e., .. math:: f_4 = f_5 [ v_1 , v_2 , v_3 , v_4 , v_5 ( v_3 , v_1 ) ] Given the information from the forward sweep, we have :math:`v_3 = 0.5` and :math:`v_1 = 0.5`. It follows that .. math:: \begin{array}{rcll} \D{f_4}{v_1} & = & \D{f_5}{v_1} + \D{f_5}{v_5} * \D{v_5}{v_1} & = 0.25 \\ \D{f_4}{v_2} & = & \D{f_5}{v_2} & = 0 \\ \D{f_4}{v_3} & = & \D{f_5}{v_3} + \D{f_5}{v_5} * \D{v_5}{v_3} & = 0.25 \\ \D{f_4}{v_4} & = & \D{f_5}{v_4} & = 1 \end{array} Index 4: f_3 ************ The previous operation has index 4, .. math:: v_4 = 1 + v_3 We define the function :math:`f_3 ( v_1 , v_2 , v_3 )` as equal to :math:`f_4` except that :math:`v_4` is eliminated using this operation; i.e., .. math:: f_3 = f_4 [ v_1 , v_2 , v_3 , v_4 ( v_3 ) ] It follows that .. math:: \begin{array}{rcll} \D{f_3}{v_1} & = & \D{f_4}{v_1} & = 0.25 \\ \D{f_3}{v_2} & = & \D{f_4}{v_2} & = 0 \\ \D{f_3}{v_3} & = & \D{f_4}{v_3} + \D{f_4}{v_4} * \D{v_4}{v_3} & = 1.25 \end{array} Index 3: f_2 ************ The previous operation has index 3, .. math:: v_3 = v_2 / 1 We define the function :math:`f_2 ( v_1 , v_2 )` as equal to :math:`f_3` except that :math:`v_3` is eliminated using this operation; i.e., .. math:: f_2 = f_4 [ v_1 , v_2 , v_3 ( v_2 ) ] It follows that .. math:: \begin{array}{rcll} \D{f_2}{v_1} & = & \D{f_3}{v_1} & = 0.25 \\ \D{f_2}{v_2} & = & \D{f_3}{v_2} + \D{f_3}{v_3} * \D{v_3}{v_2} & = 1.25 \end{array} Index 2: f_1 ************ The previous operation has index 1, .. math:: v_2 = 1 * v_1 We define the function :math:`f_1 ( v_1 )` as equal to :math:`f_2` except that :math:`v_2` is eliminated using this operation; i.e., .. math:: f_1 = f_2 [ v_1 , v_2 ( v_1 ) ] It follows that .. math:: \begin{array}{rcll} \D{f_1}{v_1} & = & \D{f_2}{v_1} + \D{f_2}{v_2} * \D{v_2}{v_1} & = 1.5 \end{array} Note that :math:`v_1` is equal to :math:`x`, so the derivative of ``exp_eps`` ( *x* , *epsilon* ) at *x* equal to .5 and *epsilon* equal .2 is 1.5 in the *x* direction and zero in the *epsilon* direction. We also note that :ref:`forward` mode gave the same result for the partial in the *x* direction. {xrst_toc_hidden introduction/exp_eps_rev1.cpp } Verification ************ The file :ref:`exp_eps_rev1.cpp-name` contains a routine that verifies the values computed above. It only tests the partial derivatives of :math:`f_j` that might not be equal to the corresponding partials of :math:`f_{j+1}`; i.e., the other partials of :math:`f_j` must be equal to the corresponding partials of :math:`f_{j+1}`. Exercises ********* #. Consider the case where :math:`x = .1` and we first preform a zero order forward mode sweep for the operation sequence used above (in reverse order). What are the results of a first order reverse mode sweep; i.e., what are the corresponding values for :math:`\D{f_j}{v_k}` for all :math:`j, k` such that :math:`\D{f_j}{v_k} \neq 0`. #. Create a modified version of :ref:`exp_eps_rev1.cpp-name` that verifies the values you obtained for the previous exercise. Also create and run a main program that reports the result of calling the modified version of :ref:`exp_eps_rev1.cpp-name` . {xrst_end exp_eps_rev1} ----------------------------------------------------------------------------- {xrst_begin exp_eps_for2} exp_eps: Second Order Forward Mode ################################## Second Order Expansion ********************** We define :math:`x(t)` and :math:`\varepsilon(t) ]` near :math:`t = 0` by the second order expansions .. math:: :nowrap: \begin{eqnarray} x(t) & = & x^{(0)} + x^{(1)} * t + x^{(2)} * t^2 / 2 \\ \varepsilon(t) & = & \varepsilon^{(0)} + \varepsilon^{(1)} * t + \varepsilon^{(2)} * t^2 / 2 \end{eqnarray} It follows that for :math:`k = 0 , 1 , 2`, .. math:: :nowrap: \begin{eqnarray} x^{(k)} & = & \dpow{k}{t} x (0) \\ \varepsilon^{(k)} & = & \dpow{k}{t} \varepsilon (0) \end{eqnarray} Purpose ******* In general, a second order forward sweep is given the :ref:`exp_2_for1@First Order Expansion` for all of the variables in an operation sequence, and the second order derivatives for the independent variables. It uses these to compute the second order derivative, and thereby obtain the second order expansion, for all the variables in the operation sequence. Mathematical Form ***************** Suppose that we use the algorithm :ref:`exp_eps.hpp-name` to compute ``exp_eps`` ( *x* , *epsilon* ) with *x* is equal to .5 and *epsilon* is equal to .2. For this case, the mathematical function for the operation sequence corresponding to ``exp_eps`` is .. math:: f ( x , \varepsilon ) = 1 + x + x^2 / 2 The corresponding second partial derivative with respect to :math:`x`, and the value of the derivative, are .. math:: \Dpow{2}{x} f ( x , \varepsilon ) = 1. Operation Sequence ****************** Index ===== The Index column contains the index in the operation sequence of the corresponding atomic operation. A Forward sweep starts with the first operation and ends with the last. Zero ==== The Zero column contains the zero order sweep results for the corresponding variable in the operation sequence (see :ref:`zero order sweep` ). Operation ========= The Operation column contains the first order sweep operation for this variable. First ===== The First column contains the first order sweep results for the corresponding variable in the operation sequence (see :ref:`first order sweep` ). Derivative ========== The Derivative column contains the mathematical function corresponding to the second derivative with respect to :math:`t`, at :math:`t = 0`, for each variable in the sequence. Second ====== The Second column contains the second order derivatives for the corresponding variable in the operation sequence; i.e., the second order expansion for the *i*-th variable is given by .. math:: v_i (t) = v_i^{(0)} + v_i^{(1)} * t + v_i^{(2)} * t^2 / 2 We use :math:`x^{(1)} = 1`, :math:`x^{(2)} = 0`, use :math:`\varepsilon^{(1)} = 1`, and :math:`\varepsilon^{(2)} = 0` so that second order differentiation with respect to :math:`t`, at :math:`t = 0`, is the same as the second partial differentiation with respect to :math:`x` at :math:`x = x^{(0)}`. Sweep ===== .. list-table:: :widths: auto * - **Index** - - **Zero** - - **Operation** - - **First** - - **Derivative** - - **Second** * - 1 - - 0.5 - - :math:`v_1^{(1)} = x^{(1)}` - - 1 - - :math:`v_2^{(2)} = x^{(2)}` - - 0 * - 2 - - 0.5 - - :math:`v_2^{(1)} = 1 * v_1^{(1)}` - - 1 - - :math:`v_2^{(2)} = 1 * v_1^{(2)}` - - 0 * - 3 - - 0.5 - - :math:`v_3^{(1)} = v_2^{(1)} / 1` - - 1 - - :math:`v_3^{(2)} = v_2^{(2)} / 1` - - 0 * - 4 - - 1.5 - - :math:`v_4^{(1)} = v_3^{(1)}` - - 1 - - :math:`v_4^{(2)} = v_3^{(2)}` - - 0 * - 5 - - 0.25 - - :math:`v_5^{(1)} = v_3^{(1)} * v_1^{(0)} + v_3^{(0)} * v_1^{(1)}` - - 1 - - :math:`v_5^{(2)} = v_3^{(2)} * v_1^{(0)} + 2 * v_3^{(1)} * v_1^{(1)}` :math:`+ v_3^{(0)} * v_1^{(2)}` - - 2 * - 6 - - 0.125 - - :math:`v_6^{(1)} = v_5^{(1)} / 2` - - 0.5 - - :math:`v_6^{(2)} = v_5^{(2)} / 2` - - 1 * - 7 - - 1.625 - - :math:`v_7^{(1)} = v_4^{(1)} + v_6^{(1)}` - - 1.5 - - :math:`v_7^{(2)} = v_4^{(2)} + v_6^{(2)}` - - 1 Return Value ************ The second derivative of the return value for this case is .. math:: :nowrap: \begin{eqnarray} 1 & = & v_7^{(2)} = \left[ \Dpow{2}{t} v_7 \right]_{t=0} = \left[ \Dpow{2}{t} f( x^{(0)} + x^{(1)} * t , \varepsilon^{(0)} ) \right]_{t=0} \\ & = & x^{(1)} * \Dpow{2}{x} f ( x^{(0)} , \varepsilon^{(0)} ) * x^{(1)} = \Dpow{2}{x} f ( x^{(0)} , \varepsilon^{(0)} ) \end{eqnarray} (We have used the fact that :math:`x^{(1)} = 1`, :math:`x^{(2)} = 0`, :math:`\varepsilon^{(1)} = 1`, and :math:`\varepsilon^{(2)} = 0`.) {xrst_toc_hidden introduction/exp_eps_for2.cpp } Verification ************ The file :ref:`exp_eps_for2.cpp-name` contains a routine which verifies the values computed above. Exercises ********* #. Which statement in the routine defined by :ref:`exp_eps_for2.cpp-name` uses the values that are calculated by the routine defined by :ref:`exp_eps_for1.cpp-name` ? #. Suppose that :math:`x = .1`, what are the results of a zero, first, and second order forward sweep for the operation sequence above; i.e., what are the corresponding values for :math:`v_i^{(k)}` for :math:`i = 1, \ldots , 7` and :math:`k = 0, 1, 2`. #. Create a modified version of :ref:`exp_eps_for2.cpp-name` that verifies the derivative values from the previous exercise. Also create and run a main program that reports the result of calling the modified version of :ref:`exp_eps_for2.cpp-name` . {xrst_end exp_eps_for2} ----------------------------------------------------------------------------- {xrst_begin exp_eps_rev2} {xrst_spell rcll } exp_eps: Second Order Reverse Sweep ################################### Purpose ******* In general, a second order reverse sweep is given the :ref:`exp_eps_for1@First Order Expansion` for all of the variables in an operation sequence. Given a choice of a particular variable, it computes the derivative, of that variables first order expansion coefficient, with respect to all of the independent variables. Mathematical Form ***************** Suppose that we use the algorithm :ref:`exp_eps.hpp-name` to compute ``exp_eps`` ( *x* , *epsilon* ) with *x* is equal to .5 and *epsilon* is equal to .2. For this case, the mathematical function for the operation sequence corresponding to ``exp_eps`` is .. math:: f ( x , \varepsilon ) = 1 + x + x^2 / 2 The corresponding derivative of the partial derivative with respect to :math:`x` is .. math:: :nowrap: \begin{eqnarray} \Dpow{2}{x} f ( x , \varepsilon ) & = & 1 \\ \partial_\varepsilon \partial_x f ( x , \varepsilon ) & = & 0 \end{eqnarray} epsilon ******* Since :math:`\varepsilon` is an independent variable, it could included as an argument to all of the :math:`f_j` functions below. The result would be that all the partials with respect to :math:`\varepsilon` would be zero and hence we drop it to simplify the presentation. f_7 *** In reverse mode we choose one dependent variable and compute its derivative with respect to all the independent variables. For our example, we chose the value returned by :ref:`exp_eps.hpp-name` which is :math:`v_7`. We begin with the function :math:`f_7` where :math:`v_7` is both an argument and the value of the function; i.e., .. math:: :nowrap: \begin{eqnarray} f_7 \left( v_1^{(0)} , v_1^{(1)} , \ldots , v_7^{(0)} , v_7^{(1)} \right) & = & v_7^{(1)} \\ \D{f_7}{v_7^{(1)}} & = & 1 \end{eqnarray} All the other partial derivatives of :math:`f_7` are zero. Index 7: f_6 ************ The last operation has index 7, .. math:: :nowrap: \begin{eqnarray} v_7^{(0)} & = & v_4^{(0)} + v_6^{(0)} \\ v_7^{(1)} & = & v_4^{(1)} + v_6^{(1)} \end{eqnarray} We define the function :math:`f_6 \left( v_1^{(0)} , \ldots , v_6^{(1)} \right)` as equal to :math:`f_7` except that :math:`v_7^{(0)}` and :math:`v_7^{(1)}` are eliminated using this operation; i.e. .. math:: f_6 = f_7 \left[ v_1^{(0)} , \ldots , v_6^{(1)} , v_7^{(0)} \left( v_4^{(0)} , v_6^{(0)} \right) , v_7^{(1)} \left( v_4^{(1)} , v_6^{(1)} \right) \right] It follows that .. math:: \begin{array}{rcll} \D{f_6}{v_4^{(1)}} & = & \D{f_7}{v_4^{(1)}} + \D{f_7}{v_7^{(1)}} * \D{v_7^{(1)}}{v_4^{(1)}} & = 1 \\ \D{f_6}{v_6^{(1)}} & = & \D{f_7}{v_6^{(1)}} + \D{f_7}{v_7^{(1)}} * \D{v_7^{(1)}}{v_6^{(1)}} & = 1 \end{array} All the other partial derivatives of :math:`f_6` are zero. Index 6: f_5 ************ The previous operation has index 6, .. math:: :nowrap: \begin{eqnarray} v_6^{(0)} & = & v_5^{(0)} / 2 \\ v_6^{(1)} & = & v_5^{(1)} / 2 \end{eqnarray} We define the function :math:`f_5 \left( v_1^{(0)} , \ldots , v_5^{(1)} \right)` as equal to :math:`f_6` except that :math:`v_6^{(0)}` and :math:`v_6^{(1)}` are eliminated using this operation; i.e. .. math:: f_5 = f_6 \left[ v_1^{(0)} , \ldots , v_5^{(1)} , v_6^{(0)} \left( v_5^{(0)} \right) , v_6^{(1)} \left( v_5^{(1)} \right) \right] It follows that .. math:: \begin{array}{rcll} \D{f_5}{v_4^{(1)}} & = & \D{f_6}{v_4^{(1)}} & = 1 \\ \D{f_5}{v_5^{(1)}} & = & \D{f_6}{v_5} + \D{f_6}{v_6^{(1)}} * \D{v_6^{(1)}}{v_5^{(1)}} & = 0.5 \end{array} All the other partial derivatives of :math:`f_5` are zero. Index 5: f_4 ************ The previous operation has index 5, .. math:: :nowrap: \begin{eqnarray} v_5^{(0)} & = & v_3^{(0)} * v_1^{(0)} \\ v_5^{(1)} & = & v_3^{(1)} * v_1^{(0)} + v_3^{(0)} * v_1^{(1)} \end{eqnarray} We define the function :math:`f_4 \left( v_1^{(0)} , \ldots , v_4^{(1)} \right)` as equal to :math:`f_5` except that :math:`v_5^{(0)}` and :math:`v_5^{(1)}` are eliminated using this operation; i.e. .. math:: f_4 = f_5 \left[ v_1^{(0)} , \ldots , v_4^{(1)} , v_5^{(0)} \left( v_1^{(0)}, v_3^{(0)} \right) , v_5^{(1)} \left( v_1^{(0)}, v_1^{(1)}, v_3^{(0)} , v_3^{(1)} \right) , \right] Given the information from the forward sweep, we have :math:`v_1^{(0)} = 0.5`, :math:`v_3^{(0)} = 0.5`, :math:`v_1^{(1)} = 1`, :math:`v_3^{(1)} = 1`, and the fact that the partial of :math:`f_5` with respect to :math:`v_5^{(0)}` is zero, we have .. math:: \begin{array}{rcll} \D{f_4}{v_1^{(0)}} & = & \D{f_5}{v_1^{(0)}} + \D{f_5}{v_5^{(1)}} * \D{v_5^{(1)}}{v_1^{(0)}} & = 0.5 \\ \D{f_4}{v_1^{(1)}} & = & \D{f_5}{v_1^{(1)}} + \D{f_5}{v_5^{(1)}} * \D{v_5^{(1)}}{v_1^{(1)}} & = 0.25 \\ \D{f_4}{v_3^{(0)}} & = & \D{f_5}{v_3^{(0)}} + \D{f_5}{v_5^{(1)}} * \D{v_5^{(1)}}{v_3^{(0)}} & = 0.5 \\ \D{f_4}{v_3^{(1)}} & = & \D{f_3}{v_1^{(1)}} + \D{f_5}{v_5^{(1)}} * \D{v_5^{(1)}}{v_3^{(1)}} & = 0.25 \\ \D{f_4}{v_4^{(1)}} & = & \D{f_5}{v_4^{(1)}} & = 1 \end{array} All the other partial derivatives of :math:`f_5` are zero. Index 4: f_3 ************ The previous operation has index 4, .. math:: :nowrap: \begin{eqnarray} v_4^{(0)} = 1 + v_3^{(0)} \\ v_4^{(1)} = v_3^{(1)} \end{eqnarray} We define the function :math:`f_3 \left( v_1^{(0)} , \ldots , v_3^{(1)} \right)` as equal to :math:`f_4` except that :math:`v_4^{(0)}` and :math:`v_4^{(1)}` are eliminated using this operation; i.e. .. math:: f_3 = f_4 \left[ v_1^{(0)} , \ldots , v_3^{(1)} , v_4^{(0)} \left( v_3^{(0)} \right) , v_4^{(1)} \left( v_3^{(1)} \right) \right] It follows that .. math:: \begin{array}{rcll} \D{f_3}{v_1^{(0)}} & = & \D{f_4}{v_1^{(0)}} & = 0.5 \\ \D{f_3}{v_1^{(1)}} & = & \D{f_4}{v_1^{(1)}} & = 0.25 \\ \D{f_3}{v_2^{(0)}} & = & \D{f_4}{v_2^{(0)}} & = 0 \\ \D{f_3}{v_2^{(1)}} & = & \D{f_4}{v_2^{(1)}} & = 0 \\ \D{f_3}{v_3^{(0)}} & = & \D{f_4}{v_3^{(0)}} + \D{f_4}{v_4^{(0)}} * \D{v_4^{(0)}}{v_3^{(0)}} & = 0.5 \\ \D{f_3}{v_3^{(1)}} & = & \D{f_4}{v_3^{(1)}} + \D{f_4}{v_4^{(1)}} * \D{v_4^{(1)}}{v_3^{(1)}} & = 1.25 \end{array} Index 3: f_2 ************ The previous operation has index 3, .. math:: :nowrap: \begin{eqnarray} v_3^{(0)} & = & v_2^{(0)} / 1 \\ v_3^{(1)} & = & v_2^{(1)} / 1 \end{eqnarray} We define the function :math:`f_2 \left( v_1^{(0)} , \ldots , v_2^{(1)} \right)` as equal to :math:`f_3` except that :math:`v_3^{(0)}` and :math:`v_3^{(1)}` are eliminated using this operation; i.e. .. math:: f_2 = f_3 \left[ v_1^{(0)} , \ldots , v_2^{(1)} , v_3^{(0)} \left( v_2^{(0)} \right) , v_3^{(1)} \left( v_2^{(1)} \right) \right] It follows that .. math:: \begin{array}{rcll} \D{f_2}{v_1^{(0)}} & = & \D{f_3}{v_1^{(0)}} & = 0.5 \\ \D{f_2}{v_1^{(1)}} & = & \D{f_3}{v_1^{(1)}} & = 0.25 \\ \D{f_2}{v_2^{(0)}} & = & \D{f_3}{v_2^{(0)}} + \D{f_3}{v_3^{(0)}} * \D{v_3^{(0)}}{v_2^{(0)}} & = 0.5 \\ \D{f_2}{v_2^{(1)}} & = & \D{f_3}{v_2^{(1)}} + \D{f_3}{v_3^{(1)}} * \D{v_3^{(1)}}{v_2^{(0)}} & = 1.25 \end{array} Index 2: f_1 ************ The previous operation has index 1, .. math:: :nowrap: \begin{eqnarray} v_2^{(0)} & = & 1 * v_1^{(0)} \\ v_2^{(1)} & = & 1 * v_1^{(1)} \end{eqnarray} We define the function :math:`f_1 \left( v_1^{(0)} , v_1^{(1)} \right)` as equal to :math:`f_2` except that :math:`v_2^{(0)}` and :math:`v_2^{(1)}` are eliminated using this operation; i.e. .. math:: f_1 = f_2 \left[ v_1^{(0)} , v_1^{(1)} , v_2^{(0)} \left( v_1^{(0)} \right) , v_2^{(1)} \left( v_1^{(1)} \right) \right] It follows that .. math:: \begin{array}{rcll} \D{f_1}{v_1^{(0)}} & = & \D{f_2}{v_1^{(0)}} + \D{f_2}{v_2^{(0)}} * \D{v_2^{(0)}}{v_1^{(0)}} & = 1 \\ \D{f_1}{v_1^{(1)}} & = & \D{f_2}{v_1^{(1)}} + \D{f_2}{v_2^{(1)}} * \D{v_2^{(1)}}{v_1^{(1)}} & = 1.5 \end{array} Note that :math:`v_1` is equal to :math:`x`, so the second partial derivative of ``exp_eps`` ( *x* , *epsilon* ) at *x* equal to .5 and *epsilon* equal .2 is .. math:: \Dpow{2}{x} v_7^{(0)} = \D{v_7^{(1)}}{x} = \D{f_1}{v_1^{(0)}} = 1 There is a theorem about algorithmic differentiation that explains why the other partial of :math:`f_1` is equal to the first partial of ``exp_eps`` ( *x* , *epsilon* ) with respect to :math:`x`. {xrst_toc_hidden introduction/exp_eps_rev2.cpp } Verification ************ The file :ref:`exp_eps_rev2.cpp-name` contains a routine that verifies the values computed above. It only tests the partial derivatives of :math:`f_j` that might not be equal to the corresponding partials of :math:`f_{j+1}`; i.e., the other partials of :math:`f_j` must be equal to the corresponding partials of :math:`f_{j+1}`. Exercises ********* #. Consider the case where :math:`x = .1` and we first preform a zero order forward mode sweep for the operation sequence used above (in reverse order). What are the results of a first order reverse mode sweep; i.e., what are the corresponding values for :math:`\D{f_j}{v_k}` for all :math:`j, k` such that :math:`\D{f_j}{v_k} \neq 0`. #. Create a modified version of :ref:`exp_eps_rev2.cpp-name` that verifies the values you obtained for the previous exercise. Also create and run a main program that reports the result of calling the modified version of :ref:`exp_eps_rev2.cpp-name` . {xrst_end exp_eps_rev2} ----------------------------------------------------------------------------- ================================================ FILE: introduction/exp_eps_cppad.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin exp_eps_cppad} exp_eps: CppAD Forward and Reverse Sweeps ######################################### Purpose ******* Use CppAD forward and reverse modes to compute the partial derivative with respect to :math:`x`, at the point :math:`x = .5` and :math:`\varepsilon = .2`, of the function ``exp_eps`` ( *x* , *epsilon* ) as defined by the :ref:`exp_eps.hpp-name` include file. Exercises ********* #. Create and test a modified version of the routine below that computes the same order derivatives with respect to :math:`x`, at the point :math:`x = .1` and :math:`\varepsilon = .2`, of the function ``exp_eps`` ( *x* , *epsilon* ) #. Create and test a modified version of the routine below that computes partial derivative with respect to :math:`x`, at the point :math:`x = .1` and :math:`\varepsilon = .2`, of the function corresponding to the operation sequence for :math:`x = .5` and :math:`\varepsilon = .2`. Hint: you could define a vector u with two components and use *f* . ``Forward`` (0, *u* ) to run zero order forward mode at a point different form the point where the operation sequence corresponding to *f* was recorded. {xrst_spell_off} {xrst_code cpp} */ # include // https://www.coin-or.org/CppAD/ # include "exp_eps.hpp" // our example exponential function approximation bool exp_eps_cppad(void) { bool ok = true; using CppAD::AD; using CppAD::vector; // can use any simple vector template class using CppAD::NearEqual; // checks if values are nearly equal // domain space vector size_t n = 2; // dimension of the domain space vector< AD > U(n); U[0] = .5; // value of x for this operation sequence U[1] = .2; // value of e for this operation sequence // declare independent variables and start recording operation sequence CppAD::Independent(U); // evaluate our exponential approximation AD x = U[0]; AD epsilon = U[1]; AD apx = exp_eps(x, epsilon); // range space vector size_t m = 1; // dimension of the range space vector< AD > Y(m); Y[0] = apx; // variable that represents only range space component // Create f: U -> Y corresponding to this operation sequence // and stop recording. This also executes a zero order forward // mode sweep using values in U for x and e. CppAD::ADFun f(U, Y); // first order forward mode sweep that computes partial w.r.t x vector du(n); // differential in domain space vector dy(m); // differential in range space du[0] = 1.; // x direction in domain space du[1] = 0.; dy = f.Forward(1, du); // partial w.r.t. x double check = 1.5; ok &= NearEqual(dy[0], check, 1e-10, 1e-10); // first order reverse mode sweep that computes the derivative vector w(m); // weights for components of the range vector dw(n); // derivative of the weighted function w[0] = 1.; // there is only one weight dw = f.Reverse(1, w); // derivative of w[0] * exp_eps(x, epsilon) check = 1.5; // partial w.r.t. x ok &= NearEqual(dw[0], check, 1e-10, 1e-10); check = 0.; // partial w.r.t. epsilon ok &= NearEqual(dw[1], check, 1e-10, 1e-10); // second order forward sweep that computes // second partial of exp_eps(x, epsilon) w.r.t. x vector x2(n); // second order Taylor coefficients vector y2(m); x2[0] = 0.; // evaluate partial w.r.t x x2[1] = 0.; y2 = f.Forward(2, x2); check = 0.5 * 1.; // Taylor coef is 1/2 second derivative ok &= NearEqual(y2[0], check, 1e-10, 1e-10); // second order reverse sweep that computes // derivative of partial of exp_eps(x, epsilon) w.r.t. x dw.resize(2 * n); // space for first and second derivative dw = f.Reverse(2, w); check = 1.; // result should be second derivative ok &= NearEqual(dw[0*2+1], check, 1e-10, 1e-10); return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end exp_eps_cppad} */ ================================================ FILE: introduction/exp_eps_for0.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin exp_eps_for0.cpp} exp_eps: Verify Zero Order Forward Sweep ######################################## {xrst_spell_off} {xrst_code cpp} */ # include // for fabs function bool exp_eps_for0(double *v0) // double v0[8] { bool ok = true; double x = .5; v0[1] = x; // abs_x = x; ok &= std::fabs( v0[1] - 0.5) < 1e-10; v0[2] = 1. * v0[1]; // temp = term * abs_x; ok &= std::fabs( v0[2] - 0.5) < 1e-10; v0[3] = v0[2] / 1.; // term = temp / Type(k); ok &= std::fabs( v0[3] - 0.5) < 1e-10; v0[4] = 1. + v0[3]; // sum = sum + term; ok &= std::fabs( v0[4] - 1.5) < 1e-10; v0[5] = v0[3] * v0[1]; // temp = term * abs_x; ok &= std::fabs( v0[5] - 0.25) < 1e-10; v0[6] = v0[5] / 2.; // term = temp / Type(k); ok &= std::fabs( v0[6] - 0.125) < 1e-10; v0[7] = v0[4] + v0[6]; // sum = sum + term; ok &= std::fabs( v0[7] - 1.625) < 1e-10; return ok; } bool exp_eps_for0(void) { double v0[8]; return exp_eps_for0(v0); } /* {xrst_code} {xrst_spell_on} {xrst_end exp_eps_for0.cpp} */ ================================================ FILE: introduction/exp_eps_for1.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin exp_eps_for1.cpp} exp_eps: Verify First Order Forward Sweep ######################################### {xrst_spell_off} {xrst_code cpp} */ # include // for fabs function extern bool exp_eps_for0(double *v0); // computes zero order forward sweep bool exp_eps_for1(double *v1) // double v[8] { bool ok = true; double v0[8]; // set the value of v0[j] for j = 1 , ... , 7 ok &= exp_eps_for0(v0); v1[1] = 1.; // v1 = x ok &= std::fabs( v1[1] - 1. ) <= 1e-10; v1[2] = 1. * v1[1]; // v2 = 1 * v1 ok &= std::fabs( v1[2] - 1. ) <= 1e-10; v1[3] = v1[2] / 1.; // v3 = v2 / 1 ok &= std::fabs( v1[3] - 1. ) <= 1e-10; v1[4] = v1[3]; // v4 = 1 + v3 ok &= std::fabs( v1[4] - 1. ) <= 1e-10; v1[5] = v1[3] * v0[1] + v0[3] * v1[1]; // v5 = v3 * v1 ok &= std::fabs( v1[5] - 1. ) <= 1e-10; v1[6] = v1[5] / 2.; // v6 = v5 / 2 ok &= std::fabs( v1[6] - 0.5 ) <= 1e-10; v1[7] = v1[4] + v1[6]; // v7 = v4 + v6 ok &= std::fabs( v1[7] - 1.5 ) <= 1e-10; return ok; } bool exp_eps_for1(void) { double v1[8]; return exp_eps_for1(v1); } /* {xrst_code} {xrst_spell_on} {xrst_end exp_eps_for1.cpp} */ ================================================ FILE: introduction/exp_eps_for2.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin exp_eps_for2.cpp} exp_eps: Verify Second Order Forward Sweep ########################################## {xrst_spell_off} {xrst_code cpp} */ # include // for fabs function extern bool exp_eps_for0(double *v0); // computes zero order forward sweep extern bool exp_eps_for1(double *v1); // computes first order forward sweep bool exp_eps_for2(void) { bool ok = true; double v0[8], v1[8], v2[8]; // set the value of v0[j], v1[j] for j = 1 , ... , 7 ok &= exp_eps_for0(v0); ok &= exp_eps_for1(v1); v2[1] = 0.; // v1 = x ok &= std::fabs( v2[1] - 0. ) <= 1e-10; v2[2] = 1. * v2[1]; // v2 = 1 * v1 ok &= std::fabs( v2[2] - 0. ) <= 1e-10; v2[3] = v2[2] / 1.; // v3 = v2 / 1 ok &= std::fabs( v2[3] - 0. ) <= 1e-10; v2[4] = v2[3]; // v4 = 1 + v3 ok &= std::fabs( v2[4] - 0. ) <= 1e-10; v2[5] = v2[3] * v0[1] + 2. * v1[3] * v1[1] // v5 = v3 * v1 + v0[3] * v2[1]; ok &= std::fabs( v2[5] - 2. ) <= 1e-10; v2[6] = v2[5] / 2.; // v6 = v5 / 2 ok &= std::fabs( v2[6] - 1. ) <= 1e-10; v2[7] = v2[4] + v2[6]; // v7 = v4 + v6 ok &= std::fabs( v2[7] - 1. ) <= 1e-10; return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end exp_eps_for2.cpp} */ ================================================ FILE: introduction/exp_eps_rev1.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin exp_eps_rev1.cpp} exp_eps: Verify First Order Reverse Sweep ######################################### {xrst_spell_off} {xrst_code cpp} */ # include // define size_t # include // for fabs function extern bool exp_eps_for0(double *v0); // computes zero order forward sweep bool exp_eps_rev1(void) { bool ok = true; // set the value of v0[j] for j = 1 , ... , 7 double v0[8]; ok &= exp_eps_for0(v0); // initial all partial derivatives as zero double f_v[8]; size_t j; for(j = 0; j < 8; j++) f_v[j] = 0.; // set partial derivative for f7 f_v[7] = 1.; ok &= std::fabs( f_v[7] - 1. ) <= 1e-10; // f7_v7 // f6( v1 , v2 , v3 , v4 , v5 , v6 ) f_v[4] += f_v[7] * 1.; f_v[6] += f_v[7] * 1.; ok &= std::fabs( f_v[4] - 1. ) <= 1e-10; // f6_v4 ok &= std::fabs( f_v[6] - 1. ) <= 1e-10; // f6_v6 // f5( v1 , v2 , v3 , v4 , v5 ) f_v[5] += f_v[6] / 2.; ok &= std::fabs( f_v[5] - 0.5 ) <= 1e-10; // f5_v5 // f4( v1 , v2 , v3 , v4 ) f_v[1] += f_v[5] * v0[3]; f_v[3] += f_v[5] * v0[1]; ok &= std::fabs( f_v[1] - 0.25) <= 1e-10; // f4_v1 ok &= std::fabs( f_v[3] - 0.25) <= 1e-10; // f4_v3 // f3( v1 , v2 , v3 ) f_v[3] += f_v[4] * 1.; ok &= std::fabs( f_v[3] - 1.25) <= 1e-10; // f3_v3 // f2( v1 , v2 ) f_v[2] += f_v[3] / 1.; ok &= std::fabs( f_v[2] - 1.25) <= 1e-10; // f2_v2 // f1( v1 ) f_v[1] += f_v[2] * 1.; ok &= std::fabs( f_v[1] - 1.5 ) <= 1e-10; // f1_v2 return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end exp_eps_rev1.cpp} */ ================================================ FILE: introduction/exp_eps_rev2.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin exp_eps_rev2.cpp} exp_eps: Verify Second Order Reverse Sweep ########################################## {xrst_spell_off} {xrst_code cpp} */ # include // define size_t # include // for fabs function extern bool exp_eps_for0(double *v0); // computes zero order forward sweep extern bool exp_eps_for1(double *v1); // computes first order forward sweep bool exp_eps_rev2(void) { bool ok = true; // set the value of v0[j], v1[j] for j = 1 , ... , 7 double v0[8], v1[8]; ok &= exp_eps_for0(v0); ok &= exp_eps_for1(v1); // initial all partial derivatives as zero double f_v0[8], f_v1[8]; size_t j; for(j = 0; j < 8; j++) { f_v0[j] = 0.; f_v1[j] = 0.; } // set partial derivative for f_7 f_v1[7] = 1.; ok &= std::fabs( f_v1[7] - 1. ) <= 1e-10; // partial f_7 w.r.t. v_7^1 // f_6 = f_7( v_1^0 , ... , v_6^1 , v_4^0 + v_6^0, v_4^1 , v_6^1 ) f_v0[4] += f_v0[7]; f_v0[6] += f_v0[7]; f_v1[4] += f_v1[7]; f_v1[6] += f_v1[7]; ok &= std::fabs( f_v0[4] - 0. ) <= 1e-10; // partial f_6 w.r.t. v_4^0 ok &= std::fabs( f_v0[6] - 0. ) <= 1e-10; // partial f_6 w.r.t. v_6^0 ok &= std::fabs( f_v1[4] - 1. ) <= 1e-10; // partial f_6 w.r.t. v_4^1 ok &= std::fabs( f_v1[6] - 1. ) <= 1e-10; // partial f_6 w.r.t. v_6^1 // f_5 = f_6( v_1^0 , ... , v_5^1 , v_5^0 / 2 , v_5^1 / 2 ) f_v0[5] += f_v0[6] / 2.; f_v1[5] += f_v1[6] / 2.; ok &= std::fabs( f_v0[5] - 0. ) <= 1e-10; // partial f_5 w.r.t. v_5^0 ok &= std::fabs( f_v1[5] - 0.5 ) <= 1e-10; // partial f_5 w.r.t. v_5^1 // f_4 = f_5( v_1^0 , ... , v_4^1 , v_3^0 * v_1^0 , // v_3^1 * v_1^0 + v_3^0 * v_1^1 ) f_v0[1] += f_v0[5] * v0[3] + f_v1[5] * v1[3]; f_v0[3] += f_v0[5] * v0[1] + f_v1[5] * v1[1]; f_v1[1] += f_v1[5] * v0[3]; f_v1[3] += f_v1[5] * v0[1]; ok &= std::fabs( f_v0[1] - 0.5 ) <= 1e-10; // partial f_4 w.r.t. v_1^0 ok &= std::fabs( f_v0[3] - 0.5 ) <= 1e-10; // partial f_4 w.r.t. v_3^0 ok &= std::fabs( f_v1[1] - 0.25 ) <= 1e-10; // partial f_4 w.r.t. v_1^1 ok &= std::fabs( f_v1[3] - 0.25 ) <= 1e-10; // partial f_4 w.r.t. v_3^1 // f_3 = f_4( v_1^0 , ... , v_3^1 , 1 + v_3^0 , v_3^1 ) f_v0[3] += f_v0[4]; f_v1[3] += f_v1[4]; ok &= std::fabs( f_v0[3] - 0.5 ) <= 1e-10; // partial f_3 w.r.t. v_3^0 ok &= std::fabs( f_v1[3] - 1.25) <= 1e-10; // partial f_3 w.r.t. v_3^1 // f_2 = f_3( v_1^0 , ... , v_2^1 , v_2^0 , v_2^1 ) f_v0[2] += f_v0[3]; f_v1[2] += f_v1[3]; ok &= std::fabs( f_v0[2] - 0.5 ) <= 1e-10; // partial f_2 w.r.t. v_2^0 ok &= std::fabs( f_v1[2] - 1.25) <= 1e-10; // partial f_2 w.r.t. v_2^1 // f_1 = f_2 ( v_1^0 , v_2^0 , v_1^0 , v_2^0 ) f_v0[1] += f_v0[2]; f_v1[1] += f_v1[2]; ok &= std::fabs( f_v0[1] - 1. ) <= 1e-10; // partial f_1 w.r.t. v_1^0 ok &= std::fabs( f_v1[1] - 1.5 ) <= 1e-10; // partial f_1 w.r.t. v_1^1 return ok; } /* {xrst_code} {xrst_spell_on} {xrst_end exp_eps_rev2.cpp} */ ================================================ FILE: introduction/introduction.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin exp_apx.cpp} Correctness Tests For Exponential Approximation in Introduction ############################################################### Running Tests ************* To build this program and run its correctness tests see :ref:`cmake_check-name` . Source ****** {xrst_literal // BEGIN C++ // END C++ } {xrst_end exp_apx.cpp} */ // BEGIN C++ // system include files used for I/O # include // memory allocation routine # include // test runner # include // external complied tests extern bool exp_2(void); extern bool exp_2_cppad(void); extern bool exp_2_for1(void); extern bool exp_2_for2(void); extern bool exp_2_rev1(void); extern bool exp_2_rev2(void); extern bool exp_2_for0(void); extern bool exp_eps(void); extern bool exp_eps_cppad(void); extern bool exp_eps_for1(void); extern bool exp_eps_for2(void); extern bool exp_eps_for0(void); extern bool exp_eps_rev1(void); extern bool exp_eps_rev2(void); // main program that runs all the tests int main(void) { std::string group = "introduction"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This comment is used by OneTest // external compiled tests Run( exp_2, "exp_2" ); Run( exp_2_cppad, "exp_2_cppad" ); Run( exp_2_for0, "exp_2_for0" ); Run( exp_2_for1, "exp_2_for1" ); Run( exp_2_for2, "exp_2_for2" ); Run( exp_2_rev1, "exp_2_rev1" ); Run( exp_2_rev2, "exp_2_rev2" ); Run( exp_eps, "exp_eps" ); Run( exp_eps_cppad, "exp_eps_cppad" ); Run( exp_eps_for0, "exp_eps_for0" ); Run( exp_eps_for1, "exp_eps_for1" ); Run( exp_eps_for2, "exp_eps_for2" ); Run( exp_eps_rev1, "exp_eps_rev1" ); Run( exp_eps_rev2, "exp_eps_rev2" ); // // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } // END C++ ================================================ FILE: pkgconfig/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- # Construct cppad.pc from cppad.pc.in; see # http://people.freedesktop.org/~dbn/pkg-config-guide.html # ----------------------------------------------------------------------------- # {xrst_begin pkgconfig} # {xrst_spell # builddir # cflags # datadir # includedir # libdir # libs # } # {xrst_comment_ch #} # # CppAD pkg-config Files # ###################### # # Purpose # ******* # The ``pkg-config`` program helps with the used if installed libraries # and include files; see its # `guide `_ # for more information on writing an using pkg-config files. # # cppad.pc # ******** # # PKG_CONFIG_PATH # =============== # The ``cppad.pc`` file is installed in the following two directories: # # | |tab| *prefix* / *libdir* / ``pkgconfig`` # | |tab| *prefix* / *datadir* / ``pkgconfig`` # # where *prefix* is :ref:`cmake@cppad_prefix` , # *libdir* is the first entry in # :ref:`cmake@cmake_install_libdirs` , # and # *datadir* is # :ref:`cmake@cmake_install_datadir` . # In order to use ``pkg-config`` , # one of these directories must your ``PKG_CONFIG_PATH`` . # # Compile Flags # ============= # The necessary flags for compiling code that includes CppAD can # be obtained with the command # :: # # pkg-config --cflags cppad # # Link Flags # ========== # The flags for linking can be obtains with the command # :: # # pkg-config --libs cppad # # Extra Definitions # ================= # The ``cppad.pc`` file contains the text: # # | |tab| ``prefix`` = *prefix* # | |tab| ``exec_prefix`` = *exec_prefix* # | |tab| ``includedir`` = *includedir* # | |tab| ``libdir`` = *libdir* # # where *prefix* , *exec_prefix* , *includedir* , and # *libdir* are the # `gnu installation variables `_. # # cppad-uninstalled.pc # ******************** # # PKG_CONFIG_PATH # =============== # The ``cppad-uninstalled.pc`` file is located in the following directory: # # *builddir* / ``pkgconfig`` # # where *builddir* is the directory where the :ref:`cmake-name` command # is executed. # # Compile Flags # ============= # The necessary flags for compiling code that includes CppAD, # before CppAD is installed, can be obtained with the command # :: # # pkg-config --cflags cppad-uninstalled # # Link Flags # ========== # The flags for linking can be obtains with the command # :: # # pkg-config --libs cppad-uninstalled # # Extra Definitions # ================= # The ``cppad-uninstalled.pc`` file has the same extra variables # as the ``cppad.pc`` file. # # {xrst_end pkgconfig} # ---------------------------------------------------------------------------- # # The following variables are used by cppad.pc.in # and set in the top source CMakeLists.txt: # cppad_prefix, # cppad_description, # cppad_version, # cppad_url, # cppad_lib # The other variables used by cppad.pc.in are set below # Note that cppad_SOURCE_DIR is used by cppad-uninstalled.pc # and is set by cmake to the top source directory. # # cppad_includedir LIST(GET cmake_install_includedirs 0 cppad_includedir) # # cppad_libdir LIST(GET cmake_install_libdirs 0 cppad_libdir) # # add_to_set MACRO(add_to_set variable_name element) IF( "${${variable_name}}" STREQUAL "" ) SET(${variable_name} ${element} ) ELSE( "${${variable_name}}" STREQUAL "" ) LIST(FIND ${variable_name} ${element} index) IF( index EQUAL -1 ) SET(${variable_name} "${${variable_name}} ${element}") ENDIF( index EQUAL -1 ) ENDIF( "${${variable_name}}" STREQUAL "" ) ENDMACRO(add_to_set variable_name element) # ----------------------------------------------------------------------------- # initialize SET(cppad_libdir_list "-L${cppad_prefix}/${cppad_libdir}") SET(cppad_lib_list "-l${cppad_lib}") SET(cppad_requires "") SET(cppad_libs_private "") SET(cppad_requires_private "") # # Colpack does not have a pkgconfig file. SET(colpack_libdir NOTFOUND) IF( cppad_has_colpack ) FOREACH(dir ${cmake_install_libdirs}) FILE(GLOB file_list "${colpack_prefix}/${dir}/libColPack.*" ) IF( file_list ) SET(colpack_libdir "${colpack_prefix}/${dir}") ENDIF( file_list ) ENDFOREACH(dir ${cmake_install_libdirs}) IF( NOT colpack_libdir ) MESSAGE(FATAL_ERROR "Cannit find libColPack.* below ${colpack_prefix}") ENDIF( NOT colpack_libdir ) add_to_set(cppad_libdir_list "-L${colpack_libdir}") add_to_set(cppad_lib_list "-lColPack") ENDIF( cppad_has_colpack ) # # Ipopt and eigen have pkgconfig files. IF( cppad_has_eigen ) SET(cppad_requires "${cppad_requires} eigen3") ENDIF( cppad_has_eigen ) IF( cppad_has_ipopt ) SET(cppad_requires "${cppad_requires} ipopt") add_to_set(cppad_lib_list "-lcppad_ipopt" ) ENDIF( cppad_has_ipopt ) # ----------------------------------------------------------------------------- # cppad.pc CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/cppad.pc.in ${CMAKE_CURRENT_BINARY_DIR}/cppad.pc @ONLY ) # cppad-uninstalled.pc CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/cppad-uninstalled.pc.in ${CMAKE_CURRENT_BINARY_DIR}/cppad-uninstalled.pc @ONLY ) # During install copy cppad.pc to datadir INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/cppad.pc DESTINATION ${cppad_abs_datadir}/pkgconfig ) # During install also copy cppad.pc to libdir INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/cppad.pc DESTINATION ${cppad_abs_libdir}/pkgconfig ) ================================================ FILE: pkgconfig/cppad-uninstalled.pc.in ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # The settings in this file file are the same as in cppad.pc except for the # following line: # includedir=@cppad_SOURCE_DIR@/include # prefix=@cppad_prefix@ exec_prefix=${prefix} includedir=@cppad_SOURCE_DIR@/include libdir=${exec_prefix}/@cppad_libdir@ # Name: cppad Description: @cppad_description@ Version: @cppad_version@ URL: @cppad_url@ # Cflags: -I${includedir} Libs: @cppad_libdir_list@ @cppad_lib_list@ Requires: @cppad_requires@ Libs.private: @cppad_libs_private@ Requires.private: @cppad_requires_private@ ================================================ FILE: pkgconfig/cppad.pc.in ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # # The following variable settings seem to by required by the vcpkg packager, # see https://www.gnu.org/prep/standards/html_node/Directory-Variables.html # for the meaning of these variables. # prefix=@cppad_prefix@ exec_prefix=${prefix} includedir=${prefix}/@cppad_includedir@ libdir=${exec_prefix}/@cppad_libdir@ # # See https://people.freedesktop.org/~dbn/pkg-config-guide.html # for the meaning of the settings below: # Name: cppad Description: @cppad_description@ Version: @cppad_version@ URL: @cppad_url@ # Cflags: -I${includedir} Libs: @cppad_libdir_list@ @cppad_lib_list@ Requires: @cppad_requires@ Libs.private: @cppad_libs_private@ Requires.private: @cppad_requires_private@ ================================================ FILE: readme.md ================================================ # CppAD: A Package for Differentiation of C++ Algorithms ## Documentation [users guide](https://cppad.readthedocs.io/latest/user_guide.html) ## License SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later ## Install - The preferred method to test and [install](https://cppad.readthedocs.io/latest/Install.html) CppAD uses [cmake](https://cmake.org). - A deprecated [autotools](https://cppad.readthedocs.io/latest/cmake.html#autotools) procedure can be used for this purpose, but it will eventually be removed. ## Getting Started [get_started](https://cppad.readthedocs.io/latest/get_started.cpp.html) demonstrates using CppAD by computing the derivative of a simple example function. ================================================ FILE: speed/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- # Initialize list of tests as empty SET(check_speed_depends "") # The CMakeLists.txt file in the specified source directory is processed # before the current input file continues beyond this command. # add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL]) ADD_SUBDIRECTORY(src) ADD_SUBDIRECTORY(cppad) ADD_SUBDIRECTORY(double) ADD_SUBDIRECTORY(example) ADD_SUBDIRECTORY(xpackage) IF ( cppad_profile_flag ) ADD_SUBDIRECTORY(profile) ENDIF ( cppad_profile_flag ) # IF( cppad_has_adolc ) ADD_SUBDIRECTORY(adolc) ENDIF( cppad_has_adolc ) IF( cppad_has_fadbad ) ADD_SUBDIRECTORY(fadbad) ENDIF( cppad_has_fadbad ) IF( cppad_has_cppadcg ) ADD_SUBDIRECTORY(cppadcg) ENDIF( cppad_has_cppadcg ) IF( cppad_has_sacado ) ADD_SUBDIRECTORY(sacado) ENDIF( cppad_has_sacado ) IF( NOT cppad_link_flags_has_m32 ) ADD_SUBDIRECTORY(cppad_jit) ENDIF( NOT cppad_link_flags_has_m32 ) # check_speed ADD_CUSTOM_TARGET(check_speed DEPENDS ${check_speed_depends} ) MESSAGE(STATUS "make check_speed: available") # Change check depends in parent environment add_to_list(check_depends check_speed) SET(check_depends "${check_depends}" PARENT_SCOPE) ================================================ FILE: speed/add_test.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # Use this shell script when a new test is added to initially create # the source code files */test_name.cpp which return false (for not available). # echo "Change this script so that it automates the omhelp commands" echo "at the beginning of the created files." echo exit 1 # if [ "$1" == "" ] then echo "usage: new_test.sh test_name" echo "where test_name is the name of the new test being added" exit 1 fi test_name="$1" if [ ! -e "link_$test_name.cpp" ] then echo "The file ./link_$test_name.cpp does not yet exist." echo "It must first be created before executing this script." exit 1 fi if ! grep "speed\/link_$test_name.cpp" main.cpp then echo "link_$test_name.cpp has not yet been added to main.cpp" exit 1 fi list=" adolc cppad double fadbad sacado " for dir in profile $list do if grep "link_$test_name.cpp" $dir/makefile.am then echo "$test_name.cpp is already in $dir/makefile.am" exit 1 fi if [ -e $dir/$test_name.cpp ] then echo "The file $dir/$test_name.cpp already exists." exit 1 fi done # sed -i main.cpp -e "s/speed\/link[^%]*\$/&%\n\tspeed\/link_$test_name.cpp/" # copy=`sed -n ../COPYING -e '/^\/\*/,/\*\/$/p'` link=`sed -n link_$test_name.cpp -e "/^ *extern *bool *link_$test_name/,/^);/p"` fun=`echo "$link" | sed -e 's/extern */\n/' -e 's/^);/)\n{\n\treturn false;\n}/'` for dir in $list do echo "$copy$fun" > $dir/$test_name.cpp sed -i $dir/makefile.am \ -e "s/\/main.cpp.*/&\n\t..\/link_$test_name.cpp \\\\/" \ -e "s/\/link_$test_name.cpp.*/&\n\t$test_name.cpp \\\\/" done sed -i profile/makefile.am \ -e "s/\/main.cpp.*/&\n\t..\/link_$test_name.cpp \\\\/" \ -e "s/\/link_$test_name.cpp.*/&\n\t..\/cppad\/$test_name.cpp \\\\/" ================================================ FILE: speed/adolc/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-23 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the speed/adolc directory tests # Inherit build type from ../CMakeList.txt # assert include_adolc is true assert(include_adolc ) # Adds flags to the compiler command line for sources in the current directory # and below. This command can be used to add any flags, but it was originally # intended to add preprocessor definitions. ADD_DEFINITIONS("-DCPPAD_ADOLC_SPEED") # Local include directories to search (not in package_prefix/includdir) INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../src ) # add_executable( [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] # source1 source2 ... sourceN # ) SET(source_list ../main.cpp alloc_mat.cpp det_lu.cpp det_minor.cpp mat_mul.cpp ode.cpp poly.cpp sparse_hessian.cpp sparse_jacobian.cpp ) set_compile_flags( speed_adolc "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE( speed_adolc EXCLUDE_FROM_ALL ${source_list} ) # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(speed_adolc speed_src ${cppad_lib} ${adolc_LINK_LIBRARIES} ${colpack_libs} ) # check_speed_adolc add_check_executable(check_speed adolc "correct 54321 colpack") ================================================ FILE: speed/adolc/adolc_alloc_mat.hpp ================================================ # ifndef CPPAD_SPEED_ADOLC_ADOLC_ALLOC_MAT_HPP # define CPPAD_SPEED_ADOLC_ADOLC_ALLOC_MAT_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- double** adolc_alloc_mat(size_t m, size_t n); void adolc_free_mat(double** mat); # endif ================================================ FILE: speed/adolc/adolc_usrparms.sh ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- #! /bin/bash # message=" usage: adolc_usrparms.sh adolc_usrparms.sh show adolc_usrparms.sh modify The first usage prints the value of BUFSIZE and TBUFSIZE. The second usage show how the usrparms.h file would be modified. The third usage actually modifies the file. : The distribution directory corresponding to adolc; e.g., adolc-1.10.2. The file where the buffer sizes are specified is /adolc/usrparms.h. BUFSIZE: Buffer size for tapes. TBUFSIZE: Buffer size for temporary Taylor store. : the value we are changing the Adolc BUFSIZE parameter to. : the value we are changing the Adolc TBUFSIZE parameter to. " if [ "$1" == "" ] then echo "$message" exit 1 fi file="$1/adolc/usrparms.h" if [ ! -e $file ] then echo "adolc_usrparms.sh: cannot find the file $file" exit 1 fi # # case where we print the value of BUFSIZE and TBUFSIZE if [ "$2" == "" ] then grep "^#define T*BUFSIZE" < $file exit 0 fi same="/* Previous: \1\2 */ \3\n#define" cmd_one="s|^\(#define BUFSIZE *\)\([0-9]*\)\(.*\)|$same BUFSIZE $2|" cmd_two="s|^\(#define TBUFSIZE *\)\([0-9]*\)\(.*\)|$same TBUFSIZE $3|" if [ "$4" == "show" ] then sed < $file > adolc_usrparms.tmp \ -e "$cmd_one" -e "$cmd_two" diff $file adolc_usrparms.tmp exit 0 fi if [ "$4" == "modify" ] then sed < $file > adolc_usrparms.tmp \ -e "$cmd_one" -e "$cmd_two" diff $file adolc_usrparms.tmp mv adolc_usrparms.tmp $file echo "Execute the following commands for the change to take effect:" echo "cd $1" echo "make" echo "make install" exit 0 fi echo "$message" exit 1 ================================================ FILE: speed/adolc/alloc_mat.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin adolc_alloc_mat} Adolc Test Utility: Allocate and Free Memory For a Matrix ######################################################### Syntax ****** | *mat* = ``adolc_alloc_mat`` ( *m* , *n* ) | *adolc_free_mat* ( ``mat`` ) Purpose ******* Use the :ref:`thread_alloc-name` memory allocator to allocate and free memory that can be used as a matrix with the Adolc package. m * Is the number of rows in the matrix. n * Is the number of columns in the matrix. mat *** Is the matrix. To be specific, between a call to ``adolc_alloc_mat`` , and the corresponding call to ``adolc_free_mat`` , for *i* = 0 , ... , *m* ``-1`` and *j* = 0 , ... , *n* ``-1`` , *mat* [ *i* ][ *j* ] is the element in row *i* and column *j* . {xrst_end adolc_alloc_mat} */ # include double** adolc_alloc_mat(size_t m, size_t n) { using CppAD::thread_alloc; size_t size_min = size_t(m * n), size_out; double* vec = thread_alloc::create_array(size_min, size_out); double** mat = thread_alloc::create_array(size_min, size_out); for(size_t i = 0; i < m; i++) mat[i] = vec + i * n; return mat; } void adolc_free_mat(double** mat) { using CppAD::thread_alloc; thread_alloc::delete_array(mat[0]); thread_alloc::delete_array(mat); return; } ================================================ FILE: speed/adolc/det_lu.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin adolc_det_lu.cpp} Adolc Speed: Gradient of Determinant Using Lu Factorization ########################################################### Specifications ************** See :ref:`link_det_lu-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ // suppress conversion warnings before other includes # include // # include # include # include # include // list of possible options # include extern std::map global_option; bool link_det_lu( size_t size , size_t repeat , CppAD::vector &matrix , CppAD::vector &gradient ) { // speed test global option values if( global_option["onetape"] || global_option["atomic"] ) return false; if( global_option["memory"] || global_option["optimize"] ) return false; // ----------------------------------------------------- // setup short tag = 0; // tape identifier int keep = 1; // keep forward mode results in buffer int m = 1; // number of dependent variables int n = int(size * size); // number of independent variables double f; // function value int j; // temporary index // set up for thread_alloc memory allocator (fast and checks for leaks) using CppAD::thread_alloc; // the allocator size_t size_min; // requested number of elements size_t size_out; // capacity of an allocation // object for computing determinant typedef adouble ADScalar; typedef ADScalar* ADVector; CppAD::det_by_lu Det(size); // AD value of determinant ADScalar detA; // AD version of matrix size_min = size_t(n); ADVector A = thread_alloc::create_array(size_min, size_out); // vectors of reverse mode weights size_min = size_t(m); double* u = thread_alloc::create_array(size_min, size_out); u[0] = 1.; // vector with matrix value size_min = size_t(n); double* mat = thread_alloc::create_array(size_min, size_out); // vector to receive gradient result size_min = size_t(n); double* grad = thread_alloc::create_array(size_min, size_out); // ------------------------------------------------------ while(repeat--) { // get the next matrix CppAD::uniform_01( size_t(n), mat); // declare independent variables trace_on(tag, keep); for(j = 0; j < n; j++) A[j] <<= mat[j]; // AD computation of the determinant detA = Det(A); // create function object f : A -> detA detA >>= f; trace_off(); // evaluate and return gradient using reverse mode fos_reverse(tag, m, n, u, grad); } // ------------------------------------------------------ // return matrix and gradient for(j = 0; j < n; j++) { matrix[j] = mat[j]; gradient[j] = grad[j]; } // tear down thread_alloc::delete_array(grad); thread_alloc::delete_array(mat); thread_alloc::delete_array(u); thread_alloc::delete_array(A); return true; } /* {xrst_code} {xrst_spell_on} {xrst_end adolc_det_lu.cpp} */ ================================================ FILE: speed/adolc/det_minor.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin adolc_det_minor.cpp} Adolc Speed: Gradient of Determinant by Minor Expansion ####################################################### Specifications ************** See :ref:`link_det_minor-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ // suppress conversion warnings before other includes # include // # include # include # include # include // list of possible options # include extern std::map global_option; namespace { void setup(short tag, size_t size, const CppAD::vector& matrix) { // number of independent variables int n = int(size * size); // object for computing determinant CppAD::det_by_minor a_det(size); // declare independent variables int keep = 1; // keep forward mode results trace_on(tag, keep); CppAD::vector a_A(n); for(int j = 0; j < n; ++j) a_A[j] <<= matrix[j]; // AD computation of the determinant adouble a_detA = a_det(a_A); // create function object f : A -> detA double f; a_detA >>= f; trace_off(); } } bool link_det_minor( const std::string& job , size_t size , size_t repeat , CppAD::vector &matrix , CppAD::vector &gradient ) { // -------------------------------------------------------------------- // check global options // Allow colpack true even though it is not used below because it is // true durng the adolc correctness tests. const char* valid[] = { "onetape", "optimize", "colpack"}; size_t n_valid = sizeof(valid) / sizeof(valid[0]); typedef std::map::iterator iterator; // for(iterator itr=global_option.begin(); itr!=global_option.end(); ++itr) { if( itr->second ) { bool ok = false; for(size_t i = 0; i < n_valid; i++) ok |= itr->first == valid[i]; if( ! ok ) return false; } } // ----------------------------------------------------- // size corresponding to current tape static size_t static_size = 0; // // number of independent variables int n = int(size * size); // // tape identifier short tag = 0; // bool onetape = global_option["onetape"]; // ---------------------------------------------------------------------- if( job == "setup" ) { if( onetape ) { // get a matrix CppAD::uniform_01(size_t(n), matrix); // // record the tape setup(tag, size, matrix); static_size = size; } else { static_size = 0; } return true; } if( job == "teardown" ) { // 2DO: How does one free an adolc tape ? return true; } // ---------------------------------------------------------------------- CPPAD_ASSERT_UNKNOWN( job == "run" ); // // number of dependent variables int m = 1; // // vectors of reverse mode weights CppAD::vector u(m); u[0] = 1.; // if( onetape ) while(repeat--) { if( size != static_size ) { CPPAD_ASSERT_UNKNOWN( size == static_size ); } // choose a matrix CppAD::uniform_01( size_t(n), matrix); // evaluate the determinant at the new matrix value int keep = 1; // keep this forward mode result double f; // function result zos_forward(tag, m, n, keep, matrix.data(), &f); // evaluate and return gradient using reverse mode fos_reverse(tag, m, n, u.data(), gradient.data()); } else while(repeat--) { // choose a matrix CppAD::uniform_01( size_t(n), matrix); // record the tape setup(tag, size, matrix); // evaluate and return gradient using reverse mode fos_reverse(tag, m, n, u.data(), gradient.data()); } // -------------------------------------------------------------------- return true; } /* {xrst_code} {xrst_spell_on} {xrst_end adolc_det_minor.cpp} */ ================================================ FILE: speed/adolc/mat_mul.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin adolc_mat_mul.cpp} Adolc Speed: Matrix Multiplication ################################## Specifications ************** See :ref:`link_mat_mul-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ // suppress conversion warnings before other includes # include // # include # include # include # include # include // list of possible options # include extern std::map global_option; bool link_mat_mul( size_t size , size_t repeat , CppAD::vector& x , CppAD::vector& z , CppAD::vector& dz ) { // speed test global option values if( global_option["memory"] || global_option["atomic"] || global_option["optimize"] ) return false; // ----------------------------------------------------- // setup typedef adouble ADScalar; typedef ADScalar* ADVector; short tag = 0; // tape identifier int m = 1; // number of dependent variables int n = int(size * size); // number of independent variables double f; // function value int j; // temporary index // set up for thread_alloc memory allocator (fast and checks for leaks) using CppAD::thread_alloc; // the allocator size_t capacity; // capacity of an allocation // AD domain space vector ADVector X = thread_alloc::create_array(size_t(n), capacity); // Product matrix ADVector Y = thread_alloc::create_array(size_t(n), capacity); // AD range space vector ADVector Z = thread_alloc::create_array(size_t(m), capacity); // vector with matrix value double* mat = thread_alloc::create_array(size_t(n), capacity); // vector of reverse mode weights double* u = thread_alloc::create_array(size_t(m), capacity); u[0] = 1.; // gradient double* grad = thread_alloc::create_array(size_t(n), capacity); // ---------------------------------------------------------------------- if( ! global_option["onetape"] ) while(repeat--) { // choose a matrix CppAD::uniform_01( size_t(n), mat); // declare independent variables int keep = 1; // keep forward mode results trace_on(tag, keep); for(j = 0; j < n; j++) X[j] <<= mat[j]; // do computations CppAD::mat_sum_sq(size, X, Y, Z); // create function object f : X -> Z Z[0] >>= f; trace_off(); // evaluate and return gradient using reverse mode fos_reverse(tag, m, n, u, grad); } else { // choose a matrix CppAD::uniform_01( size_t(n), mat); // declare independent variables int keep = 0; // do not keep forward mode results trace_on(tag, keep); for(j = 0; j < n; j++) X[j] <<= mat[j]; // do computations CppAD::mat_sum_sq(size, X, Y, Z); // create function object f : X -> Z Z[0] >>= f; trace_off(); while(repeat--) { // choose a matrix CppAD::uniform_01( size_t(n), mat); // evaluate the determinant at the new matrix value keep = 1; // keep this forward mode result zos_forward(tag, m, n, keep, mat, &f); // evaluate and return gradient using reverse mode fos_reverse(tag, m, n, u, grad); } } // return function, matrix, and gradient z[0] = f; for(j = 0; j < n; j++) { x[j] = mat[j]; dz[j] = grad[j]; } // tear down thread_alloc::delete_array(X); thread_alloc::delete_array(Y); thread_alloc::delete_array(Z); thread_alloc::delete_array(mat); thread_alloc::delete_array(u); thread_alloc::delete_array(grad); return true; } /* {xrst_code} {xrst_spell_on} {xrst_end adolc_mat_mul.cpp} */ ================================================ FILE: speed/adolc/ode.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin adolc_ode.cpp} Adolc Speed: Ode ################ Specifications ************** See :ref:`link_ode-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ // suppress conversion warnings before other includes # include // # include # include # include # include // list of possible options # include extern std::map global_option; bool link_ode( size_t size , size_t repeat , CppAD::vector &x , CppAD::vector &jac ) { // speed test global option values if( global_option["atomic"] ) return false; if( global_option["memory"] || global_option["optimize"] ) return false; // ------------------------------------------------------------- // setup assert( x.size() == size ); assert( jac.size() == size * size ); typedef CppAD::vector ADVector; typedef CppAD::vector DblVector; size_t i, j; short tag = 0; // tape identifier int keep = 0; // do not keep forward mode results size_t p = 0; // use ode to calculate function values size_t n = size; // number of independent variables size_t m = n; // number of dependent variables ADVector X(n), Y(m); // independent and dependent variables DblVector f(m); // function value // set up for thread_alloc memory allocator (fast and checks for leaks) using CppAD::thread_alloc; // the allocator size_t size_min; // requested number of elements size_t size_out; // capacity of an allocation // raw memory for use with adolc size_min = size_t(n); double *x_raw = thread_alloc::create_array(size_min, size_out); size_min = size_t(m * n); double *jac_raw = thread_alloc::create_array(size_min, size_out); size_min = size_t(m); double **jac_ptr = thread_alloc::create_array(size_min, size_out); for(i = 0; i < m; i++) jac_ptr[i] = jac_raw + i * n; // ------------------------------------------------------------- if( ! global_option["onetape"] ) while(repeat--) { // choose next x value uniform_01( size_t(n), x); // declare independent variables trace_on(tag, keep); for(j = 0; j < n; j++) X[j] <<= x[j]; // evaluate function CppAD::ode_evaluate(X, p, Y); // create function object f : X -> Y for(i = 0; i < m; i++) Y[i] >>= f[i]; trace_off(); // evaluate the Jacobian for(j = 0; j < n; j++) x_raw[j] = x[j]; jacobian(tag, int(m), int(n), x_raw, jac_ptr); } else { // choose next x value uniform_01( size_t(n), x); // declare independent variables trace_on(tag, keep); for(j = 0; j < n; j++) X[j] <<= x[j]; // evaluate function CppAD::ode_evaluate(X, p, Y); // create function object f : X -> Y for(i = 0; i < m; i++) Y[i] >>= f[i]; trace_off(); while(repeat--) { // get next argument value uniform_01( size_t(n), x); for(j = 0; j < n; j++) x_raw[j] = x[j]; // evaluate jacobian jacobian(tag, int(m), int(n), x_raw, jac_ptr); } } // convert return value to a simple vector for(i = 0; i < m; i++) { for(j = 0; j < n; j++) jac[i * n + j] = jac_ptr[i][j]; } // ---------------------------------------------------------------------- // tear down thread_alloc::delete_array(x_raw); thread_alloc::delete_array(jac_raw); thread_alloc::delete_array(jac_ptr); return true; } /* {xrst_code} {xrst_spell_on} {xrst_end adolc_ode.cpp} */ ================================================ FILE: speed/adolc/poly.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin adolc_poly.cpp} Adolc Speed: Second Derivative of a Polynomial ############################################## Specifications ************** See :ref:`link_poly-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ // suppress conversion warnings before other includes # include // # include # include # include # include # include # include # include "adolc_alloc_mat.hpp" // list of possible options # include extern std::map global_option; bool link_poly( size_t size , size_t repeat , CppAD::vector &a , // coefficients of polynomial CppAD::vector &z , // polynomial argument value CppAD::vector &ddp ) // second derivative w.r.t z { if( global_option["atomic"] ) return false; if( global_option["memory"] || global_option["optimize"] ) return false; // ----------------------------------------------------- // setup size_t i; short tag = 0; // tape identifier int keep = 0; // do not keep forward mode results in buffer int m = 1; // number of dependent variables int n = 1; // number of independent variables int d = 2; // highest derivative degree double f; // function value // set up for thread_alloc memory allocator (fast and checks for leaks) using CppAD::thread_alloc; // the allocator size_t capacity; // capacity of an allocation // choose a vector of polynomial coefficients CppAD::uniform_01(size, a); // AD copy of the polynomial coefficients std::vector A(size); for(i = 0; i < size; i++) A[i] = a[i]; // domain and range space AD values adouble Z, P; // allocate arguments to hos_forward double* x0 = thread_alloc::create_array(size_t(n), capacity); double* y0 = thread_alloc::create_array(size_t(m), capacity); double** x = adolc_alloc_mat(size_t(n), size_t(d)); double** y = adolc_alloc_mat(size_t(m), size_t(d)); // Taylor coefficient for argument x[0][0] = 1.; // first order x[0][1] = 0.; // second order // ---------------------------------------------------------------------- if( ! global_option["onetape"] ) while(repeat--) { // choose an argument value CppAD::uniform_01(1, z); // declare independent variables trace_on(tag, keep); Z <<= z[0]; // AD computation of the function value P = CppAD::Poly(0, A, Z); // create function object f : Z -> P P >>= f; trace_off(); // set the argument value x0[0] = z[0]; // evaluate the polynomial at the new argument value hos_forward(tag, m, n, d, keep, x0, x, y0, y); // second derivative is twice second order Taylor coef ddp[0] = 2. * y[0][1]; } else { // choose an argument value CppAD::uniform_01(1, z); // declare independent variables trace_on(tag, keep); Z <<= z[0]; // AD computation of the function value P = CppAD::Poly(0, A, Z); // create function object f : Z -> P P >>= f; trace_off(); while(repeat--) { // get the next argument value CppAD::uniform_01(1, z); x0[0] = z[0]; // evaluate the polynomial at the new argument value hos_forward(tag, m, n, d, keep, x0, x, y0, y); // second derivative is twice second order Taylor coef ddp[0] = 2. * y[0][1]; } } // ------------------------------------------------------ // tear down adolc_free_mat(x); adolc_free_mat(y); thread_alloc::delete_array(x0); thread_alloc::delete_array(y0); return true; } /* {xrst_code} {xrst_spell_on} {xrst_end adolc_poly.cpp} */ ================================================ FILE: speed/adolc/sparse_hessian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin adolc_sparse_hessian.cpp} Adolc Speed: Sparse Hessian ########################### Specifications ************** See :ref:`link_sparse_hessian-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ // suppress conversion warnings before other includes # include // # include # include # include # include # include # include # include // list of possible options # include extern std::map global_option; bool link_sparse_hessian( size_t size , size_t repeat , const CppAD::vector& row , const CppAD::vector& col , CppAD::vector& x_return , CppAD::vector& hessian , size_t& n_color ) { if( global_option["atomic"] || (! global_option["colpack"]) ) return false; if( global_option["memory"] || global_option["optimize"] || global_option["boolsparsity"] ) return false; // ----------------------------------------------------- // setup typedef unsigned int* IntVector; typedef double* DblVector; typedef adouble ADScalar; typedef ADScalar* ADVector; 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 // setup for thread_alloc memory allocator (fast and checks for leaks) using CppAD::thread_alloc; // the allocator size_t capacity; // capacity of an allocation // tape identifier short tag = 0; // AD domain space vector ADVector a_x = thread_alloc::create_array(n, capacity); // AD range space vector ADVector a_y = thread_alloc::create_array(m, capacity); // double argument value DblVector x = thread_alloc::create_array(n, capacity); // double function value double f; // options that control sparse_hess int options[2]; options[0] = 0; // safe mode options[1] = 0; // indirect recovery // structure that holds some of the work done by sparse_hess int nnz; // number of non-zero values IntVector rind = nullptr; // row indices IntVector cind = nullptr; // column indices DblVector values = nullptr; // Hessian values // ---------------------------------------------------------------------- if( ! global_option["onetape"] ) while(repeat--) { // choose a value for x CppAD::uniform_01( size_t(n), x); // declare independent variables int keep = 0; // keep forward mode results trace_on(tag, keep); for(size_t j = 0; j < n; j++) a_x[j] <<= x[j]; // AD computation of f (x) CppAD::sparse_hes_fun(n, a_x, row, col, order, a_y); // create function object f : x -> y a_y[0] >>= f; trace_off(); // is this a repeat call with the same sparsity pattern int same_pattern = 0; // calculate the hessian at this x rind = nullptr; cind = nullptr; values = nullptr; sparse_hess(tag, int(n), same_pattern, x, &nnz, &rind, &cind, &values, options ); // free raw memory allocated by sparse_hess // (keep on last repeat for correctness testing) if( repeat != 0 ) { free(rind); free(cind); free(values); } } else { // choose a value for x CppAD::uniform_01( size_t(n), x); // declare independent variables int keep = 0; // keep forward mode results trace_on(tag, keep); for(size_t j = 0; j < n; j++) a_x[j] <<= x[j]; // AD computation of f (x) CppAD::sparse_hes_fun(n, a_x, row, col, order, a_y); // create function object f : x -> y a_y[0] >>= f; trace_off(); // is this a repeat call at the same argument int same_pattern = 0; while(repeat--) { // choose a value for x CppAD::uniform_01( size_t(n), x); // calculate the hessian at this x sparse_hess(tag, int(n), same_pattern, x, &nnz, &rind, &cind, &values, options ); same_pattern = 1; } } // Adolc returns upper triangle in row major order while row, col are // lower triangle in row major order. CppAD::vector keys(nnz), ind(nnz); for(int ell = 0; ell < nnz; ++ell) { // transpose to get lower triangle size_t i = size_t( cind[ell] ); size_t j = size_t( rind[ell] ); keys[ell] = i * n + j; // row major order for lower triangle } CppAD::index_sort(keys, ind); size_t k = 0; // initialize index in row, col size_t r = row[k]; size_t c = col[k]; for(int ell = 0; ell < nnz; ++ell) { // Adolc version of lower triangle of Hessian in row major order size_t ind_ell = ind[ell]; size_t i = size_t( cind[ind_ell] ); size_t j = size_t( rind[ind_ell] ); while( (r < i) || ( (r == i) && (c < j) ) ) { // (r, c) not in Adolc sparsity pattern hessian[k++] = 0.0; if( k < row.size() ) { r = row[k]; c = col[k]; } else { r = n; c = n; } } if( (r == i) && (c == j) ) { // adolc value for (r, c) hessian[k++] = values[ind_ell]; if( k < row.size() ) { r = row[k]; c = col[k]; } else { r = n; c = n; } } else { // Hessian at (i, j) must be zero (but Adolc does not know this) assert( values[ind_ell] == 0.0 ); } } // free raw memory allocated by sparse_hessian free(rind); free(cind); free(values); // // return argument for(size_t j = 0; j < n; j++) x_return[j] = x[j]; // do not know how to return number of sweeps used n_color = 0; // tear down thread_alloc::delete_array(a_x); thread_alloc::delete_array(a_y); thread_alloc::delete_array(x); return true; } /* {xrst_code} {xrst_spell_on} {xrst_end adolc_sparse_hessian.cpp} */ ================================================ FILE: speed/adolc/sparse_jacobian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include /* {xrst_begin adolc_sparse_jacobian.cpp} Adolc Speed: Sparse Jacobian ############################ Specifications ************** See :ref:`link_sparse_jacobian-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ // suppress conversion warnings before other includes # include // # include # include # include # include # include // list of possible options # include extern std::map global_option; namespace { using CppAD::vector; typedef vector s_vector; typedef vector d_vector; typedef vector a_vector; void setup( // inputs short tag , size_t size , size_t m , const s_vector& row , const s_vector& col , const d_vector& x , int* options , // const but adolc want non-const arg // outputs size_t& n_color , int& nnz , unsigned int*& rind , unsigned int*& cind , double*& values ) { // independent variables CPPAD_ASSERT_UNKNOWN( size = x.size() ); int keep = 0; // keep forward mode results trace_on(tag, keep); size_t n = size; a_vector a_x(n); for(size_t j = 0; j < n; ++j) a_x[j] <<= x[j]; // // dependent variables a_vector a_y(m); // // AD computation of f(x) size_t order = 0; CppAD::sparse_jac_fun(m, n, a_x, row, col, order, a_y); // // create function object f : x -> y double yi; for(size_t i = 0; i < m; i++) a_y[i] >>= yi; trace_off(); // // null pointers for recalculation of sparsity pattern free(rind); free(cind); free(values); rind = nullptr; cind = nullptr; values = nullptr; // // Retrieve n_color using undocumented feature of sparsedrivers.cpp int same_pattern = 0; n_color = size_t( sparse_jac(tag, int(m), int(n), same_pattern, x.data(), &nnz, &rind, &cind, &values, options ) ); } } bool link_sparse_jacobian( const std::string& job , size_t size , size_t repeat , size_t m , const CppAD::vector& row , const CppAD::vector& col , CppAD::vector& x_return , CppAD::vector& jacobian , size_t& n_color ) { // -------------------------------------------------------------------- // check global options // Allow colpack true even though it is not used below because it is // true durng the adolc correctness tests. const char* valid[] = { "onetape", "optimize", "colpack"}; size_t n_valid = sizeof(valid) / sizeof(valid[0]); typedef std::map::iterator iterator; // for(iterator itr=global_option.begin(); itr!=global_option.end(); ++itr) { if( itr->second ) { bool ok = false; for(size_t i = 0; i < n_valid; i++) ok |= itr->first == valid[i]; if( ! ok ) return false; } } // ----------------------------------------------------- static size_t static_size = 0; static int static_nnz = 0; unsigned int* static_rind = nullptr; unsigned int* static_cind = nullptr; double* static_values = nullptr; // ----------------------------------------------------- short tag = 0; // // options that control sparse_jac int options[4]; if( global_option["boolsparsity"] ) options[0] = 1; // sparsity by propagation of bit pattern else options[0] = 0; // sparsity pattern by index domains options[1] = 0; // 0 = safe mode, 1 = tight mode options[2] = 0; // 0 = autodetect, 1 = forward, 2 = reverse options[3] = 0; // 0 = column compression, 1 = row compression // // independent variiables size_t n = size; d_vector x(n); // // default value for n_color n_color = 0; // bool onetape = global_option["onetape"]; // ----------------------------------------------------- if( job == "setup" ) { // get a value for x CppAD::uniform_01( size_t(n), x); // // record the tape and run coloring problem options[2] = -1; setup(tag, size, m, row, col, x, options, n_color, static_nnz, static_rind, static_cind, static_values ); static_size = size; // return true; } if( job == "teardown" ) { free(static_rind); free(static_cind); free(static_values); static_rind = nullptr; static_cind = nullptr; static_values = nullptr; return true; } // ----------------------------------------------------- CPPAD_ASSERT_UNKNOWN( job == "run" ); // while (repeat--) { // choose a value for x CppAD::uniform_01( size_t(n), x); // if ( ! onetape ) { // retape and calculate jacobian options[2] = -1; // stop at sparsity pattern, return n_color setup(tag, size, m, row, col, x, options, n_color, static_nnz, static_rind, static_cind, static_values ); options[2] = 0; } else { if( size != static_size ) CPPAD_ASSERT_UNKNOWN( size == static_size ); } // calculate the jacobian at this x int same_pattern = 1; sparse_jac( tag, int(m), int(n), same_pattern, x.data(), &static_nnz, &static_rind, &static_cind, &static_values, options ); } // -------------------------------------------------------------------- // jacobian CPPAD_ASSERT_UNKNOWN( size_t(static_nnz) == row.size() ); for(int ell = 0; ell < static_nnz; ell++) { CPPAD_ASSERT_UNKNOWN( row[ell] == size_t(static_rind[ell]) ); CPPAD_ASSERT_UNKNOWN( col[ell] == size_t(static_cind[ell]) ); jacobian[ell] = static_values[ell]; } // x_return for(size_t j = 0; j < n; j++) x_return[j] = x[j]; // return true; } /* {xrst_code} {xrst_spell_on} {xrst_end adolc_sparse_jacobian.cpp} */ ================================================ FILE: speed/adolc/speed_adolc.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin speed_adolc} Speed Test of Derivatives Using Adolc ##################################### Purpose ******* CppAD has a set of speed tests that are used to compare Adolc with other AD packages. This section links to the source code the Adolc speed tests (any suggestions to make the Adolc results faster are welcome). include_adolc ************* To run these tests you must specify :ref:`cmake@include_adolc` on your cmake command line. Running Tests ************* To build these speed tests, and run their correctness tests, execute the following commands starting in the :ref:`cmake@Build Directory` : | |tab| ``cd speed/adolc`` | |tab| ``make check_speed_adolc VERBOSE`` =1 You can then run the corresponding speed tests with the following command ./ ``speed_adolc speed`` *seed* where *seed* is a positive integer. See :ref:`speed_main-name` for more options. Contents ******** {xrst_toc_list speed/adolc/det_minor.cpp speed/adolc/det_lu.cpp speed/adolc/mat_mul.cpp speed/adolc/ode.cpp speed/adolc/poly.cpp speed/adolc/sparse_hessian.cpp speed/adolc/sparse_jacobian.cpp speed/adolc/alloc_mat.cpp } {xrst_end speed_adolc} ================================================ FILE: speed/cppad/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the speed/cppad directory tests # Inherit build type environment from ../CMakeList.txt # Adds flags to the compiler command line for sources in the current directory # and below. This command can be used to add any flags, but it was originally # intended to add preprocessor definitions. ADD_DEFINITIONS("-DCPPAD_CPPAD_SPEED") # Local include directories to search (not in package_prefix/includdir) INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../src ) # add_executable( [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] # source1 source2 ... sourceN # ) SET(source_list ../main.cpp det_lu.cpp det_minor.cpp mat_mul.cpp ode.cpp poly.cpp sparse_hessian.cpp sparse_jacobian.cpp ) # 2DO: fix problem with the test # ./speed_cppad sparse_hessian 123 colpack # when speed_cppad is compiled for debugging. set_compile_flags( speed_cppad "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE( speed_cppad EXCLUDE_FROM_ALL ${source_list} ) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(speed_cppad ${cppad_lib} ${colpack_libs} ) # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(speed_cppad speed_src ${cppad_lib} ${colpack_libs} ) # check_speed_cppad add_check_executable(check_speed cppad "correct 54321") ================================================ FILE: speed/cppad/det_lu.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppad_det_lu.cpp} Cppad Speed: Gradient of Determinant Using Lu Factorization ########################################################### Specifications ************** See :ref:`link_det_lu-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ # include # include # include // Note that CppAD uses global_option["memory"] at the main program level # include extern std::map global_option; // see comments in main program for this external extern size_t global_cppad_thread_alloc_inuse; bool link_det_lu( size_t size , size_t repeat , CppAD::vector &matrix , CppAD::vector &gradient ) { global_cppad_thread_alloc_inuse = 0; // -------------------------------------------------------------------- // check global options const char* valid[] = { "memory", "optimize", "val_graph"}; size_t n_valid = sizeof(valid) / sizeof(valid[0]); typedef std::map::iterator iterator; // for(iterator itr=global_option.begin(); itr!=global_option.end(); ++itr) { if( itr->second ) { bool ok = false; for(size_t i = 0; i < n_valid; i++) ok |= itr->first == valid[i]; if( ! ok ) return false; } } // -------------------------------------------------------------------- // optimization options: std::string optimize_options = "no_conditional_skip no_compare_op no_print_for_op"; if( global_option["val_graph"] ) optimize_options += " val_graph"; // ----------------------------------------------------- // setup typedef CppAD::AD ADScalar; typedef CppAD::vector ADVector; CppAD::det_by_lu Det(size); size_t i; // temporary index size_t m = 1; // number of dependent variables size_t n = size * size; // number of independent variables ADVector A(n); // AD domain space vector ADVector detA(m); // AD range space vector CppAD::ADFun f; // AD function object // vectors of reverse mode weights CppAD::vector w(1); w[0] = 1.; // do not even record comparison operators size_t abort_op_index = 0; bool record_compare = false; // ------------------------------------------------------ while(repeat--) { // get the next matrix CppAD::uniform_01(n, matrix); for( i = 0; i < n; i++) A[i] = matrix[i]; // declare independent variables Independent(A, abort_op_index, record_compare); // AD computation of the determinant detA[0] = Det(A); // create function object f : A -> detA f.Dependent(A, detA); if( global_option["optimize"] ) f.optimize(optimize_options); // evaluate and return gradient using reverse mode f.Forward(0, matrix); gradient = f.Reverse(1, w); } size_t thread = CppAD::thread_alloc::thread_num(); global_cppad_thread_alloc_inuse = CppAD::thread_alloc::inuse(thread); return true; } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_det_lu.cpp} */ ================================================ FILE: speed/cppad/det_minor.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppad_det_minor.cpp} Cppad Speed: Gradient of Determinant by Minor Expansion ####################################################### Specifications ************** See :ref:`link_det_minor-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ # include # include # include // Note that CppAD uses global_option["memory"] at the main program level # include extern std::map global_option; // see comments in main program for this external extern size_t global_cppad_thread_alloc_inuse; namespace { // typedefs typedef CppAD::AD a_double; typedef CppAD::vector a_vector; // // setup void setup( // inputs size_t size , // outputs CppAD::ADFun& f ) { // object for computing determinant CppAD::det_by_minor a_det(size); // // number of independent variables size_t nx = size * size; // // choose a matrix CppAD::vector matrix(nx); CppAD::uniform_01(nx, matrix); // // copy to independent variables a_vector a_A(nx); for(size_t j = 0; j < nx; ++j) a_A[j] = matrix[j]; // // declare independent variables for function computation bool record_compare = false; size_t abort_op_index = 0; CppAD::Independent(a_A, abort_op_index, record_compare); // // AD computation of the determinant a_vector a_detA(1); a_detA[0] = a_det(a_A); // // f : A -> detA f.Dependent(a_A, a_detA); // // optimize std::string optimize_options = "no_conditional_skip no_compare_op no_print_for_op no_cumulative_sum_op"; if( global_option["val_graph"] ) optimize_options += " val_graph"; if( global_option["optimize"] ) f.optimize(optimize_options); } } bool link_det_minor( const std::string& job , size_t size , size_t repeat , CppAD::vector &matrix , CppAD::vector &gradient ) { global_cppad_thread_alloc_inuse = 0; // -------------------------------------------------------------------- // check global options const char* valid[] = { "memory", "onetape", "optimize", "val_graph"}; size_t n_valid = sizeof(valid) / sizeof(valid[0]); typedef std::map::iterator iterator; // for(iterator itr=global_option.begin(); itr!=global_option.end(); ++itr) { if( itr->second ) { bool ok = false; for(size_t i = 0; i < n_valid; i++) ok |= itr->first == valid[i]; if( ! ok ) return false; } } // --------------------------------------------------------------------- // // AD function mapping matrix to determinant static CppAD::ADFun static_f; // // size corresponding to static_f static size_t static_size = 0; // // number of independent variables size_t nx = size * size; // // vectors of reverse mode weights CppAD::vector w(1); w[0] = 1.; // // onetape bool onetape = global_option["onetape"]; // ----------------------------------------------------------------------- if( job == "setup" ) { if( onetape ) { setup(size, static_f); static_size = size; } else { static_size = 0; } return true; } if( job == "teardown" ) { static_f = CppAD::ADFun(); return true; } // ----------------------------------------------------------------------- CPPAD_ASSERT_UNKNOWN( job == "run" ); while(repeat--) { if( onetape ) { // use if before assert to avoid warning if( size != static_size ) { CPPAD_ASSERT_UNKNOWN( size == static_size ); } } else { setup(size, static_f); } // get next matrix CppAD::uniform_01(nx, matrix); // evaluate the gradient static_f.Forward(0, matrix); gradient = static_f.Reverse(1, w); } size_t thread = CppAD::thread_alloc::thread_num(); global_cppad_thread_alloc_inuse = CppAD::thread_alloc::inuse(thread); return true; } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_det_minor.cpp} */ ================================================ FILE: speed/cppad/mat_mul.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppad_mat_mul.cpp} CppAD Speed, Matrix Multiplication ################################## Specifications ************** See :ref:`link_mat_mul-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ # include # include # include # include // Note that CppAD uses global_option["memory"] at the main program level # include extern std::map global_option; // see comments in main program for this external extern size_t global_cppad_thread_alloc_inuse; bool link_mat_mul( size_t size , size_t repeat , CppAD::vector& x , CppAD::vector& z , CppAD::vector& dz ) { global_cppad_thread_alloc_inuse = 0; // -------------------------------------------------------------------- // check global options const char* valid[] = { "memory", "onetape", "optimize", "atomic", "val_graph" }; size_t n_valid = sizeof(valid) / sizeof(valid[0]); typedef std::map::iterator iterator; // for(iterator itr=global_option.begin(); itr!=global_option.end(); ++itr) { if( itr->second ) { bool ok = false; for(size_t i = 0; i < n_valid; i++) ok |= itr->first == valid[i]; if( ! ok ) return false; } } // -------------------------------------------------------------------- // optimization options: no conditional skips or compare operators std::string optimize_options = "no_conditional_skip no_compare_op no_print_for_op"; if( global_option["val_graph"] ) optimize_options += " val_graph"; // ----------------------------------------------------- // setup typedef CppAD::AD ADScalar; typedef CppAD::vector ADVector; size_t j; // temporary index size_t m = 1; // number of dependent variables size_t n = size * size; // number of independent variables ADVector X(n); // AD domain space vector ADVector Y(n); // Store product matrix ADVector Z(m); // AD range space vector CppAD::ADFun f; // AD function object // vectors of reverse mode weights CppAD::vector w(1); w[0] = 1.; // atomic function information CppAD::vector ax(2 * n), ay(n); CppAD::atomic_mat_mul atom_mul("atom_mul"); // // do not even record comparison operators size_t abort_op_index = 0; bool record_compare = false; // ------------------------------------------------------ if( ! global_option["onetape"] ) while(repeat--) { // get the next matrix CppAD::uniform_01(n, x); for( j = 0; j < n; j++) X[j] = x[j]; // declare independent variables Independent(X, abort_op_index, record_compare); // do computations if( ! global_option["atomic"] ) mat_sum_sq(size, X, Y, Z); else { for(j = 0; j < n; j++) { ax[j] = X[j]; ax[n + j] = X[j]; } // Y = X * X size_t call_id = atom_mul.set(size, size, size); atom_mul(call_id, ax, ay); Z[0] = 0.; for(j = 0; j < n; j++) Z[0] += ay[j]; } // create function object f : X -> Z f.Dependent(X, Z); if( global_option["optimize"] ) f.optimize(optimize_options); // skip comparison operators f.compare_change_count(0); // evaluate and return gradient using reverse mode z = f.Forward(0, x); dz = f.Reverse(1, w); } else { // get a next matrix CppAD::uniform_01(n, x); for(j = 0; j < n; j++) X[j] = x[j]; // declare independent variables Independent(X, abort_op_index, record_compare); // do computations if( ! global_option["atomic"] ) mat_sum_sq(size, X, Y, Z); else { for(j = 0; j < n; j++) { ax[j] = X[j]; ax[j+n] = X[j]; } // Y = X * X atom_mul(ax, ay); Z[0] = 0.; for(j = 0; j < n; j++) Z[0] += ay[j]; } // create function object f : X -> Z f.Dependent(X, Z); if( global_option["optimize"] ) f.optimize(optimize_options); // skip comparison operators f.compare_change_count(0); while(repeat--) { // get a next matrix CppAD::uniform_01(n, x); // evaluate and return gradient using reverse mode z = f.Forward(0, x); dz = f.Reverse(1, w); } } size_t thread = CppAD::thread_alloc::thread_num(); global_cppad_thread_alloc_inuse = CppAD::thread_alloc::inuse(thread); // -------------------------------------------------------------------- // Free temporary work space (any future atomic_mat_mul constructors // would create new temporary work space.) CppAD::user_atomic::clear(); // -------------------------------------------------------------------- return true; } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_mat_mul.cpp} */ ================================================ FILE: speed/cppad/ode.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppad_ode.cpp} Cppad Speed: Gradient of Ode Solution ##################################### Specifications ************** See :ref:`link_ode-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ # include # include # include # include // Note that CppAD uses global_option["memory"] at the main program level # include extern std::map global_option; // see comments in main program for this external extern size_t global_cppad_thread_alloc_inuse; bool link_ode( size_t size , size_t repeat , CppAD::vector &x , CppAD::vector &jacobian ) { global_cppad_thread_alloc_inuse = 0; // -------------------------------------------------------------------- // check global options const char* valid[] = { "memory", "onetape", "optimize", "val_graph"}; size_t n_valid = sizeof(valid) / sizeof(valid[0]); typedef std::map::iterator iterator; // for(iterator itr=global_option.begin(); itr!=global_option.end(); ++itr) { if( itr->second ) { bool ok = false; for(size_t i = 0; i < n_valid; i++) ok |= itr->first == valid[i]; if( ! ok ) return false; } } // -------------------------------------------------------------------- // optimization options: no conditional skips or compare operators std::string optimize_options = "no_conditional_skip no_compare_op no_print_for_op"; if( global_option["val_graph"] ) optimize_options += " val_graph"; // -------------------------------------------------------------------- // setup assert( x.size() == size ); assert( jacobian.size() == size * size ); typedef CppAD::AD ADScalar; typedef CppAD::vector ADVector; size_t j; size_t p = 0; // use ode to calculate function values size_t n = size; // number of independent variables size_t m = n; // number of dependent variables ADVector X(n), Y(m); // independent and dependent variables CppAD::ADFun f; // AD function // do not even record comparison operators size_t abort_op_index = 0; bool record_compare = false; // ------------------------------------------------------------- if( ! global_option["onetape"] ) while(repeat--) { // choose next x value uniform_01(n, x); for(j = 0; j < n; j++) X[j] = x[j]; // declare independent variables Independent(X, abort_op_index, record_compare); // evaluate function CppAD::ode_evaluate(X, p, Y); // create function object f : X -> Y f.Dependent(X, Y); if( global_option["optimize"] ) f.optimize(optimize_options); // skip comparison operators f.compare_change_count(0); jacobian = f.Jacobian(x); } else { // an x value uniform_01(n, x); for(j = 0; j < n; j++) X[j] = x[j]; // declare the independent variable vector Independent(X, abort_op_index, record_compare); // evaluate function CppAD::ode_evaluate(X, p, Y); // create function object f : X -> Y f.Dependent(X, Y); if( global_option["optimize"] ) f.optimize(optimize_options); // skip comparison operators f.compare_change_count(0); while(repeat--) { // get next argument value uniform_01(n, x); // evaluate jacobian jacobian = f.Jacobian(x); } } size_t thread = CppAD::thread_alloc::thread_num(); global_cppad_thread_alloc_inuse = CppAD::thread_alloc::inuse(thread); return true; } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_ode.cpp} */ ================================================ FILE: speed/cppad/poly.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppad_poly.cpp} Cppad Speed: Second Derivative of a Polynomial ############################################## Specifications ************** See :ref:`link_poly-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ # include # include // Note that CppAD uses global_option["memory"] at the main program level # include extern std::map global_option; // see comments in main program for this external extern size_t global_cppad_thread_alloc_inuse; bool link_poly( size_t size , size_t repeat , CppAD::vector &a , // coefficients of polynomial CppAD::vector &z , // polynomial argument value CppAD::vector &ddp ) // second derivative w.r.t z { global_cppad_thread_alloc_inuse = 0; // -------------------------------------------------------------------- // check global options const char* valid[] = { "memory", "onetape", "optimize", "val_graph"}; size_t n_valid = sizeof(valid) / sizeof(valid[0]); typedef std::map::iterator iterator; // for(iterator itr=global_option.begin(); itr!=global_option.end(); ++itr) { if( itr->second ) { bool ok = false; for(size_t i = 0; i < n_valid; i++) ok |= itr->first == valid[i]; if( ! ok ) return false; } } // -------------------------------------------------------------------- // optimization options: no conditional skips or compare operators std::string optimize_options = "no_conditional_skip no_compare_op no_print_for_op"; if( global_option["val_graph"] ) optimize_options += " val_graph"; // ----------------------------------------------------- // setup typedef CppAD::AD ADScalar; typedef CppAD::vector ADVector; size_t i; // temporary index size_t m = 1; // number of dependent variables size_t n = 1; // number of independent variables ADVector Z(n); // AD domain space vector ADVector P(m); // AD range space vector // choose the polynomial coefficients CppAD::uniform_01(size, a); // AD copy of the polynomial coefficients ADVector A(size); for(i = 0; i < size; i++) A[i] = a[i]; // forward mode first and second differentials CppAD::vector p(1), dp(1), dz(1), ddz(1); dz[0] = 1.; ddz[0] = 0.; // AD function object CppAD::ADFun f; // do not even record comparison operators size_t abort_op_index = 0; bool record_compare = false; // -------------------------------------------------------------------- if( ! global_option["onetape"] ) while(repeat--) { // choose an argument value CppAD::uniform_01(1, z); Z[0] = z[0]; // declare independent variables Independent(Z, abort_op_index, record_compare); // AD computation of the function value P[0] = CppAD::Poly(0, A, Z[0]); // create function object f : A -> detA f.Dependent(Z, P); if( global_option["optimize"] ) f.optimize(optimize_options); // skip comparison operators f.compare_change_count(0); // pre-allocate memory for three forward mode calculations f.capacity_order(3); // evaluate the polynomial p = f.Forward(0, z); // evaluate first order Taylor coefficient dp = f.Forward(1, dz); // second derivative is twice second order Taylor coef ddp = f.Forward(2, ddz); ddp[0] *= 2.; } else { // choose an argument value CppAD::uniform_01(1, z); Z[0] = z[0]; // declare independent variables Independent(Z, abort_op_index, record_compare); // AD computation of the function value P[0] = CppAD::Poly(0, A, Z[0]); // create function object f : A -> detA f.Dependent(Z, P); if( global_option["optimize"] ) f.optimize(optimize_options); // skip comparison operators f.compare_change_count(0); while(repeat--) { // sufficient memory is allocated by second repetition // get the next argument value CppAD::uniform_01(1, z); // evaluate the polynomial at the new argument value p = f.Forward(0, z); // evaluate first order Taylor coefficient dp = f.Forward(1, dz); // second derivative is twice second order Taylor coef ddp = f.Forward(2, ddz); ddp[0] *= 2.; } } size_t thread = CppAD::thread_alloc::thread_num(); global_cppad_thread_alloc_inuse = CppAD::thread_alloc::inuse(thread); return true; } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_poly.cpp} */ ================================================ FILE: speed/cppad/sparse_hessian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppad_sparse_hessian.cpp} Cppad Speed: Sparse Hessian ########################### Specifications ************** See :ref:`link_sparse_hessian-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ # include # include # include // Note that CppAD uses global_option["memory"] at the main program level # include extern std::map global_option; // see comments in main program for this external extern size_t global_cppad_thread_alloc_inuse; namespace { // typedefs using CppAD::vector; typedef CppAD::AD a1double; typedef CppAD::AD a2double; typedef vector b_vector; typedef vector s_vector; typedef vector d_vector; typedef vector a1vector; typedef vector a2vector; typedef CppAD::sparse_rc sparsity_pattern; typedef CppAD::sparse_rcv sparse_matrix; // ------------------------------------------------------------------------ void create_fun( const d_vector& x , const s_vector& row , const s_vector& col , CppAD::ADFun& fun ) { // initialize a1double version of independent variables size_t n = x.size(); a1vector a1x(n); for(size_t j = 0; j < n; j++) a1x[j] = x[j]; // // optimization options std::string optimize_options = "no_conditional_skip no_compare_op no_print_for_op"; if( global_option["val_graph"] ) optimize_options += " val_graph"; // // order of derivative in sparse_hes_fun size_t order = 0; // // do not even record comparison operators size_t abort_op_index = 0; bool record_compare = false; // if( ! global_option["hes2jac"] ) { // declare independent variables Independent(a1x, abort_op_index, record_compare); // // AD computation of y a1vector a1y(1); CppAD::sparse_hes_fun(n, a1x, row, col, order, a1y); // // create function object f : X -> Y fun.Dependent(a1x, a1y); // if( global_option["optimize"] ) fun.optimize(optimize_options); // // skip comparison operators fun.compare_change_count(0); // // fun corresponds to f(x) return; } // declare independent variables for f(x) a2vector a2x(n); for(size_t j = 0; j < n; j++) a2x[j] = a1x[j]; Independent(a2x, abort_op_index, record_compare); // // a2double computation of y a2vector a2y(1); CppAD::sparse_hes_fun(n, a2x, row, col, order, a2y); // // create function object corresponding to y = f(x) CppAD::ADFun a1f; a1f.Dependent(a2x, a2y); // // declare independent variables for g(x) Independent(a1x, abort_op_index, record_compare); // // a1double computation of z a1vector a1w(1), a1z(n); a1w[0] = 1.0; a1f.Forward(0, a1x); a1z = a1f.Reverse(1, a1w); // // create function object z = g(x) = f'(x) fun.Dependent(a1x, a1z); // if( global_option["optimize"] ) fun.optimize(optimize_options); // // skip comparison operators fun.compare_change_count(0); // // fun corresponds to g(x) return; } // ------------------------------------------------------------------------ void calc_sparsity( sparsity_pattern& sparsity , CppAD::ADFun& fun ) { size_t n = fun.Domain(); size_t m = fun.Range(); // bool transpose = false; // if( global_option["subsparsity"] ) { CPPAD_ASSERT_UNKNOWN( global_option["hes2jac"] ) CPPAD_ASSERT_UNKNOWN( n == m ); b_vector select_domain(n), select_range(m); for(size_t j = 0; j < n; ++j) select_domain[j] = true; for(size_t i = 0; i < m; ++i) select_range[i] = true; // // fun corresponds to g(x) fun.subgraph_sparsity( select_domain, select_range, transpose, sparsity ); return; } bool dependency = false; bool reverse = global_option["revsparsity"]; bool internal_bool = global_option["boolsparsity"]; // if( ! global_option["hes2jac"] ) { // fun corresponds to f(x) // CPPAD_ASSERT_UNKNOWN( m == 1 ); // b_vector select_range(m); select_range[0] = true; // if( reverse ) { sparsity_pattern identity; identity.resize(n, n, n); for(size_t k = 0; k < n; k++) identity.set(k, k, k); fun.for_jac_sparsity( identity, transpose, dependency, internal_bool, sparsity ); fun.rev_hes_sparsity( select_range, transpose, internal_bool, sparsity ); } else { b_vector select_domain(n); for(size_t j = 0; j < n; j++) select_domain[j] = true; fun.for_hes_sparsity( select_domain, select_range, internal_bool, sparsity ); } return; } // fun corresponds to g(x) CPPAD_ASSERT_UNKNOWN( m == n ); // // sparsity pattern for identity matrix sparsity_pattern eye; eye.resize(n, n, n); for(size_t k = 0; k < n; k++) eye.set(k, k, k); // if( reverse ) { fun.rev_jac_sparsity( eye, transpose, dependency, internal_bool, sparsity ); } else { fun.for_jac_sparsity( eye, transpose, dependency, internal_bool, sparsity ); } return; } // ------------------------------------------------------------------------ size_t calc_hessian( d_vector& hessian , const d_vector& x , sparse_matrix& subset , const sparsity_pattern& sparsity , CppAD::sparse_jac_work& jac_work , CppAD::sparse_hes_work& hes_work , CppAD::ADFun& fun ) { size_t n_color; // if( ! global_option["hes2jac"] ) { // fun corresponds to f(x) // // coloring method std::string coloring = "cppad"; if( global_option["colpack"] ) coloring = "colpack"; if( global_option["symmetric"] ) coloring += ".symmetric"; else coloring += ".general"; // // only one function component d_vector w(1); w[0] = 1.0; // // compute hessian n_color = fun.sparse_hes( x, w, subset, sparsity, coloring, hes_work ); } else { // fun corresponds to g(x) // if( global_option["subgraph"] ) { fun.subgraph_jac_rev(x, subset); n_color = 0; } else { // // coloring method std::string coloring = "cppad"; # if CPPAD_HAS_COLPACK if( global_option["colpack"] ) coloring = "colpack"; # endif size_t group_max = 1; n_color = fun.sparse_jac_for( group_max, x, subset, sparsity, coloring, jac_work ); } } // return result const d_vector& val( subset.val() ); size_t nnz = subset.nnz(); for(size_t k = 0; k < nnz; k++) hessian[k] = val[k]; // return n_color; } } bool link_sparse_hessian( size_t size , size_t repeat , const CppAD::vector& row , const CppAD::vector& col , CppAD::vector& x , CppAD::vector& hessian , size_t& n_color ) { global_cppad_thread_alloc_inuse = 0; // -------------------------------------------------------------------- // check global options const char* valid[] = { "memory", "onetape", "optimize", "hes2jac", "subgraph", "boolsparsity", "revsparsity", "symmetric", "val_graph" # if CPPAD_HAS_COLPACK , "colpack" # else , "subsparsity" # endif }; size_t n_valid = sizeof(valid) / sizeof(valid[0]); typedef std::map::iterator iterator; // for(iterator itr=global_option.begin(); itr!=global_option.end(); ++itr) { if( itr->second ) { bool ok = false; for(size_t i = 0; i < n_valid; i++) ok |= itr->first == valid[i]; if( ! ok ) return false; } } if( global_option["subsparsity"] ) { if( global_option["boolsparsity"] || global_option["revsparsity"] ) return false; if( ! global_option["hes2jac"] ) return false; } if( global_option["subgraph"] ) { if( ! global_option["hes2jac"] ) return false; } # if ! CPPAD_HAS_COLPACK if( global_option["colpack"] ) return false; # endif // ----------------------------------------------------------------------- // setup size_t n = size; // number of independent variables CppAD::ADFun fun; // AD function object used to calculate Hessian // // declare sparsity pattern sparsity_pattern sparsity; // // declare subset where Hessian is evaluated sparsity_pattern subset_pattern; size_t nr = n; size_t nc = n; size_t nnz = row.size(); subset_pattern.resize(nr, nc, nnz); for(size_t k = 0; k < nnz; k++) subset_pattern.set(k, row[k], col[k]); sparse_matrix subset( subset_pattern ); // // structures that holds some of the work done by sparse_jac, sparse_hes CppAD::sparse_jac_work jac_work; CppAD::sparse_hes_work hes_work; // ----------------------------------------------------------------------- if( ! global_option["onetape"] ) while(repeat--) { // choose a value for x CppAD::uniform_01(n, x); // // create f(x) create_fun(x, row, col, fun); // // calculate the sparsity pattern for Hessian of f(x) calc_sparsity(sparsity, fun); // // calculate the Hessian at this x jac_work.clear(); // without work from previous calculation hes_work.clear(); n_color = calc_hessian( hessian, x, subset, sparsity, jac_work, hes_work, fun ); } else { // choose a value for x CppAD::uniform_01(n, x); // // create f(x) create_fun(x, row, col, fun); // // calculate the sparsity pattern for Hessian of f(x) calc_sparsity(sparsity, fun); // while(repeat--) { // choose a value for x CppAD::uniform_01(n, x); // // calculate this Hessian at this x n_color = calc_hessian( hessian, x, subset, sparsity, jac_work, hes_work, fun ); } } size_t thread = CppAD::thread_alloc::thread_num(); global_cppad_thread_alloc_inuse = CppAD::thread_alloc::inuse(thread); return true; } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_sparse_hessian.cpp} */ ================================================ FILE: speed/cppad/sparse_jacobian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppad_sparse_jacobian.cpp} Cppad Speed: Sparse Jacobian ############################ Specifications ************** See :ref:`link_sparse_jacobian-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ # include # include # include // Note that CppAD uses global_option["memory"] at the main program level # include extern std::map global_option; // see comments in main program for this external extern size_t global_cppad_thread_alloc_inuse; namespace { using CppAD::vector; typedef CppAD::AD a_double; typedef vector s_vector; typedef vector b_vector; typedef vector d_vector; typedef vector a_vector; typedef CppAD::sparse_rc sparsity; typedef CppAD::sparse_rcv sparse_matrix; void calc_sparsity( CppAD::sparse_rc& pattern , CppAD::ADFun& f ) { bool reverse = global_option["revsparsity"]; bool transpose = false; bool internal_bool = global_option["boolsparsity"]; bool dependency = false; bool subgraph = global_option["subsparsity"]; size_t n = f.Domain(); size_t m = f.Range(); if( subgraph ) { b_vector select_domain(n), select_range(m); for(size_t j = 0; j < n; ++j) select_domain[j] = true; for(size_t i = 0; i < m; ++i) select_range[i] = true; f.subgraph_sparsity( select_domain, select_range, transpose, pattern ); } else { size_t q = n; if( reverse ) q = m; // CppAD::sparse_rc identity; identity.resize(q, q, q); for(size_t k = 0; k < q; k++) identity.set(k, k, k); // if( reverse ) { f.rev_jac_sparsity( identity, transpose, dependency, internal_bool, pattern ); } else { f.for_jac_sparsity( identity, transpose, dependency, internal_bool, pattern ); } } } // -------------------------------------------------------------------- void setup( // inputs size_t size , size_t m , const s_vector& row , const s_vector& col , // outputs size_t& n_color , CppAD::ADFun& f , sparse_matrix& subset , CppAD::sparse_jac_work& work ) { // optimization options std::string optimize_options = "no_conditional_skip no_compare_op no_print_for_op"; if( global_option["val_graph"] ) optimize_options += " val_graph"; // // default value for n_color n_color = 0; // // independent variable vector size_t nc = size; a_vector a_x(nc); d_vector x(nc); // // dependent variable vector size_t nr = m; a_vector a_y(nr); // // choose a value for independent variable vector CppAD::uniform_01(nc, x); for(size_t j = 0; j < nc; j++) a_x[j] = x[j]; // // declare independent variables size_t abort_op_index = 0; bool record_compare = false; CppAD::Independent(a_x, abort_op_index, record_compare); // // AD computation of f(x) size_t order = 0; CppAD::sparse_jac_fun(nr, nc, a_x, row, col, order, a_y); // // create function object f : x -> y f.Dependent(a_x, a_y); // if( global_option["optimize"] ) f.optimize(optimize_options); // // coloring method std::string coloring = "cppad"; # if CPPAD_HAS_COLPACK if( global_option["colpack"] ) coloring = "colpack"; # else CPPAD_ASSERT_UNKNOWN( ! global_option["colpack"] ); # endif // // sparsity pattern for subset of Jacobian that is evaluated size_t nnz = row.size(); sparsity subset_pattern(nr, nc, nnz); for(size_t k = 0; k < nnz; ++k) subset_pattern.set(k, row[k], col[k]); // // sparse matrix for subset of Jacobian that is evaluated subset = sparse_matrix( subset_pattern ); // // maximum number of colors at once size_t group_max = 25; // if( global_option["subgraph"] ) { // This would cache some information in f, but would it enough ? // The time it takes to compute derivatives that are not used // slows down the test when onetape is false. // f.subgraph_jac_rev(x, ac_subset); } else { // need full sparsity pattern // (could use subset_sparsity, but pretend we do not know that) sparsity pattern; calc_sparsity(pattern, f); // // Use forward mode to compute the Jacobian // (this caches information in work), work.clear(); n_color = f.sparse_jac_for( group_max, x, subset, pattern, coloring, work ); } } } bool link_sparse_jacobian( const std::string& job , size_t size , size_t repeat , size_t m , const CppAD::vector& row , const CppAD::vector& col , CppAD::vector& x , CppAD::vector& jacobian , size_t& n_color ) { global_cppad_thread_alloc_inuse = 0; // -------------------------------------------------------------------- // check global options const char* valid[] = { "memory", "onetape", "optimize", "subgraph", "boolsparsity", "revsparsity", "subsparsity", "val_graph" # if CPPAD_HAS_COLPACK , "colpack" # endif }; size_t n_valid = sizeof(valid) / sizeof(valid[0]); typedef std::map::iterator iterator; // for(iterator itr=global_option.begin(); itr!=global_option.end(); ++itr) { if( itr->second ) { bool ok = false; for(size_t i = 0; i < n_valid; i++) ok |= itr->first == valid[i]; if( ! ok ) return false; } } if( global_option["subsparsity"] ) { if( global_option["boolsparisty"] || global_option["revsparsity"] || global_option["colpack"] ) return false; } // ----------------------------------------------------- // size corresponding to static_f static size_t static_size = 0; // // function object corresponding to f(x) static CppAD::ADFun static_f; // // subset of Jacobian that we are using static sparse_matrix static_subset; // // information used by for_sparse_jac_for static CppAD::sparse_jac_work static_work; // // sparsity pattern not used because work is non-empty sparsity empty_pattern; // ----------------------------------------------------------------------- // // default value for n_color n_color = 0; // bool onetape = global_option["onetape"]; // if( job == "setup" ) { if( onetape ) { setup(size, m, row, col, n_color, static_f, static_subset, static_work ); static_size = size; } else { static_size = 0; } return true; } if( job == "teardown" ) { static_f = CppAD::ADFun(); sparse_matrix empty_matrix; static_subset.swap( empty_matrix ); static_work.clear(); static_size = 0; return true; } // ------------------------------------------------------------------------ CPPAD_ASSERT_UNKNOWN( job == "run" ); // // number of independent variables static size_t n = size; // // maximum number of colors at once size_t group_max = 25; // // coloring method std::string coloring = "cppad"; if( global_option["colpack"] ) coloring = "colpack"; // ------------------------------------------------------ while(repeat--) { if( onetape ) { if( size != static_size ) CPPAD_ASSERT_UNKNOWN( size == static_size ); } else { setup(size, m, row, col, n_color, static_f, static_subset, static_work ); } // choose a value for x CppAD::uniform_01(n, x); if( global_option["subgraph"] ) { // user reverse mode because forward not yet implemented static_f.subgraph_jac_rev(x, static_subset); } else { // Use forward mode because m > n (is this sufficient reason ?) n_color = static_f.sparse_jac_for(group_max, x, static_subset, empty_pattern, coloring, static_work ); } jacobian = static_subset.val(); } size_t thread = CppAD::thread_alloc::thread_num(); global_cppad_thread_alloc_inuse = CppAD::thread_alloc::inuse(thread); return true; } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_sparse_jacobian.cpp} */ ================================================ FILE: speed/cppad/speed_cppad.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin speed_cppad} Speed Test Derivatives Using CppAD ################################## Purpose ******* CppAD has a set of speed tests that are used to determine if certain changes improve its execution speed (and to compare CppAD with other AD packages). This section links to the source code the CppAD speed tests (any suggestions to make the CppAD results faster are welcome). Running Tests ************* To build these speed tests, and run their correctness tests, execute the following commands starting in the :ref:`cmake@Build Directory` : | |tab| ``cd speed/cppad`` | |tab| ``make check_speed_cppad VERBOSE`` =1 You can then run the corresponding speed tests with the following command ./ ``speed_cppad speed`` *seed* where *seed* is a positive integer. See :ref:`speed_main-name` for more options. Contents ******** {xrst_toc_list speed/cppad/det_minor.cpp speed/cppad/det_lu.cpp speed/cppad/mat_mul.cpp speed/cppad/ode.cpp speed/cppad/poly.cpp speed/cppad/sparse_hessian.cpp speed/cppad/sparse_jacobian.cpp } {xrst_end speed_cppad} ================================================ FILE: speed/cppad_jit/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-23 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the speed/cppad_jit directory tests # Inherit build type from ../CMakeList.txt # Adds flags to the compiler command line for sources in the current directory # and below. This command can be used to add any flags, but it was originally # intended to add preprocessor definitions. ADD_DEFINITIONS("-DCPPAD_CPPAD_JIT_SPEED") # Local include directories to search (not in package_prefix/includdir) INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../src ) # add_executable( [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] # source1 source2 ... sourceN # ) SET(source_list ../main.cpp det_lu.cpp det_minor.cpp mat_mul.cpp ode.cpp poly.cpp sparse_hessian.cpp sparse_jacobian.cpp ) set_compile_flags( speed_cppad_jit "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE( speed_cppad_jit EXCLUDE_FROM_ALL ${source_list} ) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(speed_cppad_jit ${cppad_lib} ${colpack_libs} ) # cppad_jit is an include file only library TARGET_LINK_LIBRARIES(speed_cppad_jit speed_src ) # check_speed_cppad_jit add_check_executable(check_speed cppad_jit "correct 54321") ================================================ FILE: speed/cppad_jit/det_lu.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppad_jit_det_lu.cpp} cppad_jit Speed: Gradient of Determinant Using Lu Factorization ############################################################### Specifications ************** :ref:`link_det_lu-name` Implementation ************** A cppad_jit version of this test is not yet implemented {xrst_spell_off} {xrst_code cpp} */ # include # include // list of possible options extern std::map global_option; bool link_det_lu( size_t size , size_t repeat , CppAD::vector &matrix , CppAD::vector &gradient ) { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_jit_det_lu.cpp} */ ================================================ FILE: speed/cppad_jit/det_minor.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppad_jit_det_minor.cpp} cppad_jit Speed: Gradient of Determinant by Minor Expansion ########################################################### Specifications ************** See :ref:`link_det_minor-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ # include # include # include # include extern std::map global_option; # ifdef _WIN32 # define DLL_EXT ".dll" # else # define DLL_EXT ".so" # endif # if ! (CPPAD_C_COMPILER_GNU_FLAGS || CPPAD_C_COMPILER_MSVC_FLAGS ) bool link_det_minor( const std::string& job , size_t size , size_t repeat , CppAD::vector &matrix , CppAD::vector &gradient ) { return false; } # else namespace { // // using using std::string; // // typedefs typedef CppAD::AD a_double; typedef CppAD::vector d_vector; typedef CppAD::vector ad_vector; // // get_function_ptr CppAD::jit_double get_function_ptr( CppAD::link_dll_lib* dll_linker ) { std::string function_name = "cppad_jit_gradient_det"; string err_msg; void* void_ptr = (*dll_linker)(function_name, err_msg); if( err_msg != "" ) { std::cerr << "link_det_minor: err_msg = " << err_msg << "\n"; return nullptr; } CppAD::jit_double function_ptr = reinterpret_cast(void_ptr); return function_ptr; } // // setup CppAD::link_dll_lib* setup(size_t size) { // optimization options string optimize_options = "no_conditional_skip no_compare_op no_print_for_op"; // // adet // object for computing determinant CppAD::det_by_minor adet(size); // // nx // number of independent variables size_t nx = size * size; // // matrix CppAD::vector matrix(nx); CppAD::uniform_01(nx, matrix); // // aA ad_vector aA(nx); for(size_t j = 0; j < nx; ++j) aA[j] = matrix[j]; // // Independent // declare independent variables for function computation bool record_compare = false; size_t abort_op_index = 0; CppAD::Independent(aA, abort_op_index, record_compare); // // adet_A // AD computation of the determinant ad_vector adet_A(1); adet_A[0] = adet(aA); // // f // f( matrix ) = det(matrix) CppAD::ADFun f; f.Dependent(aA, adet_A); if( global_option["optimize"] ) f.optimize(optimize_options); // // af CppAD::ADFun af; af = f.base2ad(); // // Independent // declare independent variables for gradient computation CppAD::Independent(aA, abort_op_index, record_compare); // // aw // vectors of reverse mode weights CppAD::vector aw(1); aw[0] = a_double(1.0); // // agradient ad_vector agradient(nx); af.Forward(0, aA); agradient = af.Reverse(1, aw); // // g // function objects for g : A -> det'( detA ) CppAD::ADFun g; g.Dependent(aA, agradient); if( global_option["optimize"] ) g.optimize(optimize_options); g.function_name_set("gradient_det"); // // csrc_file string type = "double"; string csrc_file = "gradient_det.c"; std::ofstream ofs; ofs.open(csrc_file, std::ofstream::out); g.to_csrc(ofs, type); ofs.close(); // // dll_file string dll_file = "gradient_det" DLL_EXT; CppAD::vector< string > csrc_files(1); csrc_files[0] = csrc_file; std::map< string, string > dll_options; # if CPPAD_C_COMPILER_MSVC_FLAGS dll_options["compile"] = CPPAD_C_COMPILER_CMD " /EHs /EHc /c /TC /O2"; # endif # if CPPAD_C_COMPILER_GNU_FLAGS dll_options["compile"] = "gcc -c -fPIC -O2"; # endif string err_msg = CppAD::create_dll_lib(dll_file, csrc_files, dll_options); if( err_msg != "" ) { std::cerr << "link_det_minor: err_msg = " << err_msg << "\n"; return nullptr; } // // dll_linker_ptr CppAD::link_dll_lib* dll_linker_ptr = new CppAD::link_dll_lib(dll_file, err_msg); if( err_msg != "" ) { std::cerr << "link_det_minor: err_msg = " << err_msg << "\n"; delete dll_linker_ptr; return nullptr; } return dll_linker_ptr; } } bool link_det_minor( const std::string& job , size_t size , size_t repeat , CppAD::vector &matrix , CppAD::vector &gradient ) { CPPAD_ASSERT_UNKNOWN( matrix.size() == size * size ); CPPAD_ASSERT_UNKNOWN( gradient.size() == size * size ); // -------------------------------------------------------------------- // check global options const char* valid[] = { "onetape", "optimize"}; size_t n_valid = sizeof(valid) / sizeof(valid[0]); typedef std::map::iterator iterator; // for(iterator itr=global_option.begin(); itr!=global_option.end(); ++itr) { if( itr->second ) { bool ok = false; for(size_t i = 0; i < n_valid; i++) ok |= itr->first == valid[i]; if( ! ok ) return false; } } // -------------------------------------------------------------------- // pointer to dll linker static CppAD::link_dll_lib* static_dll_linker = nullptr; // // pointer to gradient_det function static CppAD::jit_double static_gradient_det; // // size corresponding static_fun static size_t static_size = 0; // // number of independent variables size_t nx = size * size; // // onetape bool onetape = global_option["onetape"]; // ---------------------------------------------------------------------- if( job == "setup" ) { if( onetape ) { if( static_dll_linker != nullptr ) delete static_dll_linker; static_dll_linker = setup(size); // static_gradient_det = get_function_ptr(static_dll_linker); static_size = size; if( static_gradient_det == nullptr ) return false; } else { static_gradient_det = nullptr; static_size = 0; } return true; } if( job == "teardown" ) { if( static_dll_linker != nullptr ) { delete static_dll_linker; static_dll_linker = nullptr; } return true; } // ----------------------------------------------------------------------- CPPAD_ASSERT_UNKNOWN( job == "run" ); if( onetape ) while(repeat--) { // use if before assert to avoid warning that static_size is not used if( size != static_size ) { CPPAD_ASSERT_UNKNOWN( size == static_size ); } // get next matrix CppAD::uniform_01(nx, matrix); // evaluate the gradient size_t compare_change = 0; static_gradient_det( nx, matrix.data(), nx, gradient.data(), &compare_change ); } else while(repeat--) { if( static_dll_linker != nullptr ) delete static_dll_linker; static_dll_linker = setup(size); // static_gradient_det = get_function_ptr(static_dll_linker); static_size = size; if( static_gradient_det == nullptr ) return false; // // get next matrix CppAD::uniform_01(nx, matrix); // evaluate the gradient size_t compare_change = 0; static_gradient_det( nx, matrix.data(), nx, gradient.data(), &compare_change ); } return true; } # endif // CPPAD_C_COMPILER_GNU_FLAGS || CPPAD_C_COMPILER_MSVC_FLAGS /* {xrst_code} {xrst_spell_on} {xrst_end cppad_jit_det_minor.cpp} */ ================================================ FILE: speed/cppad_jit/mat_mul.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppad_jit_mat_mul.cpp} cppad_jit Speed: Matrix Multiplication ###################################### Specifications ************** :ref:`link_mat_mul-name` Implementation ************** // a cppad_jit version of this test is not yet implemented {xrst_spell_off} {xrst_code cpp} */ # include # include // list of possible options extern std::map global_option; bool link_mat_mul( size_t size , size_t repeat , CppAD::vector& x , CppAD::vector& z , CppAD::vector& dz ) { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_jit_mat_mul.cpp} */ ================================================ FILE: speed/cppad_jit/ode.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppad_jit_ode.cpp} cppad_jit Speed: Ode #################### Specifications ************** :ref:`link_ode-name` Implementation ************** // a cppad_jit version of this test is not yet implemented {xrst_spell_off} {xrst_code cpp} */ # include # include // list of possible options extern std::map global_option; bool link_ode( size_t size , size_t repeat , CppAD::vector &x , CppAD::vector &jacobian ) { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_jit_ode.cpp} */ ================================================ FILE: speed/cppad_jit/poly.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppad_jit_poly.cpp} cppad_jit Speed: Second Derivative of a Polynomial ################################################## Specifications ************** :ref:`link_poly-name` Implementation ************** // a cppad_jit version of this test is not yet implemented {xrst_spell_off} {xrst_code cpp} */ # include # include // list of possible options extern std::map global_option; bool link_poly( size_t size , size_t repeat , CppAD::vector &a , // coefficients of polynomial CppAD::vector &z , // polynomial argument value CppAD::vector &ddp ) // second derivative w.r.t z { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_jit_poly.cpp} */ ================================================ FILE: speed/cppad_jit/sparse_hessian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include /* {xrst_begin cppad_jit_sparse_hessian.cpp} cppad_jit Speed: Sparse Hessian ############################### Specifications ************** :ref:`link_sparse_hessian-name` {xrst_spell_off} {xrst_code cpp} */ // A cppad_jit version of this test is not yet available bool link_sparse_hessian( size_t size , size_t repeat , const CppAD::vector& row , const CppAD::vector& col , CppAD::vector& x , CppAD::vector& hessian , size_t& n_color ) { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_jit_sparse_hessian.cpp} */ ================================================ FILE: speed/cppad_jit/sparse_jacobian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include /* {xrst_begin cppad_jit_sparse_jacobian.cpp} cppad_jit Speed: sparse_jacobian ################################ Specifications ************** :ref:`link_sparse_jacobian-name` {xrst_spell_off} {xrst_code cpp} */ // A cppad_jit version of this test is not yet available bool link_sparse_jacobian( const std::string& job , size_t size , size_t repeat , size_t m , const CppAD::vector& row , const CppAD::vector& col , CppAD::vector& x , CppAD::vector& jacobian , size_t& n_color ) { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end cppad_jit_sparse_jacobian.cpp} */ ================================================ FILE: speed/cppad_jit/speed_cppad_jit.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin speed_cppad_jit} Speed Test Derivatives Using cppad_jit ###################################### Purpose ******* CppAD has a set of speed tests that are used to compare cppad_jit with other AD packages. This section links to the source code the cppad_jit speed tests (any suggestions to make the cppad_jit results faster are welcome). Running Tests ************* To build these speed tests, and run their correctness tests, execute the following commands starting in the :ref:`cmake@Build Directory` : | |tab| ``cd speed/cppad_jit`` | |tab| ``make check_speed_cppad_jit VERBOSE`` =1 You can then run the corresponding speed tests with the following command ./ ``speed_cppad_jit speed`` *seed* where *seed* is a positive integer. See :ref:`speed_main-name` for more options. Contents ******** {xrst_toc_list speed/cppad_jit/det_minor.cpp speed/cppad_jit/det_lu.cpp speed/cppad_jit/mat_mul.cpp speed/cppad_jit/ode.cpp speed/cppad_jit/poly.cpp speed/cppad_jit/sparse_hessian.cpp speed/cppad_jit/sparse_jacobian.cpp } {xrst_end speed_cppad_jit} ================================================ FILE: speed/cppadcg/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the speed/cppadcg directory tests # Inherit build type from ../CMakeList.txt # assert ( include_cppadcg ) # # Adds flags to the compiler command line for sources in the current directory # and below. This command can be used to add any flags, but it was originally # intended to add preprocessor definitions. ADD_DEFINITIONS("-DCPPAD_CPPADCG_SPEED") # Local include directories to search (not in package_prefix/includdir) INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../src ) SET(source_list ../main.cpp det_lu.cpp det_minor.cpp mat_mul.cpp ode.cpp poly.cpp sparse_hessian.cpp sparse_jacobian.cpp ) set_compile_flags( speed_cppadcg "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(speed_cppadcg EXCLUDE_FROM_ALL ${source_list} ) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(speed_cppadcg ${cppad_lib} ${colpack_libs} ) # cppadcg is an include file only library TARGET_LINK_LIBRARIES(speed_cppadcg speed_src ${cppad_lib} ${colpack_libs} ) # check_speed_cppadcg SET(random_seed 54321) add_check_executable(check_speed cppadcg "correct ${random_seed} onetape") ================================================ FILE: speed/cppadcg/det_lu.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppadcg_det_lu.cpp} Cppadcg Speed: Gradient of Determinant Using Lu Factorization ############################################################# Specifications ************** :ref:`link_det_lu-name` Implementation ************** A cppadcg version of this test is not yet implemented {xrst_spell_off} {xrst_code cpp} */ # include # include // list of possible options extern std::map global_option; bool link_det_lu( size_t size , size_t repeat , CppAD::vector &matrix , CppAD::vector &gradient ) { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end cppadcg_det_lu.cpp} */ ================================================ FILE: speed/cppadcg/det_minor.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppadcg_det_minor.cpp} cppadcg Speed: Gradient of Determinant by Minor Expansion ######################################################### Specifications ************** See :ref:`link_det_minor-name` . PASS_JACOBIAN_TO_CODE_GEN ************************* If this is one, the Jacobian of the determinant is the function passed to CppADCodeGen. In this case, the ``code_gen_fun`` :ref:`code_gen_fun@Syntax@function` is used to calculate the Jacobian of the determinant. Otherwise, this flag is zero and the determinant function is passed to CppADCodeGen. In this case, the ``code_gen_fun`` :ref:`code_gen_fun@Syntax@jacobian` is used to calculate the Jacobian of the determinant. {xrst_spell_off} {xrst_code cpp} */ # define PASS_JACOBIAN_TO_CODE_GEN 1 /* {xrst_code} {xrst_spell_on} Implementation ************** {xrst_spell_off} {xrst_code cpp} */ # include # include # include # include # include extern std::map global_option; namespace { // // typedefs typedef CppAD::cg::CG c_double; typedef CppAD::AD ac_double; typedef CppAD::vector d_vector; typedef CppAD::vector ac_vector; // // setup void setup( // inputs size_t size , // outputs code_gen_fun& fun ) { // optimization options std::string optimize_options = "no_conditional_skip no_compare_op no_print_for_op"; // // object for computing determinant CppAD::det_by_minor ac_det(size); // // number of independent variables size_t nx = size * size; // // choose a matrix CppAD::vector matrix(nx); CppAD::uniform_01(nx, matrix); // // copy to independent variables ac_vector ac_A(nx); for(size_t j = 0; j < nx; ++j) ac_A[j] = matrix[j]; // // declare independent variables for function computation bool record_compare = false; size_t abort_op_index = 0; CppAD::Independent(ac_A, abort_op_index, record_compare); // // AD computation of the determinant ac_vector ac_detA(1); ac_detA[0] = ac_det(ac_A); // // create function objects for f : A -> detA CppAD::ADFun c_f; c_f.Dependent(ac_A, ac_detA); if( global_option["optimize"] ) c_f.optimize(optimize_options); # if ! PASS_JACOBIAN_TO_CODE_GEN // f(x) is the determinant function code_gen_fun::evaluation_enum eval_jac = code_gen_fun::dense_enum; code_gen_fun f_tmp("det_minor", c_f, eval_jac); fun.swap(f_tmp); # else CppAD::ADFun ac_f; ac_f = c_f.base2ad(); // // declare independent variables for gradient computation CppAD::Independent(ac_A, abort_op_index, record_compare); // // vectors of reverse mode weights CppAD::vector ac_w(1); ac_w[0] = ac_double(1.0); // // AD computation of the gradient ac_vector ac_gradient(nx); ac_f.Forward(0, ac_A); ac_gradient = ac_f.Reverse(1, ac_w); // // create function objects for g : A -> det'( detA ) CppAD::ADFun c_g; c_g.Dependent(ac_A, ac_gradient); if( global_option["optimize"] ) c_g.optimize(optimize_options); // g(x) is the Jacobian of the determinant code_gen_fun g_tmp("det_minor", c_g); fun.swap(g_tmp); # endif } } bool link_det_minor( const std::string& job , size_t size , size_t repeat , CppAD::vector &matrix , CppAD::vector &gradient ) { CPPAD_ASSERT_UNKNOWN( matrix.size() == size * size ); CPPAD_ASSERT_UNKNOWN( gradient.size() == size * size ); // -------------------------------------------------------------------- // check global options const char* valid[] = { "onetape", "optimize"}; size_t n_valid = sizeof(valid) / sizeof(valid[0]); typedef std::map::iterator iterator; // for(iterator itr=global_option.begin(); itr!=global_option.end(); ++itr) { if( itr->second ) { bool ok = false; for(size_t i = 0; i < n_valid; i++) ok |= itr->first == valid[i]; if( ! ok ) return false; } } // -------------------------------------------------------------------- // // function object mapping matrix to gradient of determinant static code_gen_fun static_fun; // // size corresponding static_fun static size_t static_size = 0; // // number of independent variables size_t nx = size * size; // // onetape bool onetape = global_option["onetape"]; // ---------------------------------------------------------------------- if( job == "setup" ) { if( onetape ) { setup(size, static_fun); static_size = size; } else { static_size = 0; } return true; } if( job == "teardown" ) { code_gen_fun fun; static_fun.swap(fun); return true; } // ----------------------------------------------------------------------- CPPAD_ASSERT_UNKNOWN( job == "run" ); if( onetape ) while(repeat--) { // use if before assert to avoid warning that static_size is not used if( size != static_size ) { CPPAD_ASSERT_UNKNOWN( size == static_size ); } // get next matrix CppAD::uniform_01(nx, matrix); // evaluate the gradient # if PASS_JACOBIAN_TO_CODE_GEN gradient = static_fun(matrix); # else gradient = static_fun.jacobian(matrix); # endif } else while(repeat--) { setup(size, static_fun); static_size = size; // get next matrix CppAD::uniform_01(nx, matrix); // evaluate the gradient # if PASS_JACOBIAN_TO_CODE_GEN gradient = static_fun(matrix); # else gradient = static_fun.jacobian(matrix); # endif } return true; } /* {xrst_code} {xrst_spell_on} {xrst_end cppadcg_det_minor.cpp} */ ================================================ FILE: speed/cppadcg/mat_mul.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppadcg_mat_mul.cpp} Cppadcg Speed: Matrix Multiplication #################################### Specifications ************** :ref:`link_mat_mul-name` Implementation ************** // a cppadcg version of this test is not yet implemented {xrst_spell_off} {xrst_code cpp} */ # include # include // list of possible options extern std::map global_option; bool link_mat_mul( size_t size , size_t repeat , CppAD::vector& x , CppAD::vector& z , CppAD::vector& dz ) { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end cppadcg_mat_mul.cpp} */ ================================================ FILE: speed/cppadcg/ode.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppadcg_ode.cpp} Cppadcg Speed: Ode ################## Specifications ************** :ref:`link_ode-name` Implementation ************** // a cppadcg version of this test is not yet implemented {xrst_spell_off} {xrst_code cpp} */ # include # include // list of possible options extern std::map global_option; bool link_ode( size_t size , size_t repeat , CppAD::vector &x , CppAD::vector &jacobian ) { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end cppadcg_ode.cpp} */ ================================================ FILE: speed/cppadcg/poly.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppadcg_poly.cpp} Cppadcg Speed: Second Derivative of a Polynomial ################################################ Specifications ************** :ref:`link_poly-name` Implementation ************** // a cppadcg version of this test is not yet implemented {xrst_spell_off} {xrst_code cpp} */ # include # include // list of possible options extern std::map global_option; bool link_poly( size_t size , size_t repeat , CppAD::vector &a , // coefficients of polynomial CppAD::vector &z , // polynomial argument value CppAD::vector &ddp ) // second derivative w.r.t z { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end cppadcg_poly.cpp} */ ================================================ FILE: speed/cppadcg/sparse_hessian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include /* {xrst_begin cppadcg_sparse_hessian.cpp} Cppadcg Speed: Sparse Hessian ############################# Specifications ************** :ref:`link_sparse_hessian-name` {xrst_spell_off} {xrst_code cpp} */ // A cppadcg version of this test is not yet available bool link_sparse_hessian( size_t size , size_t repeat , const CppAD::vector& row , const CppAD::vector& col , CppAD::vector& x , CppAD::vector& hessian , size_t& n_color ) { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end cppadcg_sparse_hessian.cpp} */ ================================================ FILE: speed/cppadcg/sparse_jacobian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin cppadcg_sparse_jacobian.cpp} Cppadcg Speed: Sparse Jacobian ############################## Specifications ************** See :ref:`link_sparse_jacobian-name` . PASS_SPARSE_JACOBIAN_TO_CODE_GEN ******************************** If this is one, the sparse Jacobian is the is the function passed to CppADCodeGen, In this case, the ``code_gen_fun`` :ref:`code_gen_fun@Syntax@function` is used to calculate the sparse Jacobian. Otherwise, this flag is zero and the original function passed to CppADCodeGen. In this case, the ``code_gen_fun`` :ref:`code_gen_fun@Syntax@sparse_jacobian` is used to calculate the sparse Jacobian. {xrst_spell_off} {xrst_code cpp} */ # define PASS_SPARSE_JACOBIAN_TO_CODE_GEN 1 /* {xrst_code} {xrst_spell_on} Implementation ************** {xrst_spell_off} {xrst_code cpp} */ # include # include # include # include # include extern std::map global_option; namespace { // ----------------------------------------------------------------------- // typedefs typedef CppAD::cg::CG c_double; typedef CppAD::AD ac_double; typedef CppAD::vector b_vector; typedef CppAD::vector s_vector; typedef CppAD::vector d_vector; typedef CppAD::vector ac_vector; typedef CppAD::sparse_rc sparsity; // ------------------------------------------------------------------------ # if PASS_SPARSE_JACOBIAN_TO_CODE_GEN // calc_sparsity void calc_sparsity( CppAD::sparse_rc& pattern , CppAD::ADFun& f ) { bool reverse = global_option["revsparsity"]; bool transpose = false; bool internal_bool = global_option["boolsparsity"]; bool dependency = false; bool subgraph = global_option["subsparsity"]; size_t n = f.Domain(); size_t m = f.Range(); if( subgraph ) { b_vector select_domain(n), select_range(m); for(size_t j = 0; j < n; ++j) select_domain[j] = true; for(size_t i = 0; i < m; ++i) select_range[i] = true; f.subgraph_sparsity( select_domain, select_range, transpose, pattern ); } else { size_t q = n; if( reverse ) q = m; // CppAD::sparse_rc identity; identity.resize(q, q, q); for(size_t k = 0; k < q; k++) identity.set(k, k, k); // if( reverse ) { f.rev_jac_sparsity( identity, transpose, dependency, internal_bool, pattern ); } else { f.for_jac_sparsity( identity, transpose, dependency, internal_bool, pattern ); } } } # endif // PASS_SPARSE_JACOBIAN_TO_CODE_GEN // ------------------------------------------------------------------------- // setup void setup( // inputs size_t size , const s_vector& row , const s_vector& col , // outputs size_t& n_color , code_gen_fun& fun , s_vector& row_major ) { // optimization options std::string optimize_options = "no_conditional_skip no_compare_op no_print_for_op"; // // independent variable vector size_t nc = size; ac_vector ac_x(nc); // // dependent variable vector size_t nr = 2 * nc; ac_vector ac_y(nr); // // values of independent variables do not matter for(size_t j = 0; j < nc; j++) ac_x[j] = ac_double( double(j) / double(nc) ); // // declare independent variables size_t abort_op_index = 0; bool record_compare = false; CppAD::Independent(ac_x, abort_op_index, record_compare); // // AD computation of f(x) (order zero derivative is function value) size_t order = 0; CppAD::sparse_jac_fun(nr, nc, ac_x, row, col, order, ac_y); // // create function object f : x -> y CppAD::ADFun c_f; CppAD::ADFun ac_f; c_f.Dependent(ac_x, ac_y); if( global_option["optimize"] ) c_f.optimize(optimize_options); // // number of non-zeros in sparsity pattern for Jacobian # if ! PASS_SPARSE_JACOBIAN_TO_CODE_GEN // set fun code_gen_fun::evaluation_enum eval_jac = code_gen_fun::sparse_enum; code_gen_fun f_tmp("sparse_jacobian", c_f, eval_jac); fun.swap(f_tmp); // // set row_major d_vector x(nc); CppAD::uniform_01(nc, x); CppAD::sparse_rcv Jrcv = fun.sparse_jacobian(x); row_major = Jrcv.row_major(); # ifndef NDEBUG size_t nnz = row.size(); CPPAD_ASSERT_UNKNOWN( row_major.size() == nnz ); for(size_t k = 0; k < nnz; ++k) { size_t ell = row_major[k]; CPPAD_ASSERT_UNKNOWN( Jrcv.row()[ell] == row[k] && Jrcv.col()[ell] == col[k] ); } # endif // # else // PASS_SPARSE_JACOBIAN_TO_CODE_GEN // // sparsity pattern for subset of Jacobian pattern that is evaluated size_t nnz = row.size(); sparsity subset_pattern(nr, nc, nnz); for(size_t k = 0; k < nnz; ++k) subset_pattern.set(k, row[k], col[k]); // // spoarse matrix for subset of Jacobian that is evaluated CppAD::sparse_rcv ac_subset( subset_pattern ); // // coloring method std::string coloring = "cppad"; # if CPPAD_HAS_COLPACK if( global_option["colpack"] ) coloring = "colpack"; # endif // // maximum number of colors at once size_t group_max = 1; ac_f = c_f.base2ad(); // // declare independent variables for jacobian computation CppAD::Independent(ac_x, abort_op_index, record_compare); // if( global_option["subgraph"] ) { // use reverse mode because forward not yet implemented ac_f.subgraph_jac_rev(ac_x, ac_subset); n_color = 0; } else { // calculate the Jacobian sparsity pattern for this function sparsity pattern; calc_sparsity(pattern, c_f); // // use forward mode to compute Jacobian CppAD::sparse_jac_work work; n_color = ac_f.sparse_jac_for( group_max, ac_x, ac_subset, pattern, coloring, work ); } const ac_vector ac_val ( ac_subset.val() ); // // create function g : x -> f'(x) CppAD::ADFun c_g; c_g.Dependent(ac_x, ac_val); if( global_option["optimize"] ) c_g.optimize(optimize_options); code_gen_fun g_tmp("sparse_jacobian", c_g); // // set return value fun.swap(g_tmp); # endif // PASS_SPARSE_JACOBIAN_TO_CODE_GEN return; } } bool link_sparse_jacobian( const std::string& job , size_t size , size_t repeat , size_t m , const CppAD::vector& row , const CppAD::vector& col , CppAD::vector& x , CppAD::vector& jacobian , size_t& n_color ) { assert( x.size() == size ); assert( jacobian.size() == row.size() ); // -------------------------------------------------------------------- // check global options const char* valid[] = { "memory", "onetape", "optimize", "subgraph", "boolsparsity", "revsparsity", "subsparsity" # if CPPAD_HAS_COLPACK , "colpack" # endif }; size_t n_valid = sizeof(valid) / sizeof(valid[0]); typedef std::map::iterator iterator; // for(iterator itr=global_option.begin(); itr!=global_option.end(); ++itr) { if( itr->second ) { bool ok = false; for(size_t i = 0; i < n_valid; i++) ok |= itr->first == valid[i]; if( ! ok ) return false; } } if( global_option["subsparsity"] ) { if( global_option["boolsparisty"] || global_option["revsparsity"] || global_option["colpack"] ) return false; } // ----------------------------------------------------- // size corresponding to static_fun static size_t static_size = 0; // // function object mapping x to f'(x) static code_gen_fun static_fun; // // row_major order for Jrcv static s_vector static_row_major; // # if ! PASS_SPARSE_JACOBIAN_TO_CODE_GEN // code gen value for sparse jacobian CppAD::sparse_rcv Jrcv; # endif // // number of independent variables size_t nx = size; // bool onetape = global_option["onetape"]; // // default return value n_color = 0; // ----------------------------------------------------- if( job == "setup" ) { if( onetape ) { // sets n_color when ontape is true setup(size, row, col, n_color, static_fun, static_row_major); static_size = size; } else { static_size = 0; } return true; } if( job == "teardown" ) { code_gen_fun f_tmp; static_fun.swap(f_tmp); static_row_major.clear(); // static_size = 0; return true; } // ----------------------------------------------------- CPPAD_ASSERT_UNKNOWN( job == "run" ) if( onetape ) while(repeat--) { // use if before assert to avoid warning that static_size is not used if( size != static_size ) { CPPAD_ASSERT_UNKNOWN( size == static_size ); } // get next x CppAD::uniform_01(nx, x); // evaluate the jacobian # if PASS_SPARSE_JACOBIAN_TO_CODE_GEN jacobian = static_fun(x); # else Jrcv = static_fun.sparse_jacobian(x); CPPAD_ASSERT_UNKNOWN( Jrcv.nnz() == jacobian.size() ); for(size_t k = 0; k < row.size(); ++k) jacobian[k] = Jrcv.val()[ static_row_major[k] ]; # endif } else while(repeat--) { // sets n_color when ontape is false setup(size, row, col, n_color, static_fun, static_row_major); static_size = size; // get next x CppAD::uniform_01(nx, x); // evaluate the jacobian # if PASS_SPARSE_JACOBIAN_TO_CODE_GEN jacobian = static_fun(x); # else Jrcv = static_fun.sparse_jacobian(x); CPPAD_ASSERT_UNKNOWN( Jrcv.nnz() == jacobian.size() ); for(size_t k = 0; k < row.size(); ++k) jacobian[k] = Jrcv.val()[ static_row_major[k] ]; # endif } return true; } /* {xrst_code} {xrst_spell_on} {xrst_end cppadcg_sparse_jacobian.cpp} */ ================================================ FILE: speed/cppadcg/speed_cppadcg.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin speed_cppadcg} {xrst_spell onetape } Speed Test Derivatives Using Cppadcg #################################### Purpose ******* CppAD has a set of speed tests that are used to compare Cppadcg with other AD packages. This section links to the source code the Cppadcg speed tests (any suggestions to make the Cppadcg results faster are welcome). Building Tests ************** Starting in the :ref:`cmake@Build Directory` , execute the following commands | |tab| ``cd build/speed/cppadcg`` | |tab| ./ ``speed_cppadcg`` *test* *seed* *option_list* A message saying that the sizes are incorrect will be printed. In addition, it will say that source code with the correct sizes has been created. If you then execute ``make check_speed_cppadcg VERBOSE`` =1 It will build ``speed_cppadcg`` with the proper sizes and run its correctness tests. Running Tests ************* You can then run the corresponding speed tests with the either of the following commands: | |tab| ./ ``speed_cppadcg speed`` *seed* ``onetape`` | |tab| ./ ``speed_cppadcg speed`` *seed* ``onetape optimize`` where *seed* is a positive integer. See :ref:`speed_main-name` for more options. Contents ******** {xrst_toc_list speed/cppadcg/det_minor.cpp speed/cppadcg/det_lu.cpp speed/cppadcg/mat_mul.cpp speed/cppadcg/ode.cpp speed/cppadcg/poly.cpp speed/cppadcg/sparse_hessian.cpp speed/cppadcg/sparse_jacobian.cpp } {xrst_end speed_cppadcg} ================================================ FILE: speed/dev_speed.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin dev_speed dev} Speed Testing Developer Documentation ##################################### Contents ******** {xrst_toc_table speed/src/dev_link.xrst speed/src/dev_sparse_hessian.xrst speed/src/link_sparse_jacobian.cpp } {xrst_end dev_speed} ================================================ FILE: speed/double/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the speed/double directory tests # Inherit build type environment from ../CMakeList.txt # Adds flags to the compiler command line for sources in the current directory # and below. This command can be used to add any flags, but it was originally # intended to add preprocessor definitions. ADD_DEFINITIONS("-DCPPAD_DOUBLE_SPEED") # Local include directories to search (not in package_prefix/includdir) INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../src ) # add_executable( [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] # source1 source2 ... sourceN # ) SET(source_list ../main.cpp det_lu.cpp det_minor.cpp mat_mul.cpp ode.cpp poly.cpp sparse_hessian.cpp sparse_jacobian.cpp ) set_compile_flags( speed_double "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE( speed_double EXCLUDE_FROM_ALL ${source_list} ) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(speed_double ${cppad_lib} ${colpack_libs} ) # double does not use any external library TARGET_LINK_LIBRARIES(speed_double speed_src ) # check_speed_double add_check_executable(check_speed double "correct 54321") ================================================ FILE: speed/double/det_lu.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin double_det_lu.cpp} Double Speed: Determinant Using Lu Factorization ################################################ Specifications ************** See :ref:`link_det_lu-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ # include # include # include // Note that CppAD uses global_option["memory"] at the main program level # include extern std::map global_option; bool link_det_lu( size_t size , size_t repeat , CppAD::vector &matrix , CppAD::vector &det ) { if(global_option["onetape"]||global_option["atomic"]||global_option["optimize"]) return false; // ----------------------------------------------------- // setup CppAD::det_by_lu Det(size); size_t n = size * size; // number of independent variables // ------------------------------------------------------ while(repeat--) { // get the next matrix CppAD::uniform_01(n, matrix); // computation of the determinant det[0] = Det(matrix); } return true; } /* {xrst_code} {xrst_spell_on} {xrst_end double_det_lu.cpp} */ ================================================ FILE: speed/double/det_minor.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin double_det_minor.cpp} Double Speed: Determinant by Minor Expansion ############################################ Specifications ************** See :ref:`link_det_minor-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ # include # include # include // Note that CppAD uses global_option["memory"] at the main program level # include extern std::map global_option; bool link_det_minor( const std::string& job , size_t size , size_t repeat , CppAD::vector &matrix , CppAD::vector &det ) { // -------------------------------------------------------------------------- // ignore global_option // -------------------------------------------------------------------------- if( job == "setup" || job == "teardown" ) return true; CPPAD_ASSERT_UNKNOWN( job == "run" ); // // setup CppAD::det_by_minor Det(size); // // number of independent variables size_t n = size * size; // ------------------------------------------------------------------------- while(repeat--) { // get the next matrix CppAD::uniform_01(n, matrix); // computation of the determinant det[0] = Det(matrix); } return true; } /* {xrst_code} {xrst_spell_on} {xrst_end double_det_minor.cpp} */ ================================================ FILE: speed/double/mat_mul.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin double_mat_mul.cpp} Double Speed: Matrix Multiplication ################################### Specifications ************** See :ref:`link_mat_mul-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ # include # include # include // Note that CppAD uses global_option["memory"] at the main program level # include extern std::map global_option; bool link_mat_mul( size_t size , size_t repeat , CppAD::vector& x , CppAD::vector& z , CppAD::vector& dz ) { if(global_option["onetape"]||global_option["atomic"]||global_option["optimize"]) return false; // ----------------------------------------------------- size_t n = size * size; // number of independent variables CppAD::vector y(n); while(repeat--) { // get the next matrix CppAD::uniform_01(n, x); // do computation mat_sum_sq(size, x, y, z); } return true; } /* {xrst_code} {xrst_spell_on} {xrst_end double_mat_mul.cpp} */ ================================================ FILE: speed/double/ode.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin double_ode.cpp} Double Speed: Ode Solution ########################## Specifications ************** See :ref:`link_ode-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ # include # include # include # include // Note that CppAD uses global_option["memory"] at the main program level # include extern std::map global_option; bool link_ode( size_t size , size_t repeat , CppAD::vector &x , CppAD::vector &jacobian ) { if(global_option["onetape"]||global_option["atomic"]||global_option["optimize"]) return false; // ------------------------------------------------------------- // setup assert( x.size() == size ); size_t n = size; size_t m = 0; CppAD::vector f(n); while(repeat--) { // choose next x value uniform_01(n, x); // evaluate function CppAD::ode_evaluate(x, m, f); } size_t i; for(i = 0; i < n; i++) jacobian[i] = f[i]; return true; } /* {xrst_code} {xrst_spell_on} {xrst_end double_ode.cpp} */ ================================================ FILE: speed/double/poly.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin double_poly.cpp} Double Speed: Evaluate a Polynomial ################################### Specifications ************** See :ref:`link_poly-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ # include # include // Note that CppAD uses global_option["memory"] at the main program level # include extern std::map global_option; bool link_poly( size_t size , size_t repeat , CppAD::vector &a , // coefficients of polynomial CppAD::vector &z , // polynomial argument value CppAD::vector &p ) // second derivative w.r.t z { if(global_option["onetape"]||global_option["atomic"]||global_option["optimize"]) return false; // ----------------------------------------------------- // setup // ------------------------------------------------------ while(repeat--) { // get the next argument value CppAD::uniform_01(1, z); // evaluate the polynomial at the new argument value p[0] = CppAD::Poly(0, a, z[0]); } return true; } /* {xrst_code} {xrst_spell_on} {xrst_end double_poly.cpp} */ ================================================ FILE: speed/double/sparse_hessian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin double_sparse_hessian.cpp} Double Speed: Sparse Hessian ############################ Specifications ************** See :ref:`link_sparse_hessian-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ # include # include # include // Note that CppAD uses global_option["memory"] at the main program level # include extern std::map global_option; bool link_sparse_hessian( size_t size , size_t repeat , const CppAD::vector& row , const CppAD::vector& col , CppAD::vector& x , CppAD::vector& hessian , size_t& n_color ) { if(global_option["onetape"]||global_option["atomic"]||global_option["optimize"]||global_option["boolsparsity"]) return false; // ----------------------------------------------------- // setup using CppAD::vector; size_t order = 0; // derivative order corresponding to function size_t n = size; // argument space dimension size_t m = 1; // range space dimension vector y(m); // function value // choose a value for x CppAD::uniform_01(n, x); // ------------------------------------------------------ while(repeat--) { // computation of the function CppAD::sparse_hes_fun(n, x, row, col, order, y); } hessian[0] = y[0]; return true; } /* {xrst_code} {xrst_spell_on} {xrst_end double_sparse_hessian.cpp} */ ================================================ FILE: speed/double/sparse_jacobian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin double_sparse_jacobian.cpp} Double Speed: Sparse Jacobian ############################# Specifications ************** See :ref:`link_sparse_jacobian-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ # include # include # include // Note that CppAD uses global_option["memory"] at the main program level # include extern std::map global_option; bool link_sparse_jacobian( const std::string& job , size_t size , size_t repeat , size_t m , const CppAD::vector& row , const CppAD::vector& col , CppAD::vector& x , CppAD::vector& jacobian , size_t& n_color ) { if(global_option["onetape"]||global_option["atomic"]||global_option["optimize"]||global_option["boolsparsity"]) return false; // ----------------------------------------------------- // setup using CppAD::vector; size_t i; size_t order = 0; // order for computing function value size_t n = size; // argument space dimension vector yp(m); // function value yp = f(x) // ------------------------------------------------------ while(repeat--) { // choose a value for x CppAD::uniform_01(n, x); // computation of the function CppAD::sparse_jac_fun(m, n, x, row, col, order, yp); } for(i = 0; i < m; i++) jacobian[i] = yp[i]; return true; } /* {xrst_code} {xrst_spell_on} {xrst_end double_sparse_jacobian.cpp} */ ================================================ FILE: speed/double/speed_double.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin speed_double} Speed Test of Functions in Double ################################# Purpose ******* CppAD has a set of speed tests for just calculating functions (in double precision instead of an AD type). This section links to the source code the function value speed tests. Running Tests ************* To build these speed tests, and run their correctness tests, execute the following commands starting in the :ref:`cmake@Build Directory` : | |tab| ``cd speed/double`` | |tab| ``make check_speed_double VERBOSE`` =1 You can then run the corresponding speed tests with the following command ./ ``speed_double speed`` *seed* where *seed* is a positive integer. See :ref:`speed_main-name` for more options. Contents ******** {xrst_toc_list speed/double/det_minor.cpp speed/double/det_lu.cpp speed/double/mat_mul.cpp speed/double/ode.cpp speed/double/poly.cpp speed/double/sparse_hessian.cpp speed/double/sparse_jacobian.cpp } {xrst_end speed_double} ================================================ FILE: speed/example/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the speed/cppad directory tests # Inherit build type from ../CMakeList.txt # # source_list SET(source_list example.cpp det_by_lu.cpp det_by_minor.cpp det_of_minor.cpp elapsed_seconds.cpp mat_sum_sq.cpp ode_evaluate.cpp sparse_hes_fun.cpp sparse_jac_fun.cpp speed_test.cpp time_test.cpp ) set_compile_flags( speed_example "${cppad_debug_which}" "${source_list}" ) # # speed_example ADD_EXECUTABLE( speed_example EXCLUDE_FROM_ALL ${source_list} ) TARGET_LINK_LIBRARIES(speed_example ${cppad_lib} ${colpack_libs} ) # check_speed_example add_check_executable(check_speed example) # # seed_program set_compile_flags( speed_program "${cppad_debug_which}" speed_program.cpp ) ADD_EXECUTABLE( speed_program EXCLUDE_FROM_ALL speed_program.cpp ) TARGET_LINK_LIBRARIES(speed_program ${cppad_lib} ${colpack_libs} ) # check_speed_program add_check_executable(check_speed program) ================================================ FILE: speed/example/det_by_lu.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin det_by_lu.cpp} Determinant Using Lu Factorization: Example and Test #################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end det_by_lu.cpp} */ // BEGIN C++ # include # include bool det_by_lu() { bool ok = true; double eps99 = 99.0 * std::numeric_limits::epsilon(); // dimension of the matrix size_t n = 3; // construct the determinat object CppAD::det_by_lu Det(n); double a[] = { 1., 2., 3., // a[0] a[1] a[2] 3., 2., 1., // a[3] a[4] a[5] 2., 1., 2. // a[6] a[7] a[8] }; CPPAD_TESTVECTOR(double) A(9); size_t i; for(i = 0; i < 9; i++) A[i] = a[i]; // evaluate the determinant double det = Det(A); double check; check = a[0]*(a[4]*a[8] - a[5]*a[7]) - a[1]*(a[3]*a[8] - a[5]*a[6]) + a[2]*(a[3]*a[7] - a[4]*a[6]); ok = CppAD::NearEqual(det, check, eps99, eps99); return ok; } // END C++ ================================================ FILE: speed/example/det_by_minor.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin det_by_minor.cpp} Determinant Using Expansion by Minors: Example and Test ####################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end det_by_minor.cpp} */ // BEGIN C++ # include # include bool det_by_minor() { bool ok = true; // dimension of the matrix size_t n = 3; // construct the determinat object CppAD::det_by_minor Det(n); double a[] = { 1., 2., 3., // a[0] a[1] a[2] 3., 2., 1., // a[3] a[4] a[5] 2., 1., 2. // a[6] a[7] a[8] }; CPPAD_TESTVECTOR(double) A(9); size_t i; for(i = 0; i < 9; i++) A[i] = a[i]; // evaluate the determinant double det = Det(A); double check; check = a[0]*(a[4]*a[8] - a[5]*a[7]) - a[1]*(a[3]*a[8] - a[5]*a[6]) + a[2]*(a[3]*a[7] - a[4]*a[6]); ok = det == check; return ok; } // END C++ ================================================ FILE: speed/example/det_of_minor.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin det_of_minor.cpp} Determinant of a Minor: Example and Test ######################################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end det_of_minor.cpp} */ // BEGIN C++ # include # include # include bool det_of_minor() { bool ok = true; // // a // values in the matrix A in row major order std::vector a = { 1., 2., 3., 4., 5., 6., 7., 8., 10. }; // // m // dimension of the matrix A size_t m = 3; assert( m * m == a.size() ); // // r, c // index vectors set so minor is the entire matrix A std::vector r(m + 1); std::vector c(m + 1); for(size_t i= 0; i < m; i++) { r[i] = i+1; c[i] = i+1; } r[m] = 0; c[m] = 0; // // n // size of minor that is the entire matrix A size_t n = m; // // det // evaluate the determinant of A double det = CppAD::det_of_minor(a, m, n, r, c); // // ok // check the value of the determinant of A ok &= (det == (double) (1*(5*10-6*8) - 2*(4*10-6*7) + 3*(4*8-5*7)) ); // // M // minor where row 0 and column 1 are removed r[m] = 1; // skip row index 0 by starting at row index 1 c[0] = 2; // skip column index 1 by pointing from index 0 to index 2 n = m - 1; // dimension of the minor M // // det // evaluate determinant of the minor det = CppAD::det_of_minor(a, m, m-1, r, c); // // ok // check the value of the determinant of the minor ok &= (det == (double) (4*10-6*7) ); // return ok; } // END C++ ================================================ FILE: speed/example/elapsed_seconds.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin elapsed_seconds.cpp} Elapsed Seconds: Example and Test ################################# {xrst_literal // BEGIN C++ // END C++ } {xrst_end elapsed_seconds.cpp} */ // BEGIN C++ # include # include # include # include # define CPPAD_DEBUG_ELAPSED_SECONDS 0 bool elapsed_seconds(void) { bool ok = true; double max_diff = 0.; double s0 = CppAD::elapsed_seconds(); double s1 = CppAD::elapsed_seconds(); double s2 = CppAD::elapsed_seconds(); while(s2 - s0 < 1.) { max_diff = std::max(s2 - s1, max_diff); s1 = s2; s2 = CppAD::elapsed_seconds(); } # if CPPAD_DEBUG_ELAPSED_SECONDS std::cout << "max_diff = " << max_diff << std::endl; # endif ok &= 0. < max_diff && max_diff < .04; return ok; } // END C++ ================================================ FILE: speed/example/example.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin speed_example.cpp} Speed Examples and Tests Driver ############################### Running These Tests ******************* After executing the :ref:`cmake-name` command form the :ref:`download@Distribution Directory`, you can build and run these tests with the commands:: cd build make check_speed_example Note that your choice of :ref:`cmake@generator` may require using an different version of make; e.g., ``ninja`` . {xrst_literal // BEGIN C++ // END C++ } {xrst_end speed_example.cpp} ------------------------------------------------------------------------------- */ // BEGIN C++ # include // various example routines extern bool det_of_minor(void); extern bool det_by_lu(void); extern bool det_by_minor(void); extern bool elapsed_seconds(void); extern bool mat_sum_sq(void); extern bool ode_evaluate(void); extern bool sparse_hes_fun(void); extern bool sparse_jac_fun(void); extern bool speed_test(void); extern bool time_test(void); namespace { // function that runs one test size_t Run_ok_count = 0; size_t Run_error_count = 0; const char* exception_list[] = { "elapsed_seconds", "speed_test", "time_test" }; size_t n_exception = sizeof(exception_list) / sizeof(exception_list[0]); bool Run(bool TestOk(void), std::string name) { bool ok = true; std::streamsize width = 20; std::cout.width( width ); std::cout.setf( std::ios_base::left ); std::cout << name; bool exception = false; for(size_t i = 0; i < n_exception; i++) exception |= exception_list[i] == name; // ok &= name.size() < size_t(width); ok &= TestOk(); if( ok ) { std::cout << "OK" << std::endl; Run_ok_count++; } else if ( exception ) { std::cout << "Error: perhaps too many other programs running"; std::cout << std::endl; // no change to Run_ok_count ok = true; } else { std::cout << "Error" << std::endl; Run_error_count++; } return ok; } } // main program that runs all the tests int main(void) { bool ok = true; using std::cout; using std::endl; // This line used by test_one.sh ok &= Run(det_of_minor, "det_of_minor" ); ok &= Run(det_by_minor, "det_by_minor" ); ok &= Run(det_by_lu, "det_by_lu" ); ok &= Run(elapsed_seconds, "elapsed_seconds" ); ok &= Run(mat_sum_sq, "mat_sum_sq" ); ok &= Run(ode_evaluate, "ode_evaluate" ); ok &= Run(sparse_hes_fun, "sparse_hes_fun" ); ok &= Run(sparse_jac_fun, "sparse_jac_fun" ); ok &= Run(speed_test, "speed_test" ); ok &= Run(time_test, "time_test" ); assert( ok || (Run_error_count > 0) ); // check for memory leak in previous calculations if( ! CppAD::thread_alloc::free_all() ) { ok = false; cout << "Error: memory leak detected" << endl; } if( ok ) { cout << "Check above to see if all " << int(Run_ok_count) << " tests passed.\n"; cout << "possible exceptions are: " << exception_list[0]; for(size_t i = 1; i < n_exception; ++i) cout << ", " << exception_list[i]; cout << endl; } else cout << int(Run_error_count) << " tests failed."; cout << endl; if(ok) return EXIT_SUCCESS; else return EXIT_FAILURE; } // END C++ ================================================ FILE: speed/example/mat_sum_sq.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin mat_sum_sq.cpp} Sum of the Elements of the Square of a Matrix: Example and Test ############################################################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end mat_sum_sq.cpp} */ // BEGIN C++ # include # include # include # include bool mat_sum_sq() { bool ok = true; double x_00, x_01, x_10, x_11, check; // dimension of the matrices x, y, and the result z size_t n = 2; CppAD::vector x(n * n), y(n * n), z(1); // x = [ 1 2 ; 3 4 ] x[0] = x_00 = 1.; x[1] = x_01 = 2.; x[2] = x_10 = 3.; x[3] = x_11 = 4.; // compute y = x * x and z = sum of elements in y CppAD::mat_sum_sq(n, x, y, z); // check y_00 check = x_00 * x_00 + x_01 * x_10; ok &= (check == y[0]); // check y_01 check = x_00 * x_01 + x_01 * x_11; ok &= (check == y[1]); // check y_10 check = x_10 * x_00 + x_11 * x_10; ok &= (check == y[2]); // check y_11 check = x_10 * x_01 + x_11 * x_11; ok &= (check == y[3]); // check z check = y[0] + y[1] + y[2] + y[3]; ok &= (check == z[0]); return ok; } // END C++ ================================================ FILE: speed/example/ode_evaluate.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin ode_evaluate.cpp} ode_evaluate: Example and test ############################## {xrst_literal // BEGIN C++ // END C++ } {xrst_end ode_evaluate.cpp} */ // BEGIN C++ # include # include # include bool ode_evaluate(void) { using CppAD::NearEqual; using CppAD::AD; bool ok = true; size_t n = 3; CppAD::vector x(n); CppAD::vector ym(n * n); CppAD::vector< AD > X(n); CppAD::vector< AD > Ym(n); // choose x size_t j; for(j = 0; j < n; j++) { x[j] = double(j + 1); X[j] = x[j]; } // declare independent variables Independent(X); // evaluate function size_t m = 0; CppAD::ode_evaluate(X, m, Ym); // evaluate derivative m = 1; CppAD::ode_evaluate(x, m, ym); // use AD to evaluate derivative CppAD::ADFun F(X, Ym); CppAD::vector dy(n * n); dy = F.Jacobian(x); size_t k; for(k = 0; k < n * n; k++) ok &= NearEqual(ym[k], dy[k] , 1e-7, 1e-7); return ok; } // END C++ ================================================ FILE: speed/example/sparse_hes_fun.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sparse_hes_fun.cpp} sparse_hes_fun: Example and test ################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end sparse_hes_fun.cpp} */ // BEGIN C++ # include # include # include bool sparse_hes_fun(void) { using CppAD::NearEqual; bool ok = true; typedef CppAD::AD ADScalar; size_t j, k; double eps = 10. * CppAD::numeric_limits::epsilon(); size_t n = 5; size_t m = 1; size_t K = 2 * n; CppAD::vector row(K), col(K); CppAD::vector x(n), ypp(K); CppAD::vector a_x(n), a_y(m); // choose x for(j = 0; j < n; j++) a_x[j] = x[j] = double(j + 1); // choose row, col for(k = 0; k < K; k++) { row[k] = k % 3; col[k] = k / 3; } for(k = 0; k < K; k++) { for(size_t k1 = 0; k1 < K; k1++) assert( k == k1 || row[k] != row[k1] || col[k] != col[k1] ); } // declare independent variables Independent(a_x); // evaluate function size_t order = 0; CppAD::sparse_hes_fun(n, a_x, row, col, order, a_y); // evaluate Hessian order = 2; CppAD::sparse_hes_fun(n, x, row, col, order, ypp); // use AD to evaluate Hessian CppAD::ADFun f(a_x, a_y); CppAD::vector hes(n * n); // compoute Hessian of f_0 (x) hes = f.Hessian(x, 0); for(k = 0; k < K; k++) { size_t index = row[k] * n + col[k]; ok &= NearEqual(hes[index], ypp[k] , eps, eps); } return ok; } // END C++ ================================================ FILE: speed/example/sparse_jac_fun.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sparse_jac_fun.cpp} sparse_jac_fun: Example and test ################################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end sparse_jac_fun.cpp} */ // BEGIN C++ # include # include # include bool sparse_jac_fun(void) { using CppAD::NearEqual; using CppAD::AD; bool ok = true; size_t j, k; double eps = CppAD::numeric_limits::epsilon(); size_t n = 3; size_t m = 4; size_t K = 5; CppAD::vector row(K), col(K); CppAD::vector x(n), yp(K); CppAD::vector< AD > a_x(n), a_y(m); // choose x for(j = 0; j < n; j++) a_x[j] = x[j] = double(j + 1); // choose row, col for(k = 0; k < K; k++) { row[k] = k % m; col[k] = (K - k) % n; } // declare independent variables Independent(a_x); // evaluate function size_t order = 0; CppAD::sparse_jac_fun< AD >(m, n, a_x, row, col, order, a_y); // evaluate derivative order = 1; CppAD::sparse_jac_fun(m, n, x, row, col, order, yp); // use AD to evaluate derivative CppAD::ADFun f(a_x, a_y); CppAD::vector jac(m * n); jac = f.Jacobian(x); for(k = 0; k < K; k++) { size_t index = row[k] * n + col[k]; ok &= NearEqual(jac[index], yp[k] , eps, eps); } return ok; } // END C++ ================================================ FILE: speed/example/speed_program.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin speed_program.cpp} Example Use of SpeedTest ######################## Program ******* {xrst_spell_off} {xrst_code cpp} */ # include // Some compilers have gotten smarter and do not calculate something that is not used // so make the result a global. double global_result = 0.0; std::string Test(size_t size, size_t repeat) { // setup double *a = new double[size]; double *b = new double[size]; size_t i = size;; while(i) { --i; a[i] = double(i); b[i] = double(2 * i); } // operations we are timing while(repeat--) { i = size;; while(i) { --i; global_result += a[i] * b[i]; } } // teardown delete [] a; delete [] b; // return a test name that is valid for all sizes and repeats return "double: result = sum_i a[i] * b[i]"; } int main(void) { CppAD::SpeedTest(Test, 20, 20, 100); return 0; } /* {xrst_code} {xrst_spell_on} Output ****** Executing of the program above generated the following output (the rates will be different for each particular system): :: double: c[*] = a[*] + b[*] size = 20 rate = 7,157,515 size = 40 rate = 3,887,214 size = 60 rate = 2,685,214 size = 80 rate = 2,032,124 size = 100 rate = 1,657,828 {xrst_end speed_program.cpp} */ ================================================ FILE: speed/example/speed_test.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin speed_test.cpp} speed_test: Example and test ############################ {xrst_literal // BEGIN C++ // END C++ } {xrst_end speed_test.cpp} */ // BEGIN C++ # include # include namespace { // empty namespace using CppAD::vector; vector a, b, c; void test(size_t size, size_t repeat) { // setup a.resize(size); b.resize(size); c.resize(size); size_t i = size;; while(i) { --i; a[i] = double(i); b[i] = double(2 * i); c[i] = 0.0; } // operations we are timing while(repeat--) { i = size;; while(i) { --i; c[i] += std::sqrt(a[i] * a[i] + b[i] * b[i]); } } } } bool speed_test(void) { bool ok = true; // size of the test cases vector size_vec(2); size_vec[0] = 40; size_vec[1] = 80; // minimum amount of time to run test double time_min = 0.5; // run the test cases vector rate_vec(2); rate_vec = CppAD::speed_test(test, size_vec, time_min); // time per repeat loop (note counting setup or teardown) double time_0 = 1. / double(rate_vec[0]); double time_1 = 1. / double(rate_vec[1]); // for this case, time should be linear w.r.t size double check = double(size_vec[1]) * time_0 / double(size_vec[0]); double rel_diff = (check - time_1) / time_1; ok &= (std::fabs(rel_diff) <= .1); if( ! ok ) std::cout << std::endl << "rel_diff = " << rel_diff << std::endl; a.clear(); b.clear(); c.clear(); return ok; } // END C++ ================================================ FILE: speed/example/time_test.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin time_test.cpp} time_test: Example and test ########################### {xrst_literal // BEGIN C++ // END C++ } {xrst_end time_test.cpp} */ // BEGIN C++ # include # include namespace { // empty namespace using CppAD::vector; // used to check size size_t size_; // used to check repeat size_t repeat_; vector a, b, c; void test(size_t size, size_t repeat) { // used for check size_ = size; repeat_ = repeat; // setup a.resize(size); b.resize(size); c.resize(size); size_t i = size;; while(i) { --i; a[i] = float(i); b[i] = float(2 * i); c[i] = 0.0; } // operations we are timing while(repeat--) { i = size;; while(i) { --i; c[i] += std::sqrt(a[i] * a[i] + b[i] * b[i]); } } } } bool time_test(void) { bool ok = true; using CppAD::time_test; // minimum amount of time to run test double time_min = 0.5; // size of first test case size_t test_size = 20; // run the first test case size_t repeat_first; double time_first = time_test(test, time_min, test_size, repeat_first); ok &= size_ == test_size; ok &= repeat_ == repeat_first; ok &= time_min <= double(repeat_first) * time_first; // size of second test case is twice as large test_size = 2 * test_size; // run the second test case size_t repeat_second; double time_second = time_test(test, time_min, test_size, repeat_second); ok &= size_ == test_size; ok &= repeat_ == repeat_second; ok &= time_min <= double(repeat_second) * time_second; // test above should hold without exception, one below might not assert( ok ); // for this case, time should be linear w.r.t size double rel_diff = 1. - 2. * time_first / time_second; ok &= (std::fabs(rel_diff) <= .1); if( ! ok ) std::cout << std::endl << "rel_diff = " << rel_diff << std::endl; a.clear(); b.clear(); c.clear(); return ok; } // END C++ ================================================ FILE: speed/fadbad/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the speed/fadbad directory tests # Inherit build type from ../CMakeList.txt # assert fadbad_prefix is defined assert ( fadbad_prefix ) # Adds flags to the compiler command line for sources in the current directory # and below. This command can be used to add any flags, but it was originally # intended to add preprocessor definitions. ADD_DEFINITIONS("-DCPPAD_FADBAD_SPEED") # Local include directories to search (not in package_prefix/includdir) INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../src ) # add_executable( [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] # source1 source2 ... sourceN # ) SET(source_list ../main.cpp det_lu.cpp det_minor.cpp mat_mul.cpp ode.cpp poly.cpp sparse_hessian.cpp sparse_jacobian.cpp ) set_compile_flags( speed_fadbad "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE( speed_fadbad EXCLUDE_FROM_ALL ${source_list} ) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(speed_fadbad ${cppad_lib} ${colpack_libs} ) # fadbad is an include file only library TARGET_LINK_LIBRARIES(speed_fadbad speed_src ) # check_speed_fadbad add_check_executable(check_speed fadbad "correct 54321") ================================================ FILE: speed/fadbad/det_lu.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin fadbad_det_lu.cpp} Fadbad Speed: Gradient of Determinant Using Lu Factorization ############################################################ Specifications ************** See :ref:`link_det_lu-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ // suppress conversion warnings before other includes # include // # include # include # include # include // list of possible options # include extern std::map global_option; bool link_det_lu( size_t size , size_t repeat , CppAD::vector &matrix , CppAD::vector &gradient ) { // speed test global option values if( global_option["onetape"] || global_option["atomic"] ) return false; if( global_option["memory"] || global_option["optimize"] ) return false; // ----------------------------------------------------- // setup // // object for computing determinant typedef fadbad::B ADScalar; typedef CppAD::vector ADVector; CppAD::det_by_lu Det(size); size_t i; // temporary index size_t m = 1; // number of dependent variables size_t n = size * size; // number of independent variables ADScalar detA; // AD value of the determinant ADVector A(n); // AD version of matrix // ------------------------------------------------------ while(repeat--) { // get the next matrix CppAD::uniform_01(n, matrix); // set independent variable values for(i = 0; i < n; i++) A[i] = matrix[i]; // compute the determinant detA = Det(A); // create function object f : A -> detA detA.diff(0, (unsigned int) m); // index 0 of m dependent variables // evaluate and return gradient using reverse mode for(i =0; i < n; i++) gradient[i] = A[i].d(0); // partial detA w.r.t A[i] } // --------------------------------------------------------- return true; } /* {xrst_code} {xrst_spell_on} {xrst_end fadbad_det_lu.cpp} */ ================================================ FILE: speed/fadbad/det_minor.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin fadbad_det_minor.cpp} Fadbad Speed: Gradient of Determinant by Minor Expansion ######################################################## Specifications ************** See :ref:`link_det_minor-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ // suppress conversion warnings before other includes # include // # include # include # include # include // list of possible options # include extern std::map global_option; bool link_det_minor( const std::string& job , size_t size , size_t repeat , CppAD::vector &matrix , CppAD::vector &gradient ) { // -------------------------------------------------------------------- // check not global options typedef std::map::iterator iterator; for(iterator itr=global_option.begin(); itr!=global_option.end(); ++itr) { if( itr->second ) return false; } // ----------------------------------------------------- // AD types typedef fadbad::B b_double; typedef CppAD::vector b_vector; // object that computes the determinant CppAD::det_by_minor b_det(size); // number of dependent variables unsigned int m = 1; // number of independent variables size_t n = size * size; // independent variable vector b_vector b_A(n); // AD value of the determinant b_double b_detA; // ------------------------------------------------------ while(repeat--) { // get the next matrix CppAD::uniform_01(n, matrix); // set independent variable values for(size_t j = 0; j < n; j++) b_A[j] = matrix[j]; // compute the determinant b_detA = b_det(b_A); // create function object f : A -> detA b_detA.diff(0, m); // index 0 of m dependent variables // evaluate and return gradient using reverse mode for(size_t j = 0; j < n; j++) gradient[j] = b_A[j].d(0); // partial detA w.r.t A[i] } // --------------------------------------------------------- return true; } /* {xrst_code} {xrst_spell_on} {xrst_end fadbad_det_minor.cpp} */ ================================================ FILE: speed/fadbad/mat_mul.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin fadbad_mat_mul.cpp} Fadbad Speed: Matrix Multiplication ################################### Specifications ************** See :ref:`link_mat_mul-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ // suppress conversion warnings before other includes # include // # include # include # include # include // list of possible options # include extern std::map global_option; bool link_mat_mul( size_t size , size_t repeat , CppAD::vector& x , CppAD::vector& z , CppAD::vector& dz ) { // speed test global option values if( global_option["memory"] || global_option["onetape"] || global_option["atomic"] || global_option["optimize"] ) return false; // The correctness check for this test is failing, so abort (for now). return false; // ----------------------------------------------------- // setup // object for computing determinant typedef fadbad::B ADScalar; typedef CppAD::vector ADVector; size_t j; // temporary index size_t m = 1; // number of dependent variables size_t n = size * size; // number of independent variables ADVector X(n); // AD domain space vector ADVector Y(n); // Store product matrix ADVector Z(m); // AD range space vector // ------------------------------------------------------ while(repeat--) { // get the next matrix CppAD::uniform_01(n, x); // set independent variable values for(j = 0; j < n; j++) X[j] = x[j]; // do the computation mat_sum_sq(size, X, Y, Z); // create function object f : X -> Z Z[0].diff(0, m); // index 0 of m dependent variables // evaluate and return gradient using reverse mode for(j = 0; j < n; j++) dz[j] = X[j].d(0); // partial Z[0] w.r.t X[j] } // return function value z[0] = Z[0].x(); // --------------------------------------------------------- return true; } /* {xrst_code} {xrst_spell_on} {xrst_end fadbad_mat_mul.cpp} */ ================================================ FILE: speed/fadbad/ode.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin fadbad_ode.cpp} Fadbad Speed: Ode ################# Specifications ************** See :ref:`link_ode-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ // suppress conversion warnings before other includes # include // # include # include # include # include # include # include // list of possible options # include extern std::map global_option; namespace fadbad { // define fabs for use by ode_evaluate fadbad::F fabs(const fadbad::F& x) { return std::max(-x, x); } } bool link_ode( size_t size , size_t repeat , CppAD::vector &x , CppAD::vector &jacobian ) { // speed test global option values if( global_option["atomic"] ) return false; if( global_option["memory"] || global_option["onetape"] || global_option["optimize"] ) return false; // ------------------------------------------------------------- // setup assert( x.size() == size ); assert( jacobian.size() == size * size ); typedef fadbad::F ADScalar; typedef CppAD::vector ADVector; size_t i, j; size_t p = 0; // use ode to calculate function values size_t n = size; // number of independent variables size_t m = n; // number of dependent variables ADVector X(n), Y(m); // independent and dependent variables // ------------------------------------------------------------- while(repeat--) { // choose next x value CppAD::uniform_01(n, x); for(j = 0; j < n; j++) { // set value of x[j] X[j] = x[j]; // set up for X as the independent variable vector X[j].diff((unsigned int) j, (unsigned int) n); } // evaluate function CppAD::ode_evaluate(X, p, Y); // return values with Y as the dependent variable vector for(i = 0; i < m; i++) { for(j = 0; j < n; j++) jacobian[ i * n + j ] = Y[i].d((unsigned int) j); } } return true; } /* {xrst_code} {xrst_spell_on} {xrst_end fadbad_ode.cpp} */ ================================================ FILE: speed/fadbad/poly.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin fadbad_poly.cpp} Fadbad Speed: Second Derivative of a Polynomial ############################################### Specifications ************** See :ref:`link_poly-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ # include # include # include # include // list of possible options # include extern std::map global_option; bool link_poly( size_t size , size_t repeat , CppAD::vector &a , // coefficients of polynomial CppAD::vector &z , // polynomial argument value CppAD::vector &ddp ) // second derivative w.r.t z { if( global_option["atomic"] ) return false; if( global_option["memory"] || global_option["onetape"] || global_option["optimize"] ) return false; // ----------------------------------------------------- // setup size_t i; // temporary index fadbad::T Z; // domain space AD value fadbad::T P; // range space AD value // choose the polynomial coefficients CppAD::uniform_01(size, a); // AD copy of the polynomial coefficients CppAD::vector< fadbad::T > A(size); for(i = 0; i < size; i++) A[i] = a[i]; // ------------------------------------------------------ while(repeat--) { // get the next argument value CppAD::uniform_01(1, z); // independent variable value Z = z[0]; // argument value Z[1] = 1; // argument first order Taylor coefficient // AD computation of the dependent variable P = CppAD::Poly(0, A, Z); // Taylor-expand P to degree one P.eval(2); // second derivative is twice second order Taylor coefficient ddp[0] = 2. * P[2]; // Free DAG corresponding to P does not seem to improve speed. // Probably because it gets freed the next time P is assigned. // P.reset(); } // ------------------------------------------------------ return true; } /* {xrst_code} {xrst_spell_on} {xrst_end fadbad_poly.cpp} */ ================================================ FILE: speed/fadbad/sparse_hessian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include /* {xrst_begin fadbad_sparse_hessian.cpp} Fadbad Speed: Sparse Hessian ############################ {xrst_spell_off} {xrst_code cpp} */ // A fadbad version of this test is not yet available bool link_sparse_hessian( size_t size , size_t repeat , const CppAD::vector& row , const CppAD::vector& col , CppAD::vector& x , CppAD::vector& hessian , size_t& n_color ) { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end fadbad_sparse_hessian.cpp} */ ================================================ FILE: speed/fadbad/sparse_jacobian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include /* {xrst_begin fadbad_sparse_jacobian.cpp} Fadbad Speed: sparse_jacobian ############################# {xrst_spell_off} {xrst_code cpp} */ // A fadbad version of this test is not yet available bool link_sparse_jacobian( const std::string& job , size_t size , size_t repeat , size_t m , const CppAD::vector& row , const CppAD::vector& col , CppAD::vector& x , CppAD::vector& jacobian , size_t& n_color ) { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end fadbad_sparse_jacobian.cpp} */ ================================================ FILE: speed/fadbad/speed_fadbad.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin speed_fadbad} Speed Test Derivatives Using Fadbad ################################### Purpose ******* CppAD has a set of speed tests that are used to compare Fadbad with other AD packages. This section links to the source code the Fadbad speed tests (any suggestions to make the Fadbad results faster are welcome). fadbad_prefix ************* To run these tests, you must include the :ref:`fadbad_prefix-name` in you :ref:`cmake@CMake Command` . Running Tests ************* To build these speed tests, and run their correctness tests, execute the following commands starting in the :ref:`cmake@Build Directory` : | |tab| ``cd speed/fadbad`` | |tab| ``make check_speed_fadbad VERBOSE`` =1 You can then run the corresponding speed tests with the following command ./ ``speed_fadbad speed`` *seed* where *seed* is a positive integer. See :ref:`speed_main-name` for more options. Contents ******** {xrst_toc_list speed/fadbad/det_minor.cpp speed/fadbad/det_lu.cpp speed/fadbad/mat_mul.cpp speed/fadbad/ode.cpp speed/fadbad/poly.cpp speed/fadbad/sparse_hessian.cpp speed/fadbad/sparse_jacobian.cpp } {xrst_end speed_fadbad} ================================================ FILE: speed/main.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include # include # include # include # include # include # include # include # include # include # ifdef CPPAD_ADOLC_SPEED # define AD_PACKAGE "adolc" # endif # ifdef CPPAD_CPPAD_SPEED # define AD_PACKAGE "cppad" # endif # ifdef CPPAD_DOUBLE_SPEED # define AD_PACKAGE "double" # endif # ifdef CPPAD_FADBAD_SPEED # define AD_PACKAGE "fadbad" # endif # ifdef CPPAD_PROFILE_SPEED # define AD_PACKAGE "profile" # endif # ifdef CPPAD_SACADO_SPEED # define AD_PACKAGE "sacado" # endif # ifdef CPPAD_CPPADCG_SPEED # define AD_PACKAGE "cppadcg" # endif # ifdef CPPAD_CPPAD_JIT_SPEED # define AD_PACKAGE "cppad_jit" # endif # ifdef CPPAD_XPACKAGE_SPEED # define AD_PACKAGE "xpackage" # endif /* {xrst_begin speed_main} {xrst_spell boolsparsity onetape optionlist retaped revsparsity subgraphs subsparsity underbar } Running the Speed Test Program ############################## Syntax ****** | ``speed/`` *package* / ``speed_`` *package* *test* *seed* *option_list* Purpose ******* A version of this program runs the correctness tests or the speed tests for one AD package identified by *package* . package ******* AD Package ========== The command line argument *package* specifies one of the AD package. The CppAD distribution comes with support for the following packages: :ref:`adolc` , :ref:`cppad` , :ref:`fadbad` , :ref:`sacado` , :ref:`cppadcg` . You can extend this program to include other package; see :ref:`speed_xpackage-name` . double ====== The value *package* can be ``double`` in which case the function values (instead of derivatives) are computed using double precision operations. This enables one to compare the speed of computing function values in ``double`` to the speed of the derivative computations. (It is often useful to divide the speed of the derivative computation by the speed of the function evaluation in ``double`` .) profile ======= In the special case where *package* is ``profile`` , the CppAD package is compiled and run with profiling to aid in determining where it is spending most of its time. test **** The argument *test* specifies which test to run and has the following possible values: :ref:`speed_main@test@correct` , :ref:`speed_main@test@speed` , :ref:`det_minor` , :ref:`det_lu` , :ref:`mat_mul` , :ref:`ode` , :ref:`poly` , :ref:`sparse_hessian` , :ref:`sparse_jacobian` . You can experiment with changing the implementation of a particular test for a particular package. correct ======= If *test* is equal to ``correct`` , all of the correctness tests are run. speed ===== If *test* is equal to ``speed`` , all of the speed tests are run. seed **** The command line argument *seed* is an unsigned integer (all its characters are between 0 and 9). The random number simulator :ref:`uniform_01-name` is initialized with the call ``uniform_01`` ( *seed* ) before any of the testing routines (listed above) are called. Global Options ************** This global variable has prototype {xrst_spell_off} {xrst_code cpp} extern std::map global_option; {xrst_code} {xrst_spell_on} The syntax ``global_option`` [" *option* "] has the value true, if *option* is present, and false otherwise. This is true for each option that follows *seed* . The order of the options does not matter and the list can be empty. Each option, is be a separate command line argument to the main program. The documentation below specifics how the :ref:`speed_cppad-name` program uses these options. It is the intention that other packages use each option in a similar way or make it invalid. The implementation of each test should check that the option setting are valid for that test and if not it should return false; for example, see the source code for :ref:`adolc_sparse_hessian.cpp-name` . onetape ======= If this option is present, :ref:`speed_cppad-name` will use one taping of the operation sequence for all the repetitions of that speed test. Otherwise, the :ref:`operation sequence` will be retaped for each test repetition. All of the tests, except :ref:`det_lu` , have the same operation sequence for each repetition. The operation sequence for ``det_lu`` may be different because it depends on the matrix for which the determinant is being calculated. For this reason, :ref:`cppad_det_lu.cpp-name` returns false, to indicate that the test not implemented, when ``global_onetape`` is true. memory ====== This option is special because individual CppAD speed tests need not do anything different if this option is true or false. If the ``memory`` option is present, the CppAD :ref:`hold_memory` routine will be called by the speed test main program before any of the tests are executed This should make the CppAD ``thread_alloc`` allocator faster. If it is not present, CppAD will used standard memory allocation. Another package might use this option for a different memory allocation method. optimize ======== If this option is present, CppAD will :ref:`optimize-name` the operation sequence before doing computations. If it is false, this optimization will not be done. Note that this option is usually slower unless it is combined with the ``onetape`` option. val_graph ========= If this option and optimize are present, CppAD will add the :code:`optimize@options@val_graph` option to the optimization of the operation sequence. atomic ====== If this option is present, CppAD will use a user defined :ref:`atomic` operation is used for the test. So far, CppAD has only implemented the :ref:`mat_mul` test as an atomic operation. hes2jac ======= If this option is present, :ref:`speed_cppad-name` will compute hessians as the Jacobian of the gradient. This is accomplished using :ref:`multiple levels` of AD. So far, CppAD has only implemented the :ref:`sparse_hessian` test in this manner. subgraph ======== If this option is present, :ref:`speed_cppad-name` will compute sparse Jacobians using subgraphs. The CppAD :ref:`sparse_jacobian` test is implemented for this option. In addition, the CppAD :ref:`sparse_hessian` test is implemented for this option when ``hes2jac`` is present. Sparsity Options **************** The following options only apply to the :ref:`sparse_jacobian` and :ref:`sparse_hessian` tests. The other tests return false when any of these options are present. boolsparsity ============ If this option is present, CppAD will use a :ref:`vectors of bool` to compute sparsity patterns. Otherwise CppAD will use :ref:`vectors of sets` . revsparsity =========== If this option is present, CppAD will use reverse mode for to compute sparsity patterns. Otherwise CppAD will use forward mode. subsparsity =========== If this option is present, CppAD will use subgraphs to compute sparsity patterns. If ``boolsparsity`` , ``revsparsity`` , or ``colpack`` is also present, the CppAD speed tests will return false; i.e., these options are not supported by :ref:`subgraph_sparsity-name` . colpack ======= If this option is present, CppAD will use :ref:`colpack` to do the coloring. Otherwise, it will use it's own coloring algorithm. symmetric ========= If this option is present, CppAD will use a symmetric :ref:`coloring method` for computing Hessian sparsity patterns. Otherwise, it will use a general coloring method. The CppAD :ref:`sparse_hessian` test is implemented for this option. Correctness Results ******************* One, but not both, of the following two output lines | |tab| *package* _ *test* _ *optionlist* _ ``available`` = ``false`` | |tab| *package* _ *test* _ *optionlist* _ ``ok`` = *flag* is generated for each correctness test where *package* and *test* are as above, *optionlist* are the options (in *option_list* ) separated by the underbar ``_`` character (whereas they are separated by spaces in *option_list* ), and *flag* is ``true`` or ``false`` . Speed Results ************* For each speed test, corresponds to three lines of the following form are generated: | |tab| *package* _ *test* _ *optionlist* _ ``ok`` = *flag* | |tab| *test* _ ``size`` = [ *size_1* , ..., *size_n* ] | |tab| *package* _ *test* _ ``rate`` = [ *rate_1* , ..., *rate_n* ] The values *package* , *test* , *optionlist* , and *flag* are as in the correctness results above. The values *size_1* , ..., *size_n* are the size arguments used for the corresponding tests. The values *rate_1* , ..., *rate_n* are the number of times per second that the corresponding size problem executed. n_color ======= The :ref:`sparse_jacobian` and :ref:`sparse_hessian` tests has an extra output line with the following form *package* _ ``sparse_`` *test* _ ``n_color`` = [ *n_color_1* , ..., *n_color_n* ] were *test* is ``jacobian`` (``hessian`` ). The values *n_color_1* , ..., *n_color_n* are the number of colors used for each sparse Jacobian (Hessian) calculation; see *n_color* for :ref:`sparse_jac` and *n_sweep* for :ref:`sparse_hessian` . {xrst_toc_hidden speed/src/link.xrst } Link Routines ************* Each :ref:`speed_main@package` defines it's own version of one of the :ref:`link_routines-name` listed below. Each of these routines links this main program to the corresponding test: .. csv-table:: :widths: auto link_det_lu,:ref:`link_det_lu-title` link_det_minor,:ref:`link_det_minor-title` link_mat_mul,:ref:`link_mat_mul-title` link_ode,:ref:`link_ode-title` link_poly,:ref:`link_poly-title` link_sparse_hessian,:ref:`link_sparse_hessian-title` link_sparse_jacobian,:ref:`link_sparse_jacobian-title` {xrst_end speed_main} ----------------------------------------------------------------------------- */ // external routines # define CPPAD_DECLARE_TIME(name) \ extern bool available_##name(void); \ extern bool correct_##name(bool is_package_double); \ extern double time_##name(double time_min, size_t size) CPPAD_DECLARE_TIME(det_lu); CPPAD_DECLARE_TIME(det_minor); CPPAD_DECLARE_TIME(mat_mul); CPPAD_DECLARE_TIME(ode); CPPAD_DECLARE_TIME(poly); CPPAD_DECLARE_TIME(sparse_hessian); CPPAD_DECLARE_TIME(sparse_jacobian); // // some routines defined in src subdirectory extern void info_sparse_jacobian( CppAD::vector& size_vec , CppAD::vector& n_color_vec ); extern void info_sparse_hessian(size_t size, size_t& n_color); // // -------------------------------------------------------------------------- std::map global_option; // // If return value for the previous CppAD speed test was false, this is zero. // Otherwise it is value returned by CppAD::thread_alloc::inuse for the // current thread at end of the test. size_t global_cppad_thread_alloc_inuse = 0; // // This is the value of seed in the main program command line. // It can be used by the sparse matrix routines to reset the random generator // so same sparsity pattern is obtained during source generation and usage. size_t global_seed= 0; // // -------------------------------------------------------------------------- namespace { using std::cout; using std::cerr; using std::endl; const char* option_list[] = { "memory", "onetape", "optimize", "atomic", "hes2jac", "subgraph", "boolsparsity", "revsparsity", "subsparsity", "colpack", "symmetric", "val_graph" }; size_t num_option = sizeof(option_list) / sizeof( option_list[0] ); // ---------------------------------------------------------------- // not available test message void not_available_message(const char* test_name) { cout << AD_PACKAGE << ": " << test_name; cout << " is not available with " << endl; int max_len = 0; for(size_t i = 0; i < num_option; i++) { int len = int( std::strlen( option_list[i] ) ); max_len = std::max( max_len, len); } for(size_t i = 0; i < num_option; i++) { std::string option = option_list[i]; if( global_option[option] ) cout << std::setw(max_len + 1) << option << " = true\n"; else cout << std::setw(max_len + 1) << option << " = false\n"; } } // ------------------------------------------------------ // output vector in form readable by octave or matlab // convert size_t to int to avoid warning by MS compiler void output(const CppAD::vector &v) { size_t i= 0, n = v.size(); cout << "[ "; while(i < n) { cout << int(v[i++]); if( i < n ) cout << ", "; } cout << " ]"; } // ---------------------------------------------------------------- // function that runs one correctness case static size_t Run_ok_count = 0; static size_t Run_error_count = 0; bool run_correct( bool available_case(void) , bool correct_case(bool) , const char *case_name ) { bool available = available_case(); bool ok = true; if( available ) { # ifdef CPPAD_DOUBLE_SPEED bool is_package_double = true; # else bool is_package_double = false; # endif ok = correct_case(is_package_double); } cout << AD_PACKAGE << "_" << case_name; for(size_t i = 0; i < num_option; i++) { std::string option = option_list[i]; if( global_option[option] ) cout << "_" << option; } if( ! available ) { cout << "_available = false" << endl; return ok; } cout << "_correct = "; if( ok ) { cout << " true" << endl; Run_ok_count++; } else { cout << " false" << endl; Run_error_count++; } return ok; } // ---------------------------------------------------------------- // function that runs one speed case void run_speed( double time_case(double time_min, size_t size) , const CppAD::vector& size_vec , const std::string& case_name ) { double time_min = 1.; cout << case_name << "_size = "; output(size_vec); cout << endl; cout << AD_PACKAGE << "_" << case_name << "_rate = "; cout << std::fixed; for(size_t i = 0; i < size_vec.size(); i++) { if( i == 0 ) cout << "[ "; else cout << ", "; cout << std::flush; size_t size = size_vec[i]; double time = time_case(time_min, size); double rate = 1. / time; if( rate >= 1000 ) cout << std::setprecision(0) << rate; else if( rate >= 10 ) cout << std::setprecision(2) << rate; else cout << std::setprecision(4) << rate; } cout << " ]" << endl; // return; } } // main program that runs all the tests int main(int argc, char *argv[]) { bool ok = true; enum test_enum { test_correct, test_speed, test_det_lu, test_det_minor, test_mat_mul, test_ode, test_poly, test_sparse_hessian, test_sparse_jacobian, test_error }; struct test_struct { const char *name; const test_enum index; }; const test_struct test_list[]= { { "correct", test_correct }, { "speed", test_speed }, { "det_lu", test_det_lu }, { "det_minor", test_det_minor }, { "mat_mul", test_mat_mul }, { "ode", test_ode }, { "poly", test_poly }, { "sparse_hessian", test_sparse_hessian }, { "sparse_jacobian", test_sparse_jacobian } }; const size_t n_test = sizeof(test_list) / sizeof(test_list[0]); // test_enum match = test_error; int iseed = 0; bool error = argc < 3; if( ! error ) { for(size_t i = 0; i < n_test; i++) if( strcmp(test_list[i].name, argv[1]) == 0 ) match = test_list[i].index; error = match == test_error; for(size_t i = 0; *(argv[2] + i) != '\0'; ++i) { error |= *(argv[2] + i) < '0'; error |= '9' < *(argv[2] + i); } iseed = std::atoi( argv[2] ); error |= iseed < 0; for(size_t i = 0; i < num_option; i++) global_option[ option_list[i] ] = false; for(size_t i = 3; i < size_t(argc); i++) { bool found = false; for(size_t j = 0; j < num_option; j++) { if( strcmp(argv[i], option_list[j]) == 0 ) { global_option[ option_list[j] ] = true; found = true; } } error |= ! found; } } if( error ) { cout << "usage: ./speed_" << AD_PACKAGE << " test seed option_list" << endl; cout << "test choices:"; for(size_t i = 0; i < n_test; i++) { if( i % 5 == 0 ) std::cout << "\n\t"; else std::cout << ", "; cout << test_list[i].name; } cout << "\n\nseed: is a positive integer used as a random seed."; cout << "\n\noption_list: zero or more of the following:"; for(size_t i = 0; i < num_option; i++) { if( i % 5 == 0 ) std::cout << "\n\t"; else std::cout << ", "; cout << option_list[i]; } cout << endl << endl; return 1; } if( global_option["memory"] ) CppAD::thread_alloc::hold_memory(true); // initialize the random number simulator // (may be re-initialized by sparse jacobain test) global_seed = size_t(iseed); CppAD::uniform_01(global_seed); // arguments needed for speed tests size_t n_size = 5; CppAD::vector size_det_lu(n_size); CppAD::vector size_det_minor(n_size); CppAD::vector size_mat_mul(n_size); CppAD::vector size_ode(n_size); CppAD::vector size_poly(n_size); CppAD::vector size_sparse_hessian(n_size); CppAD::vector size_sparse_jacobian(n_size); for(size_t i = 0; i < n_size; i++) { size_det_minor[i] = i + 4; size_det_lu[i] = 20 * i + 1; size_mat_mul[i] = 20 * i + 1; size_ode[i] = 20 * i + 1; size_poly[i] = 20 * i + 1; size_sparse_hessian[i] = 250 * (i + 1) * (i + 1); size_sparse_jacobian[i] = 250 * (i + 1) * (i + 1); } switch(match) { // run all the correctness tests case test_correct: ok &= run_correct( available_det_lu, correct_det_lu, "det_lu" ); ok &= run_correct( available_det_minor, correct_det_minor, "det_minor" ); ok &= run_correct( available_mat_mul, correct_mat_mul, "mat_mul" ); ok &= run_correct( available_ode, correct_ode, "ode" ); ok &= run_correct( available_poly, correct_poly, "poly" ); ok &= run_correct( available_sparse_hessian, correct_sparse_hessian, "sparse_hessian" ); ok &= run_correct( available_sparse_jacobian, correct_sparse_jacobian, "sparse_jacobian" ); // summarize results assert( ok || (Run_error_count > 0) ); if( ok ) { cout << "All " << int(Run_ok_count) << " correctness tests passed." << endl; } else { cout << int(Run_error_count) << " correctness tests failed." << endl; } break; // --------------------------------------------------------- // run all the speed tests case test_speed: if( available_det_lu() ) run_speed( time_det_lu, size_det_lu, "det_lu" ); if( available_det_minor() ) run_speed( time_det_minor, size_det_minor, "det_minor" ); if( available_mat_mul() ) run_speed( time_mat_mul, size_mat_mul, "mat_mul" ); if( available_ode() ) run_speed( time_ode, size_ode, "ode" ); if( available_poly() ) run_speed( time_poly, size_poly, "poly" ); if( available_sparse_hessian() ) run_speed( time_sparse_hessian, size_sparse_hessian, "sparse_hessian" ); if( available_sparse_jacobian() ) run_speed( time_sparse_jacobian, size_sparse_jacobian, "sparse_jacobian" ); ok = true; break; // --------------------------------------------------------- case test_det_lu: if( ! available_det_lu() ) { not_available_message( argv[1] ); exit(1); } ok &= run_correct( available_det_lu, correct_det_lu, "det_lu") ; run_speed(time_det_lu, size_det_lu, "det_lu"); break; // --------------------------------------------------------- case test_det_minor: if( ! available_det_minor() ) { not_available_message( argv[1] ); exit(1); } ok &= run_correct( available_det_minor, correct_det_minor, "det_minor" ); run_speed(time_det_minor, size_det_minor, "det_minor"); break; // --------------------------------------------------------- case test_mat_mul: if( ! available_mat_mul() ) { not_available_message( argv[1] ); exit(1); } ok &= run_correct( available_mat_mul, correct_mat_mul, "mat_mul" ); run_speed(time_mat_mul, size_mat_mul, "mat_mul"); break; // --------------------------------------------------------- case test_ode: if( ! available_ode() ) { not_available_message( argv[1] ); exit(1); } ok &= run_correct( available_ode, correct_ode, "ode" ); run_speed(time_ode, size_ode, "ode"); break; // --------------------------------------------------------- case test_poly: if( ! available_poly() ) { not_available_message( argv[1] ); exit(1); } ok &= run_correct( available_poly, correct_poly, "poly" ); run_speed(time_poly, size_poly, "poly"); break; // --------------------------------------------------------- case test_sparse_hessian: if( ! available_sparse_hessian() ) { not_available_message( argv[1] ); exit(1); } ok &= run_correct( available_sparse_hessian, correct_sparse_hessian, "sparse_hessian" ); run_speed( time_sparse_hessian, size_sparse_hessian, "sparse_hessian" ); cout << AD_PACKAGE << "_sparse_hessian_n_color = "; for(size_t i = 0; i < size_sparse_hessian.size(); i++) { if( i == 0 ) cout << "[ "; else cout << ", "; size_t n_color; info_sparse_hessian(size_sparse_hessian[i], n_color); cout << n_color; } cout << " ]" << endl; break; // --------------------------------------------------------- case test_sparse_jacobian: if( ! available_sparse_jacobian() ) { not_available_message( argv[1] ); exit(1); } ok &= run_correct( available_sparse_jacobian, correct_sparse_jacobian, "sparse_jacobian" ); run_speed( time_sparse_jacobian, size_sparse_jacobian, "sparse_jacobian" ); { // output values of n_color CppAD::vector size_vec, n_color_vec; info_sparse_jacobian(size_vec, n_color_vec); assert( size_sparse_jacobian.size() == size_vec.size() ); cout << AD_PACKAGE << "_sparse_jacobian_n_color = "; for(size_t i = 0; i < size_vec.size(); i++) { assert( size_vec[i] == size_sparse_jacobian[i] ); if( i == 0 ) cout << "[ "; else cout << ", "; cout << n_color_vec[i]; } } cout << " ]" << endl; break; // --------------------------------------------------------- default: assert(0); } # ifndef NDEBUG // return memory for vectors that are still in scope size_det_lu.clear(); size_det_minor.clear(); size_mat_mul.clear(); size_ode.clear(); size_poly.clear(); size_sparse_hessian.clear(); size_sparse_jacobian.clear(); // check for memory leak if( CppAD::thread_alloc::free_all() ) { Run_ok_count++; cout << "No memory leak detected" << endl; } else { ok = false; Run_error_count++; cout << "Memory leak detected" << endl; } # endif if( global_cppad_thread_alloc_inuse != 0 ) { cout << "memory allocated at end of last cppad speed test = "; cout << global_cppad_thread_alloc_inuse << std::endl; } if( ! ok ) { cout << "speed main: Error\n"; exit(1); } std::cout << "speed main: OK\n"; return 0; } ================================================ FILE: speed/profile/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the speed/profile directory tests # Inherit build type from ../CMakeList.txt # Adds flags to the compiler command line for sources in the current directory # and below. This command can be used to add any flags, but it was originally # intended to add preprocessor definitions. ADD_DEFINITIONS("-DCPPAD_PROFILE_SPEED ${cppad_profile_flag}") # Extra flags used by the linker when creating an executable. SET(CMAKE_EXE_LINKER_FLAGS ${cppad_profile_flag} ${cppad_link_flags}) # Local include directories to search (not in package_prefix/includdir) INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../src ) # add_executable( [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] # source1 source2 ... sourceN # ) SET(source_list ../main.cpp ../cppad/det_lu.cpp ../cppad/det_minor.cpp ../cppad/mat_mul.cpp ../cppad/ode.cpp ../cppad/poly.cpp ../cppad/sparse_hessian.cpp ../cppad/sparse_jacobian.cpp ) set_compile_flags( speed_profile "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE( speed_profile EXCLUDE_FROM_ALL ${source_list} ) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(speed_profile ${cppad_lib} ${colpack_libs} ) # profile builds it own copy of src/speed library (see ADD_EXECUTABLE above) # TARGET_LINK_LIBRARIES(speed_profile speed_src ) TARGET_LINK_LIBRARIES(speed_profile speed_src ${cppad_lib} ${colpack_libs} ) # check_speed_profile add_check_executable(check_speed profile "correct 54321") # Sed script to make gprof output more readable, use: # ./speed_profile speed 54321 # gprof speed_profile gmon.out | sed -f 'gprof.sed' > gprof.out # # configure_file(InputFile OutputFile [COPYONLY] [ESCAPE_QUOTES] [@ONLY]) CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/gprof.sed.in ${CMAKE_CURRENT_BINARY_DIR}/gprof.sed COPYONLY ) ================================================ FILE: speed/profile/gprof.sed.in ================================================ #! /bin/bash -e # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # remove template information s/<[^<>]*>//g s/<[^<>]*>//g s/<[^<>]*>//g s/<[^<>]*>//g s/<[^<>]*>//g # remove argument information s/([^()]*)//g s/([^()]*)//g # remove names space information s/[a-zA-Z0-9_]*:://g s/[a-zA-Z0-9_]*:://g s/[a-zA-Z0-9_]*:://g s/[a-zA-Z0-9_]*:://g ================================================ FILE: speed/sacado/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-23 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the speed/sacado directory tests # Inherit build type from ../CMakeList.txt # assert sacado_prefix is defined assert( sacado_prefix ) # Avoid the following warning: # The installed Kokkos configuration does not support CXX extensions. SET(CMAKE_CXX_EXTENSIONS Off) # Adds flags to the compiler command line for sources in the current directory # and below. This command can be used to add any flags, but it was originally # intended to add preprocessor definitions. ADD_DEFINITIONS("-DCPPAD_SACADO_SPEED -DRAD_AUTO_AD_Const") # Local include directories to search (not in package_prefix/includdir) INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../src ) # add_executable( [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] # source1 source2 ... sourceN # ) SET(source_list ../main.cpp det_lu.cpp det_minor.cpp mat_mul.cpp ode.cpp poly.cpp sparse_hessian.cpp sparse_jacobian.cpp ) set_compile_flags( speed_sacado "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE( speed_sacado EXCLUDE_FROM_ALL ${source_list} ) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(speed_sacado ${cppad_lib} ${colpack_libs} ) # determine libraries necessary to link Sacado FOREACH(dir ${cmake_install_libdirs}) IF( IS_DIRECTORY ${sacado_prefix}/${dir}/cmake/Trilinos ) SET(Trilinos_DIR "${sacado_prefix}/${dir}/cmake/Trilinos" ) ENDIF( IS_DIRECTORY ${sacado_prefix}/${dir}/cmake/Trilinos ) ENDFOREACH( dir ) FIND_PACKAGE(Trilinos CONFIG) print_variable(Sacado_LIBRARIES) # # include the dl library if it is in the system path CHECK_LIBRARY_EXISTS( dl dlopen "${CMAKE_SYSTEM_LIBRARY_PATH}" dl_found ) IF( dl_found ) TARGET_LINK_LIBRARIES( speed_sacado speed_src ${Sacado_LIBRARIES} dl ) ELSE( dl_found ) TARGET_LINK_LIBRARIES( speed_sacado speed_src ${Sacado_LIBRARIES} ) ENDIF( dl_found ) # # the check_speed_sacado add_check_executable(check_speed sacado "correct 54321") ================================================ FILE: speed/sacado/det_lu.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sacado_det_lu.cpp} Sacado Speed: Gradient of Determinant Using Lu Factorization ############################################################ Specifications ************** See :ref:`link_det_lu-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ // suppress conversion warnings before other includes # include // # include # include # include # include // list of possible options # include extern std::map global_option; bool link_det_lu( size_t size , size_t repeat , CppAD::vector &matrix , CppAD::vector &gradient ) { // speed test global option values if( global_option["onetape"] || global_option["atomic"] ) return false; if( global_option["memory"] || global_option["optimize"] ) return false; // ----------------------------------------------------- // setup // // object for computing determinant typedef Sacado::Rad::ADvar ADScalar; typedef CppAD::vector ADVector; CppAD::det_by_lu Det(size); size_t i; // temporary index size_t n = size * size; // number of independent variables ADScalar detA; // AD value of the determinant ADVector A(n); // AD version of matrix // ------------------------------------------------------ while(repeat--) { // get the next matrix CppAD::uniform_01(n, matrix); // set independent variable values for(i = 0; i < n; i++) A[i] = matrix[i]; // compute the determinant detA = Det(A); // compute the gradient of detA ADScalar::Gradcomp(); // evaluate and return gradient using reverse mode for(i =0; i < n; i++) gradient[i] = A[i].adj(); // partial detA w.r.t A[i] } // --------------------------------------------------------- return true; } /* {xrst_code} {xrst_spell_on} {xrst_end sacado_det_lu.cpp} */ ================================================ FILE: speed/sacado/det_minor.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sacado_det_minor.cpp} Sacado Speed: Gradient of Determinant by Minor Expansion ######################################################## Specifications ************** See :ref:`link_det_minor-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ // suppress conversion warnings before other includes # include // # include # include # include # include // list of possible options # include extern std::map global_option; bool link_det_minor( const std::string& job , size_t size , size_t repeat , CppAD::vector &matrix , CppAD::vector &gradient ) { // -------------------------------------------------------------------- // check none of the global options is true typedef std::map::iterator iterator; for(iterator itr=global_option.begin(); itr!=global_option.end(); ++itr) { if( itr->second ) return false; } // ----------------------------------------------------- // not using job // ----------------------------------------------------- // AD types typedef Sacado::Rad::ADvar r_double; typedef CppAD::vector r_vector; // object for computing deterinant CppAD::det_by_minor r_det(size); // number of independent variables size_t n = size * size; // independent variable vector r_vector r_A(n); // AD value of the determinant r_double r_detA; // ------------------------------------------------------ while(repeat--) { // get the next matrix CppAD::uniform_01(n, matrix); // set independent variable values for(size_t j = 0; j < n; ++j) r_A[j] = matrix[j]; // compute the determinant r_detA = r_det(r_A); // reverse mode compute gradient of last computed value; i.e., detA r_double::Gradcomp(); // return gradient for(size_t j =0; j < n; ++j) gradient[j] = r_A[j].adj(); // partial detA w.r.t A[j] } // --------------------------------------------------------- return true; } /* {xrst_code} {xrst_spell_on} {xrst_end sacado_det_minor.cpp} */ ================================================ FILE: speed/sacado/mat_mul.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sacado_mat_mul.cpp} Sacado Speed: Matrix Multiplication ################################### Specifications ************** See :ref:`link_mat_mul-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ // suppress conversion warnings before other includes # include // # include # include # include # include // list of possible options # include extern std::map global_option; bool link_mat_mul( size_t size , size_t repeat , CppAD::vector& x , CppAD::vector& z , CppAD::vector& dz ) { // speed test global option values if( global_option["memory"] || global_option["onetape"] || global_option["atomic"] || global_option["optimize"] ) return false; // ----------------------------------------------------- // setup // object for computing determinant typedef Sacado::Rad::ADvar ADScalar; typedef CppAD::vector ADVector; size_t j; // temporary index size_t m = 1; // number of dependent variables size_t n = size * size; // number of independent variables ADVector X(n); // AD domain space vector ADVector Y(n); // Store product matrix ADVector Z(m); // AD range space vector ADScalar f; // ------------------------------------------------------ while(repeat--) { // get the next matrix CppAD::uniform_01(n, x); // set independent variable values for(j = 0; j < n; j++) X[j] = x[j]; // do the computation mat_sum_sq(size, X, Y, Z); // create function object f : X -> Z f = Z[0]; // reverse mode gradient of last ADvar computed value; i.e., f ADScalar::Gradcomp(); // return gradient for(j = 0; j < n; j++) dz[j] = X[j].adj(); // partial f w.r.t X[j] } // return function value z[0] = f.val(); // --------------------------------------------------------- return true; } /* {xrst_code} {xrst_spell_on} {xrst_end sacado_mat_mul.cpp} */ ================================================ FILE: speed/sacado/ode.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sacado_ode.cpp} Sacado Speed: Gradient of Ode Solution ###################################### Specifications ************** See :ref:`link_ode-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ // suppress conversion warnings before other includes # include // # include # include # include # include # include // list of possible options # include extern std::map global_option; bool link_ode( size_t size , size_t repeat , CppAD::vector &x , CppAD::vector &jacobian ) { // speed test global option values if( global_option["atomic"] ) return false; if( global_option["memory"] || global_option["onetape"] || global_option["optimize"] ) return false; // ------------------------------------------------------------- // setup assert( x.size() == size ); assert( jacobian.size() == size * size ); typedef Sacado::Fad::DFad ADScalar; typedef CppAD::vector ADVector; size_t i, j; size_t p = 0; // use ode to calculate function values size_t n = size; // number of independent variables size_t m = n; // number of dependent variables ADVector X(n), Y(m); // independent and dependent variables // ------------------------------------------------------------- while(repeat--) { // choose next x value CppAD::uniform_01(n, x); for(j = 0; j < n; j++) { // set up for X as the independent variable vector X[j] = ADScalar(int(n), int(j), x[j]); } // evaluate function CppAD::ode_evaluate(X, p, Y); // return values with Y as the dependent variable vector for(i = 0; i < m; i++) { for(j = 0; j < n; j++) jacobian[ i * n + j ] = Y[i].dx( int(j) ); } } return true; } /* {xrst_code} {xrst_spell_on} {xrst_end sacado_ode.cpp} */ ================================================ FILE: speed/sacado/poly.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin sacado_poly.cpp} Sacado Speed: Second Derivative of a Polynomial ############################################### Specifications ************** See :ref:`link_poly-name` . Implementation ************** {xrst_spell_off} {xrst_code cpp} */ // suppress conversion warnings before other includes # include // # include # include # include # include // list of possible options # include extern std::map global_option; bool link_poly( size_t size , size_t repeat , CppAD::vector &a , // coefficients of polynomial CppAD::vector &z , // polynomial argument value CppAD::vector &ddp ) // second derivative w.r.t z { if( global_option["atomic"] ) return false; if( global_option["memory"] || global_option["onetape"] || global_option["optimize"] ) return false; // ----------------------------------------------------- // setup typedef Sacado::Tay::Taylor ADScalar; CppAD::vector A(size); size_t i; // temporary index ADScalar Z; // domain space AD value ADScalar P; // range space AD value int order = 2; // order of Taylor coefficients Z.resize(order+1, false); P.resize(order+1, false); // choose the polynomial coefficients CppAD::uniform_01(size, a); // AD copy of the polynomial coefficients for(i = 0; i < size; i++) A[i] = a[i]; // ------------------------------------------------------ while(repeat--) { // get the next argument value CppAD::uniform_01(1, z); // independent variable value Z.fastAccessCoeff(0) = z[0]; // argument value Z.fastAccessCoeff(1) = 1.; // first order coefficient Z.fastAccessCoeff(2) = 0.; // second order coefficient // AD computation of the dependent variable P = CppAD::Poly(0, A, Z); // second derivative is twice second order Taylor coefficient ddp[0] = 2. * P.fastAccessCoeff(2); } // ------------------------------------------------------ return true; } /* {xrst_code} {xrst_spell_on} {xrst_end sacado_poly.cpp} */ ================================================ FILE: speed/sacado/sparse_hessian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include /* {xrst_begin sacado_sparse_hessian.cpp} Sacado Speed: Sparse Hessian ############################ {xrst_spell_off} {xrst_code cpp} */ // A sacado version of this test is not yet implemented extern bool link_sparse_hessian( size_t size , size_t repeat , const CppAD::vector& row , const CppAD::vector& col , CppAD::vector& x , CppAD::vector& hessian , size_t& n_color ) { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end sacado_sparse_hessian.cpp} */ ================================================ FILE: speed/sacado/sparse_jacobian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include /* {xrst_begin sacado_sparse_jacobian.cpp} Sacado Speed: sparse_jacobian ############################# {xrst_spell_off} {xrst_code cpp} */ // A sacado version of this test is not yet available bool link_sparse_jacobian( const std::string& job , size_t size , size_t repeat , size_t m , const CppAD::vector& row , const CppAD::vector& col , CppAD::vector& x , CppAD::vector& jacobian , size_t& n_color ) { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end sacado_sparse_jacobian.cpp} */ ================================================ FILE: speed/sacado/speed_sacado.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin speed_sacado} Speed Test Derivatives Using Sacado ################################### Purpose ******* CppAD has a set of speed tests that are used to compare Sacado with other AD packages. This section links to the source code the Sacado speed tests (any suggestions to make the Sacado results faster are welcome). sacado_prefix ************* To run these tests, you must include the :ref:`sacado_prefix-name` in you :ref:`cmake@CMake Command` . Running Tests ************* To build these speed tests, and run their correctness tests, execute the following commands starting in the :ref:`cmake@Build Directory` : | |tab| ``cd speed/sacado`` | |tab| ``make check_speed_sacado VERBOSE`` =1 You can then run the corresponding speed tests with the following command ./ ``speed_sacado speed`` *seed* where *seed* is a positive integer. See :ref:`speed_main-name` for more options. Contents ******** {xrst_toc_list speed/sacado/det_minor.cpp speed/sacado/det_lu.cpp speed/sacado/mat_mul.cpp speed/sacado/ode.cpp speed/sacado/poly.cpp speed/sacado/sparse_hessian.cpp speed/sacado/sparse_jacobian.cpp } {xrst_end speed_sacado} ================================================ FILE: speed/speed.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin speed} {xrst_spell adolc cppadcg fadbad sacado } Speed Test an Operator Overloading AD Package ############################################# Purpose ******* Cppad has a set of speed tests that are used to determine if changes improve its execution speed. These tests can also be used to compare the AD packages :ref:`Adolc` , Cppad, :ref:`Fadbad` , :ref:`Cppadcg` , :ref:`Sacado` . debug_which *********** Usually, one wants to compile the speed tests in release mode. This can be done by setting :ref:`cmake@cppad_debug_which` to ``debug_none`` in the cmake command. Correctness tests are included for all the speed tests, so it is possible you will want to compile these tests for debugging; i.e., set *cppad_debug_which* to ``debug_all`` . The sections below explain how you can run these tests on your computer. Contents ******** {xrst_toc_table speed/main.cpp speed/speed_utility.xrst speed/double/speed_double.xrst speed/adolc/speed_adolc.xrst speed/cppad/speed_cppad.xrst speed/fadbad/speed_fadbad.xrst speed/cppad_jit/speed_cppad_jit.xrst speed/cppadcg/speed_cppadcg.xrst speed/sacado/speed_sacado.xrst speed/xpackage/speed_xpackage.xrst } {xrst_end speed} ================================================ FILE: speed/speed_utility.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin speed_utility} Speed Testing Utilities ####################### {xrst_toc_hidden include/cppad/speed/det_by_lu.hpp include/cppad/speed/det_of_minor.hpp include/cppad/speed/det_by_minor.hpp include/cppad/speed/det_33.hpp include/cppad/speed/det_grad_33.hpp include/cppad/speed/mat_sum_sq.hpp include/cppad/speed/ode_evaluate.hpp include/cppad/speed/sparse_jac_fun.hpp include/cppad/speed/sparse_hes_fun.hpp include/cppad/speed/uniform_01.hpp } Speed Main Program ****************** .. csv-table:: :widths: auto speed_main,:ref:`speed_main-title` Speed Utility Routines ********************** .. csv-table:: :widths: auto det_by_lu,:ref:`det_by_lu-title` det_by_minor,:ref:`det_by_minor-title` det_of_minor,:ref:`det_of_minor-title` det_33,:ref:`det_33-title` det_grad_33,:ref:`det_grad_33-title` mat_sum_sq,:ref:`mat_sum_sq-title` ode_evaluate,:ref:`ode_evaluate-title` sparse_jac_fun,:ref:`sparse_jac_fun-title` sparse_hes_fun,:ref:`sparse_hes_fun-title` uniform_01,:ref:`uniform_01-title` Library Routines **************** .. csv-table:: :widths: auto LuFactor,:ref:`LuFactor-title` LuInvert,:ref:`LuInvert-title` LuSolve,:ref:`LuSolve-title` Poly,:ref:`Poly-title` Source Code *********** .. csv-table:: :widths: auto det_by_lu.hpp,:ref:`det_by_lu.hpp-title` det_by_minor.hpp,:ref:`det_by_minor.hpp-title` det_grad_33.hpp,:ref:`det_grad_33.hpp-title` det_of_minor.hpp,:ref:`det_of_minor.hpp-title` lu_factor.hpp,:ref:`lu_factor.hpp-title` lu_invert.hpp,:ref:`lu_invert.hpp-title` lu_solve.hpp,:ref:`lu_solve.hpp-title` mat_sum_sq.hpp,:ref:`mat_sum_sq.hpp-title` poly.hpp,:ref:`poly.hpp-title` sparse_jac_fun.hpp,:ref:`sparse_jac_fun.hpp-title` sparse_hes_fun.hpp,:ref:`sparse_hes_fun.hpp-title` uniform_01.hpp,:ref:`uniform_01.hpp-title` {xrst_end speed_utility} ================================================ FILE: speed/src/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the cppad_ipopt/src library # Inherit build type from ../CMakeList.txt # add_library( [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] # source1 source2 ... sourceN) # ) # Make libspeed_src a static library because this is just for testing # and is not installed (do not have to worry about library search path). # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list link_det_lu.cpp link_det_minor.cpp link_mat_mul.cpp link_ode.cpp link_poly.cpp link_sparse_hessian.cpp link_sparse_jacobian.cpp ) # END_SORT_THIS_LINE_MINUS_2 # 2DO: fix problem with the test # ./speed_cppad sparse_hessian 123 colpack # when speed_cppad is compiled for debugging. # set_compile_flags( speed_src "${cppad_debug_which}" "${source_list}" ) # ADD_LIBRARY(speed_src STATIC EXCLUDE_FROM_ALL ${source_list} ) ================================================ FILE: speed/src/dev_link.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin speed_available dev} {xrst_spell src } Is A Speed Test Available ######################### Syntax ****** | *available* = ``available_`` *name* () name **** The text *name* in the syntax is ``det_lu`` , ``det_minor`` , ``mat_mul`` , ``ode`` , ``sparse_hessian`` , or ``sparse_jacobian`` . Namespace ********* This function is in the global namespace, not the CppAD namespace. available ********* The ``bool`` return value *available* is true (false) if the speed test *name* is (is not) available for this package. Implementation ************** This function is implemented in the file ``speed/src/link_`` *name* . ``cpp`` . {xrst_end speed_available} ------------------------------------------------------------------------------ {xrst_begin speed_correct dev} {xrst_spell src } Does a A Speed Test Return Correct Results ########################################## Syntax ****** *ok* = ``correct_`` *name* ( *is_package_double* ) name **** The text *name* in the syntax is ``det_lu`` , ``det_minor`` , ``mat_mul`` , ``ode`` , ``sparse_hessian`` , or ``sparse_jacobian`` . Namespace ********* This function is in the global namespace, not the CppAD namespace. is_package_double ***************** The ``bool`` argument *is_package_double* is true (false) if we are checking functions values (derivative values). ok ** The ``bool`` return value *ok* is true (false) if the speed test for *name* returns correct (incorrect) values. Implementation ************** This function is implemented in the file ``speed/src/link_`` *name* . ``cpp`` . {xrst_end speed_correct} ------------------------------------------------------------------------------ {xrst_begin speed_time dev} {xrst_spell src } Measure Time It Takes for a A Speed Test to Run ############################################### Syntax ****** *time* = ``time_`` *name* ( *time_min* , *size* ) name **** The text *name* in the syntax is ``det_lu`` , ``det_minor`` , ``mat_mul`` , ``ode`` , ``sparse_hessian`` , or ``sparse_jacobian`` . Namespace ********* This function is in the global namespace, not the CppAD namespace. time_min ******** Is the minimum time, in seconds, for the test. Calls to ``link_`` *name* will be repeated enough times so that the total time is at least *time_min* . size **** This ``size_t`` argument the size for this speed test; see *size* in the corresponding ``link_`` *name* documentation. time **** This is the average amount of time for each call; i.e., the total time, which is greater than or equal *time_min* , divided by the number of repeats. Implementation ************** This function is implemented in the file ``speed/src/link_`` *name* . ``cpp`` . {xrst_end speed_time} ------------------------------------------------------------------------------ {xrst_begin speed_time_callback dev} Timing Callback Functions ######################### Syntax ****** *time* = ``time_`` *name* _ ``callback`` ( *size_t* , *repeat* ) name **** The text *name* in the syntax is ``det_lu`` , ``det_minor`` , ``mat_mul`` , ``ode`` , ``sparse_hessian`` , or ``sparse_jacobian`` . Namespace ********* These functions are in the empty namespace; i.e., it is only accessed by functions in the file where it is defined. size **** This ``size_t`` argument the size for this speed test; see *size* in the corresponding ``link_`` *name* documentation. repeat ****** This ``size_t`` value is the number of times to repeat a call to the corresponding link routine. job *** The :ref:`link_routines@job` argument to the corresponding link routine will be ``run`` . {xrst_end speed_time_callback} ================================================ FILE: speed/src/dev_sparse_hessian.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin dev_sparse_hessian dev} Developer Documentation for Speed Testing Sparse Hessian ######################################################## Contents ******** {xrst_toc_table speed/src/link_sparse_hessian.cpp } {xrst_end dev_sparse_hessian} ================================================ FILE: speed/src/link.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin link_routines} {xrst_spell teardown } Package Specific Speed Test Linking Routines ############################################ Syntax ****** | *available* = *link_name* ( *job* , *size* , *repeat* , *other_arguments* ) Purpose ******* Each :ref:`speed_main@package` defines a version of each *link_name* listed below. This is used by the :ref:`speed_main-name` program to run the corresponding speed and correctness tests. Thread Save *********** The *link_name* routines may use static memory for setup and teardown and hence may not be thread safe. link_name ********* The routine *link_name* in the syntax is one of the following: Contents ******** {xrst_toc_table speed/src/link_det_lu.cpp speed/src/link_det_minor.cpp speed/src/link_mat_mul.cpp speed/src/link_ode.cpp speed/src/link_poly.cpp speed/src/link_sparse_hessian.hpp speed/src/link_sparse_jacobian.hpp } Namespace ********* These routines are in the global namespace, not the CppAD namespace. available ********* The ``bool`` return value *available* is true (false) if the speed test *name* is (is not) available for this package. job *** This argument can be used to cache information that does not depend on the function argument value without having the corresponding time included in the test results. This is expected to be important when the global option :ref:`speed_main@Global Options@onetape` is true. The ``const std::string&`` argument *job* has one of the following values: setup ===== This is the first call with a new value for *size* and will not be used for timing. It can be used to setup information that should not be included in timing how long this routine takes. run === This call will be used for timing how long this routine takes. The previous call *job* was either ``setup`` or ``run`` and had the same value of *size* . teardown ======== This is the last call with this value of *size* and will not be used for timing. It should be used to take down the setup; e.g., fee any memory that was allocated. size **** The ``size_t`` argument *size* this is the size parameter for this test. Larger sizes correspond to more computation per call. repeat ****** If *job* is ``run`` , the *size_t* argument *repeat* is the number of randomly chosen argument values that the test derivatives should be evaluated for. The value of *repeat* is always greater than or equal one and all the ``link_`` *name* output values are always calculated. other_arguments *************** Each link function routine has specific specifications for *size* and their other arguments. {xrst_end link_routines} ================================================ FILE: speed/src/link_det_lu.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include // BEGIN PROTOTYPE extern bool link_det_lu( size_t size , size_t repeat , CppAD::vector& matrix , CppAD::vector& gradient ); // END PROTOTYPE /* ------------------------------------------------------------------------------- {xrst_begin link_det_lu} Speed Testing Gradient of Determinant Using Lu Factorization ############################################################ Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Purpose ******* Each :ref:`speed_main@package` must define a version of this routine as specified below. This is used by the :ref:`speed_main-name` program to run the corresponding speed and correctness tests. Method ****** The same template routine :ref:`det_by_lu-name` is used by the different AD packages. Return Value ************ If this speed test is not yet supported by a particular *package* , the corresponding return value for ``link_det_lu`` should be ``false`` . size **** The argument *size* is the number of rows and columns in the matrix. repeat ****** The argument *repeat* is the number of different matrices that the gradient (or determinant) is computed for. matrix ****** The argument *matrix* is a vector with *size* * *size* elements. The input value of its elements does not matter. The output value of its elements is the last matrix that the gradient (or determinant) is computed for. gradient ******** The argument *gradient* is a vector with *size* * *size* elements. The input value of its elements does not matter. The output value of its elements is the gradient of the determinant of *matrix* with respect to its elements. double ====== In the case where *package* is ``double`` , only the first element of *gradient* is used and it is actually the determinant value (the gradient value is not computed). {xrst_end link_det_lu} ----------------------------------------------------------------------------- */ // --------------------------------------------------------------------------- // The routines below are documented in dev_link.omh // --------------------------------------------------------------------------- namespace { void time_det_lu_callback(size_t size, size_t repeat) { // free statically allocated memory if( size == 0 && repeat == 0 ) return; // CppAD::vector matrix(size * size); CppAD::vector gradient(size * size); link_det_lu(size, repeat, matrix, gradient); return; } } // --------------------------------------------------------------------------- bool available_det_lu(void) { size_t size = 3; size_t repeat = 1; CppAD::vector matrix(size * size); CppAD::vector gradient(size * size); return link_det_lu(size, repeat, matrix, gradient); } // --------------------------------------------------------------------------- bool correct_det_lu(bool is_package_double) { size_t size = 3; size_t repeat = 1; CppAD::vector matrix(size * size); CppAD::vector gradient(size * size); link_det_lu(size, repeat, matrix, gradient); bool ok; if( is_package_double ) ok = CppAD::det_33(matrix, gradient); else ok = CppAD::det_grad_33(matrix, gradient); return ok; } // --------------------------------------------------------------------------- double time_det_lu(double time_min, size_t size) { return CppAD::time_test(time_det_lu_callback, time_min, size); } ================================================ FILE: speed/src/link_det_minor.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include // BEGIN PROTOTYPE extern bool link_det_minor( const std::string& job , size_t size , size_t repeat , CppAD::vector& matrix , CppAD::vector& gradient ); // END PROTOTYPE /* ------------------------------------------------------------------------------- {xrst_begin link_det_minor} Speed Testing Gradient of Determinant by Minor Expansion ######################################################## Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Method ****** The same template class :ref:`det_by_minor-name` is used by the different AD packages. job *** See the standard link specifications for :ref:`link_routines@job` . size **** See the standard link specifications for :ref:`link_routines@size` In addition, *size* is the number of rows and columns in *matrix* . repeat ****** See the standard link specifications for :ref:`link_routines@repeat` . matrix ****** The argument *matrix* is a vector with *size* * *size* elements. The input value of its elements does not matter. The output value of its elements is the last matrix that the gradient (or determinant) is computed for. gradient ******** The argument *gradient* is a vector with *size* * *size* elements. The input value of its elements does not matter. The output value of its elements is the gradient of the determinant of *matrix* with respect to its elements. double ====== In the case where *package* is ``double`` , only the first element of *gradient* is used and it is actually the determinant value (the gradient value is not computed). {xrst_end link_det_minor} ----------------------------------------------------------------------------- */ // --------------------------------------------------------------------------- // The routines below are documented in dev_link.omh // --------------------------------------------------------------------------- namespace { void time_det_minor_callback(size_t size, size_t repeat) { std::string job("run"); CppAD::vector matrix(size * size); CppAD::vector gradient(size * size); // link_det_minor(job, size, repeat, matrix, gradient); return; } } // --------------------------------------------------------------------------- bool available_det_minor(void) { size_t size = 3; size_t repeat = 0; CppAD::vector matrix(size * size); CppAD::vector gradient(size * size); // std::string job = "setup"; bool result = link_det_minor(job, size, repeat, matrix, gradient); // job = "teardown"; link_det_minor(job, size, repeat, matrix, gradient); // return result; } // --------------------------------------------------------------------------- bool correct_det_minor(bool is_package_double) { size_t size = 3; size_t repeat = 1; CppAD::vector matrix(size * size); CppAD::vector gradient(size * size); // std::string job = "setup"; link_det_minor(job, size, repeat, matrix, gradient); // job = "run"; link_det_minor(job, size, repeat, matrix, gradient); // bool ok = CppAD::det_grad_33(matrix, gradient); if( is_package_double ) ok = CppAD::det_33(matrix, gradient); else ok = CppAD::det_grad_33(matrix, gradient); // job = "teardown"; link_det_minor(job, size, repeat, matrix, gradient); // return ok; } double time_det_minor(double time_min, size_t size) { CPPAD_ASSERT_UNKNOWN( size != 0 ); // CppAD::vector matrix(size * size); CppAD::vector gradient(size * size); // std::string job = "setup"; size_t repeat = 0; link_det_minor(job, size, repeat, matrix, gradient); // // job is run in time_det_minor_callback double time = CppAD::time_test(time_det_minor_callback, time_min, size); // job = "teardown"; link_det_minor(job, size, repeat, matrix, gradient); // return time; } // --------------------------------------------------------------------------- ================================================ FILE: speed/src/link_mat_mul.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include // BEGIN PROTOTYPE extern bool link_mat_mul( size_t size , size_t repeat , CppAD::vector& x , CppAD::vector& z , CppAD::vector& dz ); // END PROTOTYPE /* ------------------------------------------------------------------------------- {xrst_begin link_mat_mul} {xrst_spell dz } Speed Testing Derivative of Matrix Multiply ########################################### Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Purpose ******* Each :ref:`speed_main@package` must define a version of this routine as specified below. This is used by the :ref:`speed_main-name` program to run the corresponding speed and correctness tests. Return Value ************ If this speed test is not yet supported by a particular *package* , the corresponding return value for ``link_mat_mul`` should be ``false`` . n * The argument *n* is the number of rows and columns in the square matrix *x* . repeat ****** The argument *repeat* is the number of different argument values that the derivative of *z* (or just the value of *z* ) will be computed. x * The argument *x* is a vector with *x* . ``size`` () = *size* * *size* elements. The input value of its elements does not matter. The output value of its elements is the last random matrix that is multiplied and then summed to form *z* ; .. math:: x_{i,j} = x[ i * s + j ] where *s* = *size* . z * The argument *z* is a vector with one element. The input value of the element does not matter. The output of its element the sum of the elements of *y* = *x* * *x* ; i.e., .. math:: :nowrap: \begin{eqnarray} y_{i,j} & = & \sum_{k=0}^{s-1} x_{i,k} x_{k, j} \\ z & = & \sum_{i=0}^{s-1} \sum_{j=0}^{s-1} y_{i,j} \end{eqnarray} dz ** The argument *dz* is a vector with *dz* . ``size`` () = *size* * *size* . The input values of its elements do not matter. The output value of its elements form the derivative of *z* with respect to *x* . {xrst_end link_mat_mul} ----------------------------------------------------------------------------- */ // --------------------------------------------------------------------------- // The routines below are documented in dev_link.omh // --------------------------------------------------------------------------- namespace { void time_mat_mul_callback(size_t size, size_t repeat) { // free statically allocated memory if( size == 0 && repeat == 0 ) return; // CppAD::vector x(size * size), z(1), dz(size * size); link_mat_mul(size, repeat, x, z, dz); return; } } // --------------------------------------------------------------------------- bool available_mat_mul(void) { size_t size = 3; size_t repeat = 1; CppAD::vector x(size * size), z(1), dz(size * size); return link_mat_mul(size, repeat, x, z, dz); } // -------------------------------------------------------------------------- bool correct_mat_mul(bool is_package_double) { size_t size = 2; size_t repeat = 1; CppAD::vector x(size * size), z(1), dz(size * size); double eps99 = 99.0 * std::numeric_limits::epsilon(); link_mat_mul(size, repeat, x, z, dz); double x00 = x[0 * size + 0]; double x01 = x[0 * size + 1]; double x10 = x[1 * size + 0]; double x11 = x[1 * size + 1]; bool ok = true; double check; if( is_package_double ) { check = 0; check += x00 * x00 + x01 * x10; // y00 check += x00 * x01 + x01 * x11; // y01 check += x10 * x00 + x11 * x10; // y10 check += x10 * x01 + x11 * x11; // y11 ok &= CppAD::NearEqual(check, z[0], eps99, eps99); return ok; } // partial w.r.t. x00 check = x00 + x00 + x01 + x10; ok &= CppAD::NearEqual(check, dz[0 * size + 0], eps99, eps99); // partial w.r.t. x01 check = x10 + x00 + x11 + x10; ok &= CppAD::NearEqual(check, dz[0 * size + 1], eps99, eps99); // partial w.r.t. x10 check = x01 + x00 + x11 + x01; ok &= CppAD::NearEqual(check, dz[1 * size + 0], eps99, eps99); // partial w.r.t. x11 check = x01 + x10 + x11 + x11; ok &= CppAD::NearEqual(check, dz[1 * size + 1], eps99, eps99); return ok; } double time_mat_mul(double time_min, size_t size) { return CppAD::time_test(time_mat_mul_callback, time_min, size); } // -------------------------------------------------------------------------- ================================================ FILE: speed/src/link_ode.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include // BEGIN PROTOTYPE extern bool link_ode( size_t size , size_t repeat , CppAD::vector& x , CppAD::vector& jacobian ); // END PROTOTYPE /* ------------------------------------------------------------------------------- {xrst_begin link_ode} {xrst_spell fp } Speed Testing the Jacobian of Ode Solution ########################################## Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Purpose ******* Each :ref:`speed_main@package` must define a version of this routine as specified below. This is used by the :ref:`speed_main-name` program to run the corresponding speed and correctness tests. Method ****** The same template routine :ref:`ode_evaluate-name` is used by th different AD packages. f * The function :math:`f : \B{R}^n \rightarrow \B{R}^n` that is defined and computed by evaluating :ref:`ode_evaluate-name` with a call of the form ``ode_evaluate`` ( *x* , *p* , *fp* ) with *p* equal to zero. Calls with the value *p* equal to one are used to check the derivative values. Return Value ************ If this speed test is not yet supported by a particular *package* , the corresponding return value for ``link_ode`` should be ``false`` . size **** The argument *size* is the number of variables in the ordinary differential equations which is also equal to :math:`n`. repeat ****** The argument *repeat* is the number of times the Jacobian is computed. x * The argument *x* is a vector with :math:`n` elements. The input value of the elements of *x* does not matter. On output, it has been set to the argument value for which the function, or its derivative, is being evaluated. The value of this vector must change with each repetition. jacobian ******** The argument *jacobian* is a vector with :math:`n^2` elements. The input value of its elements does not matter. The output value of its elements is the Jacobian of the function :math:`f(x)` that corresponds to output value of *x* . To be more specific, for :math:`i = 0 , \ldots , n-1` and :math:`j = 0 , \ldots , n-1`, .. math:: \D{f[i]}{x[j]} (x) = jacobian [ i \cdot n + j ] double ====== In the case where *package* is ``double`` , only the first :math:`n` element of *jacobian* are modified and they are to the function value :math:`f(x)` corresponding to the output value of *x* . {xrst_end link_ode} ----------------------------------------------------------------------------- */ // --------------------------------------------------------------------------- // The routines below are documented in dev_link.omh // --------------------------------------------------------------------------- namespace { void time_ode_callback(size_t n, size_t repeat) { // free statically allocated memory if( n == 0 && repeat == 0 ) return; // CppAD::vector x(n); CppAD::vector jacobian(n * n); link_ode(n, repeat, x, jacobian); return; } } // --------------------------------------------------------------------------- bool available_ode(void) { size_t n = 1; size_t repeat = 1; CppAD::vector x(n); CppAD::vector jacobian(n * n); return link_ode(n, repeat, x, jacobian); } // ---------------------------------------------------------------------------- bool correct_ode(bool is_package_double) { bool ok = true; size_t n = 5; size_t repeat = 1; CppAD::vector x(n); CppAD::vector jacobian(n * n); link_ode(n, repeat, x, jacobian); size_t size = n * n; size_t p = 1; if( is_package_double ) { p = 0; // check function value size = n; } CppAD::vector check(size); CppAD::ode_evaluate(x, p, check); size_t k; for(k = 0; k < size; k++) ok &= CppAD::NearEqual(check[k], jacobian[k], 1e-6, 1e-6); return ok; } double time_ode(double time_min, size_t size) { return CppAD::time_test(time_ode_callback, time_min, size); } // ---------------------------------------------------------------------------- ================================================ FILE: speed/src/link_poly.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include // BEGIN PROTOTYPE extern bool link_poly( size_t size , size_t repeat , CppAD::vector& a , CppAD::vector& z , CppAD::vector& ddp ); // END PROTOTYPE /* ------------------------------------------------------------------------------- {xrst_begin link_poly} {xrst_spell ddp } Speed Testing Second Derivative of a Polynomial ############################################### Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Purpose ******* Each :ref:`speed_main@package` must define a version of this routine as specified below. This is used by the :ref:`speed_main-name` program to run the corresponding speed and correctness tests. Method ****** The same template routine :ref:`Poly-name` is used by the different AD packages. Return Value ************ If this speed test is not yet supported by a particular *package* , the corresponding return value for ``link_poly`` should be ``false`` . size **** The argument *size* is the order of the polynomial (the number of coefficients in the polynomial). repeat ****** The argument *repeat* is the number of different argument values that the second derivative (or just the polynomial) will be computed at. a * The argument *a* is a vector with *size* elements. The input value of its elements does not matter. The output value of its elements is the coefficients of the polynomial that is differentiated (*i*-th element is coefficient of order *i* ). z * The argument *z* is a vector with one element. The input value of the element does not matter. The output of its element is the polynomial argument value were the last second derivative (or polynomial value) was computed. ddp *** The argument *ddp* is a vector with one element. The input value of its element does not matter. The output value of its element is the second derivative of the polynomial with respect to it's argument value. double ====== In the case where *package* is ``double`` , the output value of the element of *ddp* is the polynomial value (the second derivative is not computed). {xrst_end link_poly} ----------------------------------------------------------------------------- */ // --------------------------------------------------------------------------- // The routines below are documented in dev_link.omh // --------------------------------------------------------------------------- namespace { void time_poly_callback(size_t size, size_t repeat) { // free statically allocated memory if( size == 0 && repeat == 0 ) return; // CppAD::vector a(size), z(1), ddp(1); link_poly(size, repeat, a, z, ddp); return; } } // --------------------------------------------------------------------------- bool available_poly(void) { size_t size = 10; size_t repeat = 1; CppAD::vector a(size), z(1), ddp(1); return link_poly(size, repeat, a, z, ddp); } // --------------------------------------------------------------------------- bool correct_poly(bool is_package_double) { size_t size = 10; size_t repeat = 1; CppAD::vector a(size), z(1), ddp(1); double eps99 = 99.0 * std::numeric_limits::epsilon(); link_poly(size, repeat, a, z, ddp); size_t k; double check; if( is_package_double ) k = 0; else k = 2; check = CppAD::Poly(k, a, z[0]); bool ok = CppAD::NearEqual(check, ddp[0], eps99, eps99); return ok; } // --------------------------------------------------------------------------- double time_poly(double time_min, size_t size) { return CppAD::time_test(time_poly_callback, time_min, size); } // --------------------------------------------------------------------------- ================================================ FILE: speed/src/link_sparse_hessian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include # include "link_sparse_hessian.hpp" namespace { // BEGIN_EMPTY_NAMESPACE using CppAD::vector; /* ------------------------------------------------------------------------------ {xrst_begin sparse_hessian_choose_row_col dev} Randomly choose Hessian row and column indices ############################################## Namespace ********* This function is in the empty namespace; i.e., it is only accessed by functions in this file. Prototype ********* {xrst_literal // BEGIN_choose_row_col // END_choose_row_col } n * is number of rows and columns in the Hessian. row *** The input size and elements of row do not matter. Upon return it is the chosen row indices. col *** The input size and elements of col do not matter. Upon return it is the chosen column indices. Order ***** The return row and column values are in row major order. Diagonal ******** The diagonal is included in the result; i.e., for each *i* between zero and *n* ``-1`` , there is a *k* such that *row* [ *k* ] == *col* [ *k* ] == *i* . {xrst_end sparse_hessian_choose_row_col} */ // BEGIN_choose_row_col void choose_row_col( size_t n , CppAD::vector& row , CppAD::vector& col ) // END_choose_row_col { using CppAD::vector; // maximum number of entries per row size_t max_per_row = 5; // random choices for each row, and correspond sort order vector random_01(max_per_row); vector random_index(max_per_row), order(max_per_row); // generate the row and column indices row.resize(0); col.resize(0); for(size_t i = 0; i < n; i++) { // generate max_per_row random values between 0 and 1 CppAD::uniform_01(max_per_row, random_01); // make sure the diagonal is in the result because // sparse_hes_fun requires it random_index[0] = i; // convert to column indices between 0 and i for(size_t k = 1; k < max_per_row; ++k) { random_index[k] = size_t( random_01[k] * double(i) ); random_index[k] = std::min(random_index[k], i); } // determine the sort order for the indices CppAD::index_sort(random_index, order); // set the indices for this row for(size_t k = 0; k < max_per_row; k++) { size_t j = random_index[ order[k] ]; bool ok = k == 0; if( ! ok ) ok = random_index[ order[k-1] ] < j; if( ok ) { row.push_back(i); col.push_back(j); } } } } } // END_EMPTY_NAMESPACE /* ------------------------------------------------------------------------------ {xrst_begin info_sparse_hessian dev} Sparse Hessian Speed Test Information ##################################### Namespace ********* This function is in the global namespace, not the CppAD namespace. Syntax ****** | ``info_spares_hessian`` ( *size* , *n_color* ) size **** This ``size_t`` value is equal to :ref:`speed_time_callback@size` in the corresponding call to ``time_sparse_hessian_callback`` . n_color ******* The input value of this *size_t* does not matter. Upon return, it is the value :ref:`link_sparse_hessian@n_color` returned by the corresponding call to ``link_sparse_hessian`` . {xrst_end info_sparse_hessian} */ void info_sparse_hessian(size_t size, size_t& n_color) { size_t n = size; size_t repeat = 1; vector row, col; choose_row_col(n, row, col); // note that cppad/speed/sparse_hessian.cpp assumes that x.size() // is the size corresponding to this test vector x(n); size_t K = row.size(); vector hessian(K); link_sparse_hessian(n, repeat, row, col, x, hessian, n_color); return; } // --------------------------------------------------------------------------- // The routines below are documented in dev_link.omh // --------------------------------------------------------------------------- namespace { void time_sparse_hessian_callback(size_t size, size_t repeat) { static size_t previous_size = 0; static vector row, col; // // free statically allocated memory if( size == 0 && repeat == 0 ) { row.clear(); col.clear(); previous_size = size; return; } size_t n = size; vector x(n); if( size != previous_size ) { choose_row_col(n, row, col); previous_size = size; } size_t K = row.size(); vector hessian(K); # ifndef NDEBUG for(size_t k = 0; k < K; k++) CPPAD_ASSERT_UNKNOWN( col[k] <= row[k] ); # endif // note that cppad/sparse_hessian.cpp assumes that x.size() == size size_t n_color; link_sparse_hessian(n, repeat, row, col, x, hessian, n_color); return; } } // --------------------------------------------------------------------------- bool available_sparse_hessian(void) { size_t n = 2; size_t repeat = 1; vector x(n); vector row, col; choose_row_col(n, row, col); size_t K = row.size(); vector hessian(K); size_t n_color; return link_sparse_hessian(n, repeat, row, col, x, hessian, n_color); } // --------------------------------------------------------------------------- bool correct_sparse_hessian(bool is_package_double) { double eps99 = 99.0 * std::numeric_limits::epsilon(); size_t n = 10; size_t repeat = 1; vector x(n); vector row, col; choose_row_col(n, row, col); size_t K = row.size(); vector hessian(K); # ifndef NDEBUG for(size_t k = 0; k < K; k++) CPPAD_ASSERT_UNKNOWN( col[k] <= row[k] ); # endif // The double package assumes hessian.size() >= 1 CPPAD_ASSERT_UNKNOWN( K >= 1 ); size_t n_color; link_sparse_hessian(n, repeat, row, col, x, hessian, n_color); size_t order, size; if( is_package_double) { order = 0; // check function value size = 1; } else { order = 2; // check hessian value size = K; } CppAD::vector check(size); CppAD::sparse_hes_fun(n, x, row, col, order, check); bool ok = true; size_t k; for(k = 0; k < size; k++) ok &= CppAD::NearEqual(check[k], hessian[k], eps99, eps99); return ok; } // --------------------------------------------------------------------------- double time_sparse_hessian(double time_min, size_t size) { double time = CppAD::time_test( time_sparse_hessian_callback, time_min, size ); time_sparse_hessian_callback(0, 0); return time; } ================================================ FILE: speed/src/link_sparse_hessian.hpp ================================================ # ifndef CPPAD_SPEED_SRC_LINK_SPARSE_HESSIAN_HPP # define CPPAD_SPEED_SRC_LINK_SPARSE_HESSIAN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include // BEGIN PROTOTYPE extern bool link_sparse_hessian( size_t size , size_t repeat , const CppAD::vector& row , const CppAD::vector& col , CppAD::vector& x , CppAD::vector& hessian , size_t& n_color ); // END PROTOTYPE /* ------------------------------------------------------------------------------- {xrst_begin link_sparse_hessian} Link to Speed Test Sparse Hessian ################################# Syntax ****** | *ok* = ``link_sparse_hessian`` ( | |tab| |tab| *size* , *repeat* , *row* , *col* , *x* , *hessian* , *n_color* | ) Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Namespace ********* The function ``link_sparse_hessian`` is in the global namespace, not the ``CppAD`` namespace. Method ****** Given a row index vector :math:`row` and a second column vector :math:`col`, the corresponding function :math:`f : \B{R}^n \rightarrow \B{R}` is defined by :ref:`sparse_hes_fun-name` . The non-zero entries in the Hessian of this function have one of the following forms: .. math:: \DD{f}{x[row[k]]}{x[row[k]]} \; , \; \DD{f}{x[row[k]]}{x[col[k]]} \; , \; \DD{f}{x[col[k]]}{x[row[k]]} \; , \; \DD{f}{x[col[k]]}{x[col[k]]} for some :math:`k` between zero and :math:`K-1`. All the other terms of the Hessian are zero. Sparsity Pattern **************** The combination of *row* and *col* determine the sparsity pattern for the Hessian that is computed. The calculation of this sparsity pattern, if necessary to compute the Hessian, is intended to be part of the timing for this test. size **** The argument *size* , referred to as :math:`n` below, is the dimension of the domain space for :math:`f(x)`. repeat ****** The argument *repeat* is the number of times to repeat the test (with a different value for *x* corresponding to each repetition). x * The size of *x* is :math:`n`; i.e., *x* . ``size`` () == *size* . The input value of the elements of *x* does not matter. On output, it has been set to the argument value for which the function, or its derivative, is being evaluated. The value of this vector need not change with each repetition. row *** The size of the vector *row* defines the value :math:`K`. The input value of its elements does not matter. On output, all the elements of *row* are between zero and :math:`n-1`. col *** The argument *col* is a vector with size :math:`K`. The input value of its elements does not matter. On output, all the elements of *col* are between zero and :math:`n-1`. Row Major ********* The indices *row* and *col* are in row major order; i.e., for each *k* < *row* . ``size`` () ``-2`` *row* [ *k* ] <= *row* [ *k* +1] and if *row* [ *k* ] == *row* [ *k* +1] then *col* [ *k* ] < *col* [ *k* +1] Lower Triangular **************** Only the lower triangle of the Hessian is included. *col* [ *k* ] <= *row* [ *k* ] . hessian ******* The size of *hessian* is *K* . The input value of its elements does not matter. The output value of its elements is the Hessian of the function :math:`f(x)`. To be more specific, for :math:`k = 0 , \ldots , K-1`, .. math:: \DD{f}{ x[ \R{row}[k] ] }{ x[ \R{col}[k] ]} = \R{hessian} [k] n_color ******* The input value of *n_color* does not matter. On output, it is the value :ref:`sparse_hessian@n_sweep` corresponding to the evaluation of *hessian* . This is also the number of colors corresponding to the :ref:`coloring method` , which can be set to :ref:`speed_main@Sparsity Options@colpack` , and is otherwise ``cppad`` . double ====== In the case where *package* is ``double`` , only the first element of *hessian* is used and it is actually the value of :math:`f(x)` (derivatives are not computed). {xrst_end link_sparse_hessian} ----------------------------------------------------------------------------- */ # endif ================================================ FILE: speed/src/link_sparse_jacobian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include # include "link_sparse_jacobian.hpp" namespace { // BEGIN_EMPTY_NAMESPACE static CppAD::vector info_size_vec_; static CppAD::vector info_n_color_vec_; // static size_t callback_size_ = 0; static size_t callback_n_color_ = 0; static CppAD::vector callback_row_; static CppAD::vector callback_col_; /* ------------------------------------------------------------------------------ {xrst_begin sparse_jacobian_choose_row_col dev} Randomly Choose Row and Column Indices for Sparse Jacobian ########################################################## Prototype ********* {xrst_literal // BEGIN_CHOOSE_ROW_COL // END_CHOOSE_ROW_COL } n * is the dimension of the domain space for the function f(x). m * is the dimension of the range space for the function f(x). row *** The input size and elements of *row* do not matter. Upon return it is the chosen row indices. col *** The input size and elements of *col* do not matter. Upon return it is the chosen column indices. Row Major ********* The result is in row major order. {xrst_end sparse_jacobian_choose_row_col} */ // BEGIN_CHOOSE_ROW_COL void choose_row_col( size_t n , size_t m , CppAD::vector& row , CppAD::vector& col ) // END_CHOOSE_ROW_COL { using CppAD::vector; // // get random numbers for row and column indices size_t K = 5 * std::max(m, n); vector random(2 * K); CppAD::uniform_01(2 * K, random); // // sort the temporary row and column choices vector key(K); vector ind(K); for(size_t k = 0; k < K; k++) { // convert from [0,1] to row index // avoid warning when converting double to size_t size_t r = size_t( float( double(m) * random[k] ) ); r = std::min(m-1, r); // convert from [0,1] to column index size_t c = size_t( float( double(n) * random[k + K] ) ); c = std::min(n-1, c); // // key in row major order key[k] = c + n * r; } CppAD::index_sort(key, ind); // // remove duplicates and set row, col in row major order row.resize(0); col.resize(0); size_t k = ind[0]; size_t c_previous = key[k] % n; size_t r_previous = key[k] / n; CPPAD_ASSERT_UNKNOWN( r_previous < m && c_previous < n ); CPPAD_ASSERT_UNKNOWN( key[k] == c_previous + n * r_previous ); row.push_back(r_previous); col.push_back(c_previous); for(size_t ell = 1; ell < K; ell++) { k = ind[ell]; size_t c = key[k] % n; size_t r = key[k] / n; CPPAD_ASSERT_UNKNOWN( key[k] == c + n * r ); CPPAD_ASSERT_UNKNOWN( r < m && c < n ); if( r != r_previous || c != c_previous) { row.push_back(r); col.push_back(c); } r_previous = r; c_previous = c; } # ifndef NDEBUG size_t nnz = row.size(); CPPAD_ASSERT_UNKNOWN( nnz > 0 ); r_previous = row[0]; c_previous = col[0]; for(k = 1; k < nnz; ++k) { CPPAD_ASSERT_UNKNOWN( r_previous <= row[k] ); if( r_previous == row[k] ) CPPAD_ASSERT_UNKNOWN( c_previous <= col[k] ); r_previous = row[k]; c_previous = col[k]; } # endif } } // END_EMPTY_NAMESPACE /* ------------------------------------------------------------------------------ {xrst_begin info_sparse_jacobian dev} Sparse Jacobian Speed Test Information ###################################### Namespace ********* This function is in the global namespace, not the CppAD namespace. Prototype ********* {xrst_literal // BEGIN_INFO_SPARSE_JACOBIAN // END_INFO_SPARSE_JACOBIAN } size_vec ******** The input value of *size_vec* does not matter. Upon return, it contains the values (in order) of :ref:`speed_time@size` in the previous call to ``time_sparse_jacobian`` . Only calls since the previous call to ``info_sparse_jacobian`` are included. n_color ******* The input value of *n_color_vec* does not matter. Upon return, it has the same size as *size_vec* . The value *n_color* [ *j* ] is the value of :ref:`link_sparse_jacobian@n_color` returned by a call to ``link_sparse_jacobian`` with size equal to *size_vec* [ *j* ] . {xrst_end info_sparse_jacobian} */ // BEGIN_INFO_SPARSE_JACOBIAN void info_sparse_jacobian( CppAD::vector& size_vec , CppAD::vector& n_color_vec ) // END_INFO_SPARSE_JACOBIAN { using CppAD::vector; // size_vec.clear(); n_color_vec.clear(); // size_vec.swap( info_size_vec_ ); n_color_vec.swap( info_n_color_vec_ ); // return; } // --------------------------------------------------------------------------- // The routines below are documented in dev_link.omh // --------------------------------------------------------------------------- namespace { void time_sparse_jacobian_callback(size_t size, size_t repeat) { using CppAD::vector; CPPAD_ASSERT_UNKNOWN( callback_size_ == size ); // size_t n = size; size_t m = 2 * n; vector x(n); size_t nnz = callback_row_.size(); vector jacobian(nnz); // std::string job = "run"; link_sparse_jacobian( job, n, repeat, m, callback_row_, callback_col_, x, jacobian, callback_n_color_ ); return; } } // --------------------------------------------------------------------------- bool available_sparse_jacobian(void) { using CppAD::vector; // size_t n = 3; size_t m = 2 * n; vector row, col; choose_row_col(n, m, row, col); // size_t repeat = 1; vector x(n); size_t nnz = row.size(); vector jacobian(nnz); size_t n_color; // std::string job = "setup"; bool result = link_sparse_jacobian( job, n, repeat, m, row, col, x, jacobian, n_color ); // job = "teardown"; link_sparse_jacobian( job, n, repeat, m, row, col, x, jacobian, n_color ); // return result; } // ---------------------------------------------------------------------------- bool correct_sparse_jacobian(bool is_package_double) { using CppAD::vector; bool ok = true; double eps = 10. * CppAD::numeric_limits::epsilon(); // size_t n = 10; size_t m = 2 * n; vector row, col; choose_row_col(n, m, row, col); // // The double package assumes jacobian.size() >= m size_t nnz = row.size(); CPPAD_ASSERT_UNKNOWN( nnz >= m ); // size_t repeat = 1; vector x(n); vector jacobian(nnz); size_t n_color; // ---------------------------------------------------------------------- // compute jacobian std::string job = "setup"; link_sparse_jacobian( job, n, repeat, m, row, col, x, jacobian, n_color ); // job = "run"; link_sparse_jacobian( job, n, repeat, m, row, col, x, jacobian, n_color ); // ---------------------------------------------------------------------- // check result if( is_package_double) { // check f(x) size_t order = 0; vector check(m); CppAD::sparse_jac_fun(m, n, x, row, col, order, check); for(size_t i = 0; i < m; i++) ok &= CppAD::NearEqual(check[i], jacobian[i], eps, eps); } else { // check f'(x) size_t order = 1; vector check(nnz); CppAD::sparse_jac_fun(m, n, x, row, col, order, check); for(size_t k = 0; k < nnz; k++) ok &= CppAD::NearEqual(check[k], jacobian[k], eps, eps); } // ----------------------------------------------------------------------- job = "teardown"; link_sparse_jacobian( job, n, repeat, m, row, col, x, jacobian, n_color ); return ok; } // ---------------------------------------------------------------------------- double time_sparse_jacobian(double time_min, size_t size) { CPPAD_ASSERT_UNKNOWN( size != 0 ); using CppAD::vector; // // set callback_row_, callback_col_, callback_size_ callback_size_ = size; size_t n = size; size_t m = 2 * n; choose_row_col(n, m, callback_row_, callback_col_); // size_t repeat = 1; vector x(n); size_t nnz = callback_row_.size(); vector jacobian(nnz); size_t n_color; // std::string job = "setup"; link_sparse_jacobian( job, n, repeat, m, callback_row_, callback_col_, x, jacobian, n_color ); // // job is run in time_sparse_jacoabian_callback double time = CppAD::time_test( time_sparse_jacobian_callback, time_min, size ); if( callback_n_color_ == 0 ) callback_n_color_ = n_color; job = "teardown"; link_sparse_jacobian( job, n, repeat, m, callback_row_, callback_col_, x, jacobian, n_color ); if( callback_n_color_ == 0 ) callback_n_color_ = n_color; // // memory allocated for callback_row_, callback_col_ callback_size_ = 0; callback_row_.clear(); callback_col_.clear(); // info_size_vec_.push_back(size); info_n_color_vec_.push_back(callback_n_color_); callback_n_color_ = 0; // return time; } ================================================ FILE: speed/src/link_sparse_jacobian.hpp ================================================ # ifndef CPPAD_SPEED_SRC_LINK_SPARSE_JACOBIAN_HPP # define CPPAD_SPEED_SRC_LINK_SPARSE_JACOBIAN_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include // BEGIN PROTOTYPE extern bool link_sparse_jacobian( const std::string& job , size_t size , size_t repeat , size_t m , const CppAD::vector& row , const CppAD::vector& col , CppAD::vector& x , CppAD::vector& jacobian , size_t& n_color ); // END PROTOTYPE /* ------------------------------------------------------------------------------ {xrst_begin link_sparse_jacobian} Speed Testing Sparse Jacobians ############################## Prototype ********* {xrst_literal // BEGIN PROTOTYPE // END PROTOTYPE } Method ****** Given a range space dimension *m* the row index vector :math:`row`, and column index vector :math:`col`, a corresponding function :math:`f : \B{R}^n \rightarrow \B{R}^m` is defined by :ref:`sparse_jac_fun-name` . The non-zero entries in the Jacobian of this function have the form .. math:: \D{f[row[k]]}{x[col[k]]]} for some :math:`k` between zero and *K* = *row* . ``size`` () ``-1`` . All the other terms of the Jacobian are zero. Sparsity Pattern **************** The combination of *row* and *col* determine the sparsity pattern for the Jacobian that is differentiated. The calculation of this sparsity pattern, if necessary to compute the Jacobian, is intended to be part of the timing for this test. job *** See the standard link specifications for :ref:`link_routines@job` . size **** See the standard link specifications for :ref:`link_routines@size` . In addition, *size* is referred to as :math:`n` below, is the dimension of the domain space for :math:`f(x)`. repeat ****** See the standard link specifications for :ref:`link_routines@repeat` . m * Is the dimension of the range space for the function :math:`f(x)`. row *** The size of the vector *row* defines the value :math:`K`. The input value of its elements does not matter. On output, all the elements of *row* are between zero and :math:`m-1`. col *** The argument *col* is a vector with size :math:`K`. The input value of its elements does not matter. On output, all the elements of *col* are between zero and :math:`n-1`. Row Major ********* The indices *row* and *col* are in row major order; i.e., for each *k* < *row* . ``size`` () ``-2`` *row* [ *k* ] <= *row* [ *k* +1] and if *row* [ *k* ] == *row* [ *k* +1] then *col* [ *k* ] < *col* [ *k* +1] x * The argument *x* has prototype ``CppAD::vector&`` *x* and its size is :math:`n`; i.e., *x* . ``size`` () == *size* . The input value of the elements of *x* does not matter. On output, it has been set to the argument value for which the function, or its derivative, is being evaluated and placed in *jacobian* . The value of this vector need not change with each repetition. jacobian ******** The argument *jacobian* has prototype ``CppAD::vector&`` *jacobian* and its size is *K* . The input value of its elements does not matter. The output value of its elements is the Jacobian of the function :math:`f(x)`. To be more specific, for :math:`k = 0 , \ldots , K - 1`, .. math:: \D{f[ \R{row}[k] ]}{x[ \R{col}[k] ]} (x) = \R{jacobian} [k] n_color ******* The input value of *n_color* does not matter. On output, it has value zero or :ref:`sparse_jacobian@n_sweep` corresponding to the evaluation of *jacobian* . This is also the number of colors corresponding to the :ref:`coloring method` , which can be set to :ref:`speed_main@Sparsity Options@colpack` , and is otherwise ``cppad`` . If this routine returns an non-zero *n_color* for any *job* value, the non-zero value will be reported for this test. double ====== In the case where *package* is ``double`` , only the first :math:`m` elements of *jacobian* are used and they are set to the value of :math:`f(x)`. {xrst_end link_sparse_jacobian} ----------------------------------------------------------------------------- */ # endif ================================================ FILE: speed/xpackage/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the speed/xpackage directory tests # Inherit build type from ../CMakeList.txt # Set the install prefix for this package SET(xpackage_prefix "/usr") # Adds flags to the compiler command line for sources in the current directory # and below. This command can be used to add any flags, but it was originally # intended to add preprocessor definitions. ADD_DEFINITIONS("-DCPPAD_XPACKAGE_SPEED") # Local include directories to search (not in package_prefix/includdir) INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../src ${xpackage_prefix}/include ) # add_executable( [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] # source1 source2 ... sourceN # ) SET(source_list ../main.cpp det_lu.cpp det_minor.cpp mat_mul.cpp ode.cpp poly.cpp sparse_hessian.cpp sparse_jacobian.cpp ) set_compile_flags( speed_xpackage "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE( speed_xpackage EXCLUDE_FROM_ALL ${source_list} ) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(speed_xpackage ${cppad_lib} ${colpack_libs} ) # xpackage is an include file only library TARGET_LINK_LIBRARIES(speed_xpackage speed_src ) # check_speed_xpackage add_check_executable(check_speed xpackage "correct 54321") ================================================ FILE: speed/xpackage/det_lu.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin xpackage_det_lu.cpp} Xpackage Speed: Gradient of Determinant Using Lu Factorization ############################################################## Specifications ************** :ref:`link_det_lu-name` Implementation ************** A xpackage version of this test is not yet implemented {xrst_spell_off} {xrst_code cpp} */ # include # include // list of possible options extern std::map global_option; bool link_det_lu( size_t size , size_t repeat , CppAD::vector &matrix , CppAD::vector &gradient ) { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end xpackage_det_lu.cpp} */ ================================================ FILE: speed/xpackage/det_minor.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin xpackage_det_minor.cpp} Xpackage Speed: Gradient of Determinant by Minor Expansion ########################################################## Specifications ************** :ref:`link_det_minor-name` Implementation ************** // a xpackage version of this test is not yet implemented {xrst_spell_off} {xrst_code cpp} */ # include # include // list of possible options extern std::map global_option; bool link_det_minor( const std::string& job , size_t size , size_t repeat , CppAD::vector &matrix , CppAD::vector &gradient ) { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end xpackage_det_minor.cpp} */ ================================================ FILE: speed/xpackage/mat_mul.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin xpackage_mat_mul.cpp} Xpackage Speed: Matrix Multiplication ##################################### Specifications ************** :ref:`link_mat_mul-name` Implementation ************** // a xpackage version of this test is not yet implemented {xrst_spell_off} {xrst_code cpp} */ # include # include // list of possible options extern std::map global_option; bool link_mat_mul( size_t size , size_t repeat , CppAD::vector& x , CppAD::vector& z , CppAD::vector& dz ) { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end xpackage_mat_mul.cpp} */ ================================================ FILE: speed/xpackage/ode.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin xpackage_ode.cpp} Xpackage Speed: Ode ################### Specifications ************** :ref:`link_ode-name` Implementation ************** // a xpackage version of this test is not yet implemented {xrst_spell_off} {xrst_code cpp} */ # include # include // list of possible options extern std::map global_option; bool link_ode( size_t size , size_t repeat , CppAD::vector &x , CppAD::vector &jacobian ) { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end xpackage_ode.cpp} */ ================================================ FILE: speed/xpackage/poly.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* {xrst_begin xpackage_poly.cpp} Xpackage Speed: Second Derivative of a Polynomial ################################################# Specifications ************** :ref:`link_poly-name` Implementation ************** // a xpackage version of this test is not yet implemented {xrst_spell_off} {xrst_code cpp} */ # include # include // list of possible options extern std::map global_option; bool link_poly( size_t size , size_t repeat , CppAD::vector &a , // coefficients of polynomial CppAD::vector &z , // polynomial argument value CppAD::vector &ddp ) // second derivative w.r.t z { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end xpackage_poly.cpp} */ ================================================ FILE: speed/xpackage/sparse_hessian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include /* {xrst_begin xpackage_sparse_hessian.cpp} Xpackage Speed: Sparse Hessian ############################## Specifications ************** :ref:`link_sparse_hessian-name` {xrst_spell_off} {xrst_code cpp} */ // A xpackage version of this test is not yet available bool link_sparse_hessian( size_t size , size_t repeat , const CppAD::vector& row , const CppAD::vector& col , CppAD::vector& x , CppAD::vector& hessian , size_t& n_color ) { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end xpackage_sparse_hessian.cpp} */ ================================================ FILE: speed/xpackage/sparse_jacobian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include /* {xrst_begin xpackage_sparse_jacobian.cpp} Xpackage Speed: sparse_jacobian ############################### Specifications ************** :ref:`link_sparse_jacobian-name` {xrst_spell_off} {xrst_code cpp} */ // A xpackage version of this test is not yet available bool link_sparse_jacobian( const std::string& job , size_t size , size_t repeat , size_t m , const CppAD::vector& row , const CppAD::vector& col , CppAD::vector& x , CppAD::vector& jacobian , size_t& n_color ) { return false; } /* {xrst_code} {xrst_spell_on} {xrst_end xpackage_sparse_jacobian.cpp} */ ================================================ FILE: speed/xpackage/speed_xpackage.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin speed_xpackage} {xrst_spell cp grep ifdef sed txt } Speed Test Example and Template for a New Package ################################################# Purpose ******* CppAD has a set of speed tests that are used to compare AD packages. This section can be used as a starting point for linking a new package to he CppAD speed tests. Files ***** Use the following command, in the :ref:`download@Distribution Directory` , to see which files link the example package ``xpackage`` to the speed tests: ``git grep -l xpackage`` Template ******** Suppose *your_package* is the name of a package you want to add to the speed tests. If you are using a Unix system, executing the following commands in the distribution directory will use ``xpackage`` as a template for your package: | |tab| ``cp -r speed/xpackage speed/`` *your_package* | |tab| ``for file in`` ` ``ls speed/`` *your_package* ` | |tab| ``do`` | |tab| |tab| ``sed -i speed/`` *your_package* / ``$file`` \\ | |tab| |tab| |tab| ``-e`` ' ``s/xpackage/`` *your_package* /' \\ | |tab| |tab| |tab| ``-e`` ' ``s/Xpackage/`` *Your_package* /' \\ | |tab| |tab| |tab| ``-e`` ' ``s/CPPAD_XPACKAGE_SPEED/`` *YOUR_PACKAGE* /' | |tab| ``done`` | |tab| ``git checkout speed/CMakeLists.txt`` | |tab| ``sed -i speed/CMakeLists.txt`` \\ | |tab| |tab| ``-e`` ' ``s/^.`` * ( ``xpackage`` )/ ``ADD_SUBDIRECTORY`` ( *your_package* )\\ ``n&/`` ' | |tab| ``git checkout speed/main.cpp`` | |tab| ``line1`` ='# ``ifdef CPPAD_`` *YOUR_PACKAGE* _ ``SPEED`` ' | |tab| ``line2`` ='# ``define AD_PACKAGE`` " *your_package* "' | |tab| ``line3`` ='# ``endif`` ' | |tab| ``sed -i speed/main.cpp`` \\ | |tab| |tab| ``-e`` "/ ``CPPAD_XPACKAGE_SPEED/s/^/$line1\n$line2\n$line3\n/`` " where *your_package* has been replaced by the name of the new package *Your_package* is a capitalized version of the name, and *YOUR_PACKAGE* is an all caps version of the name. Running Tests ************* Starting in the distribution directory, the following commands will build the new package version of the tests: | |tab| ``bin/run_cmake.sh --no_optional`` | |tab| ``cd build/speed/`` *your_package* | |tab| ``make check_speed_`` *your_package* ``VERBOSE`` =1 This should result in the following output: | |tab| ... | |tab| *your_package* _ ``det_lu_available`` = ``false`` | |tab| *your_package* _ ``det_minor_available`` = ``false`` | |tab| *your_package* _ ``mat_mul_available`` = ``false`` | |tab| *your_package* _ ``ode_available`` = ``false`` | |tab| *your_package* _ ``poly_available`` = ``false`` | |tab| *your_package* _ ``sparse_hessian_available`` = ``false`` | |tab| *your_package* _ ``sparse_jacobian_available`` = ``false`` | |tab| ``All 0 correctness tests passed.`` | |tab| ``No memory leak detected`` | |tab| ``speed main: OK`` | |tab| [100%] ``Built target check_speed_`` *your_package* You can not edit one or more of the * . ``cpp`` files in the *your_package* directory so that the corresponding speed test is available and then run the corresponding test using the :ref:`speed_main-name` instructions. See :ref:`speed_cppad-name` for examples of how to do this for each of the speed tests. Contents ******** {xrst_toc_list speed/xpackage/det_minor.cpp speed/xpackage/det_lu.cpp speed/xpackage/mat_mul.cpp speed/xpackage/ode.cpp speed/xpackage/poly.cpp speed/xpackage/sparse_hessian.cpp speed/xpackage/sparse_jacobian.cpp } {xrst_end speed_xpackage} ================================================ FILE: test_more/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the test_more directory tests # initialize check_depends SET(check_test_more_depends "") # deprecated tests ADD_SUBDIRECTORY(deprecated) # compare_c tests # clange-cl.exe seems to be confused about C files and gives an error message # saying that C++ forbids macroizing the keywords bool, true, and false. STRING(REGEX MATCH ".*[/\]clang-cl.exe" match "${CMAKE_C_COMPILER}" ) IF( NOT "${match}" STREQUAL "${CMAKE_C_COMPILER}" ) ADD_SUBDIRECTORY(compare_c) ENDIF( ) # # debug_rel tests IF( NOT "${CMAKE_GENERATOR}" STREQUAL "NMake Makefiles" ) # Visual studio rejects mixing debug and release flags ADD_SUBDIRECTORY(debug_rel) ENDIF( NOT "${CMAKE_GENERATOR}" STREQUAL "NMake Makefiles" ) # # cppad_for_tmb tests IF( OpenMP_CXX_FOUND ) ADD_SUBDIRECTORY(cppad_for_tmb) ENDIF( OpenMP_CXX_FOUND ) # general tests ADD_SUBDIRECTORY(general) # Add the check_example target ADD_CUSTOM_TARGET(check_test_more "" DEPENDS ${check_test_more_depends}) MESSAGE(STATUS "make check_test_more: available") # Change check depends in parent environment add_to_list(check_depends check_test_more) SET(check_depends "${check_depends}" PARENT_SCOPE) ================================================ FILE: test_more/compare_c/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # {xrst_begin compare_c app} # {xrst_comment_ch #} # # Compare Speed of C and C++ # ########################## # # Syntax # ****** # # ``test_more/compare_c/det_by_minor_c`` # # ``test_more/compare_c/det_by_minor_cpp`` # # Purpose # ******* # Compares the speed of the exact same source code compiled # using C versus C++. # # Contents # ******** # {xrst_toc_table # test_more/compare_c/det_by_minor.c # } # # {xrst_end compare_c} # ----------------------------------------------------------------------------- # # use cppad_debug_which to determine build type IF( "${cppad_debug_which}" STREQUAL debug_all ) SET(CMAKE_BUILD_TYPE DEBUG) SET(all_cxx_flags "${cppad_cxx_flags} ${CMAKE_CXX_FLAGSS_DEBUG}") ELSEIF( "${cppad_debug_which}" STREQUAL debug_odd ) SET(CMAKE_BUILD_TYPE DEBUG) SET(all_cxx_flags "${cppad_cxx_flags} ${CMAKE_CXX_FLAGSS_DEBUG}") ELSE( "${cppad_debug_which}" STREQUAL debug_odd ) SET(CMAKE_BUILD_TYPE RELEASE) SET(all_cxx_flags "${cppad_cxx_flags} ${CMAKE_CXX_FLAGSS_RELEASE}") ENDIF( "${cppad_debug_which}" STREQUAL debug_all ) # # initialize check_test_more_compare_c SET(check_test_more_compare_c_depends "") # # Microsofts C compiler does not seem to work as part of a C++ project STRING(FIND "${CMAKE_C_COMPILER_ID}" "MSVC" index) IF( "${index}" EQUAL "-1" ) ADD_SUBDIRECTORY(c) ENDIF( ) ADD_SUBDIRECTORY(cpp) # # check_test_more_compare_c target ADD_CUSTOM_TARGET( check_test_more_compare_c DEPENDS ${check_test_more_compare_c_depends} ) MESSAGE(STATUS "make check_test_more_compare_c: available") # Add check_test_more_compare_c to check depends in parent environment add_to_list(check_test_more_depends check_test_more_compare_c) SET(check_test_more_depends "${check_test_more_depends}" PARENT_SCOPE) ================================================ FILE: test_more/compare_c/c/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- # # CMAKE_C_FLAGS IF( ${cppad_link_flags_has_m32} ) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") ENDIF( ) # ADD_EXECUTABLE(test_more_compare_c_c EXCLUDE_FROM_ALL ../det_by_minor.c) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(test_more_compare_c_c ${cppad_lib} ${colpack_libs} ) SET_TARGET_PROPERTIES(test_more_compare_c_c PROPERTIES LINK_OPTIONS "${cppad_link_flags}" ) # # check_test_more_compare_c_c add_check_executable(check_test_more_compare_c c) ================================================ FILE: test_more/compare_c/cpp/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- # # det_by_minor.cpp CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/../det_by_minor.c ${CMAKE_CURRENT_BINARY_DIR}/det_by_minor.cpp COPYONLY ) ADD_EXECUTABLE(test_more_compare_c_cpp EXCLUDE_FROM_ALL det_by_minor.cpp) # SET_TARGET_PROPERTIES( test_more_compare_c_cpp PROPERTIES COMPILE_FLAGS "${all_cxx_flags}" ) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(test_more_compare_c_cpp ${cppad_lib} ${colpack_libs} ) SET_TARGET_PROPERTIES(test_more_compare_c_cpp PROPERTIES LINK_OPTIONS "${cppad_link_flags}" ) # # check_test_more_compare_c_cpp add_check_executable(check_test_more_compare_c cpp) ================================================ FILE: test_more/compare_c/det_by_minor.c ================================================ /* $Id: */ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include # include # include // In the case of plain C, we defined the type bool together with true, false # ifndef __cplusplus # define bool int # define true 1 # define false 0 # endif /* ------------------------------------------------------------------------------- {xrst_begin det_of_minor_c app} Determinant of a Minor ###################### Syntax ****** | *d* = ``det_of_minor`` ( *a* , *m* , *n* , *r* , *c* ) Purpose ******* returns the determinant of a minor of the matrix :math:`A` using expansion by minors. The elements of the :math:`n \times n` minor :math:`M` of the matrix :math:`A` are defined, for :math:`i = 0 , \ldots , n-1` and :math:`j = 0 , \ldots , n-1`, by .. math:: M_{i,j} = A_{R(i), C(j)} where the functions :math:`R(i)` is defined by the :ref:`argument r` and :math:`C(j)` is defined by the :ref:`argument c` . This function is for example and testing purposes only. Expansion by minors is chosen as an example because it uses a lot of floating point operations yet does not require much source code (on the order of *m* factorial floating point operations and about 70 lines of source code including comments). This is not an efficient method for computing a determinant; for example, using an LU factorization would be better. Determinant of A **************** If the following conditions hold, the minor is the entire matrix :math:`A` and hence ``det_of_minor`` will return the determinant of :math:`A`: #. :math:`n = m`. #. for :math:`i = 0 , \ldots , m-1`, :math:`r[i] = i+1`, and :math:`r[m] = 0`. #. for :math:`j = 0 , \ldots , m-1`, :math:`c[j] = j+1`, and :math:`c[m] = 0`. a * The argument *a* has prototype ``const double`` * *a* and is a vector with size :math:`m * m`. The elements of the :math:`m \times m` matrix :math:`A` are defined, for :math:`i = 0 , \ldots , m-1` and :math:`j = 0 , \ldots , m-1`, by .. math:: A_{i,j} = a[ i * m + j] m * The argument *m* has prototype ``size_t`` *m* and is the size of the square matrix :math:`A`. n * The argument *n* has prototype ``size_t`` *n* and is the size of the square minor :math:`M`. r * The argument *r* has prototype ``size_t`` * *r* and is a vector with :math:`m + 1` elements. This vector defines the function :math:`R(i)` which specifies the rows of the minor :math:`M`. To be specific, the function :math:`R(i)` for :math:`i = 0, \ldots , n-1` is defined by .. math:: :nowrap: \begin{eqnarray} R(0) & = & r[m] \\ R(i+1) & = & r[ R(i) ] \end{eqnarray} All the elements of *r* must have value less than or equal *m* . The elements of vector *r* are modified during the computation, and restored to their original value before the return from ``det_of_minor`` . c * The argument *c* has prototype ``size_t`` * *c* and is a vector with :math:`m + 1` elements This vector defines the function :math:`C(i)` which specifies the rows of the minor :math:`M`. To be specific, the function :math:`C(i)` for :math:`j = 0, \ldots , n-1` is defined by .. math:: :nowrap: \begin{eqnarray} C(0) & = & c[m] \\ C(j+1) & = & c[ C(j) ] \end{eqnarray} All the elements of *c* must have value less than or equal *m* . The elements of vector *c* are modified during the computation, and restored to their original value before the return from ``det_of_minor`` . d * The result *d* has prototype ``double`` *d* and is equal to the determinant of the minor :math:`M`. Source Code *********** {xrst_spell_off} {xrst_code cpp} */ double det_of_minor( const double* a , size_t m , size_t n , size_t* r , size_t* c ) { size_t R0, Cj, Cj1, j; double detM, M0j, detS; int s; R0 = r[m]; /* R(0) */ Cj = c[m]; /* C(j) (case j = 0) */ Cj1 = m; /* C(j-1) (case j = 0) */ /* check for 1 by 1 case */ if( n == 1 ) return a[ R0 * m + Cj ]; /* initialize determinant of the minor M */ detM = 0.; /* initialize sign of factor for neat sub-minor */ s = 1; /* remove row with index 0 in M from all the sub-minors of M */ r[m] = r[R0]; /* for each column of M */ for(j = 0; j < n; j++) { /* element with index (0,j) in the minor M */ M0j = a[ R0 * m + Cj ]; /* remove column with index j in M to form next sub-minor S of M */ c[Cj1] = c[Cj]; /* compute determinant of the current sub-minor S */ detS = det_of_minor(a, m, n - 1, r, c); /* restore column Cj to representation of M as a minor of A */ c[Cj1] = Cj; /* include this sub-minor term in the summation */ if( s > 0 ) detM = detM + M0j * detS; else detM = detM - M0j * detS; /* advance to neat column of M */ Cj1 = Cj; Cj = c[Cj]; s = - s; } /* restore row zero to the minor representation for M */ r[m] = R0; /* return the determinant of the minor M */ return detM; } /* {xrst_code} {xrst_spell_on} {xrst_end det_of_minor_c} ------------------------------------------------------------------------------- {xrst_begin det_by_minor_c app} Compute Determinant using Expansion by Minors ############################################# Syntax ****** *d* = ``det_by_minor`` ( *a* , *n* ) Purpose ******* returns the determinant of the matrix :math:`A` using expansion by minors. The elements of the :math:`n \times n` minor :math:`M` of the matrix :math:`A` are defined, for :math:`i = 0 , \ldots , n-1` and :math:`j = 0 , \ldots , n-1`, by .. math:: M_{i,j} = A_{i, j} a * The argument *a* has prototype ``const double`` * *a* and is a vector with size :math:`m * m`. The elements of the :math:`m \times m` matrix :math:`A` are defined, for :math:`i = 0 , \ldots , m-1` and :math:`j = 0 , \ldots , m-1`, by .. math:: A_{i,j} = a[ i * m + j] m * The argument *m* has prototype ``size_t`` *m* and is the number of rows (and columns) in the square matrix :math:`A`. Source Code *********** {xrst_spell_off} {xrst_code cpp} */ double det_by_minor(double* a, size_t m) { size_t *r, *c, i; double value; r = (size_t*) malloc( (m+1) * sizeof(size_t) ); c = (size_t*) malloc( (m+1) * sizeof(size_t) ); assert(m <= 100); for(i = 0; i < m; i++) { r[i] = i+1; c[i] = i+1; } r[m] = 0; c[m] = 0; value = det_of_minor(a, m, m, r, c); free(r); free(c); return value; } /* {xrst_code} {xrst_spell_on} {xrst_end det_by_minor_c} -------------------------------------------------------------------------- {xrst_begin uniform_01_c app} Simulate a [0,1] Uniform Random Variate ####################################### Syntax ****** ``random_seed`` ( *seed* ) ``uniform_01`` ( *n* , *a* ) Purpose ******* This routine is used to create random values for speed testing purposes. seed **** The argument *seed* has prototype ``size_t`` *seed* It specifies a seed for the uniform random number generator. n * The argument *n* has prototype ``size_t`` *n* It specifies the number of elements in the random vector *a* . a * The argument *a* has prototype ``double`` * *a* . The input value of the elements of *a* does not matter. Upon return, the elements of *a* are set to values randomly sampled over the interval [0,1]. Source Code *********** {xrst_spell_off} {xrst_code cpp} */ void random_seed(size_t seed) { srand( (unsigned int) seed ); } void uniform_01(size_t n, double* a) { static double factor = 1. / (double) RAND_MAX; while(n--) a[n] = rand() * factor; } /* {xrst_code} {xrst_spell_on} {xrst_end uniform_01_c} ------------------------------------------------------------------------------ {xrst_begin correct_det_by_minor_c app} Correctness Test of det_by_minor Routine ######################################## Syntax ****** *flag* = ``correct_det_by_minor`` () flag **** The return value has prototype ``bool`` *flag* It value is ``1`` if the test passes and ``0`` otherwise. Source Code *********** {xrst_spell_off} {xrst_code cpp} */ bool correct_det_by_minor(void) { double a[9], det, check; double eps99 = 99.0 * DBL_EPSILON; random_seed(123); uniform_01(9, a); /* compute determinant using expansion by minors */ det = det_by_minor(a, 3); /* use expansion by minors to hand code the determinant */ check = 0.; check += a[0] * ( a[4] * a[8] - a[5] * a[7] ); check -= a[1] * ( a[3] * a[8] - a[5] * a[6] ); check += a[2] * ( a[3] * a[7] - a[4] * a[6] ); if( fabs(det / check - 1.0) < eps99 ) return true; return false; } /* {xrst_code} {xrst_spell_on} {xrst_end correct_det_by_minor_c} ------------------------------------------------------------------------------ {xrst_begin repeat_det_by_minor_c app} Repeat det_by_minor Routine A Specified Number of Times ####################################################### Syntax ****** ``repeat_det_by_minor`` ( *repeat* , *size* ) repeat ****** The argument has prototype ``size_t`` *repeat* It specifies the number of times to repeat the calculation. size **** The argument has prototype ``size_t`` *size* It specifies the number of rows (and columns) in the square matrix we are computing the determinant of. Source Code *********** {xrst_spell_off} {xrst_code cpp} */ void repeat_det_by_minor(size_t repeat, size_t size) { double *a; a = (double*) malloc( (size * size) * sizeof(double) ); while(repeat--) { uniform_01(size * size, a); det_by_minor(a, size); } free(a); return; } /* {xrst_code} {xrst_spell_on} {xrst_end repeat_det_by_minor_c} ------------------------------------------------------------------------------ {xrst_begin elapsed_seconds_c app} Returns Elapsed Number of Seconds ################################# Syntax ****** *s* = ``elapsed_seconds`` () Purpose ******* This routine is accurate to within .02 seconds It does not necessary work for time intervals that are greater than a day. s * is a ``double`` equal to the number of seconds since the first call to ``elapsed_seconds`` . Source Code *********** {xrst_spell_off} {xrst_code cpp} */ # if _MSC_VER // --------------------------------------------------------------------------- // Microsoft version of timer # ifndef NOMINMAX # define NOMINMAX // so windows.h does not define min and max as macros # endif # include # include double elapsed_seconds(void) { static bool first_ = true; static SYSTEMTIME st_; double hour, minute, second, milli, diff; SYSTEMTIME st; if( first_ ) { GetSystemTime(&st_); first_ = false; return 0.; } GetSystemTime(&st); hour = (double) st.wHour - (double) st_.wHour; minute = (double) st.wMinute - (double) st_.wMinute; second = (double) st.wSecond - (double) st_.wSecond; milli = (double) st.wMilliseconds - (double) st_.wMilliseconds; diff = 1e-3*milli + second + 60.*minute + 3600.*hour; if( diff < 0. ) diff += 3600.*24.; assert( 0 <= diff && diff < 3600.*24. ); return diff; } # else // --------------------------------------------------------------------------- // Unix version of timer # include double elapsed_seconds(void) { double sec, usec, diff; static bool first_ = true; static struct timeval tv_first; struct timeval tv; if( first_ ) { gettimeofday(&tv_first, NULL); first_ = false; return 0.; } gettimeofday(&tv, NULL); assert( tv.tv_sec >= tv_first.tv_sec ); sec = (double)(tv.tv_sec - tv_first.tv_sec); usec = (double)tv.tv_usec - (double)tv_first.tv_usec; diff = sec + 1e-6*usec; return diff; } # endif /* {xrst_code} {xrst_spell_on} {xrst_end elapsed_seconds_c} ----------------------------------------------------------------------------- {xrst_begin time_det_by_minor_c app} Determine Amount of Time to Execute det_by_minor ################################################ Syntax ****** *time* = ``time_test`` ( *size* , *time_min* ) Purpose ******* reports the amount of wall clock time for ``det_by_minor`` to compute the determinant of a square matrix. The *size* has prototype ``size_t`` *size* It specifies the number of rows (and columns) in the square matrix that the determinant is being calculated for. time_min ******** The argument *time_min* has prototype ``double`` *time_min* It specifies the minimum amount of time in seconds that the *test* routine should take. The calculations is repeated the necessary number of times so that this amount of execution time (or more) is reached. time **** The return value *time* has prototype ``double`` *time* and is the number of wall clock seconds that it took for ``det_by_minor`` to compute its determinant (plus overhead which includes choosing a random matrix). Source Code *********** {xrst_spell_off} {xrst_code cpp} */ double time_det_by_minor(size_t size, double time_min) { size_t repeat; double s0, s1, time; repeat = 0; s0 = elapsed_seconds(); s1 = s0; while( s1 - s0 < time_min ) { if( repeat == 0 ) repeat = 1; else repeat = 2 * repeat; s0 = elapsed_seconds(); repeat_det_by_minor(repeat, size); s1 = elapsed_seconds(); } time = (s1 - s0) / (double) repeat; return time; } /* {xrst_code} {xrst_spell_on} {xrst_end time_det_by_minor_c} ------------------------------------------------------------------------------ {xrst_begin main_compare_c app} Main Program For Comparing C and C++ Speed ########################################## Source Code *********** {xrst_spell_off} {xrst_code cpp} */ int main(void) { bool flag; size_t i; random_seed(123); printf("correct_det_by_minor: "); flag = correct_det_by_minor(); if( flag ) printf("OK\n"); else printf("Error\n"); for(i = 0; i < 5; i++) { double time_min = 1.0; size_t size = 2 + i * 2; int i_size = (int) size; printf("time_det_minor for %d x %d matrix = ", i_size, i_size); printf("%g\n", time_det_by_minor(size, time_min) ); } if( flag ) return 0; return 1; } /* {xrst_code} {xrst_spell_on} {xrst_end main_compare_c} */ ================================================ FILE: test_more/cppad_for_tmb/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- # # Name for this text SET(name cppad_for_tmb) # # Add extra compiler flags to cppad_cxx_flags so set_compile_flags uses them. # This change only affects the cppad_for_tmb tests (NO PARENT_SCOPE). SET(cppad_cxx_flags "${cppad_cxx_flags} ${OpenMP_CXX_FLAGS} -DCPPAD_FOR_TMB") # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list ${name}.cpp implicit_ctor.cpp multi_atomic_three.cpp multi_atomic_two.cpp multi_chkpoint_one.cpp multi_chkpoint_two.cpp prefer_reverse.cpp ) # END_SORT_THIS_LINE_MINUS_2 set_compile_flags( test_more_${name} "${cppad_debug_which}" "${source_list}" ) # # now that we have the properties, add the executable ADD_EXECUTABLE( test_more_${name} EXCLUDE_FROM_ALL ${source_list} ) TARGET_LINK_LIBRARIES(test_more_${name} PRIVATE ${cppad_lib} ${colpack_libs} ${OpenMP_CXX_LIBRARIES} ) # # Extra flags used by linker for openmp support SET(CMAKE_EXE_LINKER_FLAGS ${OpenMP_CXX_FLAGS} ${cppad_link_flags} ) # # check_test_more_${name} add_check_executable(check_test_more ${name}) ================================================ FILE: test_more/cppad_for_tmb/cppad_for_tmb.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include extern bool implicit_ctor(void); extern bool prefer_reverse(void); extern bool multi_atomic_two(void); extern bool multi_atomic_three(void); extern bool multi_chkpoint_one(void); extern bool multi_chkpoint_two(void); int main(void) { std::string group = "test_more/cppad_for_tmb"; size_t width = 30; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh Run( implicit_ctor, "implicit_ctor" ); Run( prefer_reverse, "prefer_reverse" ); Run( multi_atomic_two, "multi_atomic_two" ); Run( multi_atomic_three, "multi_atomic_three" ); Run( multi_chkpoint_one, "multi_chkpoint_one" ); Run( multi_chkpoint_two, "multi_chkpoint_two" ); // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } ================================================ FILE: test_more/cppad_for_tmb/implicit_ctor.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include // implicit constructor from double bool implicit_ctor(void) { using CppAD::AD; bool ok = true; // AD< AD > x = 5.0; ok &= Value(x) == 5.0; // return ok; } ================================================ FILE: test_more/cppad_for_tmb/multi_atomic_three.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include namespace { using CppAD::vector; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR( CppAD::AD ) ad_vector; typedef CppAD::ad_type_enum ad_type_enum; // -------------------------------------------------------------------- class long_sum_atomic : public CppAD::atomic_three { private: const size_t length_of_sum_; public: // constructor long_sum_atomic(const std::string& name, size_t length_of_sum) : CppAD::atomic_three(name) , length_of_sum_(length_of_sum) { } // for_type virtual bool for_type( const vector& parameter_x , const vector& type_x , vector& type_y ) { bool ok = parameter_x.size() == 1; ok &= type_x.size() == 1; ok &= type_y.size() == 1; if( ! ok ) return false; type_y[0] = type_x[0]; return true; } // forward virtual bool forward( const vector& parameter_x , const vector& type_x , size_t need_y , size_t order_low , size_t order_up , const vector& taylor_x , vector& taylor_y ) { // check for errors in usage bool ok = order_low == 0 && order_up == 0; ok &= taylor_x.size() == 1; ok &= taylor_y.size() == 1; ok &= type_x.size() <= 1; if( ! ok ) return false; // value information taylor_y[0] = 0.0; for(size_t i = 0; i < length_of_sum_; ++i) taylor_y[0] += taylor_x[0]; return true; } }; // -------------------------------------------------------------------- // inform CppAD if we are in parallel mode bool in_parallel(void) { return omp_in_parallel() != 0; } // // inform CppAD of the current thread number size_t thread_num(void) { return static_cast( omp_get_thread_num() ); } } // multi_thread_checkpoint bool multi_atomic_three(void) { bool ok = true; // OpenMP setup size_t num_threads = 4; // number of threads omp_set_dynamic(0); // turn off dynamic thread adjustment omp_set_num_threads( int(num_threads) ); // set number of OMP threads // check that multi-threading is possible on this machine if( omp_get_max_threads() < 2 ) { std::cout << "This machine does not support multi-threading: "; } // create checkpoint version of algorithm size_t n(1), m(1); ad_vector ax(n), ay(m); ax[0] = 2.0; size_t length_of_sum = 5000; long_sum_atomic atom_fun("long_sum", length_of_sum); // setup for using CppAD in parallel mode CppAD::thread_alloc::parallel_setup(num_threads, in_parallel, thread_num); CppAD::thread_alloc::hold_memory(true); CppAD::parallel_ad(); // place to hold result for each thread d_vector y(num_threads); for(size_t thread = 0; thread < num_threads; thread++) y[thread] = 0.0; # pragma omp parallel for for(int thread = 0; thread < int(num_threads); thread++) { ad_vector au(n), av(m); au[0] = 1.0; CppAD::Independent(au); atom_fun(au, av); CppAD::ADFun f(au, av); // d_vector x(n), v(m); x[0] = double( thread + 1 ); v = f.Forward(0, x); // // this assignment has false sharing; i.e., will case cache resets // (conversion avoids boost vector conversion warning) y[size_t(thread)] = v[0]; } // check the results for(size_t thread = 0; thread < num_threads; thread++) { double check = double( length_of_sum * (thread + 1) ); ok &= check == y[thread]; } return ok; } ================================================ FILE: test_more/cppad_for_tmb/multi_atomic_two.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include namespace { using CppAD::vector; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR( CppAD::AD ) ad_vector; // -------------------------------------------------------------------- class long_sum_atomic : public CppAD::atomic_base { private: const size_t length_of_sum_; public: // constructor long_sum_atomic(const std::string& name, size_t length_of_sum) : CppAD::atomic_base(name) , length_of_sum_(length_of_sum) { } // forward (only implement zero order) virtual bool forward( size_t p , size_t q , const vector& vx , vector& vy , const vector& tx , vector& ty ) { // check for errors in usage bool ok = p == 0 && q == 0; ok &= tx.size() == 1; ok &= ty.size() == 1; ok &= vx.size() <= 1; if( ! ok ) return false; // variable information if( vx.size() > 0 ) vy[0] = vx[0]; // value information ty[0] = 0.0; for(size_t i = 0; i < length_of_sum_; ++i) ty[0] += tx[0]; return ok; } }; // -------------------------------------------------------------------- // inform CppAD if we are in parallel mode bool in_parallel(void) { return omp_in_parallel() != 0; } // // inform CppAD of the current thread number size_t thread_num(void) { return static_cast( omp_get_thread_num() ); } } // multi_thread_checkpoint bool multi_atomic_two(void) { bool ok = true; // OpenMP setup size_t num_threads = 4; // number of threads omp_set_dynamic(0); // turn off dynamic thread adjustment omp_set_num_threads( int(num_threads) ); // set number of OMP threads // check that multi-threading is possible on this machine if( omp_get_max_threads() < 2 ) { std::cout << "This machine does not support multi-threading: "; } // create checkpoint version of algorithm size_t n(1), m(1); ad_vector ax(n), ay(m); ax[0] = 2.0; size_t length_of_sum = 5000; long_sum_atomic atom_fun("long_sum", length_of_sum); // setup for using CppAD in parallel mode CppAD::thread_alloc::parallel_setup(num_threads, in_parallel, thread_num); CppAD::thread_alloc::hold_memory(true); CppAD::parallel_ad(); // place to hold result for each thread d_vector y(num_threads); for(size_t thread = 0; thread < num_threads; thread++) y[thread] = 0.0; # pragma omp parallel for for(int thread = 0; thread < int(num_threads); thread++) { ad_vector au(n), av(m); au[0] = 1.0; CppAD::Independent(au); atom_fun(au, av); CppAD::ADFun f(au, av); // d_vector x(n), v(m); x[0] = double( thread + 1 ); v = f.Forward(0, x); // // this assignment has false sharing; i.e., will case cache resets // (conversion avoids boost vector conversion warning) y[size_t(thread)] = v[0]; } // check the results for(size_t thread = 0; thread < num_threads; thread++) { double check = double( length_of_sum * (thread + 1) ); ok &= check == y[thread]; } return ok; } ================================================ FILE: test_more/cppad_for_tmb/multi_chkpoint_one.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include namespace { typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR( CppAD::AD ) ad_vector; // algorithm that we are checkpoingint const size_t length_of_sum_ = 5000; void long_sum_algo(const ad_vector& ax, ad_vector& ay) { ay[0] = 0.0; for(size_t i = 0; i < length_of_sum_; ++i) ay[0] += ax[0]; return; } // inform CppAD if we are in parallel mode bool in_parallel(void) { return omp_in_parallel() != 0; } // // inform CppAD of the current thread number size_t thread_num(void) { return static_cast( omp_get_thread_num() ); } } // multi_thread_checkpoint bool multi_chkpoint_one(void) { bool ok = true; // OpenMP setup size_t num_threads = 4; // number of threads omp_set_dynamic(0); // turn off dynamic thread adjustment omp_set_num_threads( int(num_threads) ); // set number of OMP threads // check that multi-threading is possible on this machine if( omp_get_max_threads() < 2 ) { std::cout << "This machine does not support multi-threading: "; } // create checkpoint version of algorithm size_t n(1), m(1); ad_vector ax(n), ay(m); ax[0] = 2.0; CppAD::atomic_base::option_enum sparsity = CppAD::atomic_base::set_sparsity_enum; bool optimize = false; CppAD::checkpoint atom_fun( "long_sum", long_sum_algo, ax, ay, sparsity, optimize ); // setup for using CppAD in parallel mode CppAD::thread_alloc::parallel_setup(num_threads, in_parallel, thread_num); CppAD::thread_alloc::hold_memory(true); CppAD::parallel_ad(); // place to hold result for each thread d_vector y(num_threads); for(size_t thread = 0; thread < num_threads; thread++) y[thread] = 0.0; # pragma omp parallel for for(int thread = 0; thread < int(num_threads); thread++) { ad_vector au(n), av(m); au[0] = 1.0; CppAD::Independent(au); atom_fun(au, av); CppAD::ADFun f(au, av); // d_vector x(n), v(m); x[0] = double( thread + 1 ); v = f.Forward(0, x); // // this assignment has false sharing; i.e., will case cache resets // (conversion avoids boost vector conversion warning) y[size_t(thread)] = v[0]; } // check the results for(size_t thread = 0; thread < num_threads; thread++) { double check = double( length_of_sum_ * (thread + 1) ); ok &= check == y[thread]; } return ok; } ================================================ FILE: test_more/cppad_for_tmb/multi_chkpoint_two.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include namespace { typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR( CppAD::AD ) ad_vector; // algorithm that we are checkpoingint const size_t length_of_sum_ = 5000; void long_sum_algo(const ad_vector& ax, ad_vector& ay) { ay[0] = 0.0; for(size_t i = 0; i < length_of_sum_; ++i) ay[0] += ax[0]; return; } // inform CppAD if we are in parallel mode bool in_parallel(void) { return omp_in_parallel() != 0; } // // inform CppAD of the current thread number size_t thread_num(void) { return static_cast( omp_get_thread_num() ); } } // multi_thread_chkpoint_two bool multi_chkpoint_two(void) { bool ok = true; // OpenMP setup size_t num_threads = 4; // number of threads omp_set_dynamic(0); // turn off dynamic thread adjustment omp_set_num_threads( int(num_threads) ); // set number of OMP threads // check that multi-threading is possible on this machine if( omp_get_max_threads() < 2 ) { std::cout << "This machine does not support multi-threading: "; } // create ADFun corresponding to long_sum_algo size_t n(1), m(1); ad_vector ax(n), ay(m); ax[0] = 2.0; CppAD::Independent(ax); long_sum_algo(ax, ay); CppAD::ADFun fun(ax, ay); // create chkpoint_two version of algorithm const char* name = "long_sum"; bool internal_bool = false; bool use_hes_sparsity = false; bool use_base2ad = false; bool use_in_parallel = true; CppAD::chkpoint_two chk_fun( fun, name, internal_bool, use_hes_sparsity, use_base2ad, use_in_parallel ); // setup for using CppAD in parallel mode CppAD::thread_alloc::parallel_setup(num_threads, in_parallel, thread_num); CppAD::thread_alloc::hold_memory(true); CppAD::parallel_ad(); // place to hold result for each thread d_vector y(num_threads); for(size_t thread = 0; thread < num_threads; thread++) y[thread] = 0.0; # pragma omp parallel for for(int thread = 0; thread < int(num_threads); thread++) { ad_vector au(n), av(m); au[0] = 1.0; CppAD::Independent(au); chk_fun(au, av); CppAD::ADFun f(au, av); // d_vector x(n), v(m); x[0] = double( thread + 1 ); v = f.Forward(0, x); // // this assignment has false sharing; i.e., will case cache resets // (conversion avoids boost vector conversion warning) y[size_t(thread)] = v[0]; } // check the results for(size_t thread = 0; thread < num_threads; thread++) { double check = double( length_of_sum_ * (thread + 1) ); ok &= check == y[thread]; } return ok; } ================================================ FILE: test_more/cppad_for_tmb/prefer_reverse.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include namespace { // BEGIN_EMPTY_NAMESPACE using CppAD::vector; // ---------------------------------------------------------------------------- // prefer reverse mode during computation of Jacobians // example_tmb_atomic class example_tmb_atomic : public CppAD::atomic_base { public: // constructor example_tmb_atomic(const std::string& name) : CppAD::atomic_base(name) { } // forward (only implement zero order) virtual bool forward( size_t p , size_t q , const vector& vx , vector& vy , const vector& tx , vector& ty ) { // check for errors in usage bool ok = p == 0 && q == 0; ok &= tx.size() == 1; ok &= ty.size() == 1; ok &= vx.size() <= 1; if( ! ok ) return false; // variable information if( vx.size() > 0 ) vy[0] = vx[0]; // y = 1 / x ty[0] = 1.0 / tx[0]; return ok; } // reverse (implement first order) virtual bool reverse( size_t q , const vector& tx , const vector& ty , vector& px , const vector& py ) { // check for errors in usage bool ok = q == 0; ok &= tx.size() == 1; ok &= ty.size() == 1; ok &= px.size() == 1; ok &= py.size() == 1; if( ! ok ) return false; // y = 1 / x // dy/dx = - 1 / (x * x) double dy_dx = -1.0 / ( tx[0] * tx[0] ); px[0] = py[0] * dy_dx; return ok; } }; } // END_EMPTY_NAMESPACE bool prefer_reverse(void) { bool ok = true; double eps99 = 99.0 * std::numeric_limits::epsilon(); // Create atomic functions example_tmb_atomic afun("reciprocal"); // Declare independent variables size_t n = 1; CPPAD_TESTVECTOR( CppAD::AD ) ax(n); ax[0] = 5.0; CppAD::Independent(ax); // Compute dependent variables size_t m = 1; CPPAD_TESTVECTOR( CppAD::AD ) ay(m); afun(ax, ay); // Create f(x) = 1 / x CppAD::ADFun f(ax, ay); // Use Jacobian to compute f'(x) = - 1 / (x * x). // This would fail with the normal CppAD distribution because it would use // first order forward mode for the calculation. CPPAD_TESTVECTOR(double) x(n), dy_dx(m); x[0] = 2.0; dy_dx = f.Jacobian(x); // check the result double check = -1.0 / (x[0] * x[0]); ok &= CppAD::NearEqual(dy_dx[0], check, eps99, eps99); return ok; } ================================================ FILE: test_more/debug_rel/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the test_more/general tests # set compiler flags for debug_rel.cpp and debug.cpp IF( "${cppad_debug_which}" STREQUAL "debug_all" ) SET(debug_flags "${cppad_cxx_flags} ${CMAKE_CXX_FLAGS_DEBUG}") SET(release_flags "${cppad_cxx_flags} ${CMAKE_CXX_FLAGS_DEBUG}") ELSEIF( "${cppad_debug_which}" STREQUAL "debug_none" ) SET(debug_flags "${cppad_cxx_flags} ${CMAKE_CXX_FLAGS_RELEASE}") SET(release_flags "${cppad_cxx_flags} ${CMAKE_CXX_FLAGS_RELEASE}") ELSE( "${cppad_debug_which}" ) SET(debug_flags "${cppad_cxx_flags} ${CMAKE_CXX_FLAGS_DEBUG}") SET(release_flags "${cppad_cxx_flags} ${CMAKE_CXX_FLAGS_RELEASE}") ENDIF( "${cppad_debug_which}" STREQUAL "debug_all" ) # SET_SOURCE_FILES_PROPERTIES( debug_rel.cpp debug.cpp PROPERTIES COMPILE_FLAGS "${debug_flags}" ) # SET_SOURCE_FILES_PROPERTIES( release.cpp PROPERTIES COMPILE_FLAGS "${release_flags}" ) # now that we have the properties, add the executable ADD_EXECUTABLE(test_more_debug_rel EXCLUDE_FROM_ALL debug_rel.cpp debug.cpp release.cpp ) # debug_rel does not use cppad_lib # check_test_more_debug_rel add_check_executable(check_test_more debug_rel) ================================================ FILE: test_more/debug_rel/debug.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include // checks that d_ptr points to a double with value 5.0 and then // frees the double using thread_alloc::return_memory bool debug_thread_alloc(double *d_ptr) { bool ok = true; ok &= ( *d_ptr == 5.0); void* v_ptr = reinterpret_cast(d_ptr); CppAD::thread_alloc::return_memory(v_ptr); return ok; } // just use ADFun constructor void debug_adfun_ctor(void) { CppAD::vector< CppAD::AD > ax(1), ay(1); ax[0] = 0.; CppAD::Independent(ax); ay[0] = fabs(ax[0]); CppAD::ADFun f(ax, ay); return; } ================================================ FILE: test_more/debug_rel/debug_rel.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // system include files used for I/O # include // for free_all # include namespace { size_t n_ok = 0; size_t n_error = 0; void print_test(bool ok, const char* name) { std::cout.width(20); std::cout.setf( std::ios_base::left ); std::cout << name; // if( ok ) { std::cout << "OK\n"; n_ok++; } else { std::cout << "Error\n"; n_error++; } } } // thread_alloc double *release_thread_alloc(void); bool debug_thread_alloc(double* d_ptr); // adfun_ctor void debug_adfun_ctor(void); void release_adfun_ctor(void); // main program that runs all the tests int main(void) { using std::cout; cout << "Begin test_more/debug_rel\n"; // // thread_alloc { double* d_ptr = release_thread_alloc(); bool ok = debug_thread_alloc(d_ptr); print_test(ok, "thead_alloc"); } // adfun_ctor { // this test would fail with an assert during release_adfun_ctor release_adfun_ctor(); debug_adfun_ctor(); bool ok = true; print_test(ok, "adfun_ctor"); } // memory { bool ok = CppAD::thread_alloc::free_all(); print_test(ok, "memory"); } if( n_error == 0 ) std::cout << "All " << n_ok << " tests passed." << std::endl; else std::cout << n_error << " tests failed." << std::endl; // if( n_error == 0 ) return 0; return 1; } ================================================ FILE: test_more/debug_rel/release.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include // Returns a pointer to a double that has value 5.0 // and must be freed using thread_alloc::return_memory. double *release_thread_alloc(void) { size_t min_bytes = sizeof(double); size_t cap_bytes; void* v_ptr = CppAD::thread_alloc::get_memory(min_bytes, cap_bytes); double* d_ptr = reinterpret_cast(v_ptr); *d_ptr = 5.0; return d_ptr; } // just use ADFun constructor void release_adfun_ctor(void) { CppAD::vector< CppAD::AD > ax(1), ay(1); ax[0] = 0.; CppAD::Independent(ax); ay[0] = fabs(ax[0]); CppAD::ADFun f(ax, ay); return; } ================================================ FILE: test_more/deprecated/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the test_more/deprecated tests # # initialize check_test_more_deprecated # This directory has both an executable and sub-directories SET(check_test_more_deprecated_depends "") # ADD_SUBDIRECTORY(atomic_two) ADD_SUBDIRECTORY(chkpoint_one) # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list deprecated.cpp old_mat_mul.cpp old_reciprocal.cpp old_tan.cpp old_usead_1.cpp old_usead_2.cpp omp_alloc.cpp track_new_del.cpp zdouble.cpp ) # END_SORT_THIS_LINE_MINUS_2 # set_compile_flags( test_more_deprecated "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(test_more_deprecated EXCLUDE_FROM_ALL ${source_list}) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(test_more_deprecated ${cppad_lib} ${colpack_libs} ) # # check_test_more_deprecated add_check_executable(check_test_more deprecated) ================================================ FILE: test_more/deprecated/atomic_two/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # # eigen_sources and CPPAD_HAS_EIGEN IF( cppad_has_eigen ) ELSE( cppad_has_eigen ) SET(eigen_sources "") ENDIF( cppad_has_eigen ) # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list atomic_sparsity.cpp atomic_two.cpp base2ad.cpp for_sparse_hes.cpp for_sparse_jac.cpp forward.cpp get_started.cpp mat_mul.cpp norm_sq.cpp reciprocal.cpp rev_sparse_hes.cpp rev_sparse_jac.cpp reverse.cpp set_sparsity.cpp tangent.cpp ) # END_SORT_THIS_LINE_MINUS_2 set_compile_flags(test_more_deprecated_atomic_two "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(test_more_deprecated_atomic_two EXCLUDE_FROM_ALL ${source_list} ) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(test_more_deprecated_atomic_two ${cppad_lib} ${colpack_libs} ) # # check_test_more_deprecated_atomic_two add_check_executable(check_test_more_deprecated atomic_two) ================================================ FILE: test_more/deprecated/atomic_two/atomic_sparsity.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* Atomic function g( x ) = [ x_2, x_0 * x_1 ] \] $$ */ # include namespace { // isolate items below to this file using CppAD::vector; // vector typedef vector< std::set > set_vector; // atomic_sparsity // // a utility to compute the union of two sets. using CppAD::set_union; // class atomic_set_sparsity : public CppAD::atomic_base { public: // constructor atomic_set_sparsity(const std::string& name) : // this example only uses set sparsity patterns CppAD::atomic_base(name, set_sparsity_enum ) { } private: // forward virtual bool forward( size_t p , size_t q , const vector& vx , vector& vy , const vector& tx , vector& ty ) { size_t n = tx.size() / (q + 1); # ifndef NDEBUG size_t m = ty.size() / (q + 1); # endif assert( n == 3 ); assert( m == 2 ); // only order zero bool ok = q == 0; if( ! ok ) return ok; // check for defining variable information if( vx.size() > 0 ) { ok &= vx.size() == n; vy[0] = vx[2]; vy[1] = vx[0] || vx[1]; } // Order zero forward mode. // y[0] = x[2], y[1] = x[0] * x[1] if( p <= 0 ) { ty[0] = tx[2]; ty[1] = tx[0] * tx[1]; } return ok; } // for_sparse_jac virtual bool for_sparse_jac( size_t p , const set_vector& r , set_vector& s , const vector& x ) { // This function needed if using f.ForSparseJac # ifndef NDEBUG size_t n = r.size(); size_t m = s.size(); # endif assert( n == x.size() ); assert( n == 3 ); assert( m == 2 ); // sparsity for S(x) = f'(x) * R = [ 0, 0, 1 ] * R s[0] = r[2]; // s[1] = union(r[0], r[1]) s[1] = set_union(r[0], r[1]); // return true; } virtual bool rev_sparse_jac( size_t p , const set_vector& rt , set_vector& st , const vector& x ) { // This function needed if using RevSparseJac or optimize # ifndef NDEBUG size_t n = st.size(); size_t m = rt.size(); # endif assert( n == x.size() ); assert( n == 3 ); assert( m == 2 ); // [ 0, x1 ] // sparsity for S(x)^T = f'(x)^T * R^T = [ 0, x0 ] * R^T // [ 1, 0 ] st[0] = rt[1]; st[1] = rt[1]; st[2] = rt[0]; return true; } virtual bool for_sparse_hes( const vector& vx, const vector& r , const vector& s , set_vector& h , const vector& x ) { size_t n = r.size(); # ifndef NDEBUG size_t m = s.size(); # endif assert( x.size() == n ); assert( h.size() == n ); assert( n == 3 ); assert( m == 2 ); // initialize h as empty for(size_t i = 0; i < n; i++) h[i].clear(); // only f_1 has a non-zero hessian if( ! s[1] ) return true; // only the cross term between x[0] and x[1] is non-zero if( ! ( r[0] && r[1] ) ) return true; // set the possibly non-zero terms in the hessian h[0].insert(1); h[1].insert(0); return true; } virtual bool rev_sparse_hes( const vector& vx, const vector& s , vector& t , size_t p , const set_vector& r , const set_vector& u , set_vector& v , const vector& x ) { // This function needed if using RevSparseHes # ifndef NDEBUG size_t m = s.size(); size_t n = t.size(); # endif assert( x.size() == n ); assert( r.size() == n ); assert( u.size() == m ); assert( v.size() == n ); assert( n == 3 ); assert( m == 2 ); // sparsity for T(x) = S(x) * f'(x) = S(x) * [ 0, 0, 1 ] // [ x1, x0, 0 ] t[0] = s[1]; t[1] = s[1]; t[2] = s[0]; // V(x) = f'(x)^T * g''(y) * f'(x) * R + g'(y) * f''(x) * R // U(x) = g''(y) * f'(x) * R // S(x) = g'(y) // [ 0, x1 ] // sparsity for W(x) = f'(x)^T * U(x) = [ 0, x0 ] * U(x) // [ 1, 0 ] v[0] = u[1]; v[1] = u[1]; v[2] = u[0]; // // [ 0, 1, 0 ] // sparsity for V(x) = W(x) + S_1 (x) * [ 1, 0, 0 ] * R // [ 0, 0, 0 ] if( s[1] ) { // v[0] = union( v[0], r[1] ) v[0] = set_union(v[0], r[1]); // v[1] = union( v[1], r[0] ) v[1] = set_union(v[1], r[0]); } return true; } }; // End of atomic_set_sparsity class // f(u) = g( u_0 + u_1 , u_0 + u_1 , u_2 ) // = [ u_2 , (u_0 + u_1)^2 ] bool test_one(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * std::numeric_limits::epsilon(); // Create the atomic get_started object atomic_set_sparsity afun("atomic_set_sparsity"); size_t n = 3; size_t m = 2; vector< AD > au(n), ay(m); for(size_t j = 0; j < n; j++) au[j] = double(j + 1); // declare independent variables and start tape recording CppAD::Independent(au); // ax vector< AD > ax(n); ax[0] = au[0] + au[1]; ax[1] = au[0] + au[1]; ax[2] = au[2]; // call atomic function afun(ax, ay); // create f: u -> y and stop tape recording CppAD::ADFun f(au, ay); // check function value ok &= NearEqual(ay[0] , au[2], eps, eps); ok &= NearEqual(ay[1] , (au[0] + au[1]) * (au[0] + au[1]), eps, eps); // correct Jacobian result set_vector check_s(m); check_s[0].insert(2); check_s[1].insert(0); check_s[1].insert(1); // compute and test forward mode { set_vector r(n), s(m); for(size_t i = 0; i < n; i++) r[i].insert(i); s = f.ForSparseJac(n, r); for(size_t i = 0; i < m; i++) ok &= s[i] == check_s[i]; } // compute and test reverse mode { set_vector r(m), s(m); for(size_t i = 0; i < m; i++) r[i].insert(i); s = f.RevSparseJac(m, r); for(size_t i = 0; i < m; i++) ok &= s[i] == check_s[i]; } // correct Hessian result for w_0 * f_0 (u) + w_1 * f_1(u) set_vector check_h(n); check_h[0].insert(0); check_h[0].insert(1); check_h[1].insert(0); check_h[1].insert(1); // compute and test forward mode { set_vector r(1), s(1), h(n); for(size_t i = 0; i < m; i++) s[0].insert(i); for(size_t j = 0; j < n; j++) r[0].insert(j); h = f.ForSparseHes(r, s); for(size_t i = 0; i < n; i++) ok &= h[i] == check_h[i]; } // compute and test reverse mode { set_vector s(1), h(n); for(size_t i = 0; i < m; i++) s[0].insert(i); h = f.RevSparseHes(n, s); for(size_t i = 0; i < n; i++) ok &= h[i] == check_h[i]; } return ok; } // f(u) = g( u_0 + u_1 , u_1 + u_2 , u_2 + u_0 ) // = [ u_2 + u_0 , (u_0 + u_1)*(u_1 + u_2) ] bool test_two(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * std::numeric_limits::epsilon(); // Create the atomic get_started object atomic_set_sparsity afun("atomic_set_sparsity"); size_t n = 3; size_t m = 2; vector< AD > au(n), ay(m); for(size_t j = 0; j < n; j++) au[j] = double(j + 1); // declare independent variables and start tape recording CppAD::Independent(au); // ax vector< AD > ax(n); ax[0] = au[0] + au[1]; ax[1] = au[1] + au[2]; ax[2] = au[2] + au[0]; // call atomic function afun(ax, ay); // create f: u -> y and stop tape recording CppAD::ADFun f(au, ay); // check function value ok &= NearEqual(ay[0] , au[2] + au[0], eps, eps); ok &= NearEqual(ay[1] , (au[0] + au[1]) * (au[1] + au[2]), eps, eps); // correct Jacobian result set_vector check_s(m); check_s[0].insert(2); check_s[0].insert(0); check_s[1].insert(0); check_s[1].insert(1); check_s[1].insert(2); // compute and test forward mode { set_vector r(n), s(m); for(size_t i = 0; i < n; i++) r[i].insert(i); s = f.ForSparseJac(n, r); for(size_t i = 0; i < m; i++) ok &= s[i] == check_s[i]; } // compute and test reverse mode { set_vector r(m), s(m); for(size_t i = 0; i < m; i++) r[i].insert(i); s = f.RevSparseJac(m, r); for(size_t i = 0; i < m; i++) ok &= s[i] == check_s[i]; } // ---------------------------------------------------------------------- // correct Hessian result for f_0 (u) set_vector check_h(n), s(1); s[0].insert(0); // compute and test forward mode { set_vector r(1), h(n); for(size_t j = 0; j < n; j++) r[0].insert(j); h = f.ForSparseHes(r, s); for(size_t i = 0; i < n; i++) ok &= h[i] == check_h[i]; } // compute and test reverse mode { set_vector h(n); h = f.RevSparseHes(n, s); for(size_t i = 0; i < n; i++) ok &= h[i] == check_h[i]; } // ---------------------------------------------------------------------- // correct Hessian result for f_1 (u) s[0].clear(); s[0].insert(1); check_h[0].insert(1); check_h[0].insert(2); check_h[1].insert(0); check_h[1].insert(1); check_h[1].insert(2); check_h[2].insert(0); check_h[2].insert(1); // compute and test forward mode { set_vector r(1), h(n); for(size_t j = 0; j < n; j++) r[0].insert(j); h = f.ForSparseHes(r, s); for(size_t i = 0; i < n; i++) ok &= h[i] == check_h[i]; } // compute and test reverse mode { set_vector h(n); h = f.RevSparseHes(n, s); for(size_t i = 0; i < n; i++) ok &= h[i] == check_h[i]; } return ok; } } // End empty namespace bool atomic_sparsity(void) { bool ok = true; ok &= test_one(); ok &= test_two(); return ok; } ================================================ FILE: test_more/deprecated/atomic_two/atomic_two.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // CPPAD_HAS_* defines # include // system include files used for I/O # include // C style asserts # include // for thread_alloc # include // test runner # include // external compiled tests extern bool atomic_sparsity(void); extern bool mat_mul(void); extern bool base2ad(void); extern bool for_sparse_hes(void); extern bool for_sparse_jac(void); extern bool forward(void); extern bool get_started(void); extern bool norm_sq(void); extern bool reciprocal(void); extern bool rev_sparse_hes(void); extern bool rev_sparse_jac(void); extern bool reverse(void); extern bool set_sparsity(void); extern bool tangent(void); // main program that runs all the tests int main(void) { std::string group = "test_more/deprecated/atomic_two"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // external compiled tests Run( atomic_sparsity, "atomic_sparsity"); Run( mat_mul, "mat_mul" ); Run( base2ad, "base2ad" ); Run( for_sparse_hes, "for_sparse_hes" ); Run( for_sparse_jac, "for_sparse_jac" ); Run( forward, "forward" ); Run( get_started, "get_started" ); Run( norm_sq, "norm_sq" ); Run( reciprocal, "reciprocal" ); Run( rev_sparse_hes, "rev_sparse_hes" ); Run( rev_sparse_jac, "rev_sparse_jac" ); Run( reverse, "reverse" ); Run( set_sparsity, "set_sparsity" ); Run( tangent, "tangent" ); # if CPPAD_HAS_EIGEN # endif // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } ================================================ FILE: test_more/deprecated/atomic_two/base2ad.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin atomic_two_base2ad.cpp@@ $section base2ad with Atomic Operations: Example and Test$$ $head Source Code$$ $srcthisfile%0%// BEGIN C++%// END C++%1%$$ $end */ // BEGIN C++ # include namespace { // isolate items below to this file // // abbreviations using CppAD::AD; using CppAD::vector; // class atomic_base2ad : public CppAD::atomic_base { // public: // constructor (could use const char* for name) atomic_base2ad(const std::string& name) : // this example does not use any sparsity patterns CppAD::atomic_base(name) { } private: // ---------------------------------------------------------------------- // forward mode // ---------------------------------------------------------------------- template bool template_forward( size_t p , size_t q , const vector& vx , vector& vy , const vector& tx , vector& ty ) { # ifndef NDEBUG size_t n = tx.size() / (q + 1); size_t m = ty.size() / (q + 1); # endif assert( n == 1 ); assert( m == 1 ); // return flag bool ok = q == 0; if( ! ok ) return ok; // check for defining variable information // This case must always be implemented if( vx.size() > 0 ) vy[0] = vx[0]; // Order zero forward mode. // This case must always be implemented // y^0 = f( x^0 ) = 1 / x^0 Scalar f = 1. / tx[0]; if( p <= 0 ) ty[0] = f; return ok; } // forward mode routines called by ADFun objects virtual bool forward( size_t p , size_t q , const vector& vx , vector& vy , const vector& tx , vector& ty ) { return template_forward(p, q, vx, vy, tx, ty); } // forward mode routines called by ADFun< AD , Base> objects virtual bool forward( size_t p , size_t q , const vector& vx , vector& vy , const vector< AD >& atx , vector< AD >& aty ) { return template_forward(p, q, vx, vy, atx, aty); } // ---------------------------------------------------------------------- // reverse mode // ---------------------------------------------------------------------- template bool template_reverse( size_t q , const vector& tx , const vector& ty , vector& px , const vector& py ) { # ifndef NDEBUG size_t n = tx.size() / (q + 1); size_t m = ty.size() / (q + 1); # endif assert( n == 1 ); assert( m == 1 ); // return flag bool ok = q == 0; if( ! ok ) return ok; // Order zero reverse mode. // y^0 = f( x^0 ) = 1 / x^0 // y^1 = f'( x^0 ) * x^1 = - x^1 / (x^0 * x^0) px[0] = - py[0] / ( tx[0] * tx[0] ); return ok; } // reverse mode routines called by ADFun objects virtual bool reverse( size_t q , const vector& tx , const vector& ty , vector& px , const vector& py ) { return template_reverse(q, tx, ty, px, py); } // reverse mode routines called by ADFun objects virtual bool reverse( size_t q , const vector< AD >& atx , const vector< AD >& aty , vector< AD >& apx , const vector< AD >& apy ) { return template_reverse(q, atx, aty, apx, apy); } }; // End of atomic_base2ad class } // End empty namespace bool base2ad(void) { bool ok = true; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); // // Create the atomic base2ad object atomic_base2ad afun("atomic_base2ad"); // // Create the function f(x) // size_t n = 1; double x0 = 0.5; vector< AD > ax(n); ax[0] = x0; // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 1; vector< AD > ay(m); // call atomic function and store base2ad(x) in au[0] vector< AD > au(m); afun(ax, au); // u = 1 / x // now use AD division to invert to invert the operation ay[0] = 1.0 / au[0]; // y = 1 / u = x // create f: x -> y and stop tape recording CppAD::ADFun f; f.Dependent (ax, ay); // f(x) = x // check function value double check = x0; ok &= NearEqual( Value(ay[0]) , check, eps, eps); // check zero order forward mode size_t q; vector x_q(n), y_q(m); q = 0; x_q[0] = x0; y_q = f.Forward(q, x_q); ok &= NearEqual(y_q[0] , check, eps, eps); // check first order reverse vector dw(n), w(m); w[0] = 1.0; dw = f.Reverse(q+1, w); check = 1.0; ok &= NearEqual(dw[0] , check, eps, eps); // create af : x -> y CppAD::ADFun< AD , double > af( f.base2ad() ); // check zero order forward mode vector< AD > ax_q(n), ay_q(m); q = 0; ax_q[0] = x0; ay_q = af.Forward(q, ax_q); check = x0; ok &= NearEqual( Value(ay_q[0]) , check, eps, eps); // check first order reverse vector< AD > adw(n), aw(m); aw[0] = 1.0; adw = af.Reverse(q+1, aw); check = 1.0; ok &= NearEqual( Value(adw[0]) , check, eps, eps); return ok; } // END C++ ================================================ FILE: test_more/deprecated/atomic_two/for_sparse_hes.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin atomic_two_for_sparse_hes.cpp@@ $section Atomic Forward Hessian Sparsity: Example and Test$$ $head Purpose$$ This example demonstrates calculation of the forward Hessian sparsity pattern for an atomic operation. $head function$$ For this example, the atomic function $latex f : \B{R}^3 \rightarrow \B{R}^2$$ is defined by $latex \[ f( x ) = \left( \begin{array}{c} x_2 * x_2 \\ x_0 * x_1 \end{array} \right) \] $$ The Hessians of the component functions are $latex \[ f_0^{(2)} ( x ) = \left( \begin{array}{ccc} 0 & 0 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & 2 \end{array} \right) \W{,} f_1^{(2)} ( x ) = \left( \begin{array}{ccc} 0 & 1 & 0 \\ 1 & 0 & 0 \\ 0 & 0 & 0 \end{array} \right) \] $$ $head Start Class Definition$$ $srccode%cpp% */ # include namespace { // isolate items below to this file using CppAD::vector; // abbreviate as vector // class atomic_for_sparse_hes : public CppAD::atomic_base { /* %$$ $head Constructor $$ $srccode%cpp% */ public: // constructor (could use const char* for name) atomic_for_sparse_hes(const std::string& name) : // this example only uses pack sparsity patterns CppAD::atomic_base(name, pack_sparsity_enum) { } private: /* %$$ $head forward$$ $srccode%cpp% */ // forward mode routine called by CppAD virtual bool forward( size_t p , size_t q , const vector& vx , vector& vy , const vector& tx , vector& ty ) { # ifndef NDEBUG size_t n = tx.size() / (q + 1); size_t m = ty.size() / (q + 1); # endif assert( n == 3 ); assert( m == 2 ); // return flag bool ok = q == 0; if( ! ok ) return ok; // check for defining variable information // This case must always be implemented if( vx.size() > 0 ) { vy[0] = vx[0]; vy[1] = vx[0] || vy[0]; } // Order zero forward mode. // This case must always be implemented // f(x) = [ x_0 * x_0 ] // [ x_0 * x_1 ] assert( p <= 0 ); if( p <= 0 ) { ty[0] = tx[2] * tx[2]; ty[1] = tx[0] * tx[1]; } return ok; } /* %$$ $head for_sparse_jac$$ $srccode%cpp% */ // forward Jacobian sparsity routine called by CppAD virtual bool for_sparse_jac( size_t q , const CppAD::vectorBool& r , CppAD::vectorBool& s , const vector& x ) { // This function needed because we are using ForSparseHes // with afun.option( CppAD::atomic_base::pack_sparsity_enum ) # ifndef NDEBUG size_t n = r.size() / q; size_t m = s.size() / q; # endif assert( x.size() == n ); assert( n == 3 ); assert( m == 2 ); // f'(x) = [ 0, 0, 2 x_2 ] // [ x_1, x_0, 0 ] // sparsity for first row of S(x) = f'(x) * R size_t i = 0; for(size_t j = 0; j < q; j++) s[ i * q + j ] = r[ 2 * q + j ]; // sparsity for second row of S(x) = f'(x) * R i = 1; for(size_t j = 0; j < q; j++) s[ i * q + j ] = r[ 0 * q + j ] || r[ 1 * q + j]; return true; } /* %$$ $head rev_sparse_jac$$ $srccode%cpp% */ // reverse Jacobian sparsity routine called by CppAD virtual bool rev_sparse_jac( size_t q , const CppAD::vectorBool& rt , CppAD::vectorBool& st , const vector& x ) { // This function needed because we are using ForSparseHes // with afun.option( CppAD::atomic_base::pack_sparsity_enum ) # ifndef NDEBUG size_t m = rt.size() / q; size_t n = st.size() / q; # endif assert( x.size() == n ); assert( n == 3 ); assert( m == 2 ); // [ 0, x_1 ] // f'(x)^T = [ 0, x_0 ] // [ 2 x_2, 0 ] // sparsity for first row of S(x)^T = f'(x)^T * R^T size_t i = 0; for(size_t j = 0; j < q; j++) st[ i * q + j ] = rt[ 1 * q + j ]; // sparsity for second row of S(x)^T = f'(x)^T * R^T i = 1; for(size_t j = 0; j < q; j++) st[ i * q + j ] = rt[ 1 * q + j ]; // sparsity for third row of S(x)^T = f'(x)^T * R^T i = 2; for(size_t j = 0; j < q; j++) st[ i * q + j ] = rt[ 0 * q + j ]; return true; } /* %$$ $head for_sparse_hes$$ $srccode%cpp% */ // forward Hessian sparsity routine called by CppAD virtual bool for_sparse_hes( const vector& vx, const vector& r , const vector& s , CppAD::vectorBool& h , const vector& x ) { // This function needed because we are using RevSparseHes // with afun.option( CppAD::atomic_base::pack_sparsity_enum ) size_t n = r.size(); # ifndef NDEBUG size_t m = s.size(); # endif assert( x.size() == n ); assert( n == 3 ); assert( m == 2 ); assert( h.size() == n * n ); // [ 0 , 0 , 0 ] [ 0 , 1 , 0 ] // f_0''(x) = [ 0 , 0 , 0 ] f_1^{(2)} (x) = [ 1 , 0 , 0 ] // [ 0 , 0 , 2 ] [ 0 , 0 , 0 ] // initial entire matrix as false for(size_t i = 0; i < n * n; i++) h[i] = false; // component (2, 2) h[ 2 * n + 2 ] = s[0] && r[2]; // components (1, 0) and (0, 1) h[ 1 * n + 0 ] = s[1] && r[0] && r[1]; h[ 0 * n + 1 ] = s[1] && r[0] && r[1]; return true; } }; // End of atomic_for_sparse_hes class /* %$$ $head Use Atomic Function$$ $srccode%cpp% */ bool use_atomic_for_sparse_hes(bool x_1_variable) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); // // Create the atomic for_sparse_hes object atomic_for_sparse_hes afun("atomic_for_sparse_hes"); // // Create the function f(u) // // domain space vector size_t n = 3; double x_0 = 1.00; double x_1 = 2.00; double x_2 = 3.00; vector< AD > au(n); au[0] = x_0; au[1] = x_1; au[2] = x_2; // declare independent variables and start tape recording CppAD::Independent(au); // range space vector size_t m = 2; vector< AD > ay(m); // call atomic function vector< AD > ax(n); ax[0] = au[0]; ax[2] = au[2]; if( x_1_variable ) ax[1] = au[1]; else ax[1] = x_1; afun(ax, ay); // y = [ x_2 * x_2 , x_0 * x_1 ]^T // create f: u -> y and stop tape recording CppAD::ADFun f; f.Dependent (au, ay); // f(u) = y // // check function value double check = x_2 * x_2; ok &= NearEqual( Value(ay[0]) , check, eps, eps); check = x_0 * x_1; ok &= NearEqual( Value(ay[1]) , check, eps, eps); // check zero order forward mode size_t q; vector xq(n), yq(m); q = 0; xq[0] = x_0; xq[1] = x_1; xq[2] = x_2; yq = f.Forward(q, xq); check = x_2 * x_2; ok &= NearEqual(yq[0] , check, eps, eps); check = x_0 * x_1; ok &= NearEqual(yq[1] , check, eps, eps); // forward sparse Hessian CppAD::vectorBool r(n), s(m), h(n * n); for(size_t j = 0; j < n; j++) r[j] = true; for(size_t i = 0; i < m; i++) s[i] = true; h = f.ForSparseHes(r, s); // check result CppAD::vectorBool check_h(n * n); for(size_t i = 0; i < n * n; i++) check_h[i] = false; check_h[ 2 * n + 2 ] = true; if( x_1_variable ) { check_h[0 * n + 1] = true; check_h[1 * n + 0] = true; } for(size_t i = 0; i < n * n; i++) ok &= h[ i ] == check_h[ i ]; // return ok; } } // End empty namespace /* %$$ $head Test with x_1 Both a Variable and a Parameter$$ $srccode%cpp% */ bool for_sparse_hes(void) { bool ok = true; // test with x_1 a variable ok &= use_atomic_for_sparse_hes(true); // test with x_1 a parameter ok &= use_atomic_for_sparse_hes(false); return ok; } /* %$$ $end */ ================================================ FILE: test_more/deprecated/atomic_two/for_sparse_jac.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin atomic_two_for_sparse_jac.cpp@@ $spell Jacobian $$ $section Atomic Forward Jacobian Sparsity: Example and Test$$ $head Purpose$$ This example demonstrates calculation of the forward Jacobian sparsity pattern for an atomic operation. $head function$$ For this example, the atomic function $latex f : \B{R}^3 \rightarrow \B{R}^2$$ is defined by $latex \[ f(x) = \left( \begin{array}{c} x_2 * x_2 \\ x_0 * x_1 \end{array} \right) \] $$ The corresponding Jacobian is $latex \[ f^{(1)} (x) = \left( \begin{array}{ccc} 0 & 0 & 2 x_2 \\ x_1 & x_0 & 0 \end{array} \right) \] $$ $head Start Class Definition$$ $srccode%cpp% */ # include namespace { // isolate items below to this file using CppAD::vector; // abbreviate as vector // class atomic_for_sparse_jac : public CppAD::atomic_base { /* %$$ $head Constructor $$ $srccode%cpp% */ public: // constructor (could use const char* for name) atomic_for_sparse_jac(const std::string& name) : // this example only uses pack sparsty patterns CppAD::atomic_base(name, pack_sparsity_enum) { } private: /* %$$ $head forward$$ $srccode%cpp% */ // forward mode routine called by CppAD virtual bool forward( size_t p , size_t q , const vector& vx , vector& vy , const vector& tx , vector& ty ) { # ifndef NDEBUG size_t n = tx.size() / (q + 1); size_t m = ty.size() / (q + 1); # endif assert( n == 3 ); assert( m == 2 ); // return flag bool ok = q == 0; if( ! ok ) return ok; // check for defining variable information // This case must always be implemented if( vx.size() > 0 ) { vy[0] = vx[2]; vy[1] = vx[0] || vx[1]; } // Order zero forward mode. // This case must always be implemented // f(x) = [ x_2 * x_2 ] // [ x_0 * x_1 ] assert( p <= 0 ); if( p <= 0 ) { ty[0] = tx[2] * tx[2]; ty[1] = tx[0] * tx[1]; } return ok; } /* %$$ $head for_sparse_jac$$ $srccode%cpp% */ // forward Jacobian sparsity routine called by CppAD virtual bool for_sparse_jac( size_t q , const CppAD::vectorBool& r , CppAD::vectorBool& s , const vector& x ) { // This function needed because we are using ForSparseJac // with afun.option( CppAD::atomic_base::pack_sparsity_enum ) # ifndef NDEBUG size_t n = r.size() / q; size_t m = s.size() / q; # endif assert( x.size() == n ); assert( n == 3 ); assert( m == 2 ); // f'(x) = [ 0, 0, 2 x_2 ] // [ x_1, x_0, 0 ] // sparsity for first row of S(x) = f'(x) * R size_t i = 0; for(size_t j = 0; j < q; j++) s[ i * q + j ] = r[ 2 * q + j ]; // sparsity for second row of S(x) = f'(x) * R i = 1; for(size_t j = 0; j < q; j++) s[ i * q + j ] = r[ 0 * q + j ] || r[ 1 * q + j]; return true; } }; // End of atomic_for_sparse_jac class /* %$$ $head Use Atomic Function$$ $srccode%cpp% */ bool use_atomic_for_sparse_jac(bool x_1_variable) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); // // Create the atomic for_sparse_jac object atomic_for_sparse_jac afun("atomic_for_sparse_jac"); // // Create the function f(u) // // domain space vector size_t n = 3; double x_0 = 1.00; double x_1 = 2.00; double x_2 = 3.00; vector< AD > au(n); au[0] = x_0; au[1] = x_1; au[2] = x_2; // declare independent variables and start tape recording CppAD::Independent(au); // range space vector size_t m = 2; vector< AD > ay(m); // call atomic function vector< AD > ax(n); ax[0] = au[0]; ax[2] = au[2]; if( x_1_variable ) ax[1] = au[1]; else ax[1] = x_1; afun(ax, ay); // y = [ x_2 * x_2 , x_0 * x_1 ]^T // create f: u -> y and stop tape recording CppAD::ADFun f; f.Dependent (au, ay); // f(u) = y // // check function value double check = x_2 * x_2; ok &= NearEqual( Value(ay[0]) , check, eps, eps); check = x_0 * x_1; ok &= NearEqual( Value(ay[1]) , check, eps, eps); // check zero order forward mode size_t q; vector xq(n), yq(m); q = 0; xq[0] = x_0; xq[1] = x_1; xq[2] = x_2; yq = f.Forward(q, xq); check = x_2 * x_2; ok &= NearEqual(yq[0] , check, eps, eps); check = x_0 * x_1; ok &= NearEqual(yq[1] , check, eps, eps); // forward sparse Jacobian CppAD::vectorBool r(n * n), s(m * n); // r = identity matrix for(size_t i = 0; i < n; i++) for(size_t j = 0; j < n; j++) r[ i * n + j] = i == j; s = f.ForSparseJac(n, r); // check result CppAD::vectorBool check_s(m * n); check_s[ 0 * n + 0 ] = false; check_s[ 0 * n + 1 ] = false; check_s[ 0 * n + 2 ] = true; check_s[ 1 * n + 0 ] = true; check_s[ 1 * n + 1 ] = x_1_variable; check_s[ 1 * n + 2 ] = false; // for(size_t i = 0; i < m * n; i++) ok &= s[ i ] == check_s[ i ]; // return ok; } } // End empty namespace /* %$$ $head Test with x_1 Both a Variable and a Parameter$$ $srccode%cpp% */ bool for_sparse_jac(void) { bool ok = true; // test with x_1 a variable ok &= use_atomic_for_sparse_jac(true); // test with x_1 a parameter ok &= use_atomic_for_sparse_jac(false); return ok; } /* %$$ $end */ ================================================ FILE: test_more/deprecated/atomic_two/forward.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin atomic_two_forward.cpp@@ $spell Jacobian $$ $section Atomic Forward: Example and Test$$ $head Purpose$$ This example demonstrates forward mode derivative calculation using an atomic operation. $head function$$ For this example, the atomic function $latex f : \B{R}^3 \rightarrow \B{R}^2$$ is defined by $latex \[ f(x) = \left( \begin{array}{c} x_2 * x_2 \\ x_0 * x_1 \end{array} \right) \] $$ The corresponding Jacobian is $latex \[ f^{(1)} (x) = \left( \begin{array}{ccc} 0 & 0 & 2 x_2 \\ x_1 & x_0 & 0 \end{array} \right) \] $$ The Hessians of the component functions are $latex \[ f_0^{(2)} ( x ) = \left( \begin{array}{ccc} 0 & 0 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & 2 \end{array} \right) \W{,} f_1^{(2)} ( x ) = \left( \begin{array}{ccc} 0 & 1 & 0 \\ 1 & 0 & 0 \\ 0 & 0 & 0 \end{array} \right) \] $$ $head Start Class Definition$$ $srccode%cpp% */ # include namespace { // isolate items below to this file using CppAD::vector; // abbreviate as vector // class atomic_forward : public CppAD::atomic_base { /* %$$ $head Constructor $$ $srccode%cpp% */ public: // constructor (could use const char* for name) atomic_forward(const std::string& name) : // this example does not use sparsity patterns CppAD::atomic_base(name) { } private: /* %$$ $head forward$$ $srccode%cpp% */ // forward mode routine called by CppAD virtual bool forward( size_t p , size_t q , const vector& vx , vector& vy , const vector& tx , vector& ty ) { size_t q1 = q + 1; # ifndef NDEBUG size_t n = tx.size() / q1; size_t m = ty.size() / q1; # endif assert( n == 3 ); assert( m == 2 ); assert( p <= q ); // this example only implements up to second order forward mode bool ok = q <= 2; if( ! ok ) return ok; // check for defining variable information // This case must always be implemented if( vx.size() > 0 ) { vy[0] = vx[2]; vy[1] = vx[0] || vx[1]; } // ------------------------------------------------------------------ // Zero forward mode. // This case must always be implemented // f(x) = [ x_2 * x_2 ] // [ x_0 * x_1 ] // y^0 = f( x^0 ) if( p <= 0 ) { // y_0^0 = x_2^0 * x_2^0 ty[0 * q1 + 0] = tx[2 * q1 + 0] * tx[2 * q1 + 0]; // y_1^0 = x_0^0 * x_1^0 ty[1 * q1 + 0] = tx[0 * q1 + 0] * tx[1 * q1 + 0]; } if( q <= 0 ) return ok; // ------------------------------------------------------------------ // First order one forward mode. // This case is needed if first order forward mode is used. // f'(x) = [ 0, 0, 2 * x_2 ] // [ x_1, x_0, 0 ] // y^1 = f'(x^0) * x^1 if( p <= 1 ) { // y_0^1 = 2 * x_2^0 * x_2^1 ty[0 * q1 + 1] = 2.0 * tx[2 * q1 + 0] * tx[2 * q1 + 1]; // y_1^1 = x_1^0 * x_0^1 + x_0^0 * x_1^1 ty[1 * q1 + 1] = tx[1 * q1 + 0] * tx[0 * q1 + 1]; ty[1 * q1 + 1] += tx[0 * q1 + 0] * tx[1 * q1 + 1]; } if( q <= 1 ) return ok; // ------------------------------------------------------------------ // Second order forward mode. // This case is needed if second order forward mode is used. // f'(x) = [ 0, 0, 2 x_2 ] // [ x_1, x_0, 0 ] // // [ 0 , 0 , 0 ] [ 0 , 1 , 0 ] // f_0''(x) = [ 0 , 0 , 0 ] f_1^{(2)} (x) = [ 1 , 0 , 0 ] // [ 0 , 0 , 2 ] [ 0 , 0 , 0 ] // // y_0^2 = x^1 * f_0''( x^0 ) x^1 / 2! + f_0'( x^0 ) x^2 // = ( x_2^1 * 2.0 * x_2^1 ) / 2! // + 2.0 * x_2^0 * x_2^2 ty[0 * q1 + 2] = tx[2 * q1 + 1] * tx[2 * q1 + 1]; ty[0 * q1 + 2] += 2.0 * tx[2 * q1 + 0] * tx[2 * q1 + 2]; // // y_1^2 = x^1 * f_1''( x^0 ) x^1 / 2! + f_1'( x^0 ) x^2 // = ( x_1^1 * x_0^1 + x_0^1 * x_1^1) / 2 // + x_1^0 * x_0^2 + x_0^0 + x_1^2 ty[1 * q1 + 2] = tx[1 * q1 + 1] * tx[0 * q1 + 1]; ty[1 * q1 + 2] += tx[1 * q1 + 0] * tx[0 * q1 + 2]; ty[1 * q1 + 2] += tx[0 * q1 + 0] * tx[1 * q1 + 2]; // ------------------------------------------------------------------ return ok; } }; } // End empty namespace /* %$$ $head Use Atomic Function$$ $srccode%cpp% */ bool forward(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); // // Create the atomic_forward object atomic_forward afun("atomic_forward"); // // Create the function f(u) // // domain space vector size_t n = 3; double x_0 = 1.00; double x_1 = 2.00; double x_2 = 3.00; vector< AD > au(n); au[0] = x_0; au[1] = x_1; au[2] = x_2; // declare independent variables and start tape recording CppAD::Independent(au); // range space vector size_t m = 2; vector< AD > ay(m); // call atomic function vector< AD > ax = au; afun(ax, ay); // create f: u -> y and stop tape recording CppAD::ADFun f; f.Dependent (au, ay); // y = f(u) // // check function value double check = x_2 * x_2; ok &= NearEqual( Value(ay[0]) , check, eps, eps); check = x_0 * x_1; ok &= NearEqual( Value(ay[1]) , check, eps, eps); // -------------------------------------------------------------------- // zero order forward // vector x0(n), y0(m); x0[0] = x_0; x0[1] = x_1; x0[2] = x_2; y0 = f.Forward(0, x0); check = x_2 * x_2; ok &= NearEqual(y0[0] , check, eps, eps); check = x_0 * x_1; ok &= NearEqual(y0[1] , check, eps, eps); // -------------------------------------------------------------------- // first order forward // // value of Jacobian of f double check_jac[] = { 0.0, 0.0, 2.0 * x_2, x_1, x_0, 0.0 }; vector x1(n), y1(m); // check first order forward mode for(size_t j = 0; j < n; j++) x1[j] = 0.0; for(size_t j = 0; j < n; j++) { // compute partial in j-th component direction x1[j] = 1.0; y1 = f.Forward(1, x1); x1[j] = 0.0; // check this direction for(size_t i = 0; i < m; i++) ok &= NearEqual(y1[i], check_jac[i * n + j], eps, eps); } // -------------------------------------------------------------------- // second order forward // // value of Hessian of f_0 double check_hes_0[] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0 }; // // value of Hessian of f_1 double check_hes_1[] = { 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; vector x2(n), y2(m); for(size_t j = 0; j < n; j++) x2[j] = 0.0; // compute diagonal elements of the Hessian for(size_t j = 0; j < n; j++) { // first order forward in j-th direction x1[j] = 1.0; f.Forward(1, x1); y2 = f.Forward(2, x2); // check this element of Hessian diagonal ok &= NearEqual(y2[0], check_hes_0[j * n + j] / 2.0, eps, eps); ok &= NearEqual(y2[1], check_hes_1[j * n + j] / 2.0, eps, eps); // for(size_t k = 0; k < n; k++) if( k != j ) { x1[k] = 1.0; f.Forward(1, x1); y2 = f.Forward(2, x2); // // y2 = (H_jj + H_kk + H_jk + H_kj) / 2.0 // y2 = (H_jj + H_kk) / 2.0 + H_jk // double H_jj = check_hes_0[j * n + j]; double H_kk = check_hes_0[k * n + k]; double H_jk = y2[0] - (H_kk + H_jj) / 2.0; ok &= NearEqual(H_jk, check_hes_0[j * n + k], eps, eps); // H_jj = check_hes_1[j * n + j]; H_kk = check_hes_1[k * n + k]; H_jk = y2[1] - (H_kk + H_jj) / 2.0; ok &= NearEqual(H_jk, check_hes_1[j * n + k], eps, eps); // x1[k] = 0.0; } x1[j] = 0.0; } // -------------------------------------------------------------------- return ok; } /* %$$ $end */ ================================================ FILE: test_more/deprecated/atomic_two/get_started.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin atomic_two_get_started.cpp@@ $section Getting Started with Atomic Operations: Example and Test$$ $head Purpose$$ This example demonstrates the minimal amount of information necessary for a $cref atomic_two$$ operation. $head Start Class Definition$$ $srccode%cpp% */ # include namespace { // isolate items below to this file using CppAD::vector; // abbreviate as vector class atomic_get_started : public CppAD::atomic_base { /* %$$ $head Constructor$$ $srccode%cpp% */ public: // constructor (could use const char* for name) atomic_get_started(const std::string& name) : // this example does not use any sparsity patterns CppAD::atomic_base(name) { } private: /* %$$ $head forward$$ $srccode%cpp% */ // forward mode routine called by CppAD virtual bool forward( size_t p , size_t q , const vector& vx , vector& vy , const vector& tx , vector& ty ) { # ifndef NDEBUG size_t n = tx.size() / (q + 1); size_t m = ty.size() / (q + 1); # endif assert( n == 1 ); assert( m == 1 ); // return flag bool ok = q == 0; if( ! ok ) return ok; // check for defining variable information // This case must always be implemented if( vx.size() > 0 ) vy[0] = vx[0]; // Order zero forward mode. // This case must always be implemented // y^0 = f( x^0 ) = 1 / x^0 double f = 1. / tx[0]; if( p <= 0 ) ty[0] = f; return ok; } /* %$$ $head End Class Definition$$ $srccode%cpp% */ }; // End of atomic_get_started class } // End empty namespace /* %$$ $head Use Atomic Function$$ $srccode%cpp% */ bool get_started(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); /* %$$ $subhead Constructor$$ $srccode%cpp% */ // Create the atomic get_started object atomic_get_started afun("atomic_get_started"); /* %$$ $subhead Recording$$ $srccode%cpp% */ // Create the function f(x) // // domain space vector size_t n = 1; double x0 = 0.5; vector< AD > ax(n); ax[0] = x0; // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 1; vector< AD > ay(m); // call atomic function and store get_started(x) in au[0] vector< AD > au(m); afun(ax, au); // u = 1 / x // now use AD division to invert to invert the operation ay[0] = 1.0 / au[0]; // y = 1 / u = x // create f: x -> y and stop tape recording CppAD::ADFun f; f.Dependent (ax, ay); // f(x) = x /* %$$ $subhead forward$$ $srccode%cpp% */ // check function value double check = x0; ok &= NearEqual( Value(ay[0]) , check, eps, eps); // check zero order forward mode size_t q; vector x_q(n), y_q(m); q = 0; x_q[0] = x0; y_q = f.Forward(q, x_q); ok &= NearEqual(y_q[0] , check, eps, eps); return ok; } /* %$$ $end */ ================================================ FILE: test_more/deprecated/atomic_two/mat_mul.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin atomic_two_mat_mul.cpp@@ $spell mul $$ $section User Atomic Matrix Multiply: Example and Test$$ $head See Also$$ $cref atomic_two_eigen_mat_mul.cpp$$ $children% include/cppad/example/atomic_three/mat_mul.hpp %$$ $head Class Definition$$ This example uses the file $cref atomic_three_mat_mul.hpp$$ which defines matrix multiply as a $cref atomic_two$$ operation. $head Use Atomic Function$$ $srccode%cpp% */ # include # include "mat_mul.hpp" bool mat_mul(void) { bool ok = true; using CppAD::AD; using CppAD::vector; size_t i, j; /* %$$ $subhead Constructor$$ $srccode%cpp% */ // ------------------------------------------------------------------- // object that multiplies 2 x 2 matrices atomic_mat_mul afun; /* %$$ $subhead Recording$$ $srccode%cpp% */ // start recording with four independent variables size_t n = 4; vector x(n); vector< AD > ax(n); for(j = 0; j < n; j++) ax[j] = x[j] = double(j + 1); CppAD::Independent(ax); // ------------------------------------------------------------------ size_t nr_left = 2; size_t n_middle = 2; size_t nc_right = 2; vector< AD > atom_x(3 + (nr_left + nc_right) * n_middle ); // matrix dimensions atom_x[0] = AD( nr_left ); atom_x[1] = AD( n_middle ); atom_x[2] = AD( nc_right ); // left matrix atom_x[3] = ax[0]; // left[0, 0] = x0 atom_x[4] = ax[1]; // left[0, 1] = x1 atom_x[5] = 5.; // left[1, 0] = 5 atom_x[6] = 6.; // left[1, 1] = 6 // right matrix atom_x[7] = ax[2]; // right[0, 0] = x2 atom_x[8] = 7.; // right[0, 1] = 7 atom_x[9] = ax[3]; // right[1, 0] = x3 atom_x[10] = 8.; // right[1, 1] = 8 // ------------------------------------------------------------------ /* [ x0 , x1 ] * [ x2 , 7 ] = [ x0*x2 + x1*x3 , x0*7 + x1*8 ] [ 5 , 6 ] [ x3 , 8 ] [ 5*x2 + 6*x3 , 5*7 + 6*8 ] */ vector< AD > atom_y(nr_left * nc_right); afun(atom_x, atom_y); ok &= (atom_y[0] == x[0]*x[2] + x[1]*x[3]) && Variable(atom_y[0]); ok &= (atom_y[1] == x[0]*7. + x[1]*8. ) && Variable(atom_y[1]); ok &= (atom_y[2] == 5.*x[2] + 6.*x[3]) && Variable(atom_y[2]); ok &= (atom_y[3] == 5.*7. + 6.*8. ) && Parameter(atom_y[3]); // ------------------------------------------------------------------ // define the function g : x -> atom_y // g(x) = [ x0*x2 + x1*x3 , x0*7 + x1*8 , 5*x2 + 6*x3 , 5*7 + 6*8 ]^T CppAD::ADFun g(ax, atom_y); /* %$$ $subhead forward$$ $srccode%cpp% */ // Test zero order forward mode evaluation of g(x) size_t m = atom_y.size(); vector y(m); for(j = 0; j < n; j++) x[j] = double(j + 2); y = g.Forward(0, x); ok &= y[0] == x[0] * x[2] + x[1] * x[3]; ok &= y[1] == x[0] * 7. + x[1] * 8.; ok &= y[2] == 5. * x[2] + 6. * x[3]; ok &= y[3] == 5. * 7. + 6. * 8.; //---------------------------------------------------------------------- // Test first order forward mode evaluation of g'(x) * [1, 2, 3, 4]^T // g'(x) = [ x2, x3, x0, x1 ] // [ 7 , 8, 0, 0 ] // [ 0 , 0, 5, 6 ] // [ 0 , 0, 0, 0 ] CppAD::vector dx(n), dy(m); for(j = 0; j < n; j++) dx[j] = double(j + 1); dy = g.Forward(1, dx); ok &= dy[0] == 1. * x[2] + 2. * x[3] + 3. * x[0] + 4. * x[1]; ok &= dy[1] == 1. * 7. + 2. * 8. + 3. * 0. + 4. * 0.; ok &= dy[2] == 1. * 0. + 2. * 0. + 3. * 5. + 4. * 6.; ok &= dy[3] == 1. * 0. + 2. * 0. + 3. * 0. + 4. * 0.; //---------------------------------------------------------------------- // Test second order forward mode // g_0^2 (x) = [ 0, 0, 1, 0 ], g_0^2 (x) * [1] = [3] // [ 0, 0, 0, 1 ] [2] [4] // [ 1, 0, 0, 0 ] [3] [1] // [ 0, 1, 0, 0 ] [4] [2] CppAD::vector ddx(n), ddy(m); for(j = 0; j < n; j++) ddx[j] = 0.; ddy = g.Forward(2, ddx); // [1, 2, 3, 4] * g_0^2 (x) * [1, 2, 3, 4]^T = 1*3 + 2*4 + 3*1 + 4*2 ok &= 2. * ddy[0] == 1. * 3. + 2. * 4. + 3. * 1. + 4. * 2.; // for i > 0, [1, 2, 3, 4] * g_i^2 (x) * [1, 2, 3, 4]^T = 0 ok &= ddy[1] == 0.; ok &= ddy[2] == 0.; ok &= ddy[3] == 0.; /* %$$ $subhead reverse$$ $srccode%cpp% */ // Test second order reverse mode CppAD::vector w(m), dw(2 * n); for(i = 0; i < m; i++) w[i] = 0.; w[0] = 1.; dw = g.Reverse(2, w); // g_0'(x) = [ x2, x3, x0, x1 ] ok &= dw[0*2 + 0] == x[2]; ok &= dw[1*2 + 0] == x[3]; ok &= dw[2*2 + 0] == x[0]; ok &= dw[3*2 + 0] == x[1]; // g_0'(x) * [1, 2, 3, 4] = 1 * x2 + 2 * x3 + 3 * x0 + 4 * x1 // g_0^2 (x) * [1, 2, 3, 4] = [3, 4, 1, 2] ok &= dw[0*2 + 1] == 3.; ok &= dw[1*2 + 1] == 4.; ok &= dw[2*2 + 1] == 1.; ok &= dw[3*2 + 1] == 2.; /* %$$ $subhead option$$ $srccode%cpp% */ //---------------------------------------------------------------------- // Test both the boolean and set sparsity at the atomic level for(size_t sparse_index = 0; sparse_index < 2; sparse_index++) { if( sparse_index == 0 ) afun.option( CppAD::atomic_base::bool_sparsity_enum ); else afun.option( CppAD::atomic_base::set_sparsity_enum ); /* %$$ $subhead for_sparse_jac$$ $srccode%cpp% */ // Test forward Jacobian sparsity pattern /* g(x) = [ x0*x2 + x1*x3 , x0*7 + x1*8 , 5*x2 + 6*x3 , 5*7 + 6*8 ]^T so the sparsity pattern should be s[0] = {0, 1, 2, 3} s[1] = {0, 1} s[2] = {2, 3} s[3] = {} */ CppAD::vector< std::set > r(n), s(m); for(j = 0; j < n; j++) { assert( r[j].empty() ); r[j].insert(j); } s = g.ForSparseJac(n, r); for(j = 0; j < n; j++) { // s[0] = {0, 1, 2, 3} ok &= s[0].find(j) != s[0].end(); // s[1] = {0, 1} if( j == 0 || j == 1 ) ok &= s[1].find(j) != s[1].end(); else ok &= s[1].find(j) == s[1].end(); // s[2] = {2, 3} if( j == 2 || j == 3 ) ok &= s[2].find(j) != s[2].end(); else ok &= s[2].find(j) == s[2].end(); } // s[3] == {} ok &= s[3].empty(); /* %$$ $subhead rev_sparse_jac$$ $srccode%cpp% */ // Test reverse Jacobian sparsity pattern for(i = 0; i < m; i++) { s[i].clear(); s[i].insert(i); } r = g.RevSparseJac(m, s); for(j = 0; j < n ; j++) { // r[0] = {0, 1, 2, 3} ok &= r[0].find(j) != r[0].end(); // r[1] = {0, 1} if( j == 0 || j == 1 ) ok &= r[1].find(j) != r[1].end(); else ok &= r[1].find(j) == r[1].end(); // r[2] = {2, 3} if( j == 2 || j == 3 ) ok &= r[2].find(j) != r[2].end(); else ok &= r[2].find(j) == r[2].end(); } // r[3] == {} ok &= r[3].empty(); /* %$$ $subhead rev_sparse_hes$$ $srccode%cpp% */ /* Test reverse Hessian sparsity pattern g_0^2 (x) = [ 0, 0, 1, 0 ] and for i > 0, g_i^2 = 0 [ 0, 0, 0, 1 ] [ 1, 0, 0, 0 ] [ 0, 1, 0, 0 ] so for the sparsity pattern for the first component of g is h[0] = {2} h[1] = {3} h[2] = {0} h[3] = {1} */ CppAD::vector< std::set > h(n), t(1); t[0].clear(); t[0].insert(0); h = g.RevSparseHes(n, t); size_t check[] = {2, 3, 0, 1}; for(j = 0; j < n; j++) { // h[j] = { check[j] } for(i = 0; i < n; i++) { if( i == check[j] ) ok &= h[j].find(i) != h[j].end(); else ok &= h[j].find(i) == h[j].end(); } } t[0].clear(); for( j = 1; j < n; j++) t[0].insert(j); h = g.RevSparseHes(n, t); for(j = 0; j < n; j++) { // h[j] = { } for(i = 0; i < n; i++) ok &= h[j].find(i) == h[j].end(); } //----------------------------------------------------------------- } // end for(size_t sparse_index ... //----------------------------------------------------------------- return ok; } /* %$$ $end */ ================================================ FILE: test_more/deprecated/atomic_two/mat_mul.hpp ================================================ # ifndef CPPAD_TEST_MORE_DEPRECATED_ATOMIC_TWO_MAT_MUL_HPP # define CPPAD_TEST_MORE_DEPRECATED_ATOMIC_TWO_MAT_MUL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin atomic_three_mat_mul.hpp@@ $spell Taylor ty px CppAD jac hes nr nc $$ $section Matrix Multiply as an Atomic Operation$$ $head See Also$$ $cref atomic_two_eigen_mat_mul.hpp$$ $head Matrix Dimensions$$ This example puts the matrix dimensions in the atomic function arguments, instead of the $cref/constructor/atomic_two_ctor/$$, so that they can be different for different calls to the atomic function. These dimensions are: $table $icode nr_left$$ $cnext number of rows in the left matrix $rend $icode n_middle$$ $cnext rows in the left matrix and columns in right $rend $icode nc_right$$ $cnext number of columns in the right matrix $tend $head Start Class Definition$$ $srccode%cpp% */ # include namespace { // Begin empty namespace using CppAD::vector; // using CppAD::set_union; // // matrix result = left * right class atomic_mat_mul : public CppAD::atomic_base { /* %$$ $head Constructor$$ $srccode%cpp% */ public: // --------------------------------------------------------------------- // constructor atomic_mat_mul(void) : CppAD::atomic_base("mat_mul") { } private: /* %$$ $head Left Operand Element Index$$ Index in the Taylor coefficient matrix $icode tx$$ of a left matrix element. $srccode%cpp% */ size_t left( size_t i , // left matrix row index size_t j , // left matrix column index size_t k , // Taylor coeffocient order size_t nk , // number of Taylor coefficients in tx size_t nr_left , // rows in left matrix size_t n_middle , // rows in left and columns in right size_t nc_right ) // columns in right matrix { assert( i < nr_left ); assert( j < n_middle ); return (3 + i * n_middle + j) * nk + k; } /* %$$ $head Right Operand Element Index$$ Index in the Taylor coefficient matrix $icode tx$$ of a right matrix element. $srccode%cpp% */ size_t right( size_t i , // right matrix row index size_t j , // right matrix column index size_t k , // Taylor coeffocient order size_t nk , // number of Taylor coefficients in tx size_t nr_left , // rows in left matrix size_t n_middle , // rows in left and columns in right size_t nc_right ) // columns in right matrix { assert( i < n_middle ); assert( j < nc_right ); size_t offset = 3 + nr_left * n_middle; return (offset + i * nc_right + j) * nk + k; } /* %$$ $head Result Element Index$$ Index in the Taylor coefficient matrix $icode ty$$ of a result matrix element. $srccode%cpp% */ size_t result( size_t i , // result matrix row index size_t j , // result matrix column index size_t k , // Taylor coeffocient order size_t nk , // number of Taylor coefficients in ty size_t nr_left , // rows in left matrix size_t n_middle , // rows in left and columns in right size_t nc_right ) // columns in right matrix { assert( i < nr_left ); assert( j < nc_right ); return (i * nc_right + j) * nk + k; } /* %$$ $head Forward Matrix Multiply$$ Forward mode multiply Taylor coefficients in $icode tx$$ and sum into $icode ty$$ (for one pair of left and right orders) $srccode%cpp% */ void forward_multiply( size_t k_left , // order for left coefficients size_t k_right , // order for right coefficients const vector& tx , // domain space Taylor coefficients vector& ty , // range space Taylor coefficients size_t nr_left , // rows in left matrix size_t n_middle , // rows in left and columns in right size_t nc_right ) // columns in right matrix { size_t nx = 3 + (nr_left + nc_right) * n_middle; size_t nk = tx.size() / nx; # ifndef NDEBUG size_t ny = nr_left * nc_right; assert( nk == ty.size() / ny ); # endif // size_t k_result = k_left + k_right; assert( k_result < nk ); // for(size_t i = 0; i < nr_left; i++) { for(size_t j = 0; j < nc_right; j++) { double sum = 0.0; for(size_t ell = 0; ell < n_middle; ell++) { size_t i_left = left( i, ell, k_left, nk, nr_left, n_middle, nc_right ); size_t i_right = right( ell, j, k_right, nk, nr_left, n_middle, nc_right ); sum += tx[i_left] * tx[i_right]; } size_t i_result = result( i, j, k_result, nk, nr_left, n_middle, nc_right ); ty[i_result] += sum; } } } /* %$$ $head Reverse Matrix Multiply$$ Reverse mode partials of Taylor coefficients and sum into $icode px$$ (for one pair of left and right orders) $srccode%cpp% */ void reverse_multiply( size_t k_left , // order for left coefficients size_t k_right , // order for right coefficients const vector& tx , // domain space Taylor coefficients const vector& ty , // range space Taylor coefficients vector& px , // partials w.r.t. tx const vector& py , // partials w.r.t. ty size_t nr_left , // rows in left matrix size_t n_middle , // rows in left and columns in right size_t nc_right ) // columns in right matrix { size_t nx = 3 + (nr_left + nc_right) * n_middle; size_t nk = tx.size() / nx; # ifndef NDEBUG size_t ny = nr_left * nc_right; assert( nk == ty.size() / ny ); # endif assert( tx.size() == px.size() ); assert( ty.size() == py.size() ); // size_t k_result = k_left + k_right; assert( k_result < nk ); // for(size_t i = 0; i < nr_left; i++) { for(size_t j = 0; j < nc_right; j++) { size_t i_result = result( i, j, k_result, nk, nr_left, n_middle, nc_right ); for(size_t ell = 0; ell < n_middle; ell++) { size_t i_left = left( i, ell, k_left, nk, nr_left, n_middle, nc_right ); size_t i_right = right( ell, j, k_right, nk, nr_left, n_middle, nc_right ); // sum += tx[i_left] * tx[i_right]; px[i_left] += tx[i_right] * py[i_result]; px[i_right] += tx[i_left] * py[i_result]; } } } return; } /* %$$ $head forward$$ Routine called by CppAD during $cref Forward$$ mode. $srccode%cpp% */ virtual bool forward( size_t q , size_t p , const vector& vx , vector& vy , const vector& tx , vector& ty ) { size_t n_order = p + 1; size_t nr_left = size_t( tx[ 0 * n_order + 0 ] ); size_t n_middle = size_t( tx[ 1 * n_order + 0 ] ); size_t nc_right = size_t( tx[ 2 * n_order + 0 ] ); # ifndef NDEBUG size_t nx = 3 + (nr_left + nc_right) * n_middle; size_t ny = nr_left * nc_right; # endif assert( vx.size() == 0 || nx == vx.size() ); assert( vx.size() == 0 || ny == vy.size() ); assert( nx * n_order == tx.size() ); assert( ny * n_order == ty.size() ); size_t i, j, ell; // check if we are computing vy information if( vx.size() > 0 ) { size_t nk = 1; size_t k = 0; for(i = 0; i < nr_left; i++) { for(j = 0; j < nc_right; j++) { bool var = false; for(ell = 0; ell < n_middle; ell++) { size_t i_left = left( i, ell, k, nk, nr_left, n_middle, nc_right ); size_t i_right = right( ell, j, k, nk, nr_left, n_middle, nc_right ); bool nz_left = vx[i_left] || (tx[i_left] != 0.); bool nz_right = vx[i_right] || (tx[i_right] != 0.); // if not multiplying by the constant zero if( nz_left & nz_right ) var |= bool(vx[i_left]) || bool(vx[i_right]); } size_t i_result = result( i, j, k, nk, nr_left, n_middle, nc_right ); vy[i_result] = var; } } } // initialize result as zero size_t k; for(i = 0; i < nr_left; i++) { for(j = 0; j < nc_right; j++) { for(k = q; k <= p; k++) { size_t i_result = result( i, j, k, n_order, nr_left, n_middle, nc_right ); ty[i_result] = 0.0; } } } for(k = q; k <= p; k++) { // sum the produces that result in order k for(ell = 0; ell <= k; ell++) forward_multiply( ell, k - ell, tx, ty, nr_left, n_middle, nc_right ); } // all orders are implemented, so always return true return true; } /* %$$ $head reverse$$ Routine called by CppAD during $cref Reverse$$ mode. $srccode%cpp% */ virtual bool reverse( size_t p , const vector& tx , const vector& ty , vector& px , const vector& py ) { size_t n_order = p + 1; size_t nr_left = size_t( tx[ 0 * n_order + 0 ] ); size_t n_middle = size_t( tx[ 1 * n_order + 0 ] ); size_t nc_right = size_t( tx[ 2 * n_order + 0 ] ); # ifndef NDEBUG size_t nx = 3 + (nr_left + nc_right) * n_middle; size_t ny = nr_left * nc_right; # endif assert( nx * n_order == tx.size() ); assert( ny * n_order == ty.size() ); assert( px.size() == tx.size() ); assert( py.size() == ty.size() ); // initialize summation for(size_t i = 0; i < px.size(); i++) px[i] = 0.0; // number of orders to differentiate size_t k = n_order; while(k--) { // differentiate the produces that result in order k for(size_t ell = 0; ell <= k; ell++) reverse_multiply( ell, k - ell, tx, ty, px, py, nr_left, n_middle, nc_right ); } // all orders are implemented, so always return true return true; } /* %$$ $head for_sparse_jac$$ Routines called by CppAD during $cref ForSparseJac$$. $srccode%cpp% */ // boolean sparsity patterns virtual bool for_sparse_jac( size_t q , const vector& r , vector& s , const vector& x ) { size_t nr_left = size_t( CppAD::Integer( x[0] ) ); size_t n_middle = size_t( CppAD::Integer( x[1] ) ); size_t nc_right = size_t( CppAD::Integer( x[2] ) ); # ifndef NDEBUG size_t nx = 3 + (nr_left + nc_right) * n_middle; size_t ny = nr_left * nc_right; # endif assert( nx == x.size() ); assert( nx * q == r.size() ); assert( ny * q == s.size() ); size_t p; // sparsity for S(x) = f'(x) * R size_t nk = 1; size_t k = 0; for(size_t i = 0; i < nr_left; i++) { for(size_t j = 0; j < nc_right; j++) { size_t i_result = result( i, j, k, nk, nr_left, n_middle, nc_right ); for(p = 0; p < q; p++) s[i_result * q + p] = false; for(size_t ell = 0; ell < n_middle; ell++) { size_t i_left = left( i, ell, k, nk, nr_left, n_middle, nc_right ); size_t i_right = right( ell, j, k, nk, nr_left, n_middle, nc_right ); for(p = 0; p < q; p++) { // cast avoids Microsoft warning (should not be needed) s[i_result * q + p] |= bool( r[i_left * q + p ] ); s[i_result * q + p] |= bool( r[i_right * q + p ] ); } } } } return true; } // set sparsity patterns virtual bool for_sparse_jac( size_t q , const vector< std::set >& r , vector< std::set >& s , const vector& x ) { size_t nr_left = size_t( CppAD::Integer( x[0] ) ); size_t n_middle = size_t( CppAD::Integer( x[1] ) ); size_t nc_right = size_t( CppAD::Integer( x[2] ) ); # ifndef NDEBUG size_t nx = 3 + (nr_left + nc_right) * n_middle; size_t ny = nr_left * nc_right; # endif assert( nx == x.size() ); assert( nx == r.size() ); assert( ny == s.size() ); // sparsity for S(x) = f'(x) * R size_t nk = 1; size_t k = 0; for(size_t i = 0; i < nr_left; i++) { for(size_t j = 0; j < nc_right; j++) { size_t i_result = result( i, j, k, nk, nr_left, n_middle, nc_right ); s[i_result].clear(); for(size_t ell = 0; ell < n_middle; ell++) { size_t i_left = left( i, ell, k, nk, nr_left, n_middle, nc_right ); size_t i_right = right( ell, j, k, nk, nr_left, n_middle, nc_right ); // s[i_result] = set_union(s[i_result], r[i_left] ); s[i_result] = set_union(s[i_result], r[i_right] ); } } } return true; } /* %$$ $head rev_sparse_jac$$ Routines called by CppAD during $cref RevSparseJac$$. $srccode%cpp% */ // boolean sparsity patterns virtual bool rev_sparse_jac( size_t q , const vector& rt , vector& st , const vector& x ) { size_t nr_left = size_t( CppAD::Integer( x[0] ) ); size_t n_middle = size_t( CppAD::Integer( x[1] ) ); size_t nc_right = size_t( CppAD::Integer( x[2] ) ); size_t nx = 3 + (nr_left + nc_right) * n_middle; # ifndef NDEBUG size_t ny = nr_left * nc_right; # endif assert( nx == x.size() ); assert( nx * q == st.size() ); assert( ny * q == rt.size() ); size_t i, j, p; // initialize for(i = 0; i < nx; i++) { for(p = 0; p < q; p++) st[ i * q + p ] = false; } // sparsity for S(x)^T = f'(x)^T * R^T size_t nk = 1; size_t k = 0; for(i = 0; i < nr_left; i++) { for(j = 0; j < nc_right; j++) { size_t i_result = result( i, j, k, nk, nr_left, n_middle, nc_right ); for(size_t ell = 0; ell < n_middle; ell++) { size_t i_left = left( i, ell, k, nk, nr_left, n_middle, nc_right ); size_t i_right = right( ell, j, k, nk, nr_left, n_middle, nc_right ); for(p = 0; p < q; p++) { st[i_left * q + p] |= bool( rt[i_result * q + p] ); st[i_right* q + p] |= bool( rt[i_result * q + p] ); } } } } return true; } // set sparsity patterns virtual bool rev_sparse_jac( size_t q , const vector< std::set >& rt , vector< std::set >& st , const vector& x ) { size_t nr_left = size_t( CppAD::Integer( x[0] ) ); size_t n_middle = size_t( CppAD::Integer( x[1] ) ); size_t nc_right = size_t( CppAD::Integer( x[2] ) ); size_t nx = 3 + (nr_left + nc_right) * n_middle; # ifndef NDEBUG size_t ny = nr_left * nc_right; # endif assert( nx == x.size() ); assert( nx == st.size() ); assert( ny == rt.size() ); size_t i, j; // initialize for(i = 0; i < nx; i++) st[i].clear(); // sparsity for S(x)^T = f'(x)^T * R^T size_t nk = 1; size_t k = 0; for(i = 0; i < nr_left; i++) { for(j = 0; j < nc_right; j++) { size_t i_result = result( i, j, k, nk, nr_left, n_middle, nc_right ); for(size_t ell = 0; ell < n_middle; ell++) { size_t i_left = left( i, ell, k, nk, nr_left, n_middle, nc_right ); size_t i_right = right( ell, j, k, nk, nr_left, n_middle, nc_right ); // st[i_left] = set_union(st[i_left], rt[i_result]); st[i_right] = set_union(st[i_right], rt[i_result]); } } } return true; } /* %$$ $head rev_sparse_hes$$ Routines called by $cref RevSparseHes$$. $srccode%cpp% */ // set sparsity patterns virtual bool rev_sparse_hes( const vector& vx, const vector& s , vector& t , size_t q , const vector< std::set >& r , const vector< std::set >& u , vector< std::set >& v , const vector& x ) { size_t nr_left = size_t( CppAD::Integer( x[0] ) ); size_t n_middle = size_t( CppAD::Integer( x[1] ) ); size_t nc_right = size_t( CppAD::Integer( x[2] ) ); size_t nx = 3 + (nr_left + nc_right) * n_middle; # ifndef NDEBUG size_t ny = nr_left * nc_right; # endif assert( x.size() == nx ); assert( vx.size() == nx ); assert( t.size() == nx ); assert( r.size() == nx ); assert( v.size() == nx ); assert( s.size() == ny ); assert( u.size() == ny ); // size_t i, j; // // initialize sparsity patterns as false for(j = 0; j < nx; j++) { t[j] = false; v[j].clear(); } size_t nk = 1; size_t k = 0; for(i = 0; i < nr_left; i++) { for(j = 0; j < nc_right; j++) { size_t i_result = result( i, j, k, nk, nr_left, n_middle, nc_right ); for(size_t ell = 0; ell < n_middle; ell++) { size_t i_left = left( i, ell, k, nk, nr_left, n_middle, nc_right ); size_t i_right = right( ell, j, k, nk, nr_left, n_middle, nc_right ); // // Compute sparsity for T(x) = S(x) * f'(x). // We need not use vx with f'(x) back propagation. t[i_left] |= bool( s[i_result] ); t[i_right] |= bool( s[i_result] ); // V(x) = f'(x)^T * U(x) + S(x) * f''(x) * R // U(x) = g''(y) * f'(x) * R // S(x) = g'(y) // back propagate f'(x)^T * U(x) // (no need to use vx with f'(x) propagation) v[i_left] = set_union(v[i_left], u[i_result] ); v[i_right] = set_union(v[i_right], u[i_result] ); // back propagate S(x) * f''(x) * R // (here is where we must check for cross terms) if( s[i_result] && vx[i_left] && vx[i_right] ) { v[i_left] = set_union(v[i_left], r[i_right] ); v[i_right] = set_union(v[i_right], r[i_left] ); } } } } return true; } // bool sparsity virtual bool rev_sparse_hes( const vector& vx, const vector& s , vector& t , size_t q , const vector& r , const vector& u , vector& v , const vector& x ) { size_t nr_left = size_t( CppAD::Integer( x[0] ) ); size_t n_middle = size_t( CppAD::Integer( x[1] ) ); size_t nc_right = size_t( CppAD::Integer( x[2] ) ); size_t nx = 3 + (nr_left + nc_right) * n_middle; # ifndef NDEBUG size_t ny = nr_left * nc_right; # endif assert( x.size() == nx ); assert( vx.size() == nx ); assert( t.size() == nx ); assert( r.size() == nx * q ); assert( v.size() == nx * q ); assert( s.size() == ny ); assert( u.size() == ny * q ); size_t i, j, p; // // initialize sparsity patterns as false for(j = 0; j < nx; j++) { t[j] = false; for(p = 0; p < q; p++) v[j * q + p] = false; } size_t nk = 1; size_t k = 0; for(i = 0; i < nr_left; i++) { for(j = 0; j < nc_right; j++) { size_t i_result = result( i, j, k, nk, nr_left, n_middle, nc_right ); for(size_t ell = 0; ell < n_middle; ell++) { size_t i_left = left( i, ell, k, nk, nr_left, n_middle, nc_right ); size_t i_right = right( ell, j, k, nk, nr_left, n_middle, nc_right ); // // Compute sparsity for T(x) = S(x) * f'(x). // We so not need to use vx with f'(x) propagation. t[i_left] |= bool( s[i_result] ); t[i_right] |= bool( s[i_result] ); // V(x) = f'(x)^T * U(x) + S(x) * f''(x) * R // U(x) = g''(y) * f'(x) * R // S(x) = g'(y) // back propagate f'(x)^T * U(x) // (no need to use vx with f'(x) propagation) for(p = 0; p < q; p++) { v[ i_left * q + p] |= bool( u[ i_result * q + p] ); v[ i_right * q + p] |= bool( u[ i_result * q + p] ); } // back propagate S(x) * f''(x) * R // (here is where we must check for cross terms) if( s[i_result] && vx[i_left] && vx[i_right] ) { for(p = 0; p < q; p++) { v[i_left * q + p] |= bool( r[i_right * q + p] ); v[i_right * q + p] |= bool( r[i_left * q + p] ); } } } } } return true; } /* %$$ $head End Class Definition$$ $srccode%cpp% */ }; // End of mat_mul class } // End empty namespace /* %$$ $end */ # endif ================================================ FILE: test_more/deprecated/atomic_two/norm_sq.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin atomic_two_norm_sq.cpp@@ $spell sq bool enum $$ $section Atomic Euclidean Norm Squared: Example and Test$$ $head Theory$$ This example demonstrates using $cref atomic_two$$ to define the operation $latex f : \B{R}^n \rightarrow \B{R}^m$$ where $latex n = 2$$, $latex m = 1$$, where $latex \[ f(x) = x_0^2 + x_1^2 \] $$ $head sparsity$$ This example only uses bool sparsity patterns. $head Start Class Definition$$ $srccode%cpp% */ # include namespace { // isolate items below to this file using CppAD::vector; // abbreviate as vector // class atomic_norm_sq : public CppAD::atomic_base { /* %$$ $head Constructor $$ $srccode%cpp% */ public: // constructor (could use const char* for name) atomic_norm_sq(const std::string& name) : // this example only uses boolean sparsity patterns CppAD::atomic_base(name, atomic_base::bool_sparsity_enum) { } private: /* %$$ $head forward$$ $srccode%cpp% */ // forward mode routine called by CppAD virtual bool forward( size_t p , size_t q , const vector& vx , vector& vy , const vector& tx , vector& ty ) { # ifndef NDEBUG size_t n = tx.size() / (q+1); size_t m = ty.size() / (q+1); # endif assert( n == 2 ); assert( m == 1 ); assert( p <= q ); // return flag bool ok = q <= 1; // Variable information must always be implemented. // y_0 is a variable if and only if x_0 or x_1 is a variable. if( vx.size() > 0 ) vy[0] = vx[0] || vx[1]; // Order zero forward mode must always be implemented. // y^0 = f( x^0 ) double x_00 = tx[ 0*(q+1) + 0]; // x_0^0 double x_10 = tx[ 1*(q+1) + 0]; // x_10 double f = x_00 * x_00 + x_10 * x_10; // f( x^0 ) if( p <= 0 ) ty[0] = f; // y_0^0 if( q <= 0 ) return ok; assert( vx.size() == 0 ); // Order one forward mode. // This case needed if first order forward mode is used. // y^1 = f'( x^0 ) x^1 double x_01 = tx[ 0*(q+1) + 1]; // x_0^1 double x_11 = tx[ 1*(q+1) + 1]; // x_1^1 double fp_0 = 2.0 * x_00; // partial f w.r.t x_0^0 double fp_1 = 2.0 * x_10; // partial f w.r.t x_1^0 if( p <= 1 ) ty[1] = fp_0 * x_01 + fp_1 * x_11; // f'( x^0 ) * x^1 if( q <= 1 ) return ok; // Assume we are not using forward mode with order > 1 assert( ! ok ); return ok; } /* %$$ $head reverse$$ $srccode%cpp% */ // reverse mode routine called by CppAD virtual bool reverse( size_t q , const vector& tx , const vector& ty , vector& px , const vector& py ) { # ifndef NDEBUG size_t n = tx.size() / (q+1); size_t m = ty.size() / (q+1); # endif assert( px.size() == n * (q+1) ); assert( py.size() == m * (q+1) ); assert( n == 2 ); assert( m == 1 ); bool ok = q <= 1; double fp_0, fp_1; switch(q) { case 0: // This case needed if first order reverse mode is used // F ( {x} ) = f( x^0 ) = y^0 fp_0 = 2.0 * tx[0]; // partial F w.r.t. x_0^0 fp_1 = 2.0 * tx[1]; // partial F w.r.t. x_0^1 px[0] = py[0] * fp_0;; // partial G w.r.t. x_0^0 px[1] = py[0] * fp_1;; // partial G w.r.t. x_0^1 assert(ok); break; default: // Assume we are not using reverse with order > 1 (q > 0) assert(!ok); } return ok; } /* %$$ $head for_sparse_jac$$ $srccode%cpp% */ // forward Jacobian bool sparsity routine called by CppAD virtual bool for_sparse_jac( size_t p , const vector& r , vector& s , const vector& x ) { // This function needed if using f.ForSparseJac size_t n = r.size() / p; # ifndef NDEBUG size_t m = s.size() / p; # endif assert( n == x.size() ); assert( n == 2 ); assert( m == 1 ); // sparsity for S(x) = f'(x) * R // where f'(x) = 2 * [ x_0, x_1 ] for(size_t j = 0; j < p; j++) { s[j] = false; for(size_t i = 0; i < n; i++) { // Visual Studio 2013 generates warning without bool below s[j] |= bool( r[i * p + j] ); } } return true; } /* %$$ $head rev_sparse_jac$$ $srccode%cpp% */ // reverse Jacobian bool sparsity routine called by CppAD virtual bool rev_sparse_jac( size_t p , const vector& rt , vector& st , const vector& x ) { // This function needed if using RevSparseJac or optimize size_t n = st.size() / p; # ifndef NDEBUG size_t m = rt.size() / p; # endif assert( n == x.size() ); assert( n == 2 ); assert( m == 1 ); // sparsity for S(x)^T = f'(x)^T * R^T // where f'(x)^T = 2 * [ x_0, x_1]^T for(size_t j = 0; j < p; j++) for(size_t i = 0; i < n; i++) st[i * p + j] = rt[j]; return true; } /* %$$ $head rev_sparse_hes$$ $srccode%cpp% */ // reverse Hessian bool sparsity routine called by CppAD virtual bool rev_sparse_hes( const vector& vx, const vector& s , vector& t , size_t p , const vector& r , const vector& u , vector& v , const vector& x ) { // This function needed if using RevSparseHes # ifndef NDEBUG size_t m = s.size(); # endif size_t n = t.size(); assert( x.size() == n ); assert( r.size() == n * p ); assert( u.size() == m * p ); assert( v.size() == n * p ); assert( n == 2 ); assert( m == 1 ); // There are no cross term second derivatives for this case, // so it is not necessary to use vx. // sparsity for T(x) = S(x) * f'(x) t[0] = s[0]; t[1] = s[0]; // V(x) = f'(x)^T * g''(y) * f'(x) * R + g'(y) * f''(x) * R // U(x) = g''(y) * f'(x) * R // S(x) = g'(y) // back propagate the sparsity for U size_t j; for(j = 0; j < p; j++) for(size_t i = 0; i < n; i++) v[ i * p + j] = u[j]; // include forward Jacobian sparsity in Hessian sparsity // sparsity for g'(y) * f''(x) * R (Note f''(x) has same sparsity // as the identity matrix) if( s[0] ) { for(j = 0; j < p; j++) for(size_t i = 0; i < n; i++) { // Visual Studio 2013 generates warning without bool below v[ i * p + j] |= bool( r[ i * p + j] ); } } return true; } /* %$$ $head End Class Definition$$ $srccode%cpp% */ }; // End of atomic_norm_sq class } // End empty namespace /* %$$ $head Use Atomic Function$$ $srccode%cpp% */ bool norm_sq(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); /* %$$ $subhead Constructor$$ $srccode%cpp% */ // -------------------------------------------------------------------- // Create the atomic reciprocal object atomic_norm_sq afun("atomic_norm_sq"); /* %$$ $subhead Recording$$ $srccode%cpp% */ // Create the function f(x) // // domain space vector size_t n = 2; double x0 = 0.25; double x1 = 0.75; vector< AD > ax(n); ax[0] = x0; ax[1] = x1; // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 1; vector< AD > ay(m); // call atomic function and store norm_sq(x) in au[0] afun(ax, ay); // y_0 = x_0 * x_0 + x_1 * x_1 // create f: x -> y and stop tape recording CppAD::ADFun f; f.Dependent (ax, ay); /* %$$ $subhead forward$$ $srccode%cpp% */ // check function value double check = x0 * x0 + x1 * x1; ok &= NearEqual( Value(ay[0]) , check, eps, eps); // check zero order forward mode size_t q; vector x_q(n), y_q(m); q = 0; x_q[0] = x0; x_q[1] = x1; y_q = f.Forward(q, x_q); ok &= NearEqual(y_q[0] , check, eps, eps); // check first order forward mode q = 1; x_q[0] = 0.3; x_q[1] = 0.7; y_q = f.Forward(q, x_q); check = 2.0 * x0 * x_q[0] + 2.0 * x1 * x_q[1]; ok &= NearEqual(y_q[0] , check, eps, eps); /* %$$ $subhead reverse$$ $srccode%cpp% */ // first order reverse mode q = 1; vector w(m), dw(n * q); w[0] = 1.; dw = f.Reverse(q, w); check = 2.0 * x0; ok &= NearEqual(dw[0] , check, eps, eps); check = 2.0 * x1; ok &= NearEqual(dw[1] , check, eps, eps); /* %$$ $subhead for_sparse_jac$$ $srccode%cpp% */ // forward mode sparstiy pattern size_t p = n; CppAD::vectorBool r1(n * p), s1(m * p); r1[0] = true; r1[1] = false; // sparsity pattern identity r1[2] = false; r1[3] = true; // s1 = f.ForSparseJac(p, r1); ok &= s1[0] == true; // f[0] depends on x[0] ok &= s1[1] == true; // f[0] depends on x[1] /* %$$ $subhead rev_sparse_jac$$ $srccode%cpp% */ // reverse mode sparstiy pattern q = m; CppAD::vectorBool s2(q * m), r2(q * n); s2[0] = true; // compute sparsity pattern for f[0] // r2 = f.RevSparseJac(q, s2); ok &= r2[0] == true; // f[0] depends on x[0] ok &= r2[1] == true; // f[0] depends on x[1] /* %$$ $subhead rev_sparse_hes$$ $srccode%cpp% */ // Hessian sparsity (using previous ForSparseJac call) CppAD::vectorBool s3(m), h(p * n); s3[0] = true; // compute sparsity pattern for f[0] // h = f.RevSparseHes(p, s3); ok &= h[0] == true; // partial of f[0] w.r.t. x[0],x[0] is non-zero ok &= h[1] == false; // partial of f[0] w.r.t. x[0],x[1] is zero ok &= h[2] == false; // partial of f[0] w.r.t. x[1],x[0] is zero ok &= h[3] == true; // partial of f[0] w.r.t. x[1],x[1] is non-zero // return ok; } /* %$$ $end */ ================================================ FILE: test_more/deprecated/atomic_two/reciprocal.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin atomic_two_reciprocal.cpp@@ $spell enum $$ $section Reciprocal as an Atomic Operation: Example and Test$$ $head Theory$$ This example demonstrates using $cref atomic_two$$ to define the operation $latex f : \B{R}^n \rightarrow \B{R}^m$$ where $latex n = 1$$, $latex m = 1$$, and $latex f(x) = 1 / x$$. $head sparsity$$ This example only uses set sparsity patterns. $head Start Class Definition$$ $srccode%cpp% */ # include namespace { // isolate items below to this file using CppAD::vector; // abbreviate as vector // // a utility to compute the union of two sets. using CppAD::set_union; // class atomic_reciprocal : public CppAD::atomic_base { /* %$$ $head Constructor $$ $srccode%cpp% */ public: // constructor (could use const char* for name) atomic_reciprocal(const std::string& name) : // this example only uses set sparsity patterns CppAD::atomic_base(name, atomic_base::set_sparsity_enum) { } private: /* %$$ $head forward$$ $srccode%cpp% */ // forward mode routine called by CppAD virtual bool forward( size_t p , size_t q , const vector& vx , vector& vy , const vector& tx , vector& ty ) { # ifndef NDEBUG size_t n = tx.size() / (q + 1); size_t m = ty.size() / (q + 1); # endif assert( n == 1 ); assert( m == 1 ); assert( p <= q ); // return flag bool ok = q <= 2; // check for defining variable information // This case must always be implemented if( vx.size() > 0 ) vy[0] = vx[0]; // Order zero forward mode. // This case must always be implemented // y^0 = f( x^0 ) = 1 / x^0 double f = 1. / tx[0]; if( p <= 0 ) ty[0] = f; if( q <= 0 ) return ok; assert( vx.size() == 0 ); // Order one forward mode. // This case needed if first order forward mode is used. // y^1 = f'( x^0 ) x^1 double fp = - f / tx[0]; if( p <= 1 ) ty[1] = fp * tx[1]; if( q <= 1 ) return ok; // Order two forward mode. // This case needed if second order forward mode is used. // Y''(t) = X'(t)^\R{T} f''[X(t)] X'(t) + f'[X(t)] X''(t) // 2 y^2 = x^1 * f''( x^0 ) x^1 + 2 f'( x^0 ) x^2 double fpp = - 2.0 * fp / tx[0]; ty[2] = tx[1] * fpp * tx[1] / 2.0 + fp * tx[2]; if( q <= 2 ) return ok; // Assume we are not using forward mode with order > 2 assert( ! ok ); return ok; } /* %$$ $head reverse$$ $srccode%cpp% */ // reverse mode routine called by CppAD virtual bool reverse( size_t q , const vector& tx , const vector& ty , vector& px , const vector& py ) { # ifndef NDEBUG size_t n = tx.size() / (q + 1); size_t m = ty.size() / (q + 1); # endif assert( px.size() == n * (q + 1) ); assert( py.size() == m * (q + 1) ); assert( n == 1 ); assert( m == 1 ); bool ok = q <= 2; double f, fp, fpp, fppp; switch(q) { case 0: // This case needed if first order reverse mode is used // reverse: F^0 ( tx ) = y^0 = f( x^0 ) f = ty[0]; fp = - f / tx[0]; px[0] = py[0] * fp;; assert(ok); break; case 1: // This case needed if second order reverse mode is used // reverse: F^1 ( tx ) = y^1 = f'( x^0 ) x^1 f = ty[0]; fp = - f / tx[0]; fpp = - 2.0 * fp / tx[0]; px[1] = py[1] * fp; px[0] = py[1] * fpp * tx[1]; // reverse: F^0 ( tx ) = y^0 = f( x^0 ); px[0] += py[0] * fp; assert(ok); break; case 2: // This needed if third order reverse mode is used // reverse: F^2 ( tx ) = y^2 = // = x^1 * f''( x^0 ) x^1 / 2 + f'( x^0 ) x^2 f = ty[0]; fp = - f / tx[0]; fpp = - 2.0 * fp / tx[0]; fppp = - 3.0 * fpp / tx[0]; px[2] = py[2] * fp; px[1] = py[2] * fpp * tx[1]; px[0] = py[2] * tx[1] * fppp * tx[1] / 2.0 + fpp * tx[2]; // reverse: F^1 ( tx ) = y^1 = f'( x^0 ) x^1 px[1] += py[1] * fp; px[0] += py[1] * fpp * tx[1]; // reverse: F^0 ( tx ) = y^0 = f( x^0 ); px[0] += py[0] * fp; assert(ok); break; default: assert(!ok); } return ok; } /* %$$ $head for_sparse_jac$$ $srccode%cpp% */ // forward Jacobian set sparsity routine called by CppAD virtual bool for_sparse_jac( size_t p , const vector< std::set >& r , vector< std::set >& s , const vector& x ) { // This function needed if using f.ForSparseJac # ifndef NDEBUG size_t n = r.size(); size_t m = s.size(); # endif assert( n == x.size() ); assert( n == 1 ); assert( m == 1 ); // sparsity for S(x) = f'(x) * R is same as sparsity for R s[0] = r[0]; return true; } /* %$$ $head rev_sparse_jac$$ $srccode%cpp% */ // reverse Jacobian set sparsity routine called by CppAD virtual bool rev_sparse_jac( size_t p , const vector< std::set >& rt , vector< std::set >& st , const vector& x ) { // This function needed if using RevSparseJac or optimize # ifndef NDEBUG size_t n = st.size(); size_t m = rt.size(); # endif assert( n == x.size() ); assert( n == 1 ); assert( m == 1 ); // sparsity for S(x)^T = f'(x)^T * R^T is same as sparsity for R^T st[0] = rt[0]; return true; } /* %$$ $head rev_sparse_hes$$ $srccode%cpp% */ // reverse Hessian set sparsity routine called by CppAD virtual bool rev_sparse_hes( const vector& vx, const vector& s , vector& t , size_t p , const vector< std::set >& r , const vector< std::set >& u , vector< std::set >& v , const vector& x ) { // This function needed if using RevSparseHes # ifndef NDEBUG size_t n = vx.size(); size_t m = s.size(); # endif assert( x.size() == n ); assert( t.size() == n ); assert( r.size() == n ); assert( u.size() == m ); assert( v.size() == n ); assert( n == 1 ); assert( m == 1 ); // There are no cross term second derivatives for this case, // so it is not necessary to vx. // sparsity for T(x) = S(x) * f'(x) is same as sparsity for S t[0] = s[0]; // V(x) = f'(x)^T * g''(y) * f'(x) * R + g'(y) * f''(x) * R // U(x) = g''(y) * f'(x) * R // S(x) = g'(y) // back propagate the sparsity for U, note f'(x) may be non-zero; v[0] = u[0]; // include forward Jacobian sparsity in Hessian sparsity // (note sparsty for f''(x) * R same as for R) if( s[0] ) v[0] = set_union(v[0], r[0] ); return true; } /* %$$ $head End Class Definition$$ $srccode%cpp% */ }; // End of atomic_reciprocal class } // End empty namespace /* %$$ $head Use Atomic Function$$ $srccode%cpp% */ bool reciprocal(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); /* %$$ $subhead Constructor$$ $srccode%cpp% */ // -------------------------------------------------------------------- // Create the atomic reciprocal object atomic_reciprocal afun("atomic_reciprocal"); /* %$$ $subhead Recording$$ $srccode%cpp% */ // Create the function f(x) // // domain space vector size_t n = 1; double x0 = 0.5; vector< AD > ax(n); ax[0] = x0; // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 1; vector< AD > ay(m); // call atomic function and store reciprocal(x) in au[0] vector< AD > au(m); afun(ax, au); // u = 1 / x // now use AD division to invert to invert the operation ay[0] = 1.0 / au[0]; // y = 1 / u = x // create f: x -> y and stop tape recording CppAD::ADFun f; f.Dependent (ax, ay); // f(x) = x /* %$$ $subhead forward$$ $srccode%cpp% */ // check function value double check = x0; ok &= NearEqual( Value(ay[0]) , check, eps, eps); // check zero order forward mode size_t q; vector x_q(n), y_q(m); q = 0; x_q[0] = x0; y_q = f.Forward(q, x_q); ok &= NearEqual(y_q[0] , check, eps, eps); // check first order forward mode q = 1; x_q[0] = 1; y_q = f.Forward(q, x_q); check = 1.; ok &= NearEqual(y_q[0] , check, eps, eps); // check second order forward mode q = 2; x_q[0] = 0; y_q = f.Forward(q, x_q); check = 0.; ok &= NearEqual(y_q[0] , check, eps, eps); /* %$$ $subhead reverse$$ $srccode%cpp% */ // third order reverse mode q = 3; vector w(m), dw(n * q); w[0] = 1.; dw = f.Reverse(q, w); check = 1.; ok &= NearEqual(dw[0] , check, eps, eps); check = 0.; ok &= NearEqual(dw[1] , check, eps, eps); ok &= NearEqual(dw[2] , check, eps, eps); /* %$$ $subhead for_sparse_jac$$ $srccode%cpp% */ // forward mode sparstiy pattern size_t p = n; CppAD::vectorBool r1(n * p), s1(m * p); r1[0] = true; // compute sparsity pattern for x[0] // s1 = f.ForSparseJac(p, r1); ok &= s1[0] == true; // f[0] depends on x[0] /* %$$ $subhead rev_sparse_jac$$ $srccode%cpp% */ // reverse mode sparstiy pattern q = m; CppAD::vectorBool s2(q * m), r2(q * n); s2[0] = true; // compute sparsity pattern for f[0] // r2 = f.RevSparseJac(q, s2); ok &= r2[0] == true; // f[0] depends on x[0] /* %$$ $subhead rev_sparse_hes$$ $srccode%cpp% */ // Hessian sparsity (using previous ForSparseJac call) CppAD::vectorBool s3(m), h(p * n); s3[0] = true; // compute sparsity pattern for f[0] // h = f.RevSparseHes(p, s3); ok &= h[0] == true; // second partial of f[0] w.r.t. x[0] may be non-zero // return ok; } /* %$$ $end */ ================================================ FILE: test_more/deprecated/atomic_two/rev_sparse_hes.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin atomic_two_rev_sparse_hes.cpp@@ $section Atomic Reverse Hessian Sparsity: Example and Test$$ $head Purpose$$ This example demonstrates calculation of the reverse Hessian sparsity pattern for an atomic operation. $head function$$ For this example, the atomic function $latex f : \B{R}^3 \rightarrow \B{R}^2$$ is defined by $latex \[ f( x ) = \left( \begin{array}{c} x_2 * x_2 \\ x_0 * x_1 \end{array} \right) \] $$ The Hessians of the component functions are $latex \[ f_0^{(2)} ( x ) = \left( \begin{array}{ccc} 0 & 0 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & 2 \end{array} \right) \W{,} f_1^{(2)} ( x ) = \left( \begin{array}{ccc} 0 & 1 & 0 \\ 1 & 0 & 0 \\ 0 & 0 & 0 \end{array} \right) \] $$ $head Start Class Definition$$ $srccode%cpp% */ # include namespace { // isolate items below to this file using CppAD::vector; // abbreviate as vector // class atomic_rev_sparse_hes : public CppAD::atomic_base { /* %$$ $head Constructor $$ $srccode%cpp% */ public: // constructor (could use const char* for name) atomic_rev_sparse_hes(const std::string& name) : // this example only uses pack sparsity patterns CppAD::atomic_base(name, pack_sparsity_enum) { } private: /* %$$ $head forward$$ $srccode%cpp% */ // forward mode routine called by CppAD virtual bool forward( size_t p , size_t q , const vector& vx , vector& vy , const vector& tx , vector& ty ) { # ifndef NDEBUG size_t n = tx.size() / (q + 1); size_t m = ty.size() / (q + 1); # endif assert( n == 3 ); assert( m == 2 ); // return flag bool ok = q == 0; if( ! ok ) return ok; // check for defining variable information // This case must always be implemented if( vx.size() > 0 ) { vy[0] = vx[0]; vy[1] = vx[0] || vy[0]; } // Order zero forward mode. // This case must always be implemented // f(x) = [ x_0 * x_0 ] // [ x_0 * x_1 ] assert( p <= 0 ); if( p <= 0 ) { ty[0] = tx[2] * tx[2]; ty[1] = tx[0] * tx[1]; } return ok; } /* %$$ $head for_sparse_jac$$ $srccode%cpp% */ // forward Jacobian sparsity routine called by CppAD virtual bool for_sparse_jac( size_t q , const CppAD::vectorBool& r , CppAD::vectorBool& s , const vector& x ) { // This function needed because we are using ForSparseHes // with afun.option( CppAD::atomic_base::pack_sparsity_enum ) # ifndef NDEBUG size_t n = r.size() / q; size_t m = s.size() / q; # endif assert( n == x.size() ); assert( n == 3 ); assert( m == 2 ); // f'(x) = [ 0, 0, 2 x_2 ] // [ x_1, x_0, 0 ] // sparsity for first row of S(x) = f'(x) * R size_t i = 0; for(size_t j = 0; j < q; j++) s[ i * q + j ] = r[ 2 * q + j ]; // sparsity for second row of S(x) = f'(x) * R i = 1; for(size_t j = 0; j < q; j++) s[ i * q + j ] = r[ 0 * q + j ] || r[ 1 * q + j]; return true; } /* %$$ $head rev_sparse_jac$$ $srccode%cpp% */ // reverse Jacobian sparsity routine called by CppAD virtual bool rev_sparse_jac( size_t q , const CppAD::vectorBool& rt , CppAD::vectorBool& st , const vector& x ) { // This function needed because we are using ForSparseHes // with afun.option( CppAD::atomic_base::pack_sparsity_enum ) # ifndef NDEBUG size_t m = rt.size() / q; size_t n = st.size() / q; # endif assert( n == x.size() ); assert( n == 3 ); assert( m == 2 ); // [ 0, x_1 ] // f'(x)^T = [ 0, x_0 ] // [ 2 x_2, 0 ] // sparsity for first row of S(x)^T = f'(x)^T * R^T size_t i = 0; for(size_t j = 0; j < q; j++) st[ i * q + j ] = rt[ 1 * q + j ]; // sparsity for second row of S(x)^T = f'(x)^T * R^T i = 1; for(size_t j = 0; j < q; j++) st[ i * q + j ] = rt[ 1 * q + j ]; // sparsity for third row of S(x)^T = f'(x)^T * R^T i = 2; for(size_t j = 0; j < q; j++) st[ i * q + j ] = rt[ 0 * q + j ]; return true; } /* %$$ $head rev_sparse_hes$$ $srccode%cpp% */ // reverse Hessian sparsity routine called by CppAD virtual bool rev_sparse_hes( const vector& vx, const vector& s , vector& t , size_t q , const CppAD::vectorBool& r , const CppAD::vectorBool& u , CppAD::vectorBool& v , const vector& x ) { // This function needed because we are using RevSparseHes // with afun.option( CppAD::atomic_base::pack_sparsity_enum ) # ifndef NDEBUG size_t m = s.size(); size_t n = t.size(); # endif assert( x.size() == n ); assert( r.size() == n * q ); assert( u.size() == m * q ); assert( v.size() == n * q ); assert( n == 3 ); assert( m == 2 ); // // f'(x) = [ 0, 0, 2 x_2 ] // [ x_1, x_0, 0 ] // // [ 0 , 0 , 0 ] [ 0 , 1 , 0 ] // f_0''(x) = [ 0 , 0 , 0 ] f_1^{(2)} (x) = [ 1 , 0 , 0 ] // [ 0 , 0 , 2 ] [ 0 , 0 , 0 ] // ------------------------------------------------------------------ // sparsity pattern for row vector T(x) = S(x) * f'(x) t[0] = s[1]; t[1] = s[1]; t[2] = s[0]; // ------------------------------------------------------------------ // sparsity pattern for W(x) = f'(x)^T * U(x) for(size_t j = 0; j < q; j++) { v[ 0 * q + j ] = u[ 1 * q + j ]; v[ 1 * q + j ] = u[ 1 * q + j ]; v[ 2 * q + j ] = u[ 0 * q + j ]; } // ------------------------------------------------------------------ // sparsity pattern for Q(x) = W(x) + S_0 (x) * f_0^{(2)} (x) * R if( s[0] ) { for(size_t j = 0; j < q; j++) { // cannot use |= with vectorBool v[ 2 * q + j ] = bool(v[ 2 * q + j ]) || bool(r[ 2 * q + j ]); } } // ------------------------------------------------------------------ // sparsity pattern for V(x) = Q(x) + S_1 (x) * f_1^{(2)} (x) * R if( s[1] ) { for(size_t j = 0; j < q; j++) { // cannot use |= with vectorBool v[ 0 * q + j ] = bool(v[ 0 * q + j ]) || bool(r[ 1 * q + j ]); v[ 1 * q + j ] = bool(v[ 1 * q + j ]) || bool(r[ 0 * q + j ]); } } return true; } }; // End of atomic_rev_sparse_hes class /* %$$ $head Use Atomic Function$$ $srccode%cpp% */ bool use_atomic_rev_sparse_hes(bool x_1_variable) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); // // Create the atomic rev_sparse_hes object atomic_rev_sparse_hes afun("atomic_rev_sparse_hes"); // // Create the function f(u) // // domain space vector size_t n = 3; double x_0 = 1.00; double x_1 = 2.00; double x_2 = 3.00; vector< AD > au(n); au[0] = x_0; au[1] = x_1; au[2] = x_2; // declare independent variables and start tape recording CppAD::Independent(au); // range space vector size_t m = 2; vector< AD > ay(m); // call atomic function vector< AD > ax(n); ax[0] = au[0]; ax[2] = au[2]; if( x_1_variable ) ax[1] = au[1]; else ax[1] = x_1; afun(ax, ay); // y = [ x_2 * x_2 , x_0 * x_1 ]^T // create f: u -> y and stop tape recording CppAD::ADFun f; f.Dependent (au, ay); // f(u) = y // // check function value double check = x_2 * x_2; ok &= NearEqual( Value(ay[0]) , check, eps, eps); check = x_0 * x_1; ok &= NearEqual( Value(ay[1]) , check, eps, eps); // check zero order forward mode size_t q; vector xq(n), yq(m); q = 0; xq[0] = x_0; xq[1] = x_1; xq[2] = x_2; yq = f.Forward(q, xq); check = x_2 * x_2; ok &= NearEqual(yq[0] , check, eps, eps); check = x_0 * x_1; ok &= NearEqual(yq[1] , check, eps, eps); // reverse sparse Hessian CppAD::vectorBool r(n * n), s(m), h(n * n); for(size_t i = 0; i < n; i++) { for(size_t j = 0; j < n; j++) r[i * n + j] = i == j; } for(size_t i = 0; i < m; i++) s[i] = true; f.ForSparseJac(n, r); h = f.RevSparseHes(n, s); // check result CppAD::vectorBool check_h(n * n); for(size_t i = 0; i < n * n; i++) check_h[i] = false; check_h[ 2 * n + 2 ] = true; if( x_1_variable ) { check_h[0 * n + 1] = true; check_h[1 * n + 0] = true; } for(size_t i = 0; i < n * n; i++) ok &= h[ i ] == check_h[ i ]; // return ok; } } // End empty namespace /* %$$ $head Test with x_1 Both a Variable and a Parameter$$ $srccode%cpp% */ bool rev_sparse_hes(void) { bool ok = true; // test with x_1 a variable ok &= use_atomic_rev_sparse_hes(true); // test with x_1 a parameter ok &= use_atomic_rev_sparse_hes(false); return ok; } /* %$$ $end */ ================================================ FILE: test_more/deprecated/atomic_two/rev_sparse_jac.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin atomic_two_rev_sparse_jac.cpp@@ $spell Jacobian Jacobians $$ $section Atomic Reverse Jacobian Sparsity: Example and Test$$ $head Purpose$$ This example demonstrates calculation of the reverse Jacobians sparsity pattern for an atomic operation. $head function$$ For this example, the atomic function $latex f : \B{R}^3 \rightarrow \B{R}^2$$ is defined by $latex \[ f( x ) = \left( \begin{array}{c} x_2 * x_2 \\ x_0 * x_1 \end{array} \right) \] $$ The corresponding Jacobian is $latex \[ f^{(1)} (x) = \left( \begin{array}{ccc} 0 & 0 & 2 x_2 \\ x_1 & x_0 & 0 \end{array} \right) \] $$ $head Start Class Definition$$ $srccode%cpp% */ # include namespace { // isolate items below to this file using CppAD::vector; // abbreviate as vector // class atomic_rev_sparse_jac : public CppAD::atomic_base { /* %$$ $head Constructor $$ $srccode%cpp% */ public: // constructor (could use const char* for name) atomic_rev_sparse_jac(const std::string& name) : // this example only uses pack sparsity patterns CppAD::atomic_base(name, pack_sparsity_enum) { } private: /* %$$ $head forward$$ $srccode%cpp% */ // forward mode routine called by CppAD virtual bool forward( size_t p , size_t q , const vector& vx , vector& vy , const vector& tx , vector& ty ) { # ifndef NDEBUG size_t n = tx.size() / (q + 1); size_t m = ty.size() / (q + 1); # endif assert( n == 3 ); assert( m == 2 ); // return flag bool ok = q == 0; if( ! ok ) return ok; // check for defining variable information // This case must always be implemented if( vx.size() > 0 ) { vy[0] = vx[0]; vy[1] = vx[0] || vy[0]; } // Order zero forward mode. // This case must always be implemented // f(x) = [ x_0 * x_0 ] // [ x_0 * x_1 ] assert( p <= 0 ); if( p <= 0 ) { ty[0] = tx[2] * tx[2]; ty[1] = tx[0] * tx[1]; } return ok; } /* %$$ $head rev_sparse_jac$$ $srccode%cpp% */ // reverse Jacobian sparsity routine called by CppAD virtual bool rev_sparse_jac( size_t q , const CppAD::vectorBool& rt , CppAD::vectorBool& st , const vector& x ) { // This function needed because we are using RevSparseHes // with afun.option( CppAD::atomic_base::pack_sparsity_enum ) # ifndef NDEBUG size_t m = rt.size() / q; size_t n = st.size() / q; # endif assert( n == x.size() ); assert( n == 3 ); assert( m == 2 ); // [ 0, x_1 ] // f'(x)^T = [ 0, x_0 ] // [ 2 x_2, 0 ] // sparsity for first row of S(x)^T = f'(x)^T * R^T size_t i = 0; for(size_t j = 0; j < q; j++) st[ i * q + j ] = rt[ 1 * q + j ]; // sparsity for second row of S(x)^T = f'(x)^T * R^T i = 1; for(size_t j = 0; j < q; j++) st[ i * q + j ] = rt[ 1 * q + j ]; // sparsity for third row of S(x)^T = f'(x)^T * R^T i = 2; for(size_t j = 0; j < q; j++) st[ i * q + j ] = rt[ 0 * q + j ]; return true; } }; // End of atomic_rev_sparse_jac class /* %$$ $head Use Atomic Function$$ $srccode%cpp% */ bool use_atomic_rev_sparse_jac(bool x_1_variable) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); // // Create the atomic rev_sparse_jac object atomic_rev_sparse_jac afun("atomic_rev_sparse_jac"); // // Create the function f(u) // // domain space vector size_t n = 3; double x_0 = 1.00; double x_1 = 2.00; double x_2 = 3.00; vector< AD > au(n); au[0] = x_0; au[1] = x_1; au[2] = x_2; // declare independent variables and start tape recording CppAD::Independent(au); // range space vector size_t m = 2; vector< AD > ay(m); // call atomic function vector< AD > ax(n); ax[0] = au[0]; ax[2] = au[2]; if( x_1_variable ) ax[1] = au[1]; else ax[1] = x_1; afun(ax, ay); // y = [ x_2 * x_2 , x_0 * x_1 ]^T // create f: u -> y and stop tape recording CppAD::ADFun f; f.Dependent (au, ay); // f(u) = y // // check function value double check = x_2 * x_2; ok &= NearEqual( Value(ay[0]) , check, eps, eps); check = x_0 * x_1; ok &= NearEqual( Value(ay[1]) , check, eps, eps); // check zero order forward mode size_t q; vector xq(n), yq(m); q = 0; xq[0] = x_0; xq[1] = x_1; xq[2] = x_2; yq = f.Forward(q, xq); check = x_2 * x_2; ok &= NearEqual(yq[0] , check, eps, eps); check = x_0 * x_1; ok &= NearEqual(yq[1] , check, eps, eps); // forward sparse Jacobian CppAD::vectorBool r(m * m), s(m * n); // r = identity matrix for(size_t i = 0; i < m; i++) for(size_t j = 0; j < m; j++) r[ i * m + j] = i == j; s = f.RevSparseJac(m, r); // check result CppAD::vectorBool check_s(m * n); check_s[ 0 * n + 0 ] = false; check_s[ 0 * n + 1 ] = false; check_s[ 0 * n + 2 ] = true; check_s[ 1 * n + 0 ] = true; check_s[ 1 * n + 1 ] = x_1_variable; check_s[ 1 * n + 2 ] = false; // for(size_t i = 0; i < m * n; i++) ok &= s[ i ] == check_s[ i ]; // return ok; } } // End empty namespace /* %$$ $head Test with x_1 Both a Variable and a Parameter$$ $srccode%cpp% */ bool rev_sparse_jac(void) { bool ok = true; // test with x_1 a variable ok &= use_atomic_rev_sparse_jac(true); // test with x_1 a parameter ok &= use_atomic_rev_sparse_jac(false); return ok; } /* %$$ $end */ ================================================ FILE: test_more/deprecated/atomic_two/reverse.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin atomic_two_reverse.cpp@@ $spell Jacobian $$ $section Atomic Reverse: Example and Test$$ $head Purpose$$ This example demonstrates reverse mode derivative calculation using an atomic operation. $head function$$ For this example, the atomic function $latex f : \B{R}^3 \rightarrow \B{R}^2$$ is defined by $latex \[ f(x) = \left( \begin{array}{c} x_2 * x_2 \\ x_0 * x_1 \end{array} \right) \] $$ The corresponding Jacobian is $latex \[ f^{(1)} (x) = \left( \begin{array}{ccc} 0 & 0 & 2 x_2 \\ x_1 & x_0 & 0 \end{array} \right) \] $$ The Hessians of the component functions are $latex \[ f_0^{(2)} ( x ) = \left( \begin{array}{ccc} 0 & 0 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & 2 \end{array} \right) \W{,} f_1^{(2)} ( x ) = \left( \begin{array}{ccc} 0 & 1 & 0 \\ 1 & 0 & 0 \\ 0 & 0 & 0 \end{array} \right) \] $$ $head Start Class Definition$$ $srccode%cpp% */ # include namespace { // isolate items below to this file using CppAD::vector; // abbreviate as vector // class atomic_reverse : public CppAD::atomic_base { /* %$$ $head Constructor $$ $srccode%cpp% */ public: // constructor (could use const char* for name) atomic_reverse(const std::string& name) : // this example does not use sparsity patterns CppAD::atomic_base(name) { } private: /* %$$ $head forward$$ $srccode%cpp% */ // forward mode routine called by CppAD virtual bool forward( size_t p , size_t q , const vector& vx , vector& vy , const vector& tx , vector& ty ) { size_t q1 = q + 1; # ifndef NDEBUG size_t n = tx.size() / q1; size_t m = ty.size() / q1; # endif assert( n == 3 ); assert( m == 2 ); assert( p <= q ); // this example only implements up to first order forward mode bool ok = q <= 1; if( ! ok ) return ok; // check for defining variable information // This case must always be implemented if( vx.size() > 0 ) { vy[0] = vx[2]; vy[1] = vx[0] || vx[1]; } // ------------------------------------------------------------------ // Zero forward mode. // This case must always be implemented // f(x) = [ x_2 * x_2 ] // [ x_0 * x_1 ] // y^0 = f( x^0 ) if( p <= 0 ) { // y_0^0 = x_2^0 * x_2^0 ty[0 * q1 + 0] = tx[2 * q1 + 0] * tx[2 * q1 + 0]; // y_1^0 = x_0^0 * x_1^0 ty[1 * q1 + 0] = tx[0 * q1 + 0] * tx[1 * q1 + 0]; } if( q <= 0 ) return ok; // ------------------------------------------------------------------ // First order one forward mode. // This case is needed if first order forward mode is used. // f'(x) = [ 0, 0, 2 * x_2 ] // [ x_1, x_0, 0 ] // y^1 = f'(x^0) * x^1 if( p <= 1 ) { // y_0^1 = 2 * x_2^0 * x_2^1 ty[0 * q1 + 1] = 2.0 * tx[2 * q1 + 0] * tx[2 * q1 + 1]; // y_1^1 = x_1^0 * x_0^1 + x_0^0 * x_1^1 ty[1 * q1 + 1] = tx[1 * q1 + 0] * tx[0 * q1 + 1]; ty[1 * q1 + 1] += tx[0 * q1 + 0] * tx[1 * q1 + 1]; } return ok; } /* %$$ $head reverse$$ $srccode%cpp% */ // reverse mode routine called by CppAD virtual bool reverse( size_t q , const vector& tx , const vector& ty , vector& px , const vector& py ) { size_t q1 = q + 1; size_t n = tx.size() / q1; # ifndef NDEBUG size_t m = ty.size() / q1; # endif assert( n == 3 ); assert( m == 2 ); // this example only implements up to second order reverse mode bool ok = q1 <= 2; if( ! ok ) return ok; // // initialize summation as zero for(size_t j = 0; j < n; j++) for(size_t k = 0; k < q1; k++) px[j * q1 + k] = 0.0; // if( q1 == 2 ) { // -------------------------------------------------------------- // Second order reverse first compute partials of first order // We use the notation pf_ij^k for partial of F_i^1 w.r.t. x_j^k // // y_0^1 = 2 * x_2^0 * x_2^1 // pf_02^0 = 2 * x_2^1 // pf_02^1 = 2 * x_2^0 // // y_1^1 = x_1^0 * x_0^1 + x_0^0 * x_1^1 // pf_10^0 = x_1^1 // pf_11^0 = x_0^1 // pf_10^1 = x_1^0 // pf_11^1 = x_0^0 // // px_0^0 += py_0^1 * pf_00^0 + py_1^1 * pf_10^0 // += py_1^1 * x_1^1 px[0 * q1 + 0] += py[1 * q1 + 1] * tx[1 * q1 + 1]; // // px_0^1 += py_0^1 * pf_00^1 + py_1^1 * pf_10^1 // += py_1^1 * x_1^0 px[0 * q1 + 1] += py[1 * q1 + 1] * tx[1 * q1 + 0]; // // px_1^0 += py_0^1 * pf_01^0 + py_1^1 * pf_11^0 // += py_1^1 * x_0^1 px[1 * q1 + 0] += py[1 * q1 + 1] * tx[0 * q1 + 1]; // // px_1^1 += py_0^1 * pf_01^1 + py_1^1 * pf_11^1 // += py_1^1 * x_0^0 px[1 * q1 + 1] += py[1 * q1 + 1] * tx[0 * q1 + 0]; // // px_2^0 += py_0^1 * pf_02^0 + py_1^1 * pf_12^0 // += py_0^1 * 2 * x_2^1 px[2 * q1 + 0] += py[0 * q1 + 1] * 2.0 * tx[2 * q1 + 1]; // // px_2^1 += py_0^1 * pf_02^1 + py_1^1 * pf_12^1 // += py_0^1 * 2 * x_2^0 px[2 * q1 + 1] += py[0 * q1 + 1] * 2.0 * tx[2 * q1 + 0]; } // -------------------------------------------------------------- // First order reverse computes partials of zero order coefficients // We use the notation pf_ij for partial of F_i^0 w.r.t. x_j^0 // // y_0^0 = x_2^0 * x_2^0 // pf_00 = 0, pf_01 = 0, pf_02 = 2 * x_2^0 // // y_1^0 = x_0^0 * x_1^0 // pf_10 = x_1^0, pf_11 = x_0^0, pf_12 = 0 // // px_0^0 += py_0^0 * pf_00 + py_1^0 * pf_10 // += py_1^0 * x_1^0 px[0 * q1 + 0] += py[1 * q1 + 0] * tx[1 * q1 + 0]; // // px_1^0 += py_1^0 * pf_01 + py_1^0 * pf_11 // += py_1^0 * x_0^0 px[1 * q1 + 0] += py[1 * q1 + 0] * tx[0 * q1 + 0]; // // px_2^0 += py_1^0 * pf_02 + py_1^0 * pf_12 // += py_0^0 * 2.0 * x_2^0 px[2 * q1 + 0] += py[0 * q1 + 0] * 2.0 * tx[2 * q1 + 0]; // -------------------------------------------------------------- return ok; } }; } // End empty namespace /* %$$ $head Use Atomic Function$$ $srccode%cpp% */ bool reverse(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); // // Create the atomic_reverse object atomic_reverse afun("atomic_reverse"); // // Create the function f(u) // // domain space vector size_t n = 3; double x_0 = 1.00; double x_1 = 2.00; double x_2 = 3.00; vector< AD > au(n); au[0] = x_0; au[1] = x_1; au[2] = x_2; // declare independent variables and start tape recording CppAD::Independent(au); // range space vector size_t m = 2; vector< AD > ay(m); // call atomic function vector< AD > ax = au; afun(ax, ay); // create f: u -> y and stop tape recording CppAD::ADFun f; f.Dependent (au, ay); // y = f(u) // // check function value double check = x_2 * x_2; ok &= NearEqual( Value(ay[0]) , check, eps, eps); check = x_0 * x_1; ok &= NearEqual( Value(ay[1]) , check, eps, eps); // -------------------------------------------------------------------- // zero order forward // vector x0(n), y0(m); x0[0] = x_0; x0[1] = x_1; x0[2] = x_2; y0 = f.Forward(0, x0); check = x_2 * x_2; ok &= NearEqual(y0[0] , check, eps, eps); check = x_0 * x_1; ok &= NearEqual(y0[1] , check, eps, eps); // -------------------------------------------------------------------- // first order reverse // // value of Jacobian of f double check_jac[] = { 0.0, 0.0, 2.0 * x_2, x_1, x_0, 0.0 }; vector w(m), dw(n); // // check derivative of f_0 (x) for(size_t i = 0; i < m; i++) { w[i] = 1.0; w[1-i] = 0.0; dw = f.Reverse(1, w); for(size_t j = 0; j < n; j++) { // compute partial in j-th component direction ok &= NearEqual(dw[j], check_jac[i * n + j], eps, eps); } } // -------------------------------------------------------------------- // second order reverse // // value of Hessian of f_0 double check_hes_0[] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0 }; // // value of Hessian of f_1 double check_hes_1[] = { 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; vector x1(n), dw2( 2 * n ); for(size_t j = 0; j < n; j++) { for(size_t j1 = 0; j1 < n; j1++) x1[j1] = 0.0; x1[j] = 1.0; // first order forward f.Forward(1, x1); w[0] = 1.0; w[1] = 0.0; dw2 = f.Reverse(2, w); for(size_t i = 0; i < n; i++) ok &= NearEqual(dw2[i * 2 + 1], check_hes_0[i * n + j], eps, eps); w[0] = 0.0; w[1] = 1.0; dw2 = f.Reverse(2, w); for(size_t i = 0; i < n; i++) ok &= NearEqual(dw2[i * 2 + 1], check_hes_1[i * n + j], eps, eps); } // -------------------------------------------------------------------- return ok; } /* %$$ $end */ ================================================ FILE: test_more/deprecated/atomic_two/set_sparsity.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin atomic_two_set_sparsity.cpp@@ $spell enum $$ $section Atomic Sparsity with Set Patterns: Example and Test$$ $head function$$ For this example, the atomic function $latex f : \B{R}^3 \rightarrow \B{R}^2$$ is defined by $latex \[ f( x ) = \left( \begin{array}{c} x_2 \\ x_0 * x_1 \end{array} \right) \] $$ $head set_sparsity_enum$$ This example only uses set sparsity patterns. $head Start Class Definition$$ $srccode%cpp% */ # include namespace { // isolate items below to this file using CppAD::vector; // vector typedef vector< std::set > set_vector; // atomic_sparsity // // a utility to compute the union of two sets. using CppAD::set_union; // class atomic_set_sparsity : public CppAD::atomic_base { /* %$$ $head Constructor $$ $srccode%cpp% */ public: // constructor atomic_set_sparsity(const std::string& name) : // this example only uses set sparsity patterns CppAD::atomic_base(name, set_sparsity_enum ) { } private: /* %$$ $head forward$$ $srccode%cpp% */ // forward virtual bool forward( size_t p , size_t q , const vector& vx , vector& vy , const vector& tx , vector& ty ) { size_t n = tx.size() / (q + 1); # ifndef NDEBUG size_t m = ty.size() / (q + 1); # endif assert( n == 3 ); assert( m == 2 ); // only order zero bool ok = q == 0; if( ! ok ) return ok; // check for defining variable information if( vx.size() > 0 ) { ok &= vx.size() == n; vy[0] = vx[2]; vy[1] = vx[0] || vx[1]; } // Order zero forward mode. // y[0] = x[2], y[1] = x[0] * x[1] if( p <= 0 ) { ty[0] = tx[2]; ty[1] = tx[0] * tx[1]; } return ok; } /* %$$ $head for_sparse_jac$$ $srccode%cpp% */ // for_sparse_jac virtual bool for_sparse_jac( size_t p , const set_vector& r , set_vector& s , const vector& x ) { // This function needed if using f.ForSparseJac # ifndef NDEBUG size_t n = r.size(); size_t m = s.size(); # endif assert( n == x.size() ); assert( n == 3 ); assert( m == 2 ); // sparsity for S(x) = f'(x) * R = [ 0, 0, 1 ] * R s[0] = r[2]; // s[1] = union(r[0], r[1]) s[1] = set_union(r[0], r[1]); // return true; } /* %$$ $head rev_sparse_jac$$ $srccode%cpp% */ virtual bool rev_sparse_jac( size_t p , const set_vector& rt , set_vector& st , const vector& x ) { // This function needed if using RevSparseJac or optimize # ifndef NDEBUG size_t n = st.size(); size_t m = rt.size(); # endif assert( n == x.size() ); assert( n == 3 ); assert( m == 2 ); // [ 0, x1 ] // sparsity for S(x)^T = f'(x)^T * R^T = [ 0, x0 ] * R^T // [ 1, 0 ] st[0] = rt[1]; st[1] = rt[1]; st[2] = rt[0]; return true; } /* %$$ $head for_sparse_hes$$ $srccode%cpp% */ virtual bool for_sparse_hes( const vector& vx, const vector& r , const vector& s , set_vector& h , const vector& x ) { size_t n = r.size(); # ifndef NDEBUG size_t m = s.size(); # endif assert( x.size() == n ); assert( h.size() == n ); assert( n == 3 ); assert( m == 2 ); // initialize h as empty for(size_t i = 0; i < n; i++) h[i].clear(); // only f_1 has a non-zero hessian if( ! s[1] ) return true; // only the cross term between x[0] and x[1] is non-zero if( ! ( r[0] && r[1] ) ) return true; // set the possibly non-zero terms in the hessian h[0].insert(1); h[1].insert(0); return true; } /* %$$ $head rev_sparse_hes$$ $srccode%cpp% */ virtual bool rev_sparse_hes( const vector& vx, const vector& s , vector& t , size_t p , const set_vector& r , const set_vector& u , set_vector& v , const vector& x ) { // This function needed if using RevSparseHes # ifndef NDEBUG size_t m = s.size(); size_t n = t.size(); # endif assert( x.size() == n ); assert( r.size() == n ); assert( u.size() == m ); assert( v.size() == n ); assert( n == 3 ); assert( m == 2 ); // sparsity for T(x) = S(x) * f'(x) = S(x) * [ 0, 0, 1 ] // [ x1, x0, 0 ] t[0] = s[1]; t[1] = s[1]; t[2] = s[0]; // V(x) = f'(x)^T * g''(y) * f'(x) * R + g'(y) * f''(x) * R // U(x) = g''(y) * f'(x) * R // S(x) = g'(y) // [ 0, x1 ] // sparsity for W(x) = f'(x)^T * U(x) = [ 0, x0 ] * U(x) // [ 1, 0 ] v[0] = u[1]; v[1] = u[1]; v[2] = u[0]; // // [ 0, 1, 0 ] // sparsity for V(x) = W(x) + S_1 (x) * [ 1, 0, 0 ] * R // [ 0, 0, 0 ] if( s[1] ) { // v[0] = union( v[0], r[1] ) v[0] = set_union(v[0], r[1]); // v[1] = union( v[1], r[0] ) v[1] = set_union(v[1], r[0]); } return true; } /* %$$ $head End Class Definition$$ $srccode%cpp% */ }; // End of atomic_set_sparsity class } // End empty namespace /* %$$ $head Test Atomic Function$$ $srccode%cpp% */ bool set_sparsity(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * std::numeric_limits::epsilon(); /* %$$ $subhead Constructor$$ $srccode%cpp% */ // Create the atomic get_started object atomic_set_sparsity afun("atomic_set_sparsity"); /* %$$ $subhead Recording$$ $srccode%cpp% */ size_t n = 3; size_t m = 2; vector< AD > ax(n), ay(m); for(size_t j = 0; j < n; j++) ax[j] = double(j + 1); // declare independent variables and start tape recording CppAD::Independent(ax); // call atomic function afun(ax, ay); // create f: x -> y and stop tape recording CppAD::ADFun f; f.Dependent (ax, ay); // f(x) = x // check function value ok &= NearEqual(ay[0] , ax[2], eps, eps); ok &= NearEqual(ay[1] , ax[0] * ax[1], eps, eps); /* %$$ $subhead for_sparse_jac$$ $srccode%cpp% */ // correct Jacobian result set_vector check_s(m); check_s[0].insert(2); check_s[1].insert(0); check_s[1].insert(1); // compute and test forward mode { set_vector r(n), s(m); for(size_t i = 0; i < n; i++) r[i].insert(i); s = f.ForSparseJac(n, r); for(size_t i = 0; i < m; i++) ok &= s[i] == check_s[i]; } /* %$$ $subhead rev_sparse_jac$$ $srccode%cpp% */ // compute and test reverse mode { set_vector r(m), s(m); for(size_t i = 0; i < m; i++) r[i].insert(i); s = f.RevSparseJac(m, r); for(size_t i = 0; i < m; i++) ok &= s[i] == check_s[i]; } /* %$$ $subhead for_sparse_hes$$ $srccode%cpp% */ // correct Hessian result set_vector check_h(n); check_h[0].insert(1); check_h[1].insert(0); // compute and test forward mode { set_vector r(1), s(1), h(n); for(size_t i = 0; i < m; i++) s[0].insert(i); for(size_t j = 0; j < n; j++) r[0].insert(j); h = f.ForSparseHes(r, s); for(size_t i = 0; i < n; i++) ok &= h[i] == check_h[i]; } /* %$$ $subhead rev_sparse_hes$$ Note the previous call to $code ForSparseJac$$ above stored its results in $icode f$$. $srccode%cpp% */ // compute and test reverse mode { set_vector s(1), h(n); for(size_t i = 0; i < m; i++) s[0].insert(i); h = f.RevSparseHes(n, s); for(size_t i = 0; i < n; i++) ok &= h[i] == check_h[i]; } /* %$$ $subhead Test Result$$ $srccode%cpp% */ return ok; } /* %$$ $end */ ================================================ FILE: test_more/deprecated/atomic_two/tangent.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin atomic_two_tangent.cpp@@ $spell Tanh bool $$ $section Tan and Tanh as User Atomic Operations: Example and Test$$ $head Theory$$ The code below uses the $cref tan_forward$$ and $cref tan_reverse$$ to implement the tangent and hyperbolic tangent functions as atomic function operations. $head sparsity$$ This atomic operation can use both set and bool sparsity patterns. $head Start Class Definition$$ $srccode%cpp% */ # include namespace { // Begin empty namespace using CppAD::vector; // // a utility to compute the union of two sets. using CppAD::set_union; // class atomic_tangent : public CppAD::atomic_base { /* %$$ $head Constructor $$ $srccode%cpp% */ private: const bool hyperbolic_; // is this hyperbolic tangent public: // constructor atomic_tangent(const char* name, bool hyperbolic) : CppAD::atomic_base(name), hyperbolic_(hyperbolic) { } private: /* %$$ $head forward$$ $srccode%cpp% */ // forward mode routine called by CppAD bool forward( size_t p , size_t q , const vector& vx , vector& vzy , const vector& tx , vector& tzy ) { size_t q1 = q + 1; # ifndef NDEBUG size_t n = tx.size() / q1; size_t m = tzy.size() / q1; # endif assert( n == 1 ); assert( m == 2 ); assert( p <= q ); size_t j, k; // check if this is during the call to old_tan(id, ax, ay) if( vx.size() > 0 ) { // set variable flag for both y an z vzy[0] = vx[0]; vzy[1] = vx[0]; } if( p == 0 ) { // z^{(0)} = tan( x^{(0)} ) or tanh( x^{(0)} ) if( hyperbolic_ ) tzy[0] = float( tanh( tx[0] ) ); else tzy[0] = float( tan( tx[0] ) ); // y^{(0)} = z^{(0)} * z^{(0)} tzy[q1 + 0] = tzy[0] * tzy[0]; p++; } for(j = p; j <= q; j++) { float j_inv = 1.f / float(j); if( hyperbolic_ ) j_inv = - j_inv; // z^{(j)} = x^{(j)} +- sum_{k=1}^j k x^{(k)} y^{(j-k)} / j tzy[j] = tx[j]; for(k = 1; k <= j; k++) tzy[j] += tx[k] * tzy[q1 + j-k] * float(k) * j_inv; // y^{(j)} = sum_{k=0}^j z^{(k)} z^{(j-k)} tzy[q1 + j] = 0.; for(k = 0; k <= j; k++) tzy[q1 + j] += tzy[k] * tzy[j-k]; } // All orders are implemented and there are no possible errors return true; } /* %$$ $head reverse$$ $srccode%cpp% */ // reverse mode routine called by CppAD virtual bool reverse( size_t q , const vector& tx , const vector& tzy , vector& px , const vector& pzy ) { size_t q1 = q + 1; # ifndef NDEBUG size_t n = tx.size() / q1; size_t m = tzy.size() / q1; # endif assert( px.size() == n * q1 ); assert( pzy.size() == m * q1 ); assert( n == 1 ); assert( m == 2 ); size_t j, k; // copy because partials w.r.t. y and z need to change vector qzy = pzy; // initialize accumultion of reverse mode partials for(k = 0; k < q1; k++) px[k] = 0.; // eliminate positive orders for(j = q; j > 0; j--) { float j_inv = 1.f / float(j); if( hyperbolic_ ) j_inv = - j_inv; // H_{x^{(k)}} += delta(j-k) +- H_{z^{(j)} y^{(j-k)} * k / j px[j] += qzy[j]; for(k = 1; k <= j; k++) px[k] += qzy[j] * tzy[q1 + j-k] * float(k) * j_inv; // H_{y^{j-k)} += +- H_{z^{(j)} x^{(k)} * k / j for(k = 1; k <= j; k++) qzy[q1 + j-k] += qzy[j] * tx[k] * float(k) * j_inv; // H_{z^{(k)}} += H_{y^{(j-1)}} * z^{(j-k-1)} * 2. for(k = 0; k < j; k++) qzy[k] += qzy[q1 + j-1] * tzy[j-k-1] * 2.f; } // eliminate order zero if( hyperbolic_ ) px[0] += qzy[0] * (1.f - tzy[q1 + 0]); else px[0] += qzy[0] * (1.f + tzy[q1 + 0]); return true; } /* %$$ $head for_sparse_jac$$ $srccode%cpp% */ // forward Jacobian sparsity routine called by CppAD virtual bool for_sparse_jac( size_t p , const vector& r , vector& s , const vector& x ) { # ifndef NDEBUG size_t n = r.size() / p; size_t m = s.size() / p; # endif assert( n == x.size() ); assert( n == 1 ); assert( m == 2 ); // sparsity for S(x) = f'(x) * R for(size_t j = 0; j < p; j++) { s[0 * p + j] = r[j]; s[1 * p + j] = r[j]; } return true; } // forward Jacobian sparsity routine called by CppAD virtual bool for_sparse_jac( size_t p , const vector< std::set >& r , vector< std::set >& s , const vector& x ) { # ifndef NDEBUG size_t n = r.size(); size_t m = s.size(); # endif assert( n == x.size() ); assert( n == 1 ); assert( m == 2 ); // sparsity for S(x) = f'(x) * R s[0] = r[0]; s[1] = r[0]; return true; } /* %$$ $head rev_sparse_jac$$ $srccode%cpp% */ // reverse Jacobian sparsity routine called by CppAD virtual bool rev_sparse_jac( size_t p , const vector& rt , vector& st , const vector& x ) { # ifndef NDEBUG size_t n = st.size() / p; size_t m = rt.size() / p; # endif assert( n == 1 ); assert( m == 2 ); assert( n == x.size() ); // sparsity for S(x)^T = f'(x)^T * R^T for(size_t j = 0; j < p; j++) st[j] = rt[0 * p + j] || rt[1 * p + j]; return true; } // reverse Jacobian sparsity routine called by CppAD virtual bool rev_sparse_jac( size_t p , const vector< std::set >& rt , vector< std::set >& st , const vector& x ) { # ifndef NDEBUG size_t n = st.size(); size_t m = rt.size(); # endif assert( n == 1 ); assert( m == 2 ); assert( n == x.size() ); // sparsity for S(x)^T = f'(x)^T * R^T st[0] = set_union(rt[0], rt[1]); return true; } /* %$$ $head rev_sparse_hes$$ $srccode%cpp% */ // reverse Hessian sparsity routine called by CppAD virtual bool rev_sparse_hes( const vector& vx, const vector& s , vector& t , size_t p , const vector& r , const vector& u , vector& v , const vector& x ) { # ifndef NDEBUG size_t m = s.size(); size_t n = t.size(); # endif assert( x.size() == n ); assert( r.size() == n * p ); assert( u.size() == m * p ); assert( v.size() == n * p ); assert( n == 1 ); assert( m == 2 ); // There are no cross term second derivatives for this case, // so it is not necessary to vx. // sparsity for T(x) = S(x) * f'(x) t[0] = s[0] || s[1]; // V(x) = f'(x)^T * g''(y) * f'(x) * R + g'(y) * f''(x) * R // U(x) = g''(y) * f'(x) * R // S(x) = g'(y) // back propagate the sparsity for U, note both components // of f'(x) may be non-zero; size_t j; for(j = 0; j < p; j++) v[j] = u[ 0 * p + j ] || u[ 1 * p + j ]; // include forward Jacobian sparsity in Hessian sparsity // (note sparsty for f''(x) * R same as for R) if( s[0] || s[1] ) { for(j = 0; j < p; j++) { // Visual Studio 2013 generates warning without bool below v[j] |= bool( r[j] ); } } return true; } // reverse Hessian sparsity routine called by CppAD virtual bool rev_sparse_hes( const vector& vx, const vector& s , vector& t , size_t p , const vector< std::set >& r , const vector< std::set >& u , vector< std::set >& v , const vector& x ) { # ifndef NDEBUG size_t m = s.size(); size_t n = t.size(); # endif assert( x.size() == n ); assert( r.size() == n ); assert( u.size() == m ); assert( v.size() == n ); assert( n == 1 ); assert( m == 2 ); // There are no cross term second derivatives for this case, // so it is not necessary to vx. // sparsity for T(x) = S(x) * f'(x) t[0] = s[0] || s[1]; // V(x) = f'(x)^T * g''(y) * f'(x) * R + g'(y) * f''(x) * R // U(x) = g''(y) * f'(x) * R // S(x) = g'(y) // back propagate the sparsity for U, note both components // of f'(x) may be non-zero; v[0] = set_union(u[0], u[1]); // include forward Jacobian sparsity in Hessian sparsity // (note sparsty for f''(x) * R same as for R) if( s[0] || s[1] ) v[0] = set_union(v[0], r[0]); return true; } /* %$$ $head End Class Definition$$ $srccode%cpp% */ }; // End of atomic_tangent class } // End empty namespace /* %$$ $head Use Atomic Function$$ $srccode%cpp% */ bool tangent(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; float eps = 10.f * CppAD::numeric_limits::epsilon(); /* %$$ $subhead Constructor$$ $srccode%cpp% */ // -------------------------------------------------------------------- // Create a tan and tanh object atomic_tangent my_tan("my_tan", false), my_tanh("my_tanh", true); /* %$$ $subhead Recording$$ $srccode%cpp% */ // domain space vector size_t n = 1; float x0 = 0.5; CppAD::vector< AD > ax(n); ax[0] = x0; // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 3; CppAD::vector< AD > af(m); // temporary vector for computations // (my_tan and my_tanh computes tan or tanh and its square) CppAD::vector< AD > az(2); // call atomic tan function and store tan(x) in f[0] (ignore tan(x)^2) my_tan(ax, az); af[0] = az[0]; // call atomic tanh function and store tanh(x) in f[1] (ignore tanh(x)^2) my_tanh(ax, az); af[1] = az[0]; // put a constant in f[2] = tanh(1.) (for sparsity pattern testing) CppAD::vector< AD > one(1); one[0] = 1.; my_tanh(one, az); af[2] = az[0]; // create f: x -> f and stop tape recording CppAD::ADFun F; F.Dependent(ax, af); /* %$$ $subhead forward$$ $srccode%cpp% */ // check function value float tan = std::tan(x0); ok &= NearEqual(af[0] , tan, eps, eps); float tanh = std::tanh(x0); ok &= NearEqual(af[1] , tanh, eps, eps); // check zero order forward CppAD::vector x(n), f(m); x[0] = x0; f = F.Forward(0, x); ok &= NearEqual(f[0] , tan, eps, eps); ok &= NearEqual(f[1] , tanh, eps, eps); // compute first partial of f w.r.t. x[0] using forward mode CppAD::vector dx(n), df(m); dx[0] = 1.; df = F.Forward(1, dx); /* %$$ $subhead reverse$$ $srccode%cpp% */ // compute derivative of tan - tanh using reverse mode CppAD::vector w(m), dw(n); w[0] = 1.; w[1] = 1.; w[2] = 0.; dw = F.Reverse(1, w); // tan'(x) = 1 + tan(x) * tan(x) // tanh'(x) = 1 - tanh(x) * tanh(x) float tanp = 1.f + tan * tan; float tanhp = 1.f - tanh * tanh; ok &= NearEqual(df[0], tanp, eps, eps); ok &= NearEqual(df[1], tanhp, eps, eps); ok &= NearEqual(dw[0], w[0]*tanp + w[1]*tanhp, eps, eps); // compute second partial of f w.r.t. x[0] using forward mode CppAD::vector ddx(n), ddf(m); ddx[0] = 0.; ddf = F.Forward(2, ddx); // compute second derivative of tan - tanh using reverse mode CppAD::vector ddw(2); ddw = F.Reverse(2, w); // tan''(x) = 2 * tan(x) * tan'(x) // tanh''(x) = - 2 * tanh(x) * tanh'(x) // Note that second order Taylor coefficient for u half the // corresponding second derivative. float two = 2; float tanpp = two * tan * tanp; float tanhpp = - two * tanh * tanhp; ok &= NearEqual(two * ddf[0], tanpp, eps, eps); ok &= NearEqual(two * ddf[1], tanhpp, eps, eps); ok &= NearEqual(ddw[0], w[0]*tanp + w[1]*tanhp , eps, eps); ok &= NearEqual(ddw[1], w[0]*tanpp + w[1]*tanhpp, eps, eps); /* %$$ $subhead for_sparse_jac$$ $srccode%cpp% */ // Forward mode computation of sparsity pattern for F. size_t p = n; // user vectorBool because m and n are small CppAD::vectorBool r1(p), s1(m * p); r1[0] = true; // propagate sparsity for x[0] s1 = F.ForSparseJac(p, r1); ok &= (s1[0] == true); // f[0] depends on x[0] ok &= (s1[1] == true); // f[1] depends on x[0] ok &= (s1[2] == false); // f[2] does not depend on x[0] /* %$$ $subhead rev_sparse_jac$$ $srccode%cpp% */ // Reverse mode computation of sparsity pattern for F. size_t q = m; CppAD::vectorBool s2(q * m), r2(q * n); // Sparsity pattern for identity matrix size_t i, j; for(i = 0; i < q; i++) { for(j = 0; j < m; j++) s2[i * q + j] = (i == j); } r2 = F.RevSparseJac(q, s2); ok &= (r2[0] == true); // f[0] depends on x[0] ok &= (r2[1] == true); // f[1] depends on x[0] ok &= (r2[2] == false); // f[2] does not depend on x[0] /* %$$ $subhead rev_sparse_hes$$ $srccode%cpp% */ // Hessian sparsity for f[0] CppAD::vectorBool s3(m), h(p * n); s3[0] = true; s3[1] = false; s3[2] = false; h = F.RevSparseHes(p, s3); ok &= (h[0] == true); // Hessian is non-zero // Hessian sparsity for f[2] s3[0] = false; s3[2] = true; h = F.RevSparseHes(p, s3); ok &= (h[0] == false); // Hessian is zero /* %$$ $subhead Large x Values$$ $srccode%cpp% */ // check tanh results for a large value of x x[0] = std::numeric_limits::max() / two; f = F.Forward(0, x); tanh = 1.; ok &= NearEqual(f[1], tanh, eps, eps); df = F.Forward(1, dx); tanhp = 0.; ok &= NearEqual(df[1], tanhp, eps, eps); return ok; } /* %$$ $end */ ================================================ FILE: test_more/deprecated/chkpoint_one/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list chkpoint_one.cpp extended_ode.cpp get_started.cpp mul_level.cpp ode.cpp ) # END_SORT_THIS_LINE_MINUS_2 set_compile_flags(test_more_deprecated_chkpoint_one "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(test_more_deprecated_chkpoint_one EXCLUDE_FROM_ALL ${source_list} ) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(test_more_deprecated_chkpoint_one ${cppad_lib} ${colpack_libs} ) # # check_test_more_deprecated_chkpoint_one add_check_executable(check_test_more_deprecated chkpoint_one) ================================================ FILE: test_more/deprecated/chkpoint_one/chkpoint_one.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // CPPAD_HAS_* defines # include // system include files used for I/O # include // C style asserts # include // for thread_alloc # include // test runner # include // external complied tests extern bool extended_ode(void); extern bool get_started(void); extern bool mul_level(void); extern bool ode(void); // main program that runs all the tests int main(void) { std::string group = "example/chkpoint_one"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // external compiled tests Run( extended_ode, "extended_ode" ); Run( get_started, "get_started" ); Run( mul_level, "mul_level" ); Run( ode, "ode" ); // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } ================================================ FILE: test_more/deprecated/chkpoint_one/extended_ode.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin chkpoint_one_extended_ode.cpp@@ $spell Checkpointing Runge-Kutta mul $$ $section Checkpointing an Extended ODE Solver: Example and Test$$ $index mul_level, checkpoint$$ $head See Also$$ $cref chkpoint_one_ode.cpp$$, $cref chkpoint_one_mul_level.cpp$$. $head Discussion$$ Suppose that we wish to extend an ODE to include derivatives with respect to some parameter in the ODE. In addition, suppose we wish to differentiate a function that depends on these derivatives. Applying checkpointing to at the second level of AD would not work; see $cref chkpoint_one_mul_level.cpp$$ In this example we show how one can do this by checkpointing an extended ODE solver. $head Problem$$ We consider the initial value problem with parameter $latex x$$ defined by, $latex z(0, x) = z_0 (x)$$, $latex \[ \partial_t z(t, x ) = h [ x , z(t, x) ] \]$$ Note that if $latex t$$ needs to be in the equation, one can define the first component of $latex z(t, x)$$ to be equal to $latex t$$. $head ODE Solver$$ For this example, we consider the Fourth order Runge-Kutta ODE solver. Given an approximation solution at time $latex t_k$$ denoted by $latex \tilde{z}_k (x)$$, and $latex \Delta t = t_{k+1} - t_k$$, it defines the approximation solution $latex \tilde{z}_{k+1} (x)$$ at time $latex t_{k+1}$$ by $latex \[ \begin{array}{rcl} h_1 & = & h [ x , \tilde{z}_k (x) ] \\ h_2 & = & h [ x , \tilde{z}_k (x) + \Delta t \; h_1 / 2 ] \\ h_3 & = & h [ x , \tilde{z}_k (x) + \Delta t \; h_2 / 2 ] \\ h_4 & = & h [ x , \tilde{z}_k (x) + \Delta t \; h_3 ] \\ \tilde{z}_{k+1} (x) & = & \tilde{z}_k (x) + \Delta t \; ( h_1 + 2 h_2 + 2 h_3 + h_4 ) / 6 \end{array} \] $$ If $latex \tilde{z}_k (x) = z_k (x)$$, then $latex \tilde{z}_{k+1} (x) = z_{k+1} (x) + O( \Delta t^5 )$$, then Other ODE solvers can use a similar method to the one used below. $head ODE$$ For this example the ODE is defined by $latex z(0, x) = 0$$ and $latex \[ h[ x, z(t, x) ] = \left( \begin{array}{c} x_0 \\ x_1 z_0 (t, x) \\ \vdots \\ x_{n-1} z_{n-2} (t, x) \end{array} \right) = \left( \begin{array}{c} \partial_t z_0 (t , x) \\ \partial_t z_1 (t , x) \\ \vdots \\ \partial_t z_{n-1} (t , x) \end{array} \right) \] $$ $head Solution$$ The solution of the ODE for this example, which is used to check the results, can be calculated by starting with the first row and then using the solution for the first row to solve the second and so on. Doing this we obtain $latex \[ z(t, x) = \left( \begin{array}{c} x_0 t \\ x_1 x_0 t^2 / 2 \\ \vdots \\ x_{n-1} x_{n-2} \ldots x_0 t^n / n ! \end{array} \right) \] $$ $comment %example/chkpoint_one/extended_ode.cpp%0%// BEGIN C++%// END C++%1%$$ $end */ // BEGIN C++ # include namespace { using CppAD::AD; typedef AD a1double; typedef AD a2double; // typedef CPPAD_TESTVECTOR( double ) a0vector; typedef CPPAD_TESTVECTOR( a1double ) a1vector; typedef CPPAD_TESTVECTOR( a2double ) a2vector; // // set once by main and kept that way double delta_t_ = std::numeric_limits::quiet_NaN(); size_t n_ = 0; // // The function h( x , y) template FloatVector h(const FloatVector& x, const FloatVector& y) { assert( size_t( x.size() ) == n_ ); assert( size_t( y.size() ) == n_ ); FloatVector result(n_); result[0] = x[0]; for(size_t i = 1; i < n_; i++) result[i] = x[i] * y[i-1]; return result; } // The 4-th Order Runge-Kutta Step template FloatVector Runge4(const FloatVector& x, const FloatVector& z0 ) { assert( size_t( x.size() ) == n_ ); assert( size_t( z0.size() ) == n_ ); // typedef typename FloatVector::value_type Float; // Float dt = Float(delta_t_); size_t m = z0.size(); // FloatVector h1(m), h2(m), h3(m), h4(m), result(m); h1 = h( x, z0 ); // for(size_t i = 0; i < m; i++) h2[i] = z0[i] + dt * h1[i] / 2.0; h2 = h( x, h2 ); // for(size_t i = 0; i < m; i++) h3[i] = z0[i] + dt * h2[i] / 2.0; h3 = h( x, h3 ); // for(size_t i = 0; i < m; i++) h4[i] = z0[i] + dt * h3[i]; h4 = h( x, h4 ); // for(size_t i = 0; i < m; i++) { Float dz = dt * ( h1[i] + 2.0*h2[i] + 2.0*h3[i] + h4[i] ) / 6.0; result[i] = z0[i] + dz; } return result; } // Derivative of 4-th Order Runge-Kutta Step w.r.t x a1vector Runge4_x(const a1vector& x, const a1vector& z0) { assert( size_t( x.size() ) == n_ ); assert( size_t( z0.size() ) == n_ ); // a2vector ax(n_); for(size_t j = 0; j < n_; j++) ax[j] = x[j]; // a2vector az0(n_); for(size_t i = 0; i < n_; i++) az0[i] = z0[i]; // CppAD::Independent(ax); a2vector az(n_); az = Runge4(ax, az0); CppAD::ADFun f(ax, az); // a1vector result = f.Jacobian(x); // return result; } // Derivative of 4-th Order Runge-Kutta Step w.r.t z0 a1vector Runge4_z0(const a1vector& x, const a1vector& z0) { assert( size_t( x.size() ) == n_ ); assert( size_t( z0.size() ) == n_ ); // a2vector ax(n_); for(size_t j = 0; j < n_; j++) ax[j] = x[j]; // a2vector az0(n_); for(size_t i = 0; i < n_; i++) az0[i] = z0[i]; // CppAD::Independent(az0); a2vector az(n_); az = Runge4(ax, az0); CppAD::ADFun f(az0, az); // a1vector result = f.Jacobian(z0); // return result; } // pack an extended ode vector template void pack( FloatVector& extended_ode , const FloatVector& x , const FloatVector& z , const FloatVector& z_x ) { assert( size_t( extended_ode.size() ) == n_ + n_ + n_ * n_ ); assert( size_t( x.size() ) == n_ ); assert( size_t( z.size() ) == n_ ); assert( size_t( z_x.size() ) == n_ * n_ ); // size_t offset = 0; for(size_t i = 0; i < n_; i++) extended_ode[offset + i] = x[i]; offset += n_; for(size_t i = 0; i < n_; i++) extended_ode[offset + i] = z[i]; offset += n_; for(size_t i = 0; i < n_; i++) { for(size_t j = 0; j < n_; j++) { // partial of z_i (t , x ) w.r.t x_j extended_ode[offset + i * n_ + j] = z_x[i * n_ + j]; } } } // unpack an extended ode vector template void unpack( const FloatVector& extended_ode , FloatVector& x , FloatVector& z , FloatVector& z_x ) { assert( size_t( extended_ode.size() ) == n_ + n_ + n_ * n_ ); assert( size_t( x.size() ) == n_ ); assert( size_t( z.size() ) == n_ ); assert( size_t( z_x.size() ) == n_ * n_ ); // size_t offset = 0; for(size_t i = 0; i < n_; i++) x[i] = extended_ode[offset + i]; offset += n_; for(size_t i = 0; i < n_; i++) z[i] = extended_ode[offset + i]; offset += n_; for(size_t i = 0; i < n_; i++) { for(size_t j = 0; j < n_; j++) { // partial of z_i (t , x ) w.r.t x_j z_x[i * n_ + j] = extended_ode[offset + i * n_ + j]; } } } // Algorithm that advances the partial of z(t, x) w.r.t x void ext_ode_algo(const a1vector& ext_ode_in, a1vector& ext_ode_out) { assert( size_t( ext_ode_in.size() ) == n_ + n_ + n_ * n_ ); assert( size_t( ext_ode_out.size() ) == n_ + n_ + n_ * n_ ); // // initial extended ode information a1vector x(n_), z0(n_), z0_x(n_ * n_); unpack(ext_ode_in, x, z0, z0_x); // // advance z(t, x) a1vector z1 = Runge4(x, z0); // // partial of z1 w.r.t. x a1vector z1_x = Runge4_x(x, z0); // // partial of z1 w.r.t. z0 a1vector z1_z0 = Runge4_z0(x, z0); // // total derivative of z1 w.r.t x for(size_t i = 0; i < n_; i++) { for(size_t j = 0; j < n_; j++) { a1double sum = 0.0; for(size_t k = 0; k < n_; k++) sum += z1_z0 [ i * n_ + k ] * z0_x [ k * n_ + j ]; z1_x[ i * n_ + j] += sum; } } // // final extended ode information pack(ext_ode_out, x, z1, z1_x); // return; } } // bool extended_ode(void) { bool ok = true; using CppAD::NearEqual; double eps = std::numeric_limits::epsilon(); // // number of terms in the differential equation n_ = 6; // // step size for the differentiail equation size_t n_step = 10; double T = 1.0; delta_t_ = T / double(n_step); // // set parameter value and initial value of the extended ode a1vector ax(n_), az0(n_), az0_x(n_ * n_); for(size_t i = 0; i < n_; i++) { ax[i] = a1double(i + 1); az0[i] = a1double(0); for(size_t j = 0; j < n_; j++) az0_x[ i * n_ + j ] = 0.0; } // // pack into extended ode information input vector size_t n_ext = n_ + n_ + n_ * n_; a1vector aext_ode_in(n_ext); pack(aext_ode_in, ax, az0, az0_x); // // create checkpoint version of the algorithm a1vector aext_ode_out(n_ext); CppAD::checkpoint ext_ode_check( "ext_ode", ext_ode_algo, aext_ode_in, aext_ode_out ); // // set the independent variables for recording CppAD::Independent( ax ); // // repack to get dependence on ax pack(aext_ode_in, ax, az0, az0_x); // // Now run the checkpoint algorithm n_step times for(size_t k = 0; k < n_step; k++) { ext_ode_check(aext_ode_in, aext_ode_out); aext_ode_in = aext_ode_out; } // // Unpack the results (must use ax1 so do not overwrite ax) a1vector ax1(n_), az1(n_), az1_x(n_ * n_); unpack(aext_ode_out, ax1, az1, az1_x); // // We could record a complicated function of x and z_x(T, x) in f, // but make this example simpler we record x -> z_x(T, x). CppAD::ADFun f(ax, az1_x); // // check function values a0vector x(n_), z1(n_), z1_x(n_ * n_); for(size_t j = 0; j < n_; j++) x[j] = double(j + 1); z1_x = f.Forward(0, x); // // use z(t, x) for checking solution z1[0] = x[0] * T; for(size_t i = 1; i < n_; i++) z1[i] = x[i] * T * z1[i-1] / double(i+1); // // expected accuracy for each component of of z(t, x) a0vector acc(n_); for(size_t i = 0; i < n_; i++) { if( i < 4 ) { // Runge-Kutta methods is exact for this case acc[i] = 10. * eps; } else { acc[i] = 1.0; for(size_t k = 0; k < 5; k++) acc[i] *= x[k] * delta_t_; } } // check z1(T, x) for(size_t i = 0; i < n_; i++) { for(size_t j = 0; j < n_; j++) { // check partial of z1_i w.r.t x_j double check = 0.0; if( j <= i ) check = z1[i] / x[j]; ok &= NearEqual(z1_x[ i * n_ + j ] , check, acc[i], acc[i]); } } // // Now use f to compute a derivative. For this 'simple' example it is // the derivative with respect to x of the // partial with respect to x[n-1] of z_{n-1} (t , x) a0vector w(n_ * n_), dw(n_); for(size_t i = 0; i < n_; i++) { for(size_t j = 0; j < n_; j++) { w[ i * n_ + j ] = 0.0; if( i == n_ - 1 && j == n_ - 1 ) w[ i * n_ + j ] = 1.0; } } dw = f.Reverse(1, w); for(size_t j = 0; j < n_; j++) { double check = 0.0; if( j < n_ - 1 ) check = z1[n_ - 1] / ( x[n_ - 1] * x[j] ); ok &= NearEqual(dw[j] , check, acc[n_-1], acc[n_-1]); } // return ok; } // END C++ ================================================ FILE: test_more/deprecated/chkpoint_one/get_started.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin chkpoint_one_get_started.cpp@@ $spell checkpointing Taylor $$ $section Simple Checkpointing: Example and Test$$ $head Purpose$$ Break a large computation into pieces and only store values at the interface of the pieces. In actual applications, there may be many functions, but for this example there are only two. The functions $latex F : \B{R}^2 \rightarrow \B{R}^2$$ and $latex G : \B{R}^2 \rightarrow \B{R}^2$$ defined by $latex \[ F(y) = \left( \begin{array}{c} y_0 + y_0 + y_0 \\ y_1 + y_1 + y_1 \end{array} \right) \; , \; G(x) = \left( \begin{array}{c} x_0 \cdot x_0 \cdot x_0 \\ x_1 \cdot x_1 \cdot x_1 \end{array} \right) \] $$ $comment %example/chkpoint_one/get_started.cpp%0%// BEGIN C++%// END C++%1%$$ $end */ // BEGIN C++ # include namespace { using CppAD::AD; typedef CPPAD_TESTVECTOR(AD) ADVector; typedef CppAD::atomic_base::option_enum option_enum; void f_algo(const ADVector& y, ADVector& z) { z[0] = 0.0; z[1] = 0.0; for(size_t k = 0; k < 3; k++) { z[0] += y[0]; z[1] += y[1]; } return; } void g_algo(const ADVector& x, ADVector& y) { y[0] = 1.0; y[1] = 1.0; for(size_t k = 0; k < 3; k++) { y[0] *= x[0]; y[1] *= x[1]; } return; } bool test_case( option_enum f_sparsity, option_enum g_sparsity, bool optimize ) { bool ok = true; using CppAD::checkpoint; using CppAD::ADFun; using CppAD::NearEqual; size_t i, j, k, n = 2, m = n; double eps = 10. * std::numeric_limits::epsilon(); // checkpoint version of the function F(x) ADVector ax(n), ay(n), az(m); for(j = 0; j < n; j++) ax[j] = double(j + 1); // could also use bool_sparsity_enum or set_sparsity_enum checkpoint atom_f("atom_f", f_algo, ax, ay, f_sparsity); checkpoint atom_g("atom_g", g_algo, ay, az, g_sparsity); // Record a version of z = g[f(x)] without checkpointing Independent(ax); f_algo(ax, ay); g_algo(ay, az); ADFun check_not(ax, az); // Record a version of z = g[f(x)] with checkpointing Independent(ax); atom_f(ax, ay); atom_g(ay, az); ADFun check_yes(ax, az); // checkpointing should use fewer operations ok &= check_yes.size_var() < check_not.size_var(); // this does not really save space because f and g are only used once ok &= check_not.size_var() <= check_yes.size_var() + atom_f.size_var() + atom_g.size_var(); // compare forward mode results for orders 0, 1, 2 size_t q = 2; CPPAD_TESTVECTOR(double) x_q(n*(q+1)), z_not(m*(q+1)), z_yes(m*(q+1)); for(j = 0; j < n; j++) { for(k = 0; k <= q; k++) x_q[ j * (q+1) + k ] = 1.0 / double(q + 1 - k); } z_not = check_not.Forward(q, x_q); z_yes = check_yes.Forward(q, x_q); for(i = 0; i < m; i++) { for(k = 0; k <= q; k++) { double zik_not = z_not[ i * (q+1) + k]; double zik_yes = z_yes[ i * (q+1) + k]; ok &= NearEqual(zik_not, zik_yes, eps, eps); } } // compare reverse mode results CPPAD_TESTVECTOR(double) w(m*(q+1)), dw_not(n*(q+1)), dw_yes(n*(q+1)); for(i = 0; i < m * (q + 1); i++) w[i] = 1.0 / double(i + 1); dw_not = check_not.Reverse(q+1, w); dw_yes = check_yes.Reverse(q+1, w); for(j = 0; j < n; j++) { for(k = 0; k <= q; k++) { double dwjk_not = dw_not[ j * (q+1) + k]; double dwjk_yes = dw_yes[ j * (q+1) + k]; ok &= NearEqual(dwjk_not, dwjk_yes, eps, eps); } } // compare forward mode Jacobian sparsity patterns CppAD::vector< std::set > r(n), s_not(m), s_yes(m); for(j = 0; j < n; j++) r[j].insert(j); s_not = check_not.ForSparseJac(n, r); s_yes = check_yes.ForSparseJac(n, r); for(i = 0; i < m; i++) ok &= s_not[i] == s_yes[i]; // compare reverse mode Jacobian sparsity patterns CppAD::vector< std::set > s(m), r_not(m), r_yes(m); for(i = 0; i < m; i++) s[i].insert(i); r_not = check_not.RevSparseJac(m, s); r_yes = check_yes.RevSparseJac(m, s); for(i = 0; i < m; i++) ok &= r_not[i] == r_yes[i]; // compare reverse mode Hessian sparsity patterns CppAD::vector< std::set > s_one(1), h_not(n), h_yes(n); for(i = 0; i < m; i++) s_one[0].insert(i); h_not = check_not.RevSparseHes(n, s_one); h_yes = check_yes.RevSparseHes(n, s_one); for(i = 0; i < n; i++) ok &= h_not[i] == h_yes[i]; return ok; } } bool get_started(void) { bool ok = true; // different types of sparsity option_enum pack_sparsity = CppAD::atomic_base::pack_sparsity_enum; option_enum bool_sparsity = CppAD::atomic_base::bool_sparsity_enum; option_enum set_sparsity = CppAD::atomic_base::set_sparsity_enum; // test some different cases ok &= test_case(pack_sparsity, pack_sparsity, true); ok &= test_case(pack_sparsity, bool_sparsity, false); ok &= test_case(bool_sparsity, set_sparsity, true); ok &= test_case(set_sparsity, set_sparsity, false); return ok; } // END C++ ================================================ FILE: test_more/deprecated/chkpoint_one/mul_level.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin chkpoint_one_mul_level.cpp@@ $spell checkpointing $$ $section Atomic Operations and Multiple-Levels of AD: Example and Test$$ $head Discussion$$ One can use $cref/checkpoint/chkpoint_one/$$ or $cref atomic_two$$ to code an $codei%AD<%Base%>%$$ operation as atomic. This means that derivative computations that use the type $icode Base$$ will call the corresponding $code atomic_base$$ member functions. On the other hand, if $icode Base$$ is $codei%AD<%Other%>%$$ the operations recorded at the $icode Base$$ level will not be atomic. This is demonstrated in this example. $comment %example/chkpoint_one/mul_level.cpp%0%// BEGIN C++%// END C++%1%$$ $end */ // BEGIN C++ # include namespace { using CppAD::AD; typedef AD a1double; typedef AD a2double; typedef CPPAD_TESTVECTOR(a1double) a1vector; typedef CPPAD_TESTVECTOR(a2double) a2vector; void f_algo(const a2vector& x, a2vector& y) { size_t n = x.size(); y[0] = 0.0; for(size_t j = 1; j < n; j++) y[0] += x[j-1] * x[j]; return; } } // bool mul_level(void) { bool ok = true; using CppAD::checkpoint; using CppAD::ADFun; using CppAD::Independent; // domain dimension for this problem size_t n = 10; size_t m = 1; // checkpoint version of the function F(x) a2vector a2x(n), a2y(m); for(size_t j = 0; j < n; j++) a2x[j] = a2double(j + 1); // // could also use bool_sparsity_enum or set_sparsity_enum checkpoint atom_f("atom_f", f_algo, a2x, a2y); // // Record a version of y = f(x) without checkpointing Independent(a2x); f_algo(a2x, a2y); ADFun check_not(a2x, a2y); // // number of variables in a tape of f_algo that does not use checkpointing size_t size_not = check_not.size_var(); // // Record a version of y = f(x) with checkpointing Independent(a2x); atom_f(a2x, a2y); ADFun check_yes(a2x, a2y); // // f_algo is represented by one atomic operation in this tape ok &= check_yes.size_var() < size_not; // // now record operations at a1double level a1vector a1x(n), a1y(m); for(size_t j = 0; j < n; j++) a1x[j] = a1double(j + 1); // // without checkpointing Independent(a1x); a1y = check_not.Forward(0, a1x); ADFun with_not(a1x, a1y); // // should have the same size ok &= with_not.size_var() == size_not; // // with checkpointing Independent(a1x); a1y = check_yes.Forward(0, a1x); ADFun with_yes(a1x, a1y); // // f_algo is nolonger represented by one atomic operation in this tape ok &= with_yes.size_var() == size_not; // return ok; } // END C++ ================================================ FILE: test_more/deprecated/chkpoint_one/ode.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin chkpoint_one_ode.cpp@@ $spell Checkpointing Runge-Kutta $$ $section Checkpointing an ODE Solver: Example and Test$$ $head See Also$$ $cref chkpoint_one_extended_ode.cpp$$, $head Purpose$$ In this example we $cref/checkpoint/chkpoint_one/$$ one step of an ODE solver. $head Problem$$ We consider the initial value problem with parameter $latex x$$ defined by, $latex z(0, x) = z_0 (x)$$, $latex \[ \partial_t z(t, x ) = h [ x , z(t, x) ] \]$$ Note that if $latex t$$ needs to be in the equation, one can define the first component of $latex z(t, x)$$ to be equal to $latex t$$. $head ODE Solver$$ For this example, we consider the Fourth order Runge-Kutta ODE solver. Given an approximation solution at time $latex t_k$$ denoted by $latex \tilde{z}_k (x)$$, and $latex \Delta t = t_{k+1} - t_k$$, it defines the approximation solution $latex \tilde{z}_{k+1} (x)$$ at time $latex t_{k+1}$$ by $latex \[ \begin{array}{rcl} h_1 & = & h [ x , \tilde{z}_k (x) ] \\ h_2 & = & h [ x , \tilde{z}_k (x) + \Delta t \; h_1 / 2 ] \\ h_3 & = & h [ x , \tilde{z}_k (x) + \Delta t \; h_2 / 2 ] \\ h_4 & = & h [ x , \tilde{z}_k (x) + \Delta t \; h_3 ] \\ \tilde{z}_{k+1} (x) & = & \tilde{z}_k (x) + \Delta t \; ( h_1 + 2 h_2 + 2 h_3 + h_4 ) / 6 \end{array} \] $$ If $latex \tilde{z}_k (x) = z_k (x)$$, $latex \tilde{z}_{k+1} (x) = z_{k+1} (x) + O( \Delta t^5 )$$. Other ODE solvers can use a similar method to the one used below. $head ODE$$ For this example the ODE is defined by $latex z(0, x) = 0$$ and $latex \[ h[ x, z(t, x) ] = \left( \begin{array}{c} x_0 \\ x_1 z_0 (t, x) \\ \vdots \\ x_{n-1} z_{n-2} (t, x) \end{array} \right) = \left( \begin{array}{c} \partial_t z_0 (t , x) \\ \partial_t z_1 (t , x) \\ \vdots \\ \partial_t z_{n-1} (t , x) \end{array} \right) \] $$ $head Solution$$ The solution of the ODE for this example, which is used to check the results, can be calculated by starting with the first row and then using the solution for the first row to solve the second and so on. Doing this we obtain $latex \[ z(t, x) = \left( \begin{array}{c} x_0 t \\ x_1 x_0 t^2 / 2 \\ \vdots \\ x_{n-1} x_{n-2} \ldots x_0 t^n / n ! \end{array} \right) \] $$ $comment %example/chkpoint_one/ode.cpp%0%// BEGIN C++%// END C++%1%$$ $end */ // BEGIN C++ # include namespace { using CppAD::AD; typedef AD a1double; typedef AD a2double; // typedef CPPAD_TESTVECTOR( double ) a0vector; typedef CPPAD_TESTVECTOR( a1double ) a1vector; typedef CPPAD_TESTVECTOR( a2double ) a2vector; // // set once by main and kept that way double delta_t_ = std::numeric_limits::quiet_NaN(); size_t n_ = 0; // // The function h( x , y) template FloatVector h(const FloatVector& x, const FloatVector& y) { assert( size_t( x.size() ) == n_ ); assert( size_t( y.size() ) == n_ ); FloatVector result(n_); result[0] = x[0]; for(size_t i = 1; i < n_; i++) result[i] = x[i] * y[i-1]; return result; } // The 4-th Order Runge-Kutta Step template FloatVector Runge4(const FloatVector& x, const FloatVector& z0 ) { assert( size_t( x.size() ) == n_ ); assert( size_t( z0.size() ) == n_ ); // typedef typename FloatVector::value_type Float; // Float dt = Float(delta_t_); size_t m = z0.size(); // FloatVector h1(m), h2(m), h3(m), h4(m), result(m); h1 = h( x, z0 ); // for(size_t i = 0; i < m; i++) h2[i] = z0[i] + dt * h1[i] / 2.0; h2 = h( x, h2 ); // for(size_t i = 0; i < m; i++) h3[i] = z0[i] + dt * h2[i] / 2.0; h3 = h( x, h3 ); // for(size_t i = 0; i < m; i++) h4[i] = z0[i] + dt * h3[i]; h4 = h( x, h4 ); // for(size_t i = 0; i < m; i++) { Float dz = dt * ( h1[i] + 2.0*h2[i] + 2.0*h3[i] + h4[i] ) / 6.0; result[i] = z0[i] + dz; } return result; } // pack x and z into an ode_info vector template void pack( FloatVector& ode_info , const FloatVector& x , const FloatVector& z ) { assert( size_t( ode_info.size() ) == n_ + n_ ); assert( size_t( x.size() ) == n_ ); assert( size_t( z.size() ) == n_ ); // size_t offset = 0; for(size_t i = 0; i < n_; i++) ode_info[offset + i] = x[i]; offset += n_; for(size_t i = 0; i < n_; i++) ode_info[offset + i] = z[i]; } // unpack an ode_info vector template void unpack( const FloatVector& ode_info , FloatVector& x , FloatVector& z ) { assert( size_t( ode_info.size() ) == n_ + n_ ); assert( size_t( x.size() ) == n_ ); assert( size_t( z.size() ) == n_ ); // size_t offset = 0; for(size_t i = 0; i < n_; i++) x[i] = ode_info[offset + i]; offset += n_; for(size_t i = 0; i < n_; i++) z[i] = ode_info[offset + i]; } // Algorithm that z(t, x) void ode_algo(const a1vector& ode_info_in, a1vector& ode_info_out) { assert( size_t( ode_info_in.size() ) == n_ + n_ ); assert( size_t( ode_info_out.size() ) == n_ + n_ ); // // initial ode information a1vector x(n_), z0(n_); unpack(ode_info_in, x, z0); // // advance z(t, x) a1vector z1 = Runge4(x, z0); // // final ode information pack(ode_info_out, x, z1); // return; } } // bool ode(void) { bool ok = true; using CppAD::NearEqual; double eps = std::numeric_limits::epsilon(); // // number of terms in the differential equation n_ = 6; // // step size for the differentiail equation size_t n_step = 10; double T = 1.0; delta_t_ = T / double(n_step); // // set parameter value and initial value of the ode a1vector ax(n_), az0(n_); for(size_t i = 0; i < n_; i++) { ax[i] = a1double(i + 1); az0[i] = a1double(0); } // // pack ode information input vector a1vector ode_info_in(2 * n_); pack(ode_info_in, ax, az0); // // create checkpoint version of the algorithm a1vector ode_info_out(2 * n_); CppAD::checkpoint ode_check( "ode", ode_algo, ode_info_in, ode_info_out ); // // set the independent variables for recording CppAD::Independent( ax ); // // repack to get dependence on ax pack(ode_info_in, ax, az0); // // Now run the checkpoint algorithm n_step times for(size_t k = 0; k < n_step; k++) { ode_check(ode_info_in, ode_info_out); ode_info_in = ode_info_out; } // // Unpack the results (must use ax1 so do not overwrite ax) a1vector ax1(n_), az1(n_); unpack(ode_info_out, ax1, az1); // // We could record a complicated function of x and z(T, x) in f, // but make this example simpler we record x -> z(T, x). CppAD::ADFun f(ax, az1); // // check function values a0vector x(n_), z1(n_); for(size_t j = 0; j < n_; j++) x[j] = double(j + 1); z1 = f.Forward(0, x); // // separate calculation of z(t, x) a0vector check_z1(n_); check_z1[0] = x[0] * T; for(size_t i = 1; i < n_; i++) check_z1[i] = x[i] * T * check_z1[i-1] / double(i+1); // // expected accuracy for each component of of z(t, x) a0vector acc(n_); for(size_t i = 0; i < n_; i++) { if( i < 4 ) { // Runge-Kutta methods is exact for this case acc[i] = 10. * eps; } else { acc[i] = 1.0; for(size_t k = 0; k < 5; k++) acc[i] *= x[k] * delta_t_; } } // check z1(T, x) for(size_t i = 0; i < n_; i++) ok &= NearEqual(z1[i] , check_z1[i], acc[i], acc[i]); // // Now use f to compute a derivative. For this 'simple' example it is // the derivative of z_{n-1} (T, x) respect to x of the a0vector w(n_), dw(n_); for(size_t i = 0; i < n_; i++) { w[i] = 0.0; if( i == n_ - 1 ) w[i] = 1.0; } dw = f.Reverse(1, w); for(size_t j = 0; j < n_; j++) { double check = z1[n_ - 1] / x[j]; ok &= NearEqual(dw[j] , check, 100.*eps, 100.*eps); } // return ok; } // END C++ ================================================ FILE: test_more/deprecated/deprecated.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // CPPAD_HAS_* defines # include // system include files used for I/O # include // memory leak checker # include // test runner # include extern bool old_mat_mul(void); extern bool old_reciprocal(void); extern bool old_tan(void); extern bool old_usead_1(void); extern bool old_usead_2(void); extern bool omp_alloc(void); extern bool track_new_del(void); extern bool zdouble(void); // main program that runs all the tests int main(void) { std::string group = "test_more/deprecated"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh Run( old_mat_mul, "old_mat_mul" ); Run( old_reciprocal, "old_reciprocal" ); Run( old_tan, "old_tan" ); Run( old_usead_1, "old_usead_1" ); Run( old_usead_2, "old_usead_2" ); Run( omp_alloc, "omp_alloc" ); Run( track_new_del, "track_new_del" ); Run( zdouble, "zdouble" ); // // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } // END PROGRAM ================================================ FILE: test_more/deprecated/old_mat_mul.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin old_mat_mul.cpp@@ $spell mul $$ $section Old Matrix Multiply as a User Atomic Operation: Example and Test$$ $head Deprecated 2013-05-27$$ This example has been deprecated; use $cref atomic_two_mat_mul.cpp$$ instead. $children% example/deprecated/old_mat_mul.hpp %$$ $head Include File$$ This routine uses the include file old_mat_mul.hpp. $srcthisfile%0%// BEGIN C++%// END C++%1%$$ $end */ // BEGIN C++ # include # include "old_mat_mul.hpp" bool old_mat_mul(void) { bool ok = true; using CppAD::AD; // matrix sizes for this test size_t nr_result = 2; size_t n_middle = 2; size_t nc_result = 2; // declare the AD vectors ax and ay and X size_t n = nr_result * n_middle + n_middle * nc_result; size_t m = nr_result * nc_result; CppAD::vector< AD > X(4), ax(n), ay(m); size_t i, j; for(j = 0; j < X.size(); j++) X[j] = (j + 1); // X is the vector of independent variables CppAD::Independent(X); // left matrix ax[0] = X[0]; // left[0,0] = x[0] = 1 ax[1] = X[1]; // left[0,1] = x[1] = 2 ax[2] = 5.; // left[1,0] = 5 ax[3] = 6.; // left[1,1] = 6 // right matrix ax[4] = X[2]; // right[0,0] = x[2] = 3 ax[5] = 7.; // right[0,1] = 7 ax[6] = X[3]; // right[1,0] = x[3] = 4 ax[7] = 8.; // right[1,1] = 8 /* [ x0 , x1 ] * [ x2 , 7 ] = [ x0*x2 + x1*x3 , x0*7 + x1*8 ] [ 5 , 6 ] [ x3 , 8 ] [ 5*x2 + 6*x3 , 5*7 + 6*8 ] */ // The call back routines need to know the dimensions of the matrices. // Store information about the matrix multiply for this call to mat_mul. call_info info; info.nr_result = nr_result; info.n_middle = n_middle; info.nc_result = nc_result; // info.vx gets set by forward during call to mat_mul below assert( info.vx.size() == 0 ); size_t id = info_.size(); info_.push_back(info); // user defined AD version of matrix multiply mat_mul(id, ax, ay); //---------------------------------------------------------------------- // check AD results ok &= ay[0] == (1*3 + 2*4); ok &= Variable( ay[0] ); ok &= ay[1] == (1*7 + 2*8); ok &= Variable( ay[1] ); ok &= ay[2] == (5*3 + 6*4); ok &= Variable( ay[2] ); ok &= ay[3] == (5*7 + 6*8); ok &= Parameter( ay[3] ); //---------------------------------------------------------------------- // use mat_mul to define a function g : X -> ay CppAD::ADFun G; G.Dependent(X, ay); // g(x) = [ x0*x2 + x1*x3 , x0*7 + x1*8 , 5*x2 + 6*x3 , 5*7 + 6*8 ]^T //---------------------------------------------------------------------- // Test zero order forward mode evaluation of g(x) CppAD::vector x( X.size() ), y(m); for(j = 0; j < X.size() ; j++) x[j] = double(j + 2); y = G.Forward(0, x); ok &= y[0] == x[0] * x[2] + x[1] * x[3]; ok &= y[1] == x[0] * 7. + x[1] * 8.; ok &= y[2] == 5. * x[2] + 6. * x[3]; ok &= y[3] == 5. * 7. + 6. * 8.; //---------------------------------------------------------------------- // Test first order forward mode evaluation of g'(x) * [1, 2, 3, 4]^T // g'(x) = [ x2, x3, x0, x1 ] // [ 7 , 8, 0, 0 ] // [ 0 , 0, 5, 6 ] // [ 0 , 0, 0, 0 ] CppAD::vector dx( X.size() ), dy(m); for(j = 0; j < X.size() ; j++) dx[j] = double(j + 1); dy = G.Forward(1, dx); ok &= dy[0] == 1. * x[2] + 2. * x[3] + 3. * x[0] + 4. * x[1]; ok &= dy[1] == 1. * 7. + 2. * 8. + 3. * 0. + 4. * 0.; ok &= dy[2] == 1. * 0. + 2. * 0. + 3. * 5. + 4. * 6.; ok &= dy[3] == 1. * 0. + 2. * 0. + 3. * 0. + 4. * 0.; //---------------------------------------------------------------------- // Test second order forward mode // g_0^2 (x) = [ 0, 0, 1, 0 ], g_0^2 (x) * [1] = [3] // [ 0, 0, 0, 1 ] [2] [4] // [ 1, 0, 0, 0 ] [3] [1] // [ 0, 1, 0, 0 ] [4] [2] CppAD::vector ddx( X.size() ), ddy(m); for(j = 0; j < X.size() ; j++) ddx[j] = 0.; ddy = G.Forward(2, ddx); // [1, 2, 3, 4] * g_0^2 (x) * [1, 2, 3, 4]^T = 1*3 + 2*4 + 3*1 + 4*2 ok &= 2. * ddy[0] == 1. * 3. + 2. * 4. + 3. * 1. + 4. * 2.; // for i > 0, [1, 2, 3, 4] * g_i^2 (x) * [1, 2, 3, 4]^T = 0 ok &= ddy[1] == 0.; ok &= ddy[2] == 0.; ok &= ddy[3] == 0.; //---------------------------------------------------------------------- // Test second order reverse mode CppAD::vector w(m), dw(2 * X.size() ); for(i = 0; i < m; i++) w[i] = 0.; w[0] = 1.; dw = G.Reverse(2, w); // g_0'(x) = [ x2, x3, x0, x1 ] ok &= dw[0*2 + 0] == x[2]; ok &= dw[1*2 + 0] == x[3]; ok &= dw[2*2 + 0] == x[0]; ok &= dw[3*2 + 0] == x[1]; // g_0'(x) * [1, 2, 3, 4] = 1 * x2 + 2 * x3 + 3 * x0 + 4 * x1 // g_0^2 (x) * [1, 2, 3, 4] = [3, 4, 1, 2] ok &= dw[0*2 + 1] == 3.; ok &= dw[1*2 + 1] == 4.; ok &= dw[2*2 + 1] == 1.; ok &= dw[3*2 + 1] == 2.; //---------------------------------------------------------------------- // Test forward and reverse Jacobian sparsity pattern /* [ x0 , x1 ] * [ x2 , 7 ] = [ x0*x2 + x1*x3 , x0*7 + x1*8 ] [ 5 , 6 ] [ x3 , 8 ] [ 5*x2 + 6*x3 , 5*7 + 6*8 ] so the sparsity pattern should be s[0] = {0, 1, 2, 3} s[1] = {0, 1} s[2] = {2, 3} s[3] = {} */ CppAD::vector< std::set > r( X.size() ), s(m); for(j = 0; j < X.size() ; j++) { assert( r[j].empty() ); r[j].insert(j); } s = G.ForSparseJac( X.size() , r); for(j = 0; j < X.size() ; j++) { // s[0] = {0, 1, 2, 3} ok &= s[0].find(j) != s[0].end(); // s[1] = {0, 1} if( j == 0 || j == 1 ) ok &= s[1].find(j) != s[1].end(); else ok &= s[1].find(j) == s[1].end(); // s[2] = {2, 3} if( j == 2 || j == 3 ) ok &= s[2].find(j) != s[2].end(); else ok &= s[2].find(j) == s[2].end(); } // s[3] == {} ok &= s[3].empty(); //---------------------------------------------------------------------- // Test reverse Jacobian sparsity pattern /* [ x0 , x1 ] * [ x2 , 7 ] = [ x0*x2 + x1*x3 , x0*7 + x1*8 ] [ 5 , 6 ] [ x3 , 8 ] [ 5*x2 + 6*x3 , 5*7 + 6*8 ] so the sparsity pattern should be r[0] = {0, 1, 2, 3} r[1] = {0, 1} r[2] = {2, 3} r[3] = {} */ for(i = 0; i < m; i++) { s[i].clear(); s[i].insert(i); } r = G.RevSparseJac(m, s); for(j = 0; j < X.size() ; j++) { // r[0] = {0, 1, 2, 3} ok &= r[0].find(j) != r[0].end(); // r[1] = {0, 1} if( j == 0 || j == 1 ) ok &= r[1].find(j) != r[1].end(); else ok &= r[1].find(j) == r[1].end(); // r[2] = {2, 3} if( j == 2 || j == 3 ) ok &= r[2].find(j) != r[2].end(); else ok &= r[2].find(j) == r[2].end(); } // r[3] == {} ok &= r[3].empty(); //---------------------------------------------------------------------- /* Test reverse Hessian sparsity pattern g_0^2 (x) = [ 0, 0, 1, 0 ] and for i > 0, g_i^2 = 0 [ 0, 0, 0, 1 ] [ 1, 0, 0, 0 ] [ 0, 1, 0, 0 ] so for the sparsity pattern for the first component of g is h[0] = {2} h[1] = {3} h[2] = {0} h[3] = {1} */ CppAD::vector< std::set > h( X.size() ), t(1); t[0].clear(); t[0].insert(0); h = G.RevSparseHes(X.size() , t); size_t check[] = {2, 3, 0, 1}; for(j = 0; j < X.size() ; j++) { // h[j] = { check[j] } for(i = 0; i < n; i++) { if( i == check[j] ) ok &= h[j].find(i) != h[j].end(); else ok &= h[j].find(i) == h[j].end(); } } t[0].clear(); for( j = 1; j < X.size(); j++) t[0].insert(j); h = G.RevSparseHes(X.size() , t); for(j = 0; j < X.size() ; j++) { // h[j] = { } for(i = 0; i < X.size(); i++) ok &= h[j].find(i) == h[j].end(); } // -------------------------------------------------------------------- // Free temporary work space. (If there are future calls to // old_mat_mul they would create new temporary work space.) CppAD::user_atomic::clear(); info_.clear(); return ok; } // END C++ ================================================ FILE: test_more/deprecated/old_mat_mul.hpp ================================================ # ifndef CPPAD_TEST_MORE_DEPRECATED_OLD_MAT_MUL_HPP # define CPPAD_TEST_MORE_DEPRECATED_OLD_MAT_MUL_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin old_mat_mul.hpp@@ $spell old_mat_mul.hpp cppad CppAD namespace struct nr nc bool vx const im mj ij px py std tx ty resize nz var jac Jacobian hes vy $$ $section Define Matrix Multiply as a User Atomic Operation$$ $head Syntax$$ This file is located in the $code example$$ directory. It can be copied to the current working directory and included with the syntax $codei% # include "old_mat_mul.hpp" %$$ $head Example$$ The file old_mat_mul.cpp contains an example use of $code old_mat_mul.hpp$$. $head Begin Source$$ $srccode%cpp% */ # include // Include CppAD definitions namespace { // Begin empty namespace using CppAD::vector; // Let vector denote CppAD::vector /* %$$ $head Extra Call Information$$ $srccode%cpp% */ // Information we will attach to each mat_mul call struct call_info { size_t nr_result; size_t n_middle; size_t nc_result; vector vx; }; vector info_; // vector of call information // number of orders for this operation (k + 1) size_t n_order_ = 0; // number of rows in the result matrix size_t nr_result_ = 0; // number of columns in left matrix and number of rows in right matrix size_t n_middle_ = 0; // number of columns in the result matrix size_t nc_result_ = 0; // which components of x are variables vector* vx_ = nullptr; // get the information corresponding to this call void get_info(size_t id, size_t k, size_t n, size_t m) { n_order_ = k + 1; nr_result_ = info_[id].nr_result; n_middle_ = info_[id].n_middle; nc_result_ = info_[id].nc_result; vx_ = &(info_[id].vx); assert(n == nr_result_ * n_middle_ + n_middle_ * nc_result_); assert(m == nr_result_ * nc_result_); } /* %$$ $head Matrix Indexing$$ $srccode%cpp% */ // Convert left matrix index pair and order to a single argument index size_t left(size_t i, size_t j, size_t ell) { assert( i < nr_result_ ); assert( j < n_middle_ ); return (i * n_middle_ + j) * n_order_ + ell; } // Convert right matrix index pair and order to a single argument index size_t right(size_t i, size_t j, size_t ell) { assert( i < n_middle_ ); assert( j < nc_result_ ); size_t offset = nr_result_ * n_middle_; return (offset + i * nc_result_ + j) * n_order_ + ell; } // Convert result matrix index pair and order to a single result index size_t result(size_t i, size_t j, size_t ell) { assert( i < nr_result_ ); assert( j < nc_result_ ); return (i * nc_result_ + j) * n_order_ + ell; } /* %$$ $head One Matrix Multiply$$ Forward mode matrix multiply left times right and sum into result: $srccode%cpp% */ void multiply_and_sum( size_t order_left , size_t order_right, const vector& tx , vector& ty ) { size_t i, j; size_t order_result = order_left + order_right; for(i = 0; i < nr_result_; i++) { for(j = 0; j < nc_result_; j++) { double sum = 0.; size_t middle, im_left, mj_right, ij_result; for(middle = 0; middle < n_middle_; middle++) { im_left = left(i, middle, order_left); mj_right = right(middle, j, order_right); sum += tx[im_left] * tx[mj_right]; } ij_result = result(i, j, order_result); ty[ ij_result ] += sum; } } return; } /* %$$ $head Reverse Partials One Order$$ Compute reverse mode partials for one order and sum into px: $srccode%cpp% */ void reverse_multiply( size_t order_left , size_t order_right, const vector& tx , const vector& ty , vector& px , const vector& py ) { size_t i, j; size_t order_result = order_left + order_right; for(i = 0; i < nr_result_; i++) { for(j = 0; j < nc_result_; j++) { size_t middle, im_left, mj_right, ij_result; for(middle = 0; middle < n_middle_; middle++) { ij_result = result(i, j, order_result); im_left = left(i, middle, order_left); mj_right = right(middle, j, order_right); // sum += tx[im_left] * tx[mj_right]; px[im_left] += tx[mj_right] * py[ij_result]; px[mj_right] += tx[im_left] * py[ij_result]; } } } return; } /* %$$ $head Set Union$$ $srccode%cpp% */ using CppAD::set_union; /* %$$ $head CppAD User Atomic Callback Functions$$ $srccode%cpp% */ // ---------------------------------------------------------------------- // forward mode routine called by CppAD bool mat_mul_forward( size_t id , size_t k , size_t n , size_t m , const vector& vx , vector& vy , const vector& tx , vector& ty ) { size_t i, j, ell; get_info(id, k, n, m); // check if this is during the call to mat_mul(id, ax, ay) if( vx.size() > 0 ) { assert( k == 0 && vx.size() > 0 ); // store the vx information in info_ assert( vx_->size() == 0 ); info_[id].vx.resize(n); for(j = 0; j < n; j++) info_[id].vx[j] = vx[j]; assert( vx_->size() == n ); // now compute vy for(i = 0; i < nr_result_; i++) { for(j = 0; j < nc_result_; j++) { // compute vy[ result(i, j, 0) ] bool var = false; bool nz_left, nz_right; size_t middle, im_left, mj_right, ij_result; for(middle = 0; middle < n_middle_; middle++) { im_left = left(i, middle, k); mj_right = right(middle, j, k); nz_left = vx[im_left] || (tx[im_left] != 0.); nz_right = vx[mj_right] || (tx[mj_right]!= 0.); // if not multiplying by the constant zero if( nz_left & nz_right ) var |= (vx[im_left] || vx[mj_right]); } ij_result = result(i, j, k); vy[ij_result] = var; } } } // initialize result as zero for(i = 0; i < nr_result_; i++) { for(j = 0; j < nc_result_; j++) ty[ result(i, j, k) ] = 0.; } // sum the product of proper orders for(ell = 0; ell <=k; ell++) multiply_and_sum(ell, k-ell, tx, ty); // All orders are implemented and there are no possible error // conditions, so always return true. return true; } // ---------------------------------------------------------------------- // reverse mode routine called by CppAD bool mat_mul_reverse( size_t id , size_t k , size_t n , size_t m , const vector& tx , const vector& ty , vector& px , const vector& py ) { get_info(id, k, n, m); size_t ell = n * n_order_; while(ell--) px[ell] = 0.; size_t order = n_order_; while(order--) { // reverse sum the products for specified order for(ell = 0; ell <=order; ell++) reverse_multiply(ell, order-ell, tx, ty, px, py); } // All orders are implemented and there are no possible error // conditions, so always return true. return true; } // ---------------------------------------------------------------------- // forward Jacobian sparsity routine called by CppAD bool mat_mul_for_jac_sparse( size_t id , size_t n , size_t m , size_t p , const vector< std::set >& r , vector< std::set >& s ) { size_t i, j, k, im_left, middle, mj_right, ij_result; k = 0; get_info(id, k, n, m); for(i = 0; i < nr_result_; i++) { for(j = 0; j < nc_result_; j++) { ij_result = result(i, j, k); s[ij_result].clear(); for(middle = 0; middle < n_middle_; middle++) { im_left = left(i, middle, k); mj_right = right(middle, j, k); // s[ij_result] = union( s[ij_result], r[im_left] ) s[ij_result] = set_union(s[ij_result], r[im_left]); // s[ij_result] = union( s[ij_result], r[mj_right] ) s[ij_result] = set_union(s[ij_result], r[mj_right]); } } } return true; } // ---------------------------------------------------------------------- // reverse Jacobian sparsity routine called by CppAD bool mat_mul_rev_jac_sparse( size_t id , size_t n , size_t m , size_t p , vector< std::set >& r , const vector< std::set >& s ) { size_t i, j, k, im_left, middle, mj_right, ij_result; k = 0; get_info(id, k, n, m); for(j = 0; j < n; j++) r[j].clear(); for(i = 0; i < nr_result_; i++) { for(j = 0; j < nc_result_; j++) { ij_result = result(i, j, k); for(middle = 0; middle < n_middle_; middle++) { im_left = left(i, middle, k); mj_right = right(middle, j, k); // r[im_left] = union( r[im_left], s[ij_result] ) r[im_left] = set_union(r[im_left], s[ij_result]); // r[mj_right] = union( r[mj_right], s[ij_result] ) r[mj_right] = set_union(r[mj_right], s[ij_result]); } } } return true; } // ---------------------------------------------------------------------- // reverse Hessian sparsity routine called by CppAD bool mat_mul_rev_hes_sparse( size_t id , size_t n , size_t m , size_t p , const vector< std::set >& r , const vector& s , vector& t , const vector< std::set >& u , vector< std::set >& v ) { size_t i, j, k, im_left, middle, mj_right, ij_result; k = 0; get_info(id, k, n, m); for(j = 0; j < n; j++) { t[j] = false; v[j].clear(); } assert( vx_->size() == n ); for(i = 0; i < nr_result_; i++) { for(j = 0; j < nc_result_; j++) { ij_result = result(i, j, k); for(middle = 0; middle < n_middle_; middle++) { im_left = left(i, middle, k); mj_right = right(middle, j, k); // back propagate Jacobian sparsity t[im_left] = (t[im_left] || s[ij_result]); t[mj_right] = (t[mj_right] || s[ij_result]); // Visual Studio C++ 2008 warns unsafe mix of int and // bool if we use the following code directly above: // t[im_left] |= s[ij_result]; // t[mj_right] |= s[ij_result]; // back propagate Hessian sparsity // v[im_left] = union( v[im_left], u[ij_result] ) // v[mj_right] = union( v[mj_right], u[ij_result] ) v[im_left] = set_union(v[im_left], u[ij_result] ); v[mj_right] = set_union(v[mj_right], u[ij_result] ); // Check for case where the (i,j) result element // is in reverse Jacobian and both left and right // operands in multiplication are variables if(s[ij_result] && (*vx_)[im_left] && (*vx_)[mj_right]) { // v[im_left] = union( v[im_left], r[mj_right] ) v[im_left] = set_union(v[im_left], r[mj_right] ); // v[mj_right] = union( v[mj_right], r[im_left] ) v[mj_right] = set_union(v[mj_right], r[im_left] ); } } } } return true; } /* %$$ $head Declare mat_mul Function$$ Declare the $code AD$$ routine $codei%mat_mul(%id%, %ax%, %ay%)%$$ and end empty namespace (we could use any $cref/simple vector template class/SimpleVector/$$ instead of $code CppAD::vector$$): $srccode%cpp% */ CPPAD_USER_ATOMIC( mat_mul , CppAD::vector , double , mat_mul_forward , mat_mul_reverse , mat_mul_for_jac_sparse , mat_mul_rev_jac_sparse , mat_mul_rev_hes_sparse ) } // End empty namespace /* %$$ $end */ # endif ================================================ FILE: test_more/deprecated/old_reciprocal.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin old_reciprocal.cpp@@ $section Old Atomic Operation Reciprocal: Example and Test$$ $head Deprecated 2013-05-27$$ This example has been deprecated; see $cref atomic_two_reciprocal.cpp$$ instead. $head Theory$$ The example below defines the atomic function $latex f : \B{R}^n \rightarrow \B{R}^m$$ where $latex n = 1$$, $latex m = 1$$, and $latex f(x) = 1 / x$$. $srcthisfile%0%// BEGIN C++%// END C++%1%$$ $end */ // BEGIN C++ # include namespace { // Begin empty namespace using CppAD::vector; // ---------------------------------------------------------------------- // a utility to compute the union of two sets. using CppAD::set_union; // ---------------------------------------------------------------------- // forward mode routine called by CppAD bool reciprocal_forward( size_t id , size_t k , size_t n , size_t m , const vector& vx , vector& vy , const vector& tx , vector& ty ) { assert( id == 0 ); assert( n == 1 ); assert( m == 1 ); assert( k == 0 || vx.size() == 0 ); bool ok = false; double f, fp, fpp; // Must always define the case k = 0. // Do not need case k if not using f.Forward(q, xp) for q >= k. switch(k) { case 0: // this case must be implemented if( vx.size() > 0 ) vy[0] = vx[0]; // y^0 = f( x^0 ) = 1 / x^0 ty[0] = 1. / tx[0]; ok = true; break; case 1: // needed if first order forward mode is used assert( vx.size() == 0 ); // y^1 = f'( x^0 ) x^1 f = ty[0]; fp = - f / tx[0]; ty[1] = fp * tx[1]; ok = true; break; case 2: // needed if second order forward mode is used assert( vx.size() == 0 ); // Y''(t) = X'(t)^\R{T} f''[X(t)] X'(t) + f'[X(t)] X''(t) // 2 y^2 = x^1 * f''( x^0 ) x^1 + 2 f'( x^0 ) x^2 f = ty[0]; fp = - f / tx[0]; fpp = - 2.0 * fp / tx[0]; ty[2] = tx[1] * fpp * tx[1] / 2.0 + fp * tx[2]; ok = true; break; } return ok; } // ---------------------------------------------------------------------- // reverse mode routine called by CppAD bool reciprocal_reverse( size_t id , size_t k , size_t n , size_t m , const vector& tx , const vector& ty , vector& px , const vector& py ) { // Do not need case k if not using f.Reverse(k+1, w). assert( id == 0 ); assert( n == 1 ); assert( m == 1 ); bool ok = false; double f, fp, fpp, fppp; switch(k) { case 0: // needed if first order reverse mode is used // reverse: F^0 ( tx ) = y^0 = f( x^0 ) f = ty[0]; fp = - f / tx[0]; px[0] = py[0] * fp;; ok = true; break; case 1: // needed if second order reverse mode is used // reverse: F^1 ( tx ) = y^1 = f'( x^0 ) x^1 f = ty[0]; fp = - f / tx[0]; fpp = - 2.0 * fp / tx[0]; px[1] = py[1] * fp; px[0] = py[1] * fpp * tx[1]; // reverse: F^0 ( tx ) = y^0 = f( x^0 ); px[0] += py[0] * fp; ok = true; break; case 2: // needed if third order reverse mode is used // reverse: F^2 ( tx ) = y^2 = // = x^1 * f''( x^0 ) x^1 / 2 + f'( x^0 ) x^2 f = ty[0]; fp = - f / tx[0]; fpp = - 2.0 * fp / tx[0]; fppp = - 3.0 * fpp / tx[0]; px[2] = py[2] * fp; px[1] = py[2] * fpp * tx[1]; px[0] = py[2] * tx[1] * fppp * tx[1] / 2.0 + fpp * tx[2]; // reverse: F^1 ( tx ) = y^1 = f'( x^0 ) x^1 px[1] += py[1] * fp; px[0] += py[1] * fpp * tx[1]; // reverse: F^0 ( tx ) = y^0 = f( x^0 ); px[0] += py[0] * fp; ok = true; break; } return ok; } // ---------------------------------------------------------------------- // forward Jacobian sparsity routine called by CppAD bool reciprocal_for_jac_sparse( size_t id , size_t n , size_t m , size_t p , const vector< std::set >& r , vector< std::set >& s ) { // Can just return false if not using f.ForSparseJac assert( id == 0 ); assert( n == 1 ); assert( m == 1 ); // sparsity for S(x) = f'(x) * R is same as sparsity for R s[0] = r[0]; return true; } // ---------------------------------------------------------------------- // reverse Jacobian sparsity routine called by CppAD bool reciprocal_rev_jac_sparse( size_t id , size_t n , size_t m , size_t p , vector< std::set >& r , const vector< std::set >& s ) { // Can just return false if not using RevSparseJac. assert( id == 0 ); assert( n == 1 ); assert( m == 1 ); // sparsity for R(x) = S * f'(x) is same as sparsity for S for(size_t q = 0; q < p; q++) r[q] = s[q]; return true; } // ---------------------------------------------------------------------- // reverse Hessian sparsity routine called by CppAD bool reciprocal_rev_hes_sparse( size_t id , size_t n , size_t m , size_t p , const vector< std::set >& r , const vector& s , vector& t , const vector< std::set >& u , vector< std::set >& v ) { // Can just return false if not use RevSparseHes. assert( id == 0 ); assert( n == 1 ); assert( m == 1 ); // sparsity for T(x) = S(x) * f'(x) is same as sparsity for S t[0] = s[0]; // V(x) = [ f'(x)^T * g''(y) * f'(x) + g'(y) * f''(x) ] * R // U(x) = g''(y) * f'(x) * R // S(x) = g'(y) // back propagate the sparsity for U because derivative of // reciprocal may be non-zero v[0] = u[0]; // convert forward Jacobian sparsity to Hessian sparsity // because second derivative of reciprocal may be non-zero if( s[0] ) v[0] = set_union(v[0], r[0] ); return true; } // --------------------------------------------------------------------- // Declare the AD routine reciprocal(id, ax, ay) CPPAD_USER_ATOMIC( reciprocal , CppAD::vector , double , reciprocal_forward , reciprocal_reverse , reciprocal_for_jac_sparse , reciprocal_rev_jac_sparse , reciprocal_rev_hes_sparse ) } // End empty namespace bool old_reciprocal(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); // -------------------------------------------------------------------- // Create the function f(x) // // domain space vector size_t n = 1; double x0 = 0.5; vector< AD > ax(n); ax[0] = x0; // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 1; vector< AD > ay(m); // call atomic function and store reciprocal(x) in au[0] vector< AD > au(m); size_t id = 0; // not used reciprocal(id, ax, au); // u = 1 / x // call atomic function and store reciprocal(u) in ay[0] reciprocal(id, au, ay); // y = 1 / u = x // create f: x -> y and stop tape recording CppAD::ADFun f; f.Dependent (ax, ay); // f(x) = x // -------------------------------------------------------------------- // Check forward mode results // // check function value double check = x0; ok &= NearEqual( Value(ay[0]) , check, eps, eps); // check zero order forward mode size_t q; vector x_q(n), y_q(m); q = 0; x_q[0] = x0; y_q = f.Forward(q, x_q); ok &= NearEqual(y_q[0] , check, eps, eps); // check first order forward mode q = 1; x_q[0] = 1; y_q = f.Forward(q, x_q); check = 1.; ok &= NearEqual(y_q[0] , check, eps, eps); // check second order forward mode q = 2; x_q[0] = 0; y_q = f.Forward(q, x_q); check = 0.; ok &= NearEqual(y_q[0] , check, eps, eps); // -------------------------------------------------------------------- // Check reverse mode results // // third order reverse mode q = 3; vector w(m), dw(n * q); w[0] = 1.; dw = f.Reverse(q, w); check = 1.; ok &= NearEqual(dw[0] , check, eps, eps); check = 0.; ok &= NearEqual(dw[1] , check, eps, eps); ok &= NearEqual(dw[2] , check, eps, eps); // -------------------------------------------------------------------- // forward mode sparstiy pattern size_t p = n; CppAD::vectorBool r1(n * p), s1(m * p); r1[0] = true; // compute sparsity pattern for x[0] s1 = f.ForSparseJac(p, r1); ok &= s1[0] == true; // f[0] depends on x[0] // -------------------------------------------------------------------- // reverse mode sparstiy pattern q = m; CppAD::vectorBool s2(q * m), r2(q * n); s2[0] = true; // compute sparsity pattern for f[0] r2 = f.RevSparseJac(q, s2); ok &= r2[0] == true; // f[0] depends on x[0] // -------------------------------------------------------------------- // Hessian sparsity (using previous ForSparseJac call) CppAD::vectorBool s3(m), h(p * n); s3[0] = true; // compute sparsity pattern for f[0] h = f.RevSparseHes(p, s3); ok &= h[0] == true; // second partial of f[0] w.r.t. x[0] may be non-zero // ----------------------------------------------------------------- // Free all temporary work space associated with atomic_one objects. // (If there are future calls to atomic functions, they will // create new temporary work space.) CppAD::user_atomic::clear(); return ok; } // END C++ ================================================ FILE: test_more/deprecated/old_tan.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin old_tan.cpp@@ $spell Tanh $$ $section Old Tan and Tanh as User Atomic Operations: Example and Test$$ $head Deprecated 2013-05-27$$ This example has not deprecated; see $cref atomic_two_tangent.cpp$$ instead. $head Theory$$ The code below uses the $cref tan_forward$$ and $cref tan_reverse$$ to implement the tangent ($icode%id% == 0%$$) and hyperbolic tangent ($icode%id% == 1%$$) functions as atomic function operations. $srcthisfile%0%// BEGIN C++%// END C++%1%$$ $end */ // BEGIN C++ # include namespace { // Begin empty namespace using CppAD::vector; // a utility to compute the union of two sets. using CppAD::set_union; // ---------------------------------------------------------------------- // forward mode routine called by CppAD bool old_tan_forward( size_t id , size_t order , size_t n , size_t m , const vector& vx , vector& vzy , const vector& tx , vector& tzy ) { assert( id == 0 || id == 1 ); assert( n == 1 ); assert( m == 2 ); assert( tx.size() >= (order+1) * n ); assert( tzy.size() >= (order+1) * m ); size_t n_order = order + 1; size_t j = order; size_t k; // check if this is during the call to old_tan(id, ax, ay) if( vx.size() > 0 ) { assert( vx.size() >= n ); assert( vzy.size() >= m ); // now setvzy vzy[0] = vx[0]; vzy[1] = vx[0]; } if( j == 0 ) { // z^{(0)} = tan( x^{(0)} ) or tanh( x^{(0)} ) if( id == 0 ) tzy[0] = float( tan( tx[0] ) ); else tzy[0] = float( tanh( tx[0] ) ); // y^{(0)} = z^{(0)} * z^{(0)} tzy[n_order + 0] = tzy[0] * tzy[0]; } else { float j_inv = 1.f / float(j); if( id == 1 ) j_inv = - j_inv; // z^{(j)} = x^{(j)} +- sum_{k=1}^j k x^{(k)} y^{(j-k)} / j tzy[j] = tx[j]; for(k = 1; k <= j; k++) tzy[j] += tx[k] * tzy[n_order + j-k] * float(k) * j_inv; // y^{(j)} = sum_{k=0}^j z^{(k)} z^{(j-k)} tzy[n_order + j] = 0.; for(k = 0; k <= j; k++) tzy[n_order + j] += tzy[k] * tzy[j-k]; } // All orders are implemented and there are no possible errors return true; } // ---------------------------------------------------------------------- // reverse mode routine called by CppAD bool old_tan_reverse( size_t id , size_t order , size_t n , size_t m , const vector& tx , const vector& tzy , vector& px , const vector& pzy ) { assert( id == 0 || id == 1 ); assert( n == 1 ); assert( m == 2 ); assert( tx.size() >= (order+1) * n ); assert( tzy.size() >= (order+1) * m ); assert( px.size() >= (order+1) * n ); assert( pzy.size() >= (order+1) * m ); size_t n_order = order + 1; size_t j, k; // copy because partials w.r.t. y and z need to change vector qzy = pzy; // initialize accumultion of reverse mode partials for(k = 0; k < n_order; k++) px[k] = 0.; // eliminate positive orders for(j = order; j > 0; j--) { float j_inv = 1.f / float(j); if( id == 1 ) j_inv = - j_inv; // H_{x^{(k)}} += delta(j-k) +- H_{z^{(j)} y^{(j-k)} * k / j px[j] += qzy[j]; for(k = 1; k <= j; k++) px[k] += qzy[j] * tzy[n_order + j-k] * float(k) * j_inv; // H_{y^{j-k)} += +- H_{z^{(j)} x^{(k)} * k / j for(k = 1; k <= j; k++) qzy[n_order + j-k] += qzy[j] * tx[k] * float(k) * j_inv; // H_{z^{(k)}} += H_{y^{(j-1)}} * z^{(j-k-1)} * 2. for(k = 0; k < j; k++) qzy[k] += qzy[n_order + j-1] * tzy[j-k-1] * 2.f; } // eliminate order zero if( id == 0 ) px[0] += qzy[0] * (1.f + tzy[n_order + 0]); else px[0] += qzy[0] * (1.f - tzy[n_order + 0]); return true; } // ---------------------------------------------------------------------- // forward Jacobian sparsity routine called by CppAD bool old_tan_for_jac_sparse( size_t id , size_t n , size_t m , size_t p , const vector< std::set >& r , vector< std::set >& s ) { assert( n == 1 ); assert( m == 2 ); assert( id == 0 || id == 1 ); assert( r.size() >= n ); assert( s.size() >= m ); // sparsity for z and y are the same as for x s[0] = r[0]; s[1] = r[0]; return true; } // ---------------------------------------------------------------------- // reverse Jacobian sparsity routine called by CppAD bool old_tan_rev_jac_sparse( size_t id , size_t n , size_t m , size_t p , vector< std::set >& r , const vector< std::set >& s ) { assert( n == 1 ); assert( m == 2 ); assert( id == 0 || id == 1 ); assert( r.size() >= n ); assert( s.size() >= m ); // note that, if the users code only uses z, and not y, // we could just set r[0] = s[0] r[0] = set_union(s[0], s[1]); return true; } // ---------------------------------------------------------------------- // reverse Hessian sparsity routine called by CppAD bool old_tan_rev_hes_sparse( size_t id , size_t n , size_t m , size_t p , const vector< std::set >& r , const vector& s , vector& t , const vector< std::set >& u , vector< std::set >& v ) { assert( n == 1 ); assert( m == 2 ); assert( id == 0 || id == 1 ); assert( r.size() >= n ); assert( s.size() >= m ); assert( t.size() >= n ); assert( u.size() >= m ); assert( v.size() >= n ); // back propagate Jacobian sparsity. If users code only uses z, // we could just set t[0] = s[0]; t[0] = s[0] || s[1]; // back propagate Hessian sparsity, ... v[0] = set_union(u[0], u[1]); // convert forward Jacobian sparsity to Hessian sparsity // because tan and tanh are nonlinear if( t[0] ) v[0] = set_union(v[0], r[0]); return true; } // --------------------------------------------------------------------- // Declare the AD routine old_tan(id, ax, ay) CPPAD_USER_ATOMIC( old_tan , CppAD::vector , float , old_tan_forward , old_tan_reverse , old_tan_for_jac_sparse , old_tan_rev_jac_sparse , old_tan_rev_hes_sparse ) } // End empty namespace bool old_tan(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; float eps = 10.f * CppAD::numeric_limits::epsilon(); // domain space vector size_t n = 1; float x0 = 0.5; CppAD::vector< AD > ax(n); ax[0] = x0; // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 3; CppAD::vector< AD > af(m); // temporary vector for old_tan computations // (old_tan computes tan or tanh and its square) CppAD::vector< AD > az(2); // call user tan function and store tan(x) in f[0] (ignore tan(x)^2) size_t id = 0; old_tan(id, ax, az); af[0] = az[0]; // call user tanh function and store tanh(x) in f[1] (ignore tanh(x)^2) id = 1; old_tan(id, ax, az); af[1] = az[0]; // put a constant in f[2] = tanh(1.) (for sparsity pattern testing) CppAD::vector< AD > one(1); one[0] = 1.; old_tan(id, one, az); af[2] = az[0]; // create f: x -> f and stop tape recording CppAD::ADFun F; F.Dependent(ax, af); // check function value float tan = std::tan(x0); ok &= NearEqual(af[0] , tan, eps, eps); float tanh = std::tanh(x0); ok &= NearEqual(af[1] , tanh, eps, eps); // check zero order forward CppAD::vector x(n), f(m); x[0] = x0; f = F.Forward(0, x); ok &= NearEqual(f[0] , tan, eps, eps); ok &= NearEqual(f[1] , tanh, eps, eps); // compute first partial of f w.r.t. x[0] using forward mode CppAD::vector dx(n), df(m); dx[0] = 1.; df = F.Forward(1, dx); // compute derivative of tan - tanh using reverse mode CppAD::vector w(m), dw(n); w[0] = 1.; w[1] = 1.; w[2] = 0.; dw = F.Reverse(1, w); // tan'(x) = 1 + tan(x) * tan(x) // tanh'(x) = 1 - tanh(x) * tanh(x) float tanp = 1.f + tan * tan; float tanhp = 1.f - tanh * tanh; ok &= NearEqual(df[0], tanp, eps, eps); ok &= NearEqual(df[1], tanhp, eps, eps); ok &= NearEqual(dw[0], w[0]*tanp + w[1]*tanhp, eps, eps); // compute second partial of f w.r.t. x[0] using forward mode CppAD::vector ddx(n), ddf(m); ddx[0] = 0.; ddf = F.Forward(2, ddx); // compute second derivative of tan - tanh using reverse mode CppAD::vector ddw(2); ddw = F.Reverse(2, w); // tan''(x) = 2 * tan(x) * tan'(x) // tanh''(x) = - 2 * tanh(x) * tanh'(x) // Note that second order Taylor coefficient for u half the // corresponding second derivative. float two = 2; float tanpp = two * tan * tanp; float tanhpp = - two * tanh * tanhp; ok &= NearEqual(two * ddf[0], tanpp, eps, eps); ok &= NearEqual(two * ddf[1], tanhpp, eps, eps); ok &= NearEqual(ddw[0], w[0]*tanp + w[1]*tanhp , eps, eps); ok &= NearEqual(ddw[1], w[0]*tanpp + w[1]*tanhpp, eps, eps); // Forward mode computation of sparsity pattern for F. size_t p = n; // user vectorBool because m and n are small CppAD::vectorBool r1(p), s1(m * p); r1[0] = true; // propagate sparsity for x[0] s1 = F.ForSparseJac(p, r1); ok &= (s1[0] == true); // f[0] depends on x[0] ok &= (s1[1] == true); // f[1] depends on x[0] ok &= (s1[2] == false); // f[2] does not depend on x[0] // Reverse mode computation of sparsity pattern for F. size_t q = m; CppAD::vectorBool s2(q * m), r2(q * n); // Sparsity pattern for identity matrix size_t i, j; for(i = 0; i < q; i++) { for(j = 0; j < m; j++) s2[i * q + j] = (i == j); } r2 = F.RevSparseJac(q, s2); ok &= (r2[0] == true); // f[0] depends on x[0] ok &= (r2[1] == true); // f[1] depends on x[0] ok &= (r2[2] == false); // f[2] does not depend on x[0] // Hessian sparsity for f[0] CppAD::vectorBool s3(m), h(p * n); s3[0] = true; s3[1] = false; s3[2] = false; h = F.RevSparseHes(p, s3); ok &= (h[0] == true); // Hessian is non-zero // Hessian sparsity for f[2] s3[0] = false; s3[2] = true; h = F.RevSparseHes(p, s3); ok &= (h[0] == false); // Hessian is zero // check tanh results for a large value of x x[0] = std::numeric_limits::max() / two; f = F.Forward(0, x); tanh = 1.; ok &= NearEqual(f[1], tanh, eps, eps); df = F.Forward(1, dx); tanhp = 0.; ok &= NearEqual(df[1], tanhp, eps, eps); // -------------------------------------------------------------------- // Free all temporary work space associated with atomic_one objects. // (If there are future calls to atomic functions, they will // create new temporary work space.) CppAD::user_atomic::clear(); return ok; } // END C++ ================================================ FILE: test_more/deprecated/old_usead_1.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin old_usead_1.cpp@@ $spell checkpoint var $$ $section Using AD to Compute Atomic Function Derivatives$$ $head Deprecated 2013-05-27$$ This example has been deprecated because it is easier to use the $cref/checkpoint/chkpoint_one/$$ class instead. $head Purpose$$ Consider the case where an inner function is used repeatedly in the definition of an outer function. In this case, it may reduce the number of variables $cref/size_var/fun_property/size_var/$$, and hence the required memory. $head Simple Case$$ This example is the same as old_reciprocal.cpp, except that it uses AD to compute the derivatives needed by an atomic function. This is a simple example of an inner function, and hence not really useful for the purpose above; see old_usead_2.cpp for a more complete example. $srcthisfile%0%// BEGIN C++%// END C++%1%$$ $end */ // BEGIN C++ # include namespace { // Begin empty namespace using CppAD::AD; using CppAD::ADFun; using CppAD::vector; // ---------------------------------------------------------------------- // function that computes reciprocal ADFun* r_ptr_; void create_r(void) { vector< AD > ax(1), ay(1); ax[0] = 1; CppAD::Independent(ax); ay[0] = 1.0 / ax[0]; r_ptr_ = new ADFun(ax, ay); } void destroy_r(void) { delete r_ptr_; r_ptr_ = nullptr; } // ---------------------------------------------------------------------- // forward mode routine called by CppAD bool reciprocal_forward( size_t id , size_t k , size_t n , size_t m , const vector& vx , vector& vy , const vector& tx , vector& ty ) { assert( id == 0 ); assert( n == 1 ); assert( m == 1 ); assert( k == 0 || vx.size() == 0 ); bool ok = true; vector x_q(1), y_q(1); // check for special case if( vx.size() > 0 ) vy[0] = vx[0]; // make sure r_ has proper lower order Taylor coefficients stored // then compute ty[k] for(size_t q = 0; q <= k; q++) { x_q[0] = tx[q]; y_q = r_ptr_->Forward(q, x_q); if( q == k ) ty[k] = y_q[0]; assert( q == k || ty[q] == y_q[0] ); } return ok; } // ---------------------------------------------------------------------- // reverse mode routine called by CppAD bool reciprocal_reverse( size_t id , size_t k , size_t n , size_t m , const vector& tx , const vector& ty , vector& px , const vector& py ) { assert( id == 0 ); assert( n == 1 ); assert( m == 1 ); bool ok = true; vector x_q(1), w(k+1), dw(k+1); // make sure r_ has proper forward mode coefficients size_t q; for(q = 0; q <= k; q++) { x_q[0] = tx[q]; # ifdef NDEBUG r_ptr_->Forward(q, x_q); # else vector y_q(1); y_q = r_ptr_->Forward(q, x_q); assert( ty[q] == y_q[0] ); # endif } for(q = 0; q <=k; q++) w[q] = py[q]; dw = r_ptr_->Reverse(k+1, w); for(q = 0; q <=k; q++) px[q] = dw[q]; return ok; } // ---------------------------------------------------------------------- // forward Jacobian sparsity routine called by CppAD bool reciprocal_for_jac_sparse( size_t id , size_t n , size_t m , size_t p , const vector< std::set >& r , vector< std::set >& s ) { assert( id == 0 ); assert( n == 1 ); assert( m == 1 ); bool ok = true; vector< std::set > R(1), S(1); R[0] = r[0]; S = r_ptr_->ForSparseJac(p, R); s[0] = S[0]; return ok; } // ---------------------------------------------------------------------- // reverse Jacobian sparsity routine called by CppAD bool reciprocal_rev_jac_sparse( size_t id , size_t n , size_t m , size_t p , vector< std::set >& r , const vector< std::set >& s ) { assert( id == 0 ); assert( n == 1 ); assert( m == 1 ); bool ok = true; vector< std::set > R(p), S(p); size_t q; for(q = 0; q < p; q++) S[q] = s[q]; R = r_ptr_->RevSparseJac(p, S); for(q = 0; q < p; q++) r[q] = R[q]; return ok; } // ---------------------------------------------------------------------- // reverse Hessian sparsity routine called by CppAD bool reciprocal_rev_hes_sparse( size_t id , size_t n , size_t m , size_t p , const vector< std::set >& r , const vector& s , vector& t , const vector< std::set >& u , vector< std::set >& v ) { // Can just return false if not use RevSparseHes. assert( id == 0 ); assert( n == 1 ); assert( m == 1 ); bool ok = true; // compute sparsity pattern for T(x) = S(x) * f'(x) vector T(1), S(1); S[0] = s[0]; T = r_ptr_->RevSparseJac(1, S); t[0] = T[0]; // compute sparsity pattern for A(x) = U(x)^T * f'(x) vector Ut(p), A(p); size_t q; for(q = 0; q < p; q++) Ut[q] = false; std::set::const_iterator itr; for(itr = u[0].begin(); itr != u[0].end(); itr++) Ut[*itr] = true; A = r_ptr_-> RevSparseJac(p, Ut); // compute sparsity pattern for H(x) = R^T * (S * F)''(x) vector H(p), R(n); for(q = 0; q < p; q++) R[q] = false; for(itr = r[0].begin(); itr != r[0].end(); itr++) R[*itr] = true; r_ptr_->ForSparseJac(p, R); H = r_ptr_->RevSparseHes(p, S); // compute sparsity pattern for V(x) = A(x)^T + H(x)^T v[0].clear(); for(q = 0; q < p; q++) if( A[q] || H[q] ) v[0].insert(q); return ok; } // --------------------------------------------------------------------- // Declare the AD routine reciprocal(id, ax, ay) CPPAD_USER_ATOMIC( reciprocal , CppAD::vector , double , reciprocal_forward , reciprocal_reverse , reciprocal_for_jac_sparse , reciprocal_rev_jac_sparse , reciprocal_rev_hes_sparse ) } // End empty namespace bool old_usead_1(void) { bool ok = true; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); // -------------------------------------------------------------------- // Create the ADFun r_ create_r(); // -------------------------------------------------------------------- // Create the function f(x) // // domain space vector size_t n = 1; double x0 = 0.5; vector< AD > ax(n); ax[0] = x0; // declare independent variables and start tape recording CppAD::Independent(ax); // range space vector size_t m = 1; vector< AD > ay(m); // call atomic function and store reciprocal(x) in au[0] vector< AD > au(m); size_t id = 0; // not used reciprocal(id, ax, au); // u = 1 / x // call atomic function and store reciprocal(u) in ay[0] reciprocal(id, au, ay); // y = 1 / u = x // create f: x -> y and stop tape recording ADFun f; f.Dependent(ax, ay); // f(x) = x // -------------------------------------------------------------------- // Check function value results // // check function value double check = x0; ok &= NearEqual( Value(ay[0]) , check, eps, eps); // check zero order forward mode size_t q; vector x_q(n), y_q(m); q = 0; x_q[0] = x0; y_q = f.Forward(q, x_q); ok &= NearEqual(y_q[0] , check, eps, eps); // check first order forward mode q = 1; x_q[0] = 1; y_q = f.Forward(q, x_q); check = 1.; ok &= NearEqual(y_q[0] , check, eps, eps); // check second order forward mode q = 2; x_q[0] = 0; y_q = f.Forward(q, x_q); check = 0.; ok &= NearEqual(y_q[0] , check, eps, eps); // -------------------------------------------------------------------- // Check reverse mode results // // third order reverse mode q = 3; vector w(m), dw(n * q); w[0] = 1.; dw = f.Reverse(q, w); check = 1.; ok &= NearEqual(dw[0] , check, eps, eps); check = 0.; ok &= NearEqual(dw[1] , check, eps, eps); ok &= NearEqual(dw[2] , check, eps, eps); // -------------------------------------------------------------------- // forward mode sparstiy pattern size_t p = n; CppAD::vectorBool r1(n * p), s1(m * p); r1[0] = true; // compute sparsity pattern for x[0] s1 = f.ForSparseJac(p, r1); ok &= s1[0] == true; // f[0] depends on x[0] // -------------------------------------------------------------------- // reverse mode sparstiy pattern q = m; CppAD::vectorBool s2(q * m), r2(q * n); s2[0] = true; // compute sparsity pattern for f[0] r2 = f.RevSparseJac(q, s2); ok &= r2[0] == true; // f[0] depends on x[0] // -------------------------------------------------------------------- // Hessian sparsity (using previous ForSparseJac call) CppAD::vectorBool s3(m), h(p * n); s3[0] = true; // compute sparsity pattern for f[0] h = f.RevSparseJac(p, s3); ok &= h[0] == true; // second partial of f[0] w.r.t. x[0] may be non-zero // ----------------------------------------------------------------- // Free all memory associated with the object r_ptr destroy_r(); // ----------------------------------------------------------------- // Free all temporary work space associated with atomic_one objects. // (If there are future calls to atomic functions, they will // create new temporary work space.) CppAD::user_atomic::clear(); return ok; } // END C++ ================================================ FILE: test_more/deprecated/old_usead_2.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin old_usead_2.cpp@@ $spell checkpoint var $$ $section Using AD to Compute Atomic Function Derivatives$$ $head Deprecated 2013-05-27$$ This example has been deprecated because it is easier to use the $cref/checkpoint/chkpoint_one/$$ class instead. $head Purpose$$ Consider the case where an inner function is used repeatedly in the definition of an outer function. In this case, it may reduce the number of variables $cref/size_var/fun_property/size_var/$$, and hence the required memory. $srcthisfile%0%// BEGIN C++%// END C++%1%$$ $end */ // BEGIN C++ # include namespace { // Begin empty namespace using CppAD::AD; using CppAD::ADFun; using CppAD::vector; // ---------------------------------------------------------------------- // ODE for [t, t^2 / 2 ] in form required by Runge45 class Fun { public: void Ode( const AD &t, const vector< AD > &z, vector< AD > &f) { assert( z.size() == 2 ); assert( f.size() == 2 ); f[0] = 1.0; f[1] = z[0]; } }; // ---------------------------------------------------------------------- // Create function that takes on Runge45 step for the ODE above ADFun* r_ptr_; void create_r(void) { size_t n = 3, m = 2; vector< AD > x(n), zi(m), y(m), e(m); // The value of x does not matter because the operation sequence // does not depend on x. x[0] = 0.0; // initial value z_0 (t) at t = ti x[1] = 0.0; // initial value z_1 (t) at t = ti x[2] = 0.1; // final time for this integration CppAD::Independent(x); zi[0] = x[0]; // z_0 (t) at t = ti zi[1] = x[1]; // z_1 (t) at t = ti AD ti = 0.0; // t does not appear in ODE so does not matter AD tf = x[2]; // final time size_t M = 3; // number of Runge45 steps to take Fun F; y = CppAD::Runge45(F, M, ti, tf, zi, e); r_ptr_ = new ADFun(x, y); } void destroy_r(void) { delete r_ptr_; r_ptr_ = nullptr; } // ---------------------------------------------------------------------- // forward mode routine called by CppAD bool solve_ode_forward( size_t id , size_t k , size_t n , size_t m , const vector& vx , vector& vy , const vector& tx , vector& ty ) { assert( id == 0 ); assert( n == 3 ); assert( m == 2 ); assert( k == 0 || vx.size() == 0 ); bool ok = true; vector xp(n), yp(m); size_t i, j; // check for special case if( vx.size() > 0 ) { //Compute r, a Jacobian sparsity pattern. // Use reverse mode because m < n. vector< std::set > s(m), r(m); for(i = 0; i < m; i++) s[i].insert(i); r = r_ptr_->RevSparseJac(m, s); std::set::const_iterator itr; for(i = 0; i < m; i++) { vy[i] = false; for(itr = s[i].begin(); itr != s[i].end(); itr++) { j = *itr; assert( j < n ); // y[i] depends on the value of x[j] // Visual Studio 2013 generates warning without bool below vy[i] |= bool( vx[j] ); } } } // make sure r_ has proper lower order Taylor coefficients stored // then compute ty[k] for(size_t q = 0; q <= k; q++) { for(j = 0; j < n; j++) xp[j] = tx[j * (k+1) + q]; yp = r_ptr_->Forward(q, xp); if( q == k ) { for(i = 0; i < m; i++) ty[i * (k+1) + q] = yp[i]; } # ifndef NDEBUG else { for(i = 0; i < m; i++) assert( ty[i * (k+1) + q] == yp[i] ); } # endif } // no longer need the Taylor coefficients in r_ptr_ // (have to reconstruct them every time) r_ptr_->capacity_order(0); return ok; } // ---------------------------------------------------------------------- // reverse mode routine called by CppAD bool solve_ode_reverse( size_t id , size_t k , size_t n , size_t m , const vector& tx , const vector& ty , vector& px , const vector& py ) { assert( id == 0 ); assert( n == 3 ); assert( m == 2 ); bool ok = true; vector xp(n), w( (k+1) * m ), dw( (k+1) * n ); // make sure r_ has proper forward mode coefficients size_t i, j, q; for(q = 0; q <= k; q++) { for(j = 0; j < n; j++) xp[j] = tx[j * (k+1) + q]; # ifdef NDEBUG r_ptr_->Forward(q, xp); # else vector yp(m); yp = r_ptr_->Forward(q, xp); for(i = 0; i < m; i++) assert( ty[i * (k+1) + q] == yp[i] ); # endif } for(i = 0; i < m; i++) { for(q = 0; q <=k; q++) w[ i * (k+1) + q] = py[ i * (k+1) + q]; } dw = r_ptr_->Reverse(k+1, w); for(j = 0; j < n; j++) { for(q = 0; q <=k; q++) px[ j * (k+1) + q] = dw[ j * (k+1) + q]; } // no longer need the Taylor coefficients in r_ptr_ // (have to reconstruct them every time) r_ptr_->capacity_order(0); return ok; } // ---------------------------------------------------------------------- // forward Jacobian sparsity routine called by CppAD bool solve_ode_for_jac_sparse( size_t id , size_t n , size_t m , size_t p , const vector< std::set >& r , vector< std::set >& s ) { assert( id == 0 ); assert( n == 3 ); assert( m == 2 ); bool ok = true; vector< std::set > R(n), S(m); for(size_t j = 0; j < n; j++) R[j] = r[j]; S = r_ptr_->ForSparseJac(p, R); for(size_t i = 0; i < m; i++) s[i] = S[i]; // no longer need the forward mode sparsity pattern // (have to reconstruct them every time) r_ptr_->size_forward_set(0); return ok; } // ---------------------------------------------------------------------- // reverse Jacobian sparsity routine called by CppAD bool solve_ode_rev_jac_sparse( size_t id , size_t n , size_t m , size_t p , vector< std::set >& r , const vector< std::set >& s ) { assert( id == 0 ); assert( n == 3 ); assert( m == 2 ); bool ok = true; vector< std::set > R(p), S(p); std::set::const_iterator itr; size_t i; // untranspose s for(i = 0; i < m; i++) { for(itr = s[i].begin(); itr != s[i].end(); itr++) S[*itr].insert(i); } R = r_ptr_->RevSparseJac(p, S); // transpose r for(i = 0; i < m; i++) r[i].clear(); for(i = 0; i < p; i++) { for(itr = R[i].begin(); itr != R[i].end(); itr++) r[*itr].insert(i); } return ok; } // ---------------------------------------------------------------------- // reverse Hessian sparsity routine called by CppAD bool solve_ode_rev_hes_sparse( size_t id , size_t n , size_t m , size_t p , const vector< std::set >& r , const vector& s , vector& t , const vector< std::set >& u , vector< std::set >& v ) { // Can just return false if not use RevSparseHes. assert( id == 0 ); assert( n == 3 ); assert( m == 2 ); bool ok = true; std::set::const_iterator itr; // compute sparsity pattern for T(x) = S(x) * f'(x) vector< std::set > S(1); size_t i, j; S[0].clear(); for(i = 0; i < m; i++) if( s[i] ) S[0].insert(i); t = r_ptr_->RevSparseJac(1, s); // compute sparsity pattern for A(x)^T = U(x)^T * f'(x) vector< std::set > Ut(p), At(p); for(i = 0; i < m; i++) { for(itr = u[i].begin(); itr != u[i].end(); itr++) Ut[*itr].insert(i); } At = r_ptr_->RevSparseJac(p, Ut); // compute sparsity pattern for H(x)^T = R^T * (S * F)''(x) vector< std::set > R(n), Ht(p); for(j = 0; j < n; j++) R[j] = r[j]; r_ptr_->ForSparseJac(p, R); Ht = r_ptr_->RevSparseHes(p, S); // compute sparsity pattern for V(x) = A(x) + H(x)^T for(j = 0; j < n; j++) v[j].clear(); for(i = 0; i < p; i++) { for(itr = At[i].begin(); itr != At[i].end(); itr++) v[*itr].insert(i); for(itr = Ht[i].begin(); itr != Ht[i].end(); itr++) v[*itr].insert(i); } // no longer need the forward mode sparsity pattern // (have to reconstruct them every time) r_ptr_->size_forward_set(0); return ok; } // --------------------------------------------------------------------- // Declare the AD routine solve_ode(id, ax, ay) CPPAD_USER_ATOMIC( solve_ode , CppAD::vector , double , solve_ode_forward , solve_ode_reverse , solve_ode_for_jac_sparse , solve_ode_rev_jac_sparse , solve_ode_rev_hes_sparse ) } // End empty namespace bool old_usead_2(void) { bool ok = true; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); // -------------------------------------------------------------------- // Create the ADFun r_ create_r(); // -------------------------------------------------------------------- // domain and range space vectors size_t n = 3, m = 2; vector< AD > au(n), ax(n), ay(m); au[0] = 0.0; // value of z_0 (t) = t, at t = 0 ax[1] = 0.0; // value of z_1 (t) = t^2/2, at t = 0 au[2] = 1.0; // final t CppAD::Independent(au); size_t M = 2; // number of r steps to take ax[0] = au[0]; // value of z_0 (t) = t, at t = 0 ax[1] = au[1]; // value of z_1 (t) = t^2/2, at t = 0 AD dt = au[2] / double(M); // size of each r step ax[2] = dt; for(size_t i_step = 0; i_step < M; i_step++) { size_t id = 0; // not used solve_ode(id, ax, ay); ax[0] = ay[0]; ax[1] = ay[1]; } // create f: u -> y and stop tape recording // y_0(t) = u_0 + t = u_0 + u_2 // y_1(t) = u_1 + u_0 * t + t^2 / 2 = u_1 + u_0 * u_2 + u_2^2 / 2 // where t = u_2 ADFun f; f.Dependent(au, ay); // -------------------------------------------------------------------- // Check forward mode results // // zero order forward vector up(n), yp(m); size_t q = 0; double u0 = 0.5; double u1 = 0.25; double u2 = 0.75; double check; up[0] = u0; up[1] = u1; up[2] = u2; yp = f.Forward(q, up); check = u0 + u2; ok &= NearEqual( yp[0], check, eps, eps); check = u1 + u0 * u2 + u2 * u2 / 2.0; ok &= NearEqual( yp[1], check, eps, eps); // // forward mode first derivative w.r.t t q = 1; up[0] = 0.0; up[1] = 0.0; up[2] = 1.0; yp = f.Forward(q, up); check = 1.0; ok &= NearEqual( yp[0], check, eps, eps); check = u0 + u2; ok &= NearEqual( yp[1], check, eps, eps); // // forward mode second order Taylor coefficient w.r.t t q = 2; up[0] = 0.0; up[1] = 0.0; up[2] = 0.0; yp = f.Forward(q, up); check = 0.0; ok &= NearEqual( yp[0], check, eps, eps); check = 1.0 / 2.0; ok &= NearEqual( yp[1], check, eps, eps); // -------------------------------------------------------------------- // reverse mode derivatives of \partial_t y_1 (t) vector w(m * q), dw(n * q); w[0 * q + 0] = 0.0; w[1 * q + 0] = 0.0; w[0 * q + 1] = 0.0; w[1 * q + 1] = 1.0; dw = f.Reverse(q, w); // derivative of y_1(u) = u_1 + u_0 * u_2 + u_2^2 / 2, w.r.t. u // is equal deritative of \partial_u2 y_1(u) w.r.t \partial_u2 u check = u2; ok &= NearEqual( dw[0 * q + 1], check, eps, eps); check = 1.0; ok &= NearEqual( dw[1 * q + 1], check, eps, eps); check = u0 + u2; ok &= NearEqual( dw[2 * q + 1], check, eps, eps); // derivative of \partial_t y_1 w.r.t u = u_0 + t, w.r.t u check = 1.0; ok &= NearEqual( dw[0 * q + 0], check, eps, eps); check = 0.0; ok &= NearEqual( dw[1 * q + 0], check, eps, eps); check = 1.0; ok &= NearEqual( dw[2 * q + 0], check, eps, eps); // -------------------------------------------------------------------- // forward mode sparsity pattern for the Jacobian // f_u = [ 1, 0, 1 ] // [ u_2, 1, u_2 ] size_t i, j, p = n; CppAD::vectorBool r(n * p), s(m * p); // r = identity sparsity pattern for(i = 0; i < n; i++) for(j = 0; j < p; j++) r[i*n +j] = (i == j); s = f.ForSparseJac(p, r); ok &= s[ 0 * p + 0] == true; ok &= s[ 0 * p + 1] == false; ok &= s[ 0 * p + 2] == true; ok &= s[ 1 * p + 0] == true; ok &= s[ 1 * p + 1] == true; ok &= s[ 1 * p + 2] == true; // -------------------------------------------------------------------- // reverse mode sparsity pattern for the Jacobian q = m; s.resize(q * m); r.resize(q * n); // s = identity sparsity pattern for(i = 0; i < q; i++) for(j = 0; j < m; j++) s[i*m +j] = (i == j); r = f.RevSparseJac(q, s); ok &= r[ 0 * n + 0] == true; ok &= r[ 0 * n + 1] == false; ok &= r[ 0 * n + 2] == true; ok &= r[ 1 * n + 0] == true; ok &= r[ 1 * n + 1] == true; ok &= r[ 1 * n + 2] == true; // -------------------------------------------------------------------- // Hessian sparsity for y_1 (u) = u_1 + u_0 * u_2 + u_2^2 / 2 s.resize(m); s[0] = false; s[1] = true; r.resize(n * n); for(i = 0; i < n; i++) for(j = 0; j < n; j++) r[ i * n + j ] = (i == j); CppAD::vectorBool h(n * n); h = f.RevSparseHes(n, s); ok &= h[0 * n + 0] == false; ok &= h[0 * n + 1] == false; ok &= h[0 * n + 2] == true; ok &= h[1 * n + 0] == false; ok &= h[1 * n + 1] == false; ok &= h[1 * n + 2] == false; ok &= h[2 * n + 0] == true; ok &= h[2 * n + 1] == false; ok &= h[2 * n + 2] == true; // -------------------------------------------------------------------- destroy_r(); // Free all temporary work space associated with atomic_one objects. // (If there are future calls to atomic functions, they will // create new temporary work space.) CppAD::user_atomic::clear(); return ok; } // END C++ ================================================ FILE: test_more/deprecated/omp_alloc.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin omp_alloc.cpp@@ $spell openmp $$ $section OpenMP Memory Allocator: Example and Test$$ $head Deprecated 2011-08-31$$ This example is only intended to help convert calls to $cref omp_alloc$$ to calls to $cref thread_alloc$$. $srcthisfile%0%// BEGIN C++%// END C++%1%$$ $end */ // BEGIN C++ # include # include # include namespace { // Begin empty namespace bool omp_alloc_bytes(void) { bool ok = true; using CppAD::omp_alloc; size_t thread; // check initial memory values ok &= ! CppAD::memory_leak(); // amount of static memory used by thread zero size_t static_inuse = omp_alloc::inuse(0); // determine the currently executing thread // (should be zero because not in parallel mode) thread = omp_alloc::get_thread_num(); // repeatedly allocate enough memory for at least two size_t values. size_t min_size_t = 2; size_t min_bytes = min_size_t * sizeof(size_t); size_t n_outer = 10; size_t n_inner = 5; size_t cap_bytes(0), i, j, k; for(i = 0; i < n_outer; i++) { // Do not use CppAD::vector here because its use of omp_alloc // complicates the inuse and available results. std::vector v_ptr(n_inner); for( j = 0; j < n_inner; j++) { // allocate enough memory for min_size_t size_t objects v_ptr[j] = omp_alloc::get_memory(min_bytes, cap_bytes); size_t* ptr = reinterpret_cast(v_ptr[j]); // determine the number of size_t values we have obtained size_t cap_size_t = cap_bytes / sizeof(size_t); ok &= min_size_t <= cap_size_t; // use placement new to call the size_t copy constructor for(k = 0; k < cap_size_t; k++) new(ptr + k) size_t(i + j + k); // check that the constructor worked for(k = 0; k < cap_size_t; k++) ok &= ptr[k] == (i + j + k); } // check that n_inner * cap_bytes are inuse and none are available ok &= omp_alloc::inuse(thread) == n_inner*cap_bytes + static_inuse; ok &= omp_alloc::available(thread) == 0; // return the memrory to omp_alloc for(j = 0; j < n_inner; j++) omp_alloc::return_memory(v_ptr[j]); // check that now n_inner * cap_bytes are now available // and none are in use ok &= omp_alloc::inuse(thread) == static_inuse; ok &= omp_alloc::available(thread) == n_inner * cap_bytes; } // return all the available memory to the system omp_alloc::free_available(thread); ok &= ! CppAD::memory_leak(); return ok; } class my_char { public: char ch_ ; my_char(void) : ch_(' ') { } my_char(const my_char& my_ch) : ch_(my_ch.ch_) { } }; bool omp_alloc_array(void) { bool ok = true; using CppAD::omp_alloc; size_t i; // check initial memory values size_t thread = omp_alloc::get_thread_num(); ok &= thread == 0; ok &= ! CppAD::memory_leak(); size_t static_inuse = omp_alloc::inuse(0); // initial allocation of an array size_t size_min = 3; size_t size_one; my_char *array_one = omp_alloc::create_array(size_min, size_one); // check the values and change them to null 'x' for(i = 0; i < size_one; i++) { ok &= array_one[i].ch_ == ' '; array_one[i].ch_ = 'x'; } // now create a longer array size_t size_two; my_char *array_two = omp_alloc::create_array(2 * size_min, size_two); // check the values in array one for(i = 0; i < size_one; i++) ok &= array_one[i].ch_ == 'x'; // check the values in array two for(i = 0; i < size_two; i++) ok &= array_two[i].ch_ == ' '; // check the amount of inuse and available memory // (an extra size_t value is used for each memory block). size_t check = static_inuse + sizeof(my_char)*(size_one + size_two); ok &= omp_alloc::inuse(thread) - check < sizeof(my_char); ok &= omp_alloc::available(thread) == 0; // delete the arrays omp_alloc::delete_array(array_one); omp_alloc::delete_array(array_two); ok &= omp_alloc::inuse(thread) == static_inuse; check = sizeof(my_char)*(size_one + size_two); ok &= omp_alloc::available(thread) - check < sizeof(my_char); // free the memory for use by this thread omp_alloc::free_available(thread); ok &= ! CppAD::memory_leak(); return ok; } } // End empty namespace bool omp_alloc(void) { bool ok = true; using CppAD::omp_alloc; // check initial state of allocator ok &= omp_alloc::get_max_num_threads() == 1; // set the maximum number of threads greater than one // so that omp_alloc holds onto memory CppAD::omp_alloc::set_max_num_threads(2); ok &= omp_alloc::get_max_num_threads() == 2; ok &= ! CppAD::memory_leak(); // now use memory allocator in state where it holds onto memory ok &= omp_alloc_bytes(); ok &= omp_alloc_array(); // check that the tests have not held onto memory ok &= ! CppAD::memory_leak(); // set the maximum number of threads back to one // so that omp_alloc no longer holds onto memory CppAD::omp_alloc::set_max_num_threads(1); return ok; } // END C++ ================================================ FILE: test_more/deprecated/track_new_del.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin TrackNewDel.cpp@@ $section Tracking Use of New and Delete: Example and Test$$ $srcthisfile%0%// BEGIN C++%// END C++%1%$$ $end */ // BEGIN C++ # include bool track_new_del(void) { bool ok = true; // initial count size_t count = CPPAD_TRACK_COUNT(); // allocate an array of length 5 double *ptr = nullptr; size_t newlen = 5; ptr = CPPAD_TRACK_NEW_VEC(newlen, ptr); // copy data into the array size_t ncopy = newlen; size_t i; for(i = 0; i < ncopy; i++) ptr[i] = double(i); // extend the buffer to be length 10 newlen = 10; ptr = CPPAD_TRACK_EXTEND(newlen, ncopy, ptr); // copy data into the new part of the array for(i = ncopy; i < newlen; i++) ptr[i] = double(i); // check the values in the array for(i = 0; i < newlen; i++) ok &= (ptr[i] == double(i)); // free the memory allocated since previous call to TrackCount CPPAD_TRACK_DEL_VEC(ptr); // check for memory leak ok &= (count == CPPAD_TRACK_COUNT()); return ok; } // END C++ ================================================ FILE: test_more/deprecated/zdouble.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin zdouble.cpp@@ $spell zdouble $$ $section zdouble: Example and Test$$ $srcthisfile%0%// BEGIN C++%// END C++%1%$$ $end */ // BEGIN C++ # include namespace { template bool test_one(void) { bool ok = true; Base eps99 = 99. * std::numeric_limits::epsilon(); typedef CppAD::AD a1type; typedef CppAD::AD a2type; // value during taping size_t n = 2; CPPAD_TESTVECTOR(Base) x(n); x[0] = 0.0; x[1] = 0.0; // declare independent variable CPPAD_TESTVECTOR(a2type) a2x(n); for (size_t j = 0; j < n; j++) a2x[j] = a2type( a1type(x[j]) ); Independent(a2x); // zero and one as a2type values a2type a2zero = a2type(0.0); a2type a2one = a2type(1.0); // h(x) = x[0] / x[1] if x[1] > x[0] else 1.0 a2type h_x = CondExpGt(a2x[1], a2x[0], a2x[0] / a2x[1], a2one); // f(x) = h(x) if x[0] > 0.0 else 0.0 // = x[0] / x[1] if x[1] > x[0] and x[0] > 0.0 // = 1.0 if x[0] >= x[1] and x[0] > 0.0 // = 0.0 if x[0] <= 0.0 a2type f_x = CondExpGt(a2x[0], a2zero, h_x, a2one); // define the function f(x) size_t m = 1; CPPAD_TESTVECTOR(a2type) a2y(m); a2y[0] = f_x; CppAD::ADFun af1; af1.Dependent(a2x, a2y); // Define function g(x) = gradient of f(x) CPPAD_TESTVECTOR(a1type) a1x(n), a1z(n), a1w(m); for (size_t j = 0; j < n; j++) a1x[j] = a1type(x[j]); a1w[0] = a1type(1.0); Independent(a1x); af1.Forward(0, a1x); a1z = af1.Reverse(1, a1w); CppAD::ADFun g; g.Dependent(a1x, a1z); // check result for a case where f(x) = 0.0; CPPAD_TESTVECTOR(Base) z(2); x[0] = 0.0; x[1] = 0.0; z = g.Forward(0, x); ok &= z[0] == 0.0; ok &= z[1] == 0.0; // check result for a case where f(x) = 1.0; x[0] = 1.0; x[1] = 0.5; z = g.Forward(0, x); ok &= z[0] == 0.0; ok &= z[1] == 0.0; // check result for a case where f(x) = x[0] / x[1]; x[0] = 1.0; x[1] = 2.0; z = g.Forward(0, x); ok &= CppAD::NearEqual(z[0], 1.0/x[1], eps99, eps99); ok &= CppAD::NearEqual(z[1], - x[0]/(x[1]*x[1]), eps99, eps99); return ok; } bool test_two(void) { bool ok = true; using CppAD::zdouble; // zdouble eps = CppAD::numeric_limits::epsilon(); ok &= eps == std::numeric_limits::epsilon(); // zdouble min = CppAD::numeric_limits::min(); ok &= min == std::numeric_limits::min(); // zdouble max = CppAD::numeric_limits::max(); ok &= max == std::numeric_limits::max(); // zdouble nan = CppAD::numeric_limits::quiet_NaN(); ok &= nan != nan; // int digits10 = CppAD::numeric_limits::digits10; ok &= digits10 == std::numeric_limits::digits10; // return ok; } } bool zdouble(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; using CppAD::zdouble; // ok &= test_one(); ok &= test_one(); // ok &= test_two(); // return ok; } // END C++ ================================================ FILE: test_more/general/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the test_more/general tests # # adolc_sources IF( cppad_has_adolc ) SET(adolc_sources base_adolc.cpp) ELSE( cppad_has_adolc ) SET(adolc_sources "") ENDIF( cppad_has_adolc ) # # eigen_sources IF( cppad_has_eigen ) SET(eigen_sources cppad_eigen.cpp eigen_mat_inv.cpp) ELSE( cppad_has_eigen ) SET(eigen_sources "") ENDIF( cppad_has_eigen ) # # ipopt_sourced IF( cppad_has_ipopt ) SET(ipopt_sources ipopt_solve.cpp) ELSE( cppad_has_ipopt ) SET(ipopt_sources "") ENDIF( cppad_has_ipopt ) # # BEGIN_SORT_THIS_LINE_PLUS_5 SET(source_list ${adolc_sources} ${eigen_sources} ${ipopt_sources} abs_normal.cpp acos.cpp acosh.cpp add.cpp add_eq.cpp add_zero.cpp adfun.cpp asin.cpp asinh.cpp assign.cpp atan.cpp atan2.cpp atanh.cpp atomic_four.cpp atomic_three.cpp azmul.cpp base2ad.cpp base_alloc.cpp base_complex.cpp bool_sparsity.cpp check_simple_vector.cpp chkpoint_one.cpp chkpoint_two.cpp compare.cpp compare_change.cpp cond_exp.cpp cond_exp_ad.cpp cond_exp_rev.cpp copy.cpp cos.cpp cosh.cpp cpp_graph.cpp cppad_vector.cpp dbl_epsilon.cpp dependency.cpp div.cpp div_eq.cpp div_zero_one.cpp erf.cpp exp.cpp expm1.cpp extern_value.cpp fabs.cpp for_hes_sparsity.cpp for_hess.cpp for_jac_sparsity.cpp forward.cpp forward_dir.cpp forward_order.cpp from_base.cpp fun_check.cpp general.cpp hes_sparsity.cpp jacobian.cpp json_graph.cpp local/is_pod.cpp local/json_lexer.cpp local/json_parser.cpp local/temp_file.cpp local/vector_set.cpp log.cpp log10.cpp log1p.cpp mul.cpp mul_cond_rev.cpp mul_cskip.cpp mul_eq.cpp mul_level.cpp mul_zdouble.cpp mul_zero_one.cpp near_equal_ext.cpp neg.cpp new_dynamic.cpp num_limits.cpp ode_err_control.cpp optimize.cpp parameter.cpp poly.cpp pow.cpp pow_int.cpp print_for.cpp rev_sparse_jac.cpp rev_two.cpp reverse.cpp romberg_one.cpp rosen_34.cpp runge_45.cpp simple_vector.cpp sin.cpp sin_cos.cpp sinh.cpp sparse_hessian.cpp sparse_jac_work.cpp sparse_jacobian.cpp sparse_sub_hes.cpp sparse_vec_ad.cpp sqrt.cpp std_math.cpp sub.cpp sub_eq.cpp sub_zero.cpp subgraph_1.cpp subgraph_2.cpp subgraph_hes2jac.cpp tan.cpp to_csrc.cpp to_string.cpp value.cpp vec_ad.cpp vec_ad_par.cpp vec_unary.cpp ) # END_SORT_THIS_LINE_MINUS_2 set_compile_flags( test_more_general "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(test_more_general EXCLUDE_FROM_ALL ${source_list}) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(test_more_general ${cppad_lib} ${adolc_LINK_LIBRARIES} ${ipopt_LINK_LIBRARIES} ${colpack_libs} ) # # check_test_more_general add_check_executable(check_test_more general) ================================================ FILE: test_more/general/abs_normal.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace { // BEGIN_EMPTY_NAMESPACE // join CPPAD_TESTVECTOR(double) join( const CPPAD_TESTVECTOR(double)& x , const CPPAD_TESTVECTOR(double)& u ) { size_t n = x.size(); size_t s = u.size(); CPPAD_TESTVECTOR(double) xu(n + s); for(size_t j = 0; j < n; j++) xu[j] = x[j]; for(size_t j = 0; j < s; j++) xu[n + j] = u[j]; return xu; } // test_pow bool test_pow(void) { bool ok = true; // using CppAD::AD; using CppAD::ADFun; // typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR( AD ) ad_vector; // double eps99 = 99.0 * std::numeric_limits::epsilon(); // size_t n = 2; // size of x size_t m = 1; // size of y size_t s = 1; // number of absolute value terms // // record the function f(x) ad_vector ad_x(n), ad_y(m); for(size_t j = 0; j < n; j++) ad_x[j] = double(j + 1); Independent( ad_x ); // // for this example, we the function is // f(x) = pow( |x_0|, x_1) + pow( |x_0| , 2) + pow(2, |x_0|) AD abs_x0 = abs( ad_x[0] ); AD pow_vv = pow( abs_x0 , ad_x[1] ); AD pow_vp = pow( abs_x0 , 2.0 ); AD pow_pv = pow( 2.0 , abs_x0 ); ad_y[0] = pow_vv + pow_vp + pow_pv; ADFun f(ad_x, ad_y); // create its abs_normal representation in g, a ADFun g, a; f.abs_normal_fun(g, a); // check dimension of domain and range space for g ok &= g.Domain() == n + s; ok &= g.Range() == m + s; // check dimension of domain and range space for a ok &= a.Domain() == n; ok &= a.Range() == s; // -------------------------------------------------------------------- // Choose a point x_hat d_vector x_hat(n); x_hat[0] = -2.0; x_hat[1] = 2.0; // value of a_hat = a(x_hat) d_vector a_hat = a.Forward(0, x_hat); // (x_hat, a_hat) d_vector xu_hat = join(x_hat, a_hat); // value of g[ x_hat, a_hat ] d_vector g_hat = g.Forward(0, xu_hat); // Jacobian of g[ x_hat, a_hat ] d_vector g_jac = g.Jacobian(xu_hat); // value of delta_x d_vector delta_x(n); delta_x[0] = 4.0; delta_x[1] = 1.0; // value of x d_vector x(n); for(size_t j = 0; j < n; j++) x[j] = x_hat[j] + delta_x[j]; // value of f(x) d_vector y = f.Forward(0, x); // check double check = std::pow( std::fabs(x[0]) , x[1]); check += std::pow( std::fabs(x[0]) , 2.0 ); check += std::pow( 2.0, std::fabs(x[0]) ); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); return ok; } } // END_EMPTY_NAMESPACE bool abs_normal(void) { bool ok = true; ok &= test_pow(); return ok; } ================================================ FILE: test_more/general/acos.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old acos examples now used just for valiadation testing */ # include bool acos(void) { bool ok = true; using CppAD::acos; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector CPPAD_TESTVECTOR(AD) U(1); U[0] = .5; Independent(U); // a temporary values AD x = cos(U[0]); // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); Z[0] = acos(x); // acos( cos(u) ) // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v(1); CPPAD_TESTVECTOR(double) w(1); // check value ok &= NearEqual(U[0] , Z[0], eps99 , eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; double value = 1.; v[0] = 1.; for(j = 1; j < p; j++) { jfac *= double(j); w = f.Forward(j, v); ok &= NearEqual(w[0], value/jfac, eps99, eps99);// d^jz/du^j v[0] = 0.; value = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r(p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; value = 1.; for(j = 0; j < p; j++) { ok &= NearEqual(r[j], value/jfac, eps99, eps99);// d^jz/du^j jfac *= double(j + 1); value = 0.; } return ok; } ================================================ FILE: test_more/general/acosh.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include bool acosh(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; // 10 times machine epsilon double eps = 200. * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = x0; // declare independent variables and start tape recording CppAD::Independent(ax); // a temporary value AD cosh_of_x0 = CppAD::cosh(ax[0]); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = CppAD::acosh(cosh_of_x0); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // check value ok &= NearEqual(ay[0] , x0, eps, eps); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 1., eps, eps); // forward computation of higher order partials w.r.t. x[0] size_t n_order = 5; for(size_t order = 2; order < n_order; order++) { dx[0] = 0.; dy = f.Forward(order, dx); ok &= NearEqual(dy[0], 0., eps, eps); } // reverse computation of derivatives CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n_order * n); w[0] = 1.; dw = f.Reverse(n_order, w); ok &= NearEqual(dw[0], 1., eps, eps); for(size_t order = 1; order < n_order; order++) ok &= NearEqual(dw[order * n + 0], 0., eps, eps); return ok; } // END C++ ================================================ FILE: test_more/general/add.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Two old Add examples now used just for valiadation testing */ # include namespace { // BEGIN empty namespace bool AddTestOne(void) { bool ok = true; using namespace CppAD; // independent variable vector, indices, values, and declaration CPPAD_TESTVECTOR(AD) U(2); size_t s = 0; size_t t = 1; U[s] = 3.; U[t] = 2.; Independent(U); // dependent variable vector and indices CPPAD_TESTVECTOR(AD) Z(3); size_t x = 0; size_t y = 1; size_t z = 2; // dependent variable values Z[x] = U[s] + U[t]; // AD + AD Z[y] = Z[x] + 1.; // AD + double Z[z] = 1. + Z[y]; // double + AD // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v( f.Domain() ); CPPAD_TESTVECTOR(double) w( f.Range() ); // check function values ok &= ( Z[x] == 3. + 2. ); ok &= ( Z[y] == 3. + 2. + 1. ); ok &= ( Z[z] == 1. + 3. + 2. + 1. ); // forward computation of partials w.r.t. s v[s] = 1.; v[t] = 0.; w = f.Forward(1, v); ok &= ( w[x] == 1. ); // dx/ds ok &= ( w[y] == 1. ); // dy/ds ok &= ( w[z] == 1. ); // dz/ds // reverse computation of second partials of z CPPAD_TESTVECTOR(double) r( f.Domain() * 2 ); w[x] = 0.; w[y] = 0.; w[z] = 1.; r = f.Reverse(2, w); ok &= ( r[2 * s + 1] == 0. ); // d^2 z / (ds ds) ok &= ( r[2 * t + 1] == 0. ); // d^2 z / (ds dt) return ok; } bool AddTestTwo(void) { bool ok = true; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector double u0 = .5; CPPAD_TESTVECTOR(AD) U(1); U[0] = u0; Independent(U); AD a = U[0] + 1.; // AD + double AD b = a + 2; // AD + int AD c = 3. + b; // double + AD AD d = 4 + c; // int + AD // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); Z[0] = d + U[0]; // AD + AD // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v(1); CPPAD_TESTVECTOR(double) w(1); // check value ok &= NearEqual(Z[0] , 2 * u0 + 10, eps99 , eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; double value = 2.; v[0] = 1.; for(j = 1; j < p; j++) { jfac *= double(j); w = f.Forward(j, v); ok &= NearEqual(w[0], value/jfac, eps99, eps99); // d^jz/du^j v[0] = 0.; value = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r(p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; value = 2.; for(j = 0; j < p; j++) { ok &= NearEqual(r[j], value/jfac, eps99, eps99); // d^jz/du^j jfac *= double(j + 1); value = 0.; } return ok; } } // END empty namespace bool Add(void) { bool ok = true; ok &= AddTestOne(); ok &= AddTestTwo(); return ok; } ================================================ FILE: test_more/general/add_eq.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Two old example now used just for valiadation testing */ # include namespace { // BEGIN empty namespace bool AddEqOne(void) { bool ok = true; using namespace CppAD; // independent variable vector, indices, values, and declaration CPPAD_TESTVECTOR(AD) U(2); size_t s = 0; size_t t = 1; U[s] = 3.; U[t] = 2.; Independent(U); // dependent variable vector and indices CPPAD_TESTVECTOR(AD) Z(2); size_t x = 0; size_t y = 1; // dependent variable values Z[x] = 4.; Z[y] = U[t]; Z[x] += U[s]; // parameter += variable Z[x] += U[t]; // variable += variable Z[y] += .5; // variable += double // use .5 because it is represented exactly in binary and // because it makes sure that += does not slice the double to an int // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v( f.Domain() ); CPPAD_TESTVECTOR(double) w( f.Range() ); // check function values ok &= ( Z[x] == 4. + 3. + 2. ); ok &= ( Z[y] == 2. + .5 ); // forward computation of partials w.r.t. s v[s] = 1.; v[t] = 0.; w = f.Forward(1, v); ok &= ( w[x] == 1. ); // dx/ds ok &= ( w[y] == 0. ); // dy/ds // reverse computation of second partials of x CPPAD_TESTVECTOR(double) r( f.Domain() * 2 ); w[x] = 1.; w[y] = 0.; r = f.Reverse(2, w); ok &= ( r[2 * s + 1] == 0. ); ok &= ( r[2 * t + 1] == 0. ); return ok; } bool AddEqTwo(void) { bool ok = true; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector double u0 = .5; CPPAD_TESTVECTOR(AD) U(1); U[0] = u0; Independent(U); // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); Z[0] = U[0]; // initial value Z[0] += 2; // AD += int Z[0] += 4.; // AD += double Z[0] += U[0]; // AD += AD // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v(1); CPPAD_TESTVECTOR(double) w(1); // check value ok &= NearEqual(Z[0] , u0+2+4+u0, eps99 , eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; double value = 2.; v[0] = 1.; for(j = 1; j < p; j++) { jfac *= double(j); w = f.Forward(j, v); ok &= NearEqual(w[0], value/jfac, eps99, eps99); // d^jz/du^j v[0] = 0.; value = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r(p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; value = 2.; for(j = 0; j < p; j++) { ok &= NearEqual(r[j], value/jfac, eps99, eps99); // d^jz/du^j jfac *= double(j + 1); value = 0.; } return ok; } } // END empty namespace bool AddEq(void) { bool ok = true; ok &= AddEqOne(); ok &= AddEqTwo(); return ok; } // END PROGRAM ================================================ FILE: test_more/general/add_zero.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Test the use of the special parameters zero and one with the multiply operator */ # include typedef CppAD::AD ADdouble; typedef CppAD::AD< ADdouble > ADDdouble; bool AddZero(void) { using namespace CppAD; bool ok = true; size_t i; for(i = 0; i < 2; i++) { // run through the cases x = 0, 1 size_t j; for(j = 0; j < 2; j++) { // run through the cases y = 0, 1 CPPAD_TESTVECTOR( ADdouble ) x(1); x[0] = double(i); Independent(x); CPPAD_TESTVECTOR( ADDdouble ) y(1); y[0] = ADDdouble(j); Independent(y); CPPAD_TESTVECTOR( ADDdouble ) z(2); z[0] = x[0] + y[0]; z[1] = y[0] + x[0]; z[1] += x[0]; // f(y) = z = { x + y , y + x + x } ADFun< ADdouble > f(y, z); CPPAD_TESTVECTOR( ADdouble ) u( f.Domain() ); CPPAD_TESTVECTOR( ADdouble ) v( f.Range() ); // v = f(y) u[0] = ADdouble(j); v = f.Forward(0, u); // check value of f ok &= v[0] == x[0] + ADdouble(j); ok &= v[1] == ADdouble(j) + x[0] + x[0]; // g(x) = f(y) = {x + y , y + x + x} ADFun g(x, v); CPPAD_TESTVECTOR( double ) a( g.Domain() ); CPPAD_TESTVECTOR( double ) b( g.Range() ); // b = g'(x) a[0] = 1.; b = g.Forward(1, a); // check derivatives of g ok &= (b[0] == 1.); ok &= (b[1] == 2.); } } return ok; } ================================================ FILE: test_more/general/adfun.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Test that ADFun copy constructor generates an error message. */ # include # include namespace { // BEGIN_EMPTY_NAMESPACE bool adfun_empty(void) { bool ok = true; size_t thread = CppAD::thread_alloc::thread_num(); // // Allocate this memory first incase we are using // CppAD::vector which uses thread_alloc. CPPAD_TESTVECTOR( CppAD::AD ) ax(1), ay(1); // // check that an empty function does not hold memory size_t inuse_1 = CppAD::thread_alloc::inuse(thread); CppAD::ADFun f; size_t inuse_2 = CppAD::thread_alloc::inuse(thread); ok &= inuse_1 == inuse_2; // // check that creating an adfun uses memory CppAD::Independent(ax); ay = ax; CppAD::ADFun g(ax, ay); size_t inuse_3 = CppAD::thread_alloc::inuse(thread); ok &= inuse_1 < inuse_3; // // assigning to an empty function uses assignment to pod_vectors // which just changes their length to zero g = f; size_t inuse_4 = CppAD::thread_alloc::inuse(thread); ok &= inuse_3 == inuse_4; // // assigning to a temporary empty function to g // uses move semantics (hence frees all memory in g) g = CppAD::ADFun(); size_t inuse_5 = CppAD::thread_alloc::inuse(thread); ok &= inuse_1 == inuse_5; // return ok; } bool adfun_swap(void) { bool ok = true; // // Independent variables CPPAD_TESTVECTOR( CppAD::AD ) ax(1); CppAD::Independent(ax); // // Dependent variables CPPAD_TESTVECTOR( CppAD::AD ) ay(2); ay[0] = ax[0]; ay[1] = ax[0]; // // Nonempty ADFun CppAD::ADFun f(ax, ay); ok &= f.size_var() != 0; // // Empry ADFun CppAD::ADFun g; ok &= g.size_var() == 0; // // swap f.swap(g); ok &= f.size_var() == 0; ok &= g.size_var() != 0; // return ok; } } // END_EMPTY_NAMESPACE bool adfun(void) { bool ok = true; ok &= adfun_empty(); ok &= adfun_swap(); return ok; } ================================================ FILE: test_more/general/alloc_openmp.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // BEGIN C++ # include # include # define NUMBER_THREADS 2 namespace { using CppAD::thread_alloc; // used to inform CppAD when we are in parallel execution mode bool in_parallel(void) { return static_cast( omp_in_parallel() ); } // used to inform CppAD of current thread number thread_number() size_t thread_number(void) { return static_cast( omp_get_thread_num() ); } // structure with information for one thread typedef struct { // function object (worker input) CppAD::vector x; } thread_one_t; // vector with information for all threads thread_one_t thread_all_[NUMBER_THREADS]; // -------------------------------------------------------------------- // function that does the work for one thread void worker(void) { size_t thread_num = thread_number(); thread_all_[thread_num].x.resize(1); thread_all_[thread_num].x[0]=static_cast(thread_num); } } // Test routine called by the master thread (thread_num = 0). bool alloc_openmp(void) { bool ok = true; int num_threads = NUMBER_THREADS; // call setup for using thread_alloc in parallel mode. thread_alloc::parallel_setup( size_t(num_threads), in_parallel, thread_number ); // Execute the worker function in parallel int thread_num; # pragma omp parallel for for(thread_num = 0; thread_num < num_threads; thread_num++) worker(); // end omp parallel for // now inform CppAD that there is only one thread thread_alloc::parallel_setup(1, nullptr, nullptr); for(thread_num = 0; thread_num < num_threads; thread_num++) { // check calculations by this thread in parallel model ok &= thread_all_[thread_num].x[0] == static_cast(thread_num); // test having master thread (thread number zero) // free memory that was allocated by thread number thread_num. thread_all_[thread_num].x.clear(); } return ok; } ================================================ FILE: test_more/general/asin.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old example now only used for validation testing. */ # include bool asin(void) { bool ok = true; using CppAD::asin; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector CPPAD_TESTVECTOR(AD) U(1); U[0] = .5; Independent(U); // a temporary values AD x = sin(U[0]); // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); Z[0] = asin(x); // asin( sin(u) ) // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v(1); CPPAD_TESTVECTOR(double) w(1); // check value ok &= NearEqual(U[0] , Z[0], eps99 , eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; double value = 1.; v[0] = 1.; for(j = 1; j < p; j++) { jfac *= double(j); w = f.Forward(j, v); ok &= NearEqual(w[0], value/jfac, eps99, eps99); // d^jz/du^j v[0] = 0.; value = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r(p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; value = 1.; for(j = 0; j < p; j++) { ok &= NearEqual(r[j], value/jfac, eps99, eps99); // d^jz/du^j jfac *= double(j + 1); value = 0.; } return ok; } ================================================ FILE: test_more/general/asinh.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include bool asinh(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; // 10 times machine epsilon double eps = 10. * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = x0; // declare independent variables and start tape recording CppAD::Independent(ax); // a temporary value AD sinh_of_x0 = CppAD::sinh(ax[0]); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = CppAD::asinh(sinh_of_x0); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // check value ok &= NearEqual(ay[0] , x0, eps, eps); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 1., eps, eps); // forward computation of higher order partials w.r.t. x[0] size_t n_order = 5; for(size_t order = 2; order < n_order; order++) { dx[0] = 0.; dy = f.Forward(order, dx); ok &= NearEqual(dy[0], 0., eps, eps); } // reverse computation of derivatives CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n_order * n); w[0] = 1.; dw = f.Reverse(n_order, w); ok &= NearEqual(dw[0], 1., eps, eps); for(size_t order = 1; order < n_order; order++) ok &= NearEqual(dw[order * n + 0], 0., eps, eps); return ok; } // END C++ ================================================ FILE: test_more/general/assign.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old example, now used just for validation testing */ # include bool assign(void) { bool ok = true; using CppAD::AD; // domain space vector size_t n = 3; CPPAD_TESTVECTOR(AD) x(n); x[0] = 2; // AD = int x[1] = 3.; // AD = double x[2] = x[1]; // AD = AD // declare independent variables and start tape recording CppAD::Independent(x); // range space vector size_t m = 3; CPPAD_TESTVECTOR(AD) y(m); // assign an AD object equal to an independent variable // (choose the first independent variable to check a special case) // use the value returned by the assignment (for another assignment) y[0] = y[1] = x[0]; // assign an AD object equal to an expression y[1] = x[1] + 1.; y[2] = x[2] + 2.; // check that all the resulting components of y depend on x ok &= Variable(y[0]); // y[0] = x[0] ok &= Variable(y[1]); // y[1] = x[1] + 1 ok &= Variable(y[2]); // y[2] = x[2] + 2 // construct f : x -> y and stop the tape recording CppAD::ADFun f(x, y); // check variable values ok &= ( y[0] == 2.); ok &= ( y[1] == 4.); ok &= ( y[2] == 5.); // compute partials w.r.t x[1] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 0.; dx[1] = 1.; dx[2] = 0.; dy = f.Forward(1, dx); ok &= (dy[0] == 0.); // dy[0] / dx[1] ok &= (dy[1] == 1.); // dy[1] / dx[1] ok &= (dy[2] == 0.); // dy[2] / dx[1] // compute the derivative y[2] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 0.; w[1] = 0.; w[2] = 1.; dw = f.Reverse(1, w); ok &= (dw[0] == 0.); // dy[2] / dx[0] ok &= (dw[1] == 0.); // dy[2] / dx[1] ok &= (dw[2] == 1.); // dy[2] / dx[2] // assign a VecAD::reference CppAD::VecAD v(1); AD zero(0); v[zero] = 5.; ok &= (v[0] == 5.); return ok; } ================================================ FILE: test_more/general/atan.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Two old atan examples now used just for validation testing. */ # include namespace { // BEGIN empty namespace bool AtanTestOne(void) { bool ok = true; using CppAD::atan; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector, indices, values, and declaration CPPAD_TESTVECTOR(AD) U(1); size_t s = 0; U[s] = 1.; Independent(U); // some temporary values AD x = cos(U[s]); AD y = sin(U[s]); AD z = y / x; // tan(s) // dependent variable vector and indices CPPAD_TESTVECTOR(AD) Z(1); size_t a = 0; // dependent variable values Z[a] = atan(z); // atan( tan(s) ) // create f: U -> Z and vectors used for dierivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v( f.Domain() ); CPPAD_TESTVECTOR(double) w( f.Range() ); // check value ok &= NearEqual(U[s] , Z[a], eps99 , eps99); // forward computation of partials w.r.t. s v[s] = 1.; w = f.Forward(1, v); ok &= NearEqual(w[a], 1e0, eps99 , eps99); // da/ds // reverse computation of first order partial of a w[a] = 1.; v = f.Reverse(1, w); ok &= NearEqual(v[s], 1e0, eps99 , eps99); // da/ds // forward computation of second partials w.r.t. s and s v[s] = 1.; f.Forward(1, v); v[s] = 0.; w = f.Forward(2, v); ok &= NearEqual(2. * w[a], 0e0, eps99 , eps99); // d^2 a / (ds ds) // reverse computation of second partials of a CPPAD_TESTVECTOR(double) r( f.Domain() * 2 ); w[a] = 1.; r = f.Reverse(2, w); ok &= NearEqual(r[2 * s + 1] ,0e0, eps99 , eps99 ); // d^2 a / (ds ds) return ok; } bool AtanTestTwo(void) { bool ok = true; using CppAD::atan; using CppAD::sin; using CppAD::cos; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector CPPAD_TESTVECTOR(AD) U(1); U[0] = 1.; Independent(U); // a temporary values AD x = sin(U[0]) / cos(U[0]); // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); Z[0] = atan(x); // atan( tan(u) ) // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v(1); CPPAD_TESTVECTOR(double) w(1); // check value ok &= NearEqual(U[0] , Z[0], eps99 , eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; double value = 1.; v[0] = 1.; for(j = 1; j < p; j++) { jfac *= double(j); w = f.Forward(j, v); ok &= NearEqual(w[0], value/jfac, eps99, eps99);// d^jz/du^j v[0] = 0.; value = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r(p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; value = 1.; for(j = 0; j < p; j++) { ok &= NearEqual(r[j], value/jfac, eps99, eps99);// d^jz/du^j jfac *= double(j + 1); value = 0.; } return ok; } } // END empty namespace bool atan(void) { bool ok = true; ok &= AtanTestOne(); ok &= AtanTestTwo(); return ok; } ================================================ FILE: test_more/general/atan2.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old example and test now just used for validation testing. */ # include # include namespace { // begin empty namespace bool ad_ad(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); AD< AD > x(2.), y(2.); AD< AD > z = atan2(y, x); NearEqual( Value( Value(z) ), atan(1.), eps99, eps99); return ok; } bool general(void) { bool ok = true; using CppAD::atan; using CppAD::sin; using CppAD::cos; using namespace CppAD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector CPPAD_TESTVECTOR(AD) U(1); U[0] = 1.; Independent(U); // a temporary values AD x = cos(U[0]); AD y = sin(U[0]); // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); Z[0] = atan2(y, x); // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v(1); CPPAD_TESTVECTOR(double) w(1); // check original value (u in first quadrant) ok &= NearEqual(U[0] , Z[0], eps99, eps99); // check case where u is in second quadrant v[0] = 3.; w = f.Forward(0, v); ok &= NearEqual(w[0] , v[0], eps99, eps99); // check case where u is in third quadrant v[0] = -3.; w = f.Forward(0, v); ok &= NearEqual(w[0] , v[0], eps99, eps99); // check case where u is in fourth quadrant v[0] = -1.; w = f.Forward(0, v); ok &= NearEqual(w[0] , v[0], eps99, eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; double value = 1.; v[0] = 1.; for(j = 1; j < p; j++) { jfac *= double(j); w = f.Forward(j, v); ok &= NearEqual(w[0], value/jfac, eps99, eps99); // d^jz/du^j v[0] = 0.; value = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r(p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; value = 1.; for(j = 0; j < p; j++) { ok &= NearEqual(r[j], value/jfac, eps99, eps99); // d^jz/du^j jfac *= double(j + 1); value = 0.; } return ok; } } // end empty namespace bool atan2(void) { bool ok = true; ok &= ad_ad(); ok &= general(); return ok; } ================================================ FILE: test_more/general/atanh.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include bool atanh(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; // 10 times machine epsilon double eps = 10. * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = x0; // declare independent variables and start tape recording CppAD::Independent(ax); // a temporary value AD tanh_of_x0 = CppAD::tanh(ax[0]); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = CppAD::atanh(tanh_of_x0); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // check value ok &= NearEqual(ay[0] , x0, eps, eps); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 1., eps, eps); // forward computation of higher order partials w.r.t. x[0] size_t n_order = 5; for(size_t order = 2; order < n_order; order++) { dx[0] = 0.; dy = f.Forward(order, dx); ok &= NearEqual(dy[0], 0., eps, eps); } // reverse computation of derivatives CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n_order * n); w[0] = 1.; dw = f.Reverse(n_order, w); ok &= NearEqual(dw[0], 1., eps, eps); for(size_t order = 1; order < n_order; order++) ok &= NearEqual(dw[order * n + 0], 0., eps, eps); return ok; } // END C++ ================================================ FILE: test_more/general/atomic_four.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include namespace { // BEGIN_EMPTY_NAMESPACE // atomic_norm_sq class atomic_norm_sq : public CppAD::atomic_four { public: atomic_norm_sq(const std::string& name) : CppAD::atomic_four(name) { } // END CONSTRUCTOR private: // BEGIN FOR_TYPE bool for_type( size_t call_id , const CppAD::vector& type_x , CppAD::vector& type_y ) override { assert( call_id == 0 ); // default value assert(type_y.size() == 1 ); // m // // type_y size_t n = type_x.size(); type_y[0] = CppAD::constant_enum; for(size_t j = 0; j < n; ++j) type_y[0] = std::max(type_y[0], type_x[j]); return true; } // END FOR_TYPE // BEGIN FORWARD bool forward( size_t call_id , const CppAD::vector& select_y , size_t order_low , size_t order_up , const CppAD::vector& tx , CppAD::vector& ty ) override { size_t q = order_up + 1; size_t n = tx.size() / q; # ifndef NDEBUG size_t m = ty.size() / q; assert( call_id == 0 ); assert( m == 1 ); assert( m == select_y.size() ); # endif // ok bool ok = order_up <= 1 && order_low <= order_up; if ( ! ok ) return ok; // // sum = x_0^0 * x_0^0 + x_1^0 * x_1^0 + ... double sum = 0.0; for(size_t j = 0; j < n; ++j) { double xj0 = tx[ j * q + 0]; sum += xj0 * xj0; } // // ty[0] = sum if( order_low <= 0 ) ty[0] = sum; if( order_up < 1 ) return ok; // sum = x_0^0 * x_0^1 + x_1^0 ^ x_1^1 + ... sum = 0.0; for(size_t j = 0; j < n; ++j) { double xj0 = tx[ j * q + 0]; double xj1 = tx[ j * q + 1]; sum += xj0 * xj1; } // ty[1] = 2.0 * sum assert( order_up == 1 ); ty[1] = 2.0 * sum; return ok; } }; // forward_dir bool forward_dir(void) { // ok, eps bool ok = true; double eps = 10. * CppAD::numeric_limits::epsilon(); // // atom_norm_sq atomic_norm_sq afun("atomic_norm_sq"); // // n, m size_t n = 2; size_t m = 1; // // x CPPAD_TESTVECTOR(double) x(n); for(size_t j = 0; j < n; ++j) x[j] = 1.0 / (double(j) + 1.0); // // ax CPPAD_TESTVECTOR( CppAD::AD ) ax(n); for(size_t j = 0; j < n; ++j) ax[j] = x[j]; CppAD::Independent(ax); // // ay CPPAD_TESTVECTOR( CppAD::AD ) ay(m); afun(ax, ay); // // f CppAD::ADFun f; f.Dependent (ax, ay); // // check double check = 0.0; for(size_t j = 0; j < n; ++j) check += x[j] * x[j]; // // ok // check ay[0] ok &= CppAD::NearEqual( Value(ay[0]) , check, eps, eps); // // ok // check zero order forward mode CPPAD_TESTVECTOR(double) y(m); y = f.Forward(0, x); ok &= CppAD::NearEqual(y[0] , check, eps, eps); // // y1 // first order forward mode partial w.r.t. each component of x size_t r = n; CPPAD_TESTVECTOR(double) x1(n * r), y1(m * r); for(size_t j = 0; j < n; ++j) { for(size_t ell = 0; ell < r; ++ell) { x1[j * r + ell] = 0.0; if( ell == j ) x1[j * r + ell] = 1.0; } } y1 = f.Forward(1, r, x1); // // ok for(size_t j = 0; j < n; ++j) ok &= CppAD::NearEqual(y1[j] , 2.0 * x[j], eps, eps); // return ok; } /* \{xrst_begin atomic_four_lin_ode_rev_depend.cpp} {xrst_spell cccc } Atomic Linear ODE Reverse Dependency Analysis: Example and Test ############################################################### Purpose ******* This example demonstrates calculating reverse dependency with the :ref:`atomic_four_lin_ode-name` class; see :ref:`atomic_four_lin_ode_rev_depend.hpp-name` . f(x) **** For this example, the function :math:`f(x) = z_2 (r, u)` where :math:`z(t, u)` solves the following ODE .. math:: z_t (t, x) = \left( \begin{array}{cccc} 0 & 0 & 0 & 0 \\ x_0 & 0 & 0 & 0 \\ 0 & x_1 & 0 & 0 \\ 0 & 0 & x_2 & 0 \\ \end{array} \right) z(t, u) \W{,} z(0, u) = \left( \begin{array}{c} x_3 \\ x_4 \\ x_5 \\ x_6 \\ \end{array} \right) Solution ******** The actual solution to this ODE is .. math:: z(t, x) = \left( \begin{array}{l} x_3 \\ x_4 + x_0 x_3 t \\ x_5 + x_1 x_4 t + x_1 x_0 x_3 t^2 / 2 \\ x_6 + x_2 x_5 t + x_2 x_1 x_4 t^2 / 2 + x_2 x_1 x_0 x_3 t^3 / 6 \end{array} \right) Source ****** {xrst_literal // BEGIN C++ // END C++ } \{xrst_end atomic_four_lin_ode_rev_depend.cpp} */ template Vector Y(Scalar t, const Vector& x) { size_t m = 4; Vector y(m); // y[0] = x[3]; y[1] = x[4] + x[0]*x[3]*t; y[2] = x[5] + x[1]*x[4]*t + x[1]*x[0]*x[3]*t*t/2.0; y[3] = x[6] + x[2]*x[5]*t + x[2]*x[1]*x[4]*t*t/2.0 + x[2]*x[1]*x[0]*x[3]*t*t*t/6.0; // return y; } bool rev_depend(void) { // ok, eps bool ok = true; // // sparse_rc, AD, eps99 typedef CppAD::sparse_rc< CppAD::vector > sparse_rc; using CppAD::AD; double eps99 = std::numeric_limits::epsilon() * 99.0; // ----------------------------------------------------------------------- // Record f // ----------------------------------------------------------------------- // // afun CppAD::atomic_lin_ode afun("atomic_lin_ode"); // // m, r size_t m = 4; double r = 2.0; double step = 1.0; // // pattern, transpose size_t nr = m; size_t nc = m; size_t nnz = 3; sparse_rc pattern(nr, nc, nnz); for(size_t k = 0; k < nnz; ++k) { size_t i = k + 1; size_t j = k; pattern.set(k, i, j); } bool transpose = false; // // ax CPPAD_TESTVECTOR( AD ) ax(nnz + m); for(size_t k = 0; k < nnz + m; ++k) ax[k] = double(k + 1); CppAD::Independent(ax); // // ay CPPAD_TESTVECTOR( AD ) ay(m); size_t call_id = afun.set(r, step, pattern, transpose); afun(call_id, ax, ay); // // z_index size_t z_index = 1; // // az CPPAD_TESTVECTOR( AD ) az(1); az[0] = ay[z_index]; // // f // optimize uses rev_depend CppAD::ADFun f(ax, az); f.optimize(); // ----------------------------------------------------------------------- // check_f // ----------------------------------------------------------------------- CppAD::Independent(ax); AD ar = r; ay = Y(ar, ax); az[0] = ay[z_index]; CppAD::ADFun check_f(ax, az); // ----------------------------------------------------------------------- // rev_depend // use test_rev_depend to call rev_depend directly // ----------------------------------------------------------------------- // // depend_x CppAD::vector ident_zero_x(nnz + m), depend_x(nnz + m), depend_y(m); for(size_t i = 0; i < m; ++i) { depend_y[i] = i == z_index; ident_zero_x[i] = false; } afun.test_rev_depend(call_id, ident_zero_x, depend_x, depend_y); // // x CPPAD_TESTVECTOR(double) x(nnz + m); for(size_t j = 0; j < nnz + m; ++j) x[j] = double( j + 2 ); // // dw check_f.Forward(0, x); CPPAD_TESTVECTOR(double) w(1), dw(nnz + m); w[0] = 1.0; dw = check_f.Reverse(1, w); // // ok // note that for this x, partial w.r.t x[j] is non-zero if and only if // y[z_index] depends on x[j] for(size_t j = 0; j < nnz + m; ++j) ok &= depend_x[j] == (dw[j] != 0.0); // // ----------------------------------------------------------------------- // forward mode on f // Check that the optimized version of agrees with check_f. // ----------------------------------------------------------------------- // // z // zero order forward mode computation of f(x) CPPAD_TESTVECTOR(double) z = f.Forward(0, x); // // ok CPPAD_TESTVECTOR(double) check_z = check_f.Forward(0, x); ok &= CppAD::NearEqual(z[0], check_z[0], eps99, eps99); // // du, ok CPPAD_TESTVECTOR(double) dx(nnz + m), dz(1), check_dz(1); for(size_t j = 0; j < nnz + m; ++j) dx[j] = 0.0; // for(size_t j = 0; j < nnz + m; ++j) { dx[j] = 1.0; dz = f.Forward(1, dx); check_dz = check_f.Forward(1, dx); ok &= CppAD::NearEqual(dz[0], check_dz[0], eps99, eps99); dx[j] = 0.0; } // ----------------------------------------------------------------------- return ok; } } // END_EMPTY_NAMESPACE bool atomic_four(void) { bool ok = true; ok &= forward_dir(); ok &= rev_depend(); return ok; } ================================================ FILE: test_more/general/atomic_three.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* g_0 (x) = x_0 * x_0 g_1 (x) = x_0 * x_1 g_2 (x) = x_1 * x_2 g_3 (x) = x_2 * x_2 */ # include // CppAD include file namespace { // start empty namespace using CppAD::vector; // abbreviate CppAD::vector using vector // ============================================================================ // Testing dynamic parameters in atomic_three functions. // ============================================================================ class dynamic_optimize : public CppAD::atomic_three { public: // can use const char* name when calling this constructor dynamic_optimize(const std::string& name) : // can have more arguments CppAD::atomic_three(name) // inform base class of name { } private: // calculate type_y virtual bool for_type( const vector& parameter_x , const vector& type_x , vector& type_y ) { assert( parameter_x.size() == type_x.size() ); bool ok = type_x.size() == 3; // n ok &= type_y.size() == 4; // m if( ! ok ) return false; type_y[0] = type_x[0]; type_y[1] = std::max( type_x[0], type_x[1] ); type_y[2] = std::max( type_x[1], type_x[2] ); type_y[3] = type_x[2]; return true; } // calculate depend_x virtual bool rev_depend( const vector& parameter_x , const vector& type_x , vector& depend_x , const vector& depend_y ) { assert( parameter_x.size() == depend_x.size() ); bool ok = depend_x.size() == 3; // n ok &= depend_y.size() == 4; // m if( ! ok ) return false; depend_x[0] = depend_y[0] || depend_y[1]; depend_x[1] = depend_y[1] || depend_y[2]; depend_x[2] = depend_y[2] || depend_y[3]; return true; } virtual bool forward( const vector& parameter_x , const vector& type_x , size_t need_y , size_t order_low , size_t order_up , const vector& taylor_x , vector& taylor_y ) { # ifndef NDEBUG size_t n = taylor_x.size() / (order_up + 1); size_t m = taylor_y.size() / (order_up + 1); # endif assert( n == 3 ); assert( m == 4 ); assert( order_low <= order_up ); // return flag bool ok = order_up == 0; if( ! ok ) return ok; // g_0 = x_0 * x_0 taylor_y[0] = taylor_x[0] * taylor_x[0]; // g_1 = x_0 * x_1 taylor_y[1] = taylor_x[0] * taylor_x[1]; // g_2 = x_1 * x_2 taylor_y[2] = taylor_x[1] * taylor_x[2]; // g_3 = x_2 * x_2 taylor_y[3] = taylor_x[2] * taylor_x[2]; return ok; } }; // End of dynamic_optimize class // --------------------------------------------------------------------------- bool optimize_dynamic_one(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); dynamic_optimize afun("dynamic_optimize"); // // constant parameter double c_0 = 2.0; // // independent dynamic parameter vector size_t np = 1; CPPAD_TESTVECTOR(double) p(np); CPPAD_TESTVECTOR( AD ) ap(np); ap[0] = p[0] = 3.0; // // independent variable vector size_t nu = 1; double u_0 = 0.5; CPPAD_TESTVECTOR( AD ) au(nu); au[0] = u_0; // declare independent variables and start tape recording CppAD::Independent(au, ap); // create a dynamic parameter that is not used AD ar = 2.0 * ap[0]; // call atomic function and store result in ay CPPAD_TESTVECTOR( AD ) ax(3), av(4); ax[0] = c_0; // x_0 = c ax[1] = ap[0]; // x_1 = p ax[2] = au[0]; // x_2 = u afun(ax, av); // check type of result ok &= Constant( av[0] ); // v_0 = c * c ok &= Dynamic( av[1] ); // v_1 = c * p ok &= Variable( av[2] ); // v_2 = p * u ok &= Variable( av[3] ); // v_3 = u * u // range space vector size_t ny = 3; CPPAD_TESTVECTOR( AD ) ay(ny); for(size_t i = 0; i < ny; ++i) ay[i] = av[i]; // create f: u -> y and stop tape recording CppAD::ADFun f; f.Dependent (au, ay); // f(u) = (c * c, c * p, p * u) // sequence properties ok &= f.size_dyn_ind() == 1; // p ok &= f.size_dyn_par() == 3; // p, r, c * p // Three constant parameters: nan, c, c * c ok &= f.size_par() == 6; // Normal variables: u, p * u, u * u // Extra variables: phanton at index 0, y[0], y[1] ok &= f.size_var() == 6; // optimize f.optimize(); // sequence properties ok &= f.size_dyn_ind() == 1; // p ok &= f.size_dyn_par() == 2; // c * p // Four constant parameters: nan, zero, c, c * c ok &= f.size_par() == 6; // Normal variables: u, p * u // Extra variables: phanton at index 0, y[0], y[1] ok &= f.size_var() == 5; // check double check; // check zero order forward mode size_t q; CPPAD_TESTVECTOR( double ) u_q(nu), y_q(ny); q = 0; u_q[0] = u_0; y_q = f.Forward(q, u_q); check = c_0 * c_0; ok &= NearEqual(y_q[0] , check, eps, eps); check = c_0 * p[0]; ok &= NearEqual(y_q[1] , check, eps, eps); check = p[0] * u_0; ok &= NearEqual(y_q[2] , check, eps, eps); // set new value for dynamic parameters p[0] = 2.0 * p[0]; f.new_dynamic(p); y_q = f.Forward(q, u_q); check = c_0 * c_0; ok &= NearEqual(y_q[0] , check, eps, eps); check = c_0 * p[0]; ok &= NearEqual(y_q[1] , check, eps, eps); check = p[0] * u_0; ok &= NearEqual(y_q[2] , check, eps, eps); return ok; } // --------------------------------------------------------------------------- bool optimize_dynamic_two(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); dynamic_optimize afun("dynamic_optimize"); // // independent dynamic parameter vector size_t np = 1; CPPAD_TESTVECTOR(double) p(np); CPPAD_TESTVECTOR( AD ) ap(np); ap[0] = p[0] = 3.0; // // independent variable vector size_t nu = 1; double u_0 = 0.5; CPPAD_TESTVECTOR( AD ) au(nu); au[0] = u_0; // declare independent variables and start tape recording CppAD::Independent(au, ap); // create a dynamic parameter that is used by atomic function // but not needed to compute f(u) AD ar = 2.0 * ap[0]; // call atomic function and store result in ay CPPAD_TESTVECTOR( AD ) ax(3), av(4); ax[0] = au[0]; // x_0 = u ax[1] = ap[0]; // x_1 = p ax[2] = ar; // x_2 = r afun(ax, av); // check type of result ok &= Variable( av[0] ); // v_0 = u * u , used ok &= Variable( av[1] ); // v_1 = u * p , used ok &= Dynamic( av[2] ); // v_2 = r * p , not used ok &= Dynamic( av[3] ); // v_3 = r * r , not used // range space vector size_t ny = 2; CPPAD_TESTVECTOR( AD ) ay(ny); for(size_t i = 0; i < ny; ++i) ay[i] = av[i]; // create f: u -> y and stop tape recording CppAD::ADFun f; f.Dependent (au, ay); // f(u) = (u * u, u * p) // sequence properties ok &= f.size_dyn_ind() == 1; // p ok &= f.size_dyn_par() == 4; // p, r, r * p, r * r // Two constant parameters: nan, 2.0 in computation of r ok &= f.size_par() == 6; // optimize f.optimize(); // sequence properties ok &= f.size_dyn_ind() == 1; // p ok &= f.size_dyn_par() == 1; // p // Two constant parameter: nan, zero ok &= f.size_par() == 3; // check double check; // check zero order forward mode size_t q; CPPAD_TESTVECTOR( double ) u_q(nu), y_q(ny); q = 0; u_q[0] = u_0; y_q = f.Forward(q, u_q); check = u_0 * u_0; ok &= NearEqual(y_q[0] , check, eps, eps); check = u_0 * p[0]; ok &= NearEqual(y_q[1] , check, eps, eps); // set new value for dynamic parameters p[0] = 2.0 * p[0]; f.new_dynamic(p); y_q = f.Forward(q, u_q); check = u_0 * u_0; ok &= NearEqual(y_q[0] , check, eps, eps); check = u_0 * p[0]; ok &= NearEqual(y_q[1] , check, eps, eps); return ok; } // --------------------------------------------------------------------------- bool optimize_dynamic_three(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); dynamic_optimize afun("dynamic_optimize"); // // independent dynamic parameter vector size_t np = 1; CPPAD_TESTVECTOR(double) p(np); CPPAD_TESTVECTOR( AD ) ap(np); ap[0] = p[0] = 3.0; // // independent variable vector size_t nu = 1; double u_0 = 0.5; CPPAD_TESTVECTOR( AD ) au(nu); au[0] = u_0; // declare independent variables and start tape recording CppAD::Independent(au, ap); // create a dynamic parameter that is used by atomic function // but not needed to compute f(u) AD ar = 2.0 * ap[0]; // call atomic function and store result in ay CPPAD_TESTVECTOR( AD ) ax(3), av(4); ax[0] = au[0]; // x_0 = u ax[1] = ar; // x_1 = r ax[2] = ap[0]; // x_2 = p afun(ax, av); // check type of result ok &= Variable( av[0] ); // v_0 = u * u , used ok &= Variable( av[1] ); // v_1 = u * r , used ok &= Dynamic( av[2] ); // v_2 = r * p , not used ok &= Dynamic( av[3] ); // v_3 = p * p , not used // range space vector size_t ny = 2; CPPAD_TESTVECTOR( AD ) ay(ny); for(size_t i = 0; i < ny; ++i) ay[i] = av[i]; // create f: u -> y and stop tape recording CppAD::ADFun f; f.Dependent (au, ay); // f(u) = (u * u, u * p) // sequence properties ok &= f.size_dyn_ind() == 1; // p ok &= f.size_dyn_par() == 4; // p, r, r * p, p * p // Two constant parameters: nan, 2.0 in computation of r ok &= f.size_par() == 6; // optimize f.optimize(); // sequence properties ok &= f.size_dyn_ind() == 1; // p ok &= f.size_dyn_par() == 2; // p, r // Three constant parameters: nan, zero, 2.0 in computation of r ok &= f.size_par() == 5; // check double check; // check zero order forward mode double r = 2.0 * p[0]; size_t q; CPPAD_TESTVECTOR( double ) u_q(nu), y_q(ny); q = 0; u_q[0] = u_0; y_q = f.Forward(q, u_q); check = u_0 * u_0; ok &= NearEqual(y_q[0] , check, eps, eps); check = u_0 * r; ok &= NearEqual(y_q[1] , check, eps, eps); // set new value for dynamic parameters p[0] = 2.0 * p[0]; r = 2.0 * p[0]; f.new_dynamic(p); y_q = f.Forward(q, u_q); check = u_0 * u_0; ok &= NearEqual(y_q[0] , check, eps, eps); check = u_0 * r; ok &= NearEqual(y_q[1] , check, eps, eps); return ok; } // ============================================================================ // Testing Variables that get removed // ============================================================================ class variable_optimize : public CppAD::atomic_three { public: // can use const char* name when calling this constructor variable_optimize(const std::string& name) : // can have more arguments CppAD::atomic_three(name) // inform base class of name { } private: // calculate type_y virtual bool for_type( const vector& parameter_x , const vector& type_x , vector& type_y ) { assert( parameter_x.size() == type_x.size() ); bool ok = type_x.size() == 2; // n ok &= type_y.size() == 2; // m if( ! ok ) return false; type_y[0] = type_x[0]; type_y[1] = std::max( type_x[0], type_x[1] ); return true; } // calculate depend_x virtual bool rev_depend( const vector& parameter_x , const vector& type_x , vector& depend_x , const vector& depend_y ) { assert( parameter_x.size() == depend_x.size() ); bool ok = depend_x.size() == 2; // n ok &= depend_y.size() == 2; // m if( ! ok ) return false; depend_x[0] = depend_y[0] || depend_y[1]; depend_x[1] = depend_y[1]; return true; } virtual bool forward( const vector& parameter_x , const vector& type_x , size_t need_y , size_t order_low , size_t order_up , const vector& taylor_x , vector& taylor_y ) { # ifndef NDEBUG size_t n = taylor_x.size() / (order_up + 1); size_t m = taylor_y.size() / (order_up + 1); # endif assert( n == 2 ); assert( m == 2 ); assert( order_low <= order_up ); // return flag bool ok = order_up == 0; if( ! ok ) return ok; // g_0 = exp( x_0 ) taylor_y[0] = std::exp( taylor_x[0] ); // g_1 = exp( x_0 * x_1 ) taylor_y[1] = std::exp( taylor_x[0] * taylor_x[1] ); return ok; } virtual bool reverse( const vector& parameter_x , const vector& type_x , size_t order_up , const vector& taylor_x , const vector& taylor_y , vector& partial_x , const vector& partial_y ) { size_t q1 = order_up + 1; size_t n = taylor_x.size() / q1; # ifndef NDEBUG size_t m = taylor_y.size() / q1; # endif assert( n == 2 ); assert( m == 2 ); // return flag bool ok = order_up == 0; if( ! ok ) return ok; // initialize summation as zero for(size_t j = 0; j < n; ++j) partial_x[j] = 0.0; // g_0 = exp( x_0 ) partial_x[0] += partial_y[0] * taylor_y[0]; // g_1 = exp( x_0 * x_1 ) partial_x[0] += partial_y[1] * taylor_y[1] * taylor_x[1]; partial_x[1] += partial_y[1] * taylor_y[1] * taylor_x[0]; // return ok; } }; // End of variable_optimize class // --------------------------------------------------------------------------- bool optimize_variable_one(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * CppAD::numeric_limits::epsilon(); variable_optimize afun("variable_optimize"); // // independent variable vector size_t nu = 2; CPPAD_TESTVECTOR( AD ) au(nu); for(size_t j = 0; j < nu; ++j) au[j] = double(j + 1); // declare independent variables and start tape recording CppAD::Independent(au); // call atomic function and store result in ay CPPAD_TESTVECTOR( AD ) ax(nu), av(nu); for(size_t j = 0; j < nu; ++j) ax[j] = au[j] / 2.0; // x = u / 2 afun(ax, av); // check type of result for(size_t j = 0; j < nu; ++j) ok &= Variable( av[j] ); // range space vector size_t ny = 1; CPPAD_TESTVECTOR( AD ) ay(ny); // // only the first component of av affects the function value ay[0] = av[0]; // create f: u -> y and stop tape recording CppAD::ADFun f; f.Dependent (au, ay); // f(u) = exp( u[0] / 2 ) // optimize f.optimize(); // check double check; // check zero order forward mode CPPAD_TESTVECTOR( double ) u(nu), y(ny); for(size_t j = 0; j < nu; ++j) u[j] = double(j + 1) / double(nu); y = f.Forward(0, u); check = std::exp( u[0] / 2.0 ); ok &= NearEqual(y[0] , check, eps, eps); // Check first order reverse mode. This test would vaile when // nan was used for argument and function values that were optimized out // because they were not used. CPPAD_TESTVECTOR( double ) w(ny), dw(nu); w[0] = 1.0; dw = f.Reverse(1, w); check = std::exp( u[0] / 2.0 ) / 2.0; ok &= NearEqual(dw[0] , check, eps, eps); check = 0.0; ok &= NearEqual(dw[1] , check, eps, eps); // return ok; } } // End empty namespace bool atomic_three(void) { bool ok = true; ok &= optimize_dynamic_one(); ok &= optimize_dynamic_two(); ok &= optimize_dynamic_three(); ok &= optimize_variable_one(); return ok; } ================================================ FILE: test_more/general/azmul.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include namespace { bool test_base2ad(void) { bool ok = true; double eps99 = 99.0 * std::numeric_limits::epsilon(); using CppAD::AD; // Both recordiings are done with the dynamic parameter p = 0, 1 // to make sure does not short circuit multiply for(size_t ip = 0; ip < 2; ++ip) { // f(p; x) = p[0] * x[0] * x[0] CPPAD_TESTVECTOR(AD) ap(1), ax(1), ay(1), aw(1); ap[0] = double(ip); ax[0] = 0.0; CppAD::Independent(ax, ap); ay[0] = ap[0] * ax[0] * ax[0]; CppAD::ADFun f(ax, ay); // AD version of f CppAD::ADFun< AD , double > af = f.base2ad(); // g(p; x) = d/dx f(p, x) = 2 * p[0] * x[0] CppAD::Independent(ax, ap); af.new_dynamic(ap); af.Forward(0, ax); aw[0] = 1.0; ay = af.Reverse(1, aw); CppAD::ADFun g(ax, ay); // Evaluate g(p, x) CPPAD_TESTVECTOR(double) p(1), x(1), y(1); p[0] = 2.0; x[0] = 3.0; g.new_dynamic(p); y = g.Forward(0, x); // check result double check = 2.0 * p[0] * x[0]; ok &= CppAD::NearEqual(y[0], check, eps99, eps99); } return ok; } bool test_forward(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double inf = std::numeric_limits::infinity(); double eps = 10. * std::numeric_limits::epsilon(); typedef AD a1double; typedef AD a2double; // domain space vector size_t n = 2; size_t m = 3; // double level CPPAD_TESTVECTOR(double) x(n), z(m); x[0] = 3.0; x[1] = 4.0; // start a1 level recording CPPAD_TESTVECTOR(a1double) a1x(n), a1dx(n), a1y(m), a1z(m); for(size_t j = 0; j < n; j++) a1x[j] = x[j]; CppAD::Independent(a1x); // start a2 level recording CPPAD_TESTVECTOR(a2double) a2x(n), a2y(m); for(size_t j = 0; j < n; j++) a2x[j] = a1x[j]; CppAD::Independent(a2x); // y a2y[0] = CppAD::azmul(a2x[0], a2x[1]); // azmul(variable, variable) a2y[1] = CppAD::azmul(a1x[0], a2x[1]); // azmul(parameter, variable) a2y[2] = CppAD::azmul(a2x[0], a1x[1]); // azmul(variable, parameter) // create f: x -> y and stop a2 recording CppAD::ADFun a1f; a1f.Dependent(a2x, a2y); // check y a1y = a1f.Forward(0, a1x); // azmul(variable, variable) only for(size_t i = 0; i < m; i++) ok &= NearEqual(a1y[i] , x[0] * x[1], eps, eps); for(size_t j = 0; j < n; j++) a1dx[j] = a1double(1.0); a1z = a1f.Forward(1, a1dx); // create g: x -> z and stop a1 recording CppAD::ADFun g; g.Dependent(a1x, a1z); // check value when x[0] is not zero z = g.Forward(0, x); // z_0 = d_lambda [ ( x[0] + lambda ) * ( x[1] + lambda ) ] ok &= NearEqual(z[0] , x[0] + x[1], eps, eps); // z_1 = d_lambda [ x[0] * ( x[1] + lambda ) ] ok &= NearEqual(z[1] , x[0], eps, eps); // z_2 = d_lambda [ ( x[0] + lambda ) * x[1] ] ok &= NearEqual(z[2] , x[1], eps, eps); // check value x[0] is zero and x[1] is infinity x[0] = 0.0; x[1] = inf; z = g.Forward(0, x); ok &= z[0] == inf; ok &= z[1] == 0.0; ok &= z[2] == inf; return ok; } bool test_reverse(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double inf = std::numeric_limits::infinity(); double eps = 10. * std::numeric_limits::epsilon(); typedef AD a1double; typedef AD a2double; // domain space vector size_t n = 2; size_t m = 3; // double level CPPAD_TESTVECTOR(double) x(n), z(n); x[0] = 3.0; x[1] = 4.0; // start a1 level recording CPPAD_TESTVECTOR(a1double) a1x(n), a1dx(n), a1w(m), a1z(n); for(size_t j = 0; j < n; j++) a1x[j] = x[j]; CppAD::Independent(a1x); // start a2 level recording CPPAD_TESTVECTOR(a2double) a2x(n), a2y(m); for(size_t j = 0; j < n; j++) a2x[j] = a1x[j]; CppAD::Independent(a2x); // y a2y[0] = CppAD::azmul(a2x[0], a2x[1]); // azmul(variable, variable) a2y[1] = CppAD::azmul(a1x[0], a2x[1]); // azmul(parameter, variable) a2y[2] = CppAD::azmul(a2x[0], a1x[1]); // azmul(variable, parameter) // create f: x -> y and stop a2 recording CppAD::ADFun a1f; a1f.Dependent(a2x, a2y); // w(x) = y[0] + y[1] + y[2] for(size_t i = 0; i < m; i++) a1w[i] = a1double(1.0); a1f.Forward(0, a1x); a1dx = a1f.Reverse(1, a1w); // create g: x -> z and stop a1 recording CppAD::ADFun g; g.Dependent(a1x, a1dx); // check value when x[0] is not zero z = g.Forward(0, x); // partial y[0] w.r.t x[0] = x[1] // partial y[1] w.r.t x[0] = 0 // partial y[2] w.r.t x[0] = x[1] ok &= NearEqual(z[0] , x[1] + x[1], eps, eps); // partial y[0] w.r.t x[1] = x[0] // partial y[1] w.r.t x[1] = x[0] // partial y[2] w.r.t x[1] = 0 ok &= NearEqual(z[1] , x[0] + x[0], eps, eps); // check value x[0] is zero and x[1] is infinity x[0] = 0.0; x[1] = inf; z = g.Forward(0, x); ok &= z[0] == inf; ok &= z[1] == 0.0; return ok; } bool test_forward_dir(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double inf = std::numeric_limits::infinity(); double eps = 10. * std::numeric_limits::epsilon(); typedef AD a1double; typedef AD a2double; // domain space vector size_t n = 2; size_t m = 3; size_t r = 2; // double level CPPAD_TESTVECTOR(double) x(n), z(r * m); x[0] = 3.0; x[1] = 4.0; // start a1 level recording CPPAD_TESTVECTOR(a1double) a1x(n), a1dx(r * n), a1y(m), a1z(r * m); for(size_t j = 0; j < n; j++) a1x[j] = x[j]; CppAD::Independent(a1x); // start a2 level recording CPPAD_TESTVECTOR(a2double) a2x(n), a2y(m); for(size_t j = 0; j < n; j++) a2x[j] = a1x[j]; CppAD::Independent(a2x); // y a2y[0] = CppAD::azmul(a2x[0], a2x[1]); // azmul(variable, variable) a2y[1] = CppAD::azmul(a1x[0], a2x[1]); // azmul(parameter, variable) a2y[2] = CppAD::azmul(a2x[0], a1x[1]); // azmul(variable, parameter) // create f: x -> y and stop a2 recording CppAD::ADFun a1f; a1f.Dependent(a2x, a2y); // check y a1y = a1f.Forward(0, a1x); // azmul(variable, variable) only for(size_t i = 0; i < m; i++) ok &= NearEqual(a1y[i] , x[0] * x[1], eps, eps); for(size_t j = 0; j < n; j++) { for(size_t ell = 0; ell < r; ell++) a1dx[r * j + ell] = a1double(1.0 + double(ell)); } a1z = a1f.Forward(1, r, a1dx); // create g: x -> z and stop a1 recording CppAD::ADFun g; g.Dependent(a1x, a1z); // check value when x[0] is not zero z = g.Forward(0, x); // z_00 = d_lambda [ ( x[0] + lambda ) * ( x[1] + lambda ) ] ok &= NearEqual(z[r * 0 + 0] , x[0] + x[1], eps, eps); // z_01 = d_lambda [ ( x[0] + 2 * lambda ) * ( x[1] + 2 * lambda ) ] ok &= NearEqual(z[r * 0 + 1] , 2.0*(x[0] + x[1]), eps, eps); // z_10 = d_lambda [ x[0] * ( x[1] + lambda ) ] ok &= NearEqual(z[r * 1 + 0] , x[0], eps, eps); // z_11 = d_lambda [ x[0] * ( x[1] + 2 * lambda ) ] ok &= NearEqual(z[r * 1 + 1] , 2.0 * x[0], eps, eps); // z_20 = d_lambda [ ( x[0] + lambda ) * x[1] ] ok &= NearEqual(z[r * 2 + 0] , x[1], eps, eps); // z_21 = d_lambda [ ( x[0] + 2 * lambda ) * x[1] ] ok &= NearEqual(z[r * 2 + 1] , 2.0 * x[1], eps, eps); // check value x[0] is zero and x[1] is infinity x[0] = 0.0; x[1] = inf; z = g.Forward(0, x); ok &= z[r * 0 + 0] == inf; ok &= z[r * 0 + 1] == inf; ok &= z[r * 1 + 0] == 0.0; ok &= z[r * 1 + 1] == 0.0; ok &= z[r * 2 + 0] == inf; ok &= z[r * 2 + 1] == inf; return ok; } } bool azmul(void) { bool ok = true; ok &= test_base2ad(); ok &= test_forward(); ok &= test_reverse(); ok &= test_forward_dir(); return ok; } ================================================ FILE: test_more/general/base2ad.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // Tests corresponding to some base2ad bug fixes; i.e., regression test # include namespace { // BEGIN_EMPTY_NAMESPACE // // my_integer double my_integer(const double& x) { return std::floor(x); } CPPAD_DISCRETE_FUNCTION(double, my_integer); // // discrete bool discrete(void) { bool ok = true; using CppAD::AD; // // af CPPAD_TESTVECTOR( AD ) ax(1), ay(1); ax[0] = 2.5; Independent( ax ); ay[0] = my_integer( ax[0] ); CppAD::ADFun f(ax, ay); CppAD::ADFun< AD, double> af = f.base2ad(); // // g Independent( ax ); ay = af.Forward(0, ax); CppAD::ADFun g(ax, ay); // // ok CPPAD_TESTVECTOR(double) x(1), y(1); x[0] = 5.5; y = g.Forward(0, x); ok &= y[0] == std::floor( x[0] ); // return ok; } } // END_EMPTY_NAMESPACE bool base2ad(void) { bool ok = true; ok &= discrete(); return ok; } ================================================ FILE: test_more/general/base_adolc.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // adolc examples should suppress conversion warnings # include # include # include # include // adouble definitions not in Adolc distribution and // required in order to use CppAD::AD # include # include bool base_adolc(void) { bool ok = true; // initialize test result typedef adouble ADdouble; // for first level of taping typedef CppAD::AD ADDdouble; // for second level of taping size_t n = 4; // number independent variables CPPAD_TESTVECTOR(ADdouble) a_x(n); CPPAD_TESTVECTOR(ADDdouble) aa_x(n); // value of the independent variables short tag = 0; // Adolc setup int keep = 1; trace_on(tag, keep); size_t j; for(j = 0; j < n; j++) a_x[j] <<= double(n - j); // a_x is independent for ADdouble for(j = 0; j < n; j++) aa_x[j] = a_x[j]; // track how aa_x depends on a_x CppAD::Independent(aa_x); // aa_x is independent for ADDdouble // compute function size_t m = 5; CPPAD_TESTVECTOR(ADDdouble) aa_f(m); // dependent variables // different cases of conditional expressions aa_f[0] = CondExpLt(aa_x[0], aa_x[1], aa_x[2], aa_x[3]); aa_f[1] = CondExpLe(aa_x[0], aa_x[1], aa_x[2], aa_x[3]); aa_f[2] = CondExpEq(aa_x[0], aa_x[1], aa_x[2], aa_x[3]); aa_f[3] = CondExpGe(aa_x[0], aa_x[1], aa_x[2], aa_x[3]); aa_f[4] = CondExpGt(aa_x[0], aa_x[1], aa_x[2], aa_x[3]); // declare inner function (just to stop inner taping) CppAD::ADFun a_F(aa_x, aa_f); // set values for outer function same as inner function // (corresponding to the tape of adobule operations) double f_j; for(j = 0; j < m; j++) Value(aa_f[j]) >>= f_j; trace_off(); // arrays for Adolc zos_forward double *x = nullptr, *y = nullptr; x = CPPAD_TRACK_NEW_VEC(n, x); y = CPPAD_TRACK_NEW_VEC(m, y); // switch order of arguments from when taping was done for(j = 0; j < n; j++) x[j] = double(j); zos_forward(tag, int(m), int(n), keep, x, y); // CondExpLt(0, 1, 2, 3) ok &= (y[0] == double(2)); // CondExpLe(0, 1, 2, 3) ok &= (y[1] == double(2)); // CondExpEq(0, 1, 2, 3) ok &= (y[2] == double(3)); // CondExpGe(0, 1, 2, 3) ok &= (y[3] == double(3)); // CondExpGt(0, 1, 2, 3) ok &= (y[4] == double(3)); // set left = right and true < false x[0] = x[1] = 1.; x[2] = 2.; x[3] = 3.; zos_forward(tag, int(m), int(n), keep, x, y); // CondExpLt(1, 1, 2, 3) ok &= (y[0] == double(3)); // CondExpLe(1, 1, 2, 3) ok &= (y[1] == double(2)); // CondExpEq(1, 1, 2, 3) ok &= (y[2] == double(2)); // CondExpGe(1, 1, 2, 3) ok &= (y[3] == double(2)); // CondExpGt(1, 1, 2, 3) ok &= (y[4] == double(3)); CPPAD_TRACK_DEL_VEC(x); CPPAD_TRACK_DEL_VEC(y); return ok; } // END PROGRAM ================================================ FILE: test_more/general/base_alloc.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include "../../example/general/base_alloc.hpp" # include namespace { // BEGIN empty namespace bool test_parameter(void) { bool ok = true; typedef CppAD::AD my_ad; // y = x + 0 + 1 + 2 + ... + N-1 CppAD::vector a_x, a_y; a_x.resize(1); a_x[0] = my_ad(1.); CppAD::Independent(a_x); a_y.resize(1); a_y[0] = a_x[0]; // create a new parameter for each iteration of this loop size_t i, N = 50; for(i = 0; i < N; i++) a_y[0] += double(i); CppAD::ADFun f(a_x, a_y); return ok; } bool test_numeric_limits(void) { bool ok = true; typedef CppAD::AD my_ad; // base_alloc eps = Value( CppAD::numeric_limits::epsilon() ); ok &= *eps.ptrdbl_ == std::numeric_limits::epsilon(); // base_alloc min = Value( CppAD::numeric_limits::min() ); ok &= *min.ptrdbl_ == std::numeric_limits::min(); // base_alloc max = Value( CppAD::numeric_limits::max() ); ok &= *max.ptrdbl_ == std::numeric_limits::max(); // base_alloc nan = Value( CppAD::numeric_limits::quiet_NaN() ); ok &= *nan.ptrdbl_ != *nan.ptrdbl_; // int digits10 = CppAD::numeric_limits::digits10; ok &= digits10 == std::numeric_limits::digits10; // return ok; } bool test_to_string(void) { bool ok = true; typedef CppAD::AD my_ad; // double dbl_pi = 4.0 * std::atan(1.0); my_ad ad_pi = my_ad(dbl_pi); std::string str_pi = to_string( ad_pi ); // // Check the length of the string "3.1415...". One extra character // for machine epsilon precision and another for the decimal point. ok &= str_pi.size() == size_t( 2 + std::numeric_limits::digits10 ); // // check value double eps = std::numeric_limits::epsilon(); double check = std::atof( str_pi.c_str() ); ok &= (check / dbl_pi - 1.0) <= eps; // return ok; } } // END empty namespace bool base_alloc_test(void) { bool ok = true; // ok &= test_parameter(); ok &= test_numeric_limits(); ok &= test_to_string(); // return ok; } ================================================ FILE: test_more/general/base_complex.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- #include bool base_complex(void) { // ok bool ok = true; // // base, ad_base typedef std::complex base; typedef CppAD::AD ad_base; // // f CPPAD_TESTVECTOR(ad_base) ax(1), ay(1); ax[0] = 0.0; CppAD::Independent(ax); ay[0] = ax[0] * ax[0]; CppAD::ADFun f(ax, ay); // // f f.optimize(); // // dy std::vector x(1), dx(1), dy(1); x[0] = 5; f.Forward(0, x); dx[0] = 1; dy = f.Forward(1, dx); // // ok ok &= dy[0] == 2.0 * x[0]; // return ok; } ================================================ FILE: test_more/general/bool_sparsity.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old example / test @begin bool_sparsity.cpp$$ $spell Bool $$ $section Using vectorBool Sparsity To Conserve Memory: Example and Test$$ $head Purpose$$ This example show how to conserve memory when computing sparsity patterns. $srcthisfile%0%// BEGIN C++%// END C++%1%$$ @end */ // BEGIN C++ # include namespace { using CppAD::vector; using std::cout; using CppAD::vectorBool; using CppAD::AD; using CppAD::ADFun; // function f(x) that we are computing sparsity patterns for template vector fun(const vector& x) { size_t n = x.size(); vector ret(n + 1); for(size_t j = 0; j < n; j++) { size_t k = (j + 1) % n; ret[j] = x[j] * x[j] * x[k]; } ret[n] = 0.0; return ret; } // check sparsity pattern for f(x) bool check_jac(const vectorBool& pattern, size_t n) { bool ok = true; for(size_t i = 0; i < n; i++) { size_t k = (i + 1) % n; for(size_t j = 0; j < n; j++) { bool non_zero = (i == j) || (j == k); ok &= pattern[ i * n + j] == non_zero; } } for(size_t j = 0; j < n; j++) ok &= pattern[ n * n + j] == false; return ok; } // check sparsity pattern for the Hessian of sum_i f_i(x) bool check_hes(const vectorBool& pattern, size_t n) { bool ok = true; for(size_t i = 0; i < n; i++) { size_t k1 = (i + 1) % n; size_t k2 = (n + i - 1) % n; for(size_t j = 0; j < n; j++) { bool non_zero = (i == j) || (j == k1) || (j == k2); ok &= pattern[ i * n + j] == non_zero; } } return ok; } // compute sparsity for Jacobian of f(x) using forward mode bool for_sparse_jac(ADFun& f) { bool ok = true; size_t n = f.Domain(); size_t m = f.Range(); // // number of columns of the sparsity patter to compute at a time size_t n_col = vectorBool::bit_per_unit(); vectorBool pattern(m * n), s(m * n_col), r(n * n_col); // size_t n_loop = (n - 1) / n_col + 1; for(size_t i_loop = 0; i_loop < n_loop; i_loop++) { size_t j_col = i_loop * n_col; for(size_t i = 0; i < n; i++) { for(size_t j = 0; j < n_col; j++) r[i * n_col + j] = (i == j_col + j); } s = f.ForSparseJac(n_col, r); for(size_t i = 0; i < m; i++) { for(size_t j = 0; j < n_col; j++) if( j_col + j < n ) pattern[ i * n + j_col + j ] = s[ i * n_col + j]; } } ok &= check_jac(pattern, n); // return ok; } // compute sparsity for Jacobian of f(x) using reverse mode bool rev_sparse_jac(ADFun& f) { bool ok = true; size_t n = f.Domain(); size_t m = f.Range(); // // number of rows of the sparsity patter to compute at a time size_t n_row = vectorBool::bit_per_unit(); vectorBool pattern(m * n), s(n_row * n), r(n_row * m); // size_t n_loop = (m - 1) / n_row + 1; for(size_t i_loop = 0; i_loop < n_loop; i_loop++) { size_t i_row = i_loop * n_row; for(size_t i = 0; i < n_row; i++) { for(size_t j = 0; j < m; j++) r[i * m + j] = (i_row + i == j); } s = f.RevSparseJac(n_row, r); for(size_t i = 0; i < n_row; i++) { for(size_t j = 0; j < n; j++) if( i_row + i < m ) pattern[ (i_row + i) * n + j ] = s[ i * n + j]; } } ok &= check_jac(pattern, n); // return ok; } // compute sparsity for Hessian of sum_i f_i (x) bool rev_sparse_hes(ADFun& f) { bool ok = true; size_t n = f.Domain(); size_t m = f.Range(); // // number of columns of the sparsity patter to compute at a time size_t n_col = vectorBool::bit_per_unit(); vectorBool pattern(n * n), r(n * n_col), h(n * n_col); // consider case where Hessian for sum of f_i(x) w.r.t i vectorBool s(m); for(size_t i = 0; i < m; i++) s[i] = true; // size_t n_loop = (n - 1) / n_col + 1; for(size_t i_loop = 0; i_loop < n_loop; i_loop++) { size_t j_col = i_loop * n_col; for(size_t i = 0; i < n; i++) { for(size_t j = 0; j < n_col; j++) r[i * n_col + j] = (i == j_col + j); } // f.ForSparseJac(n_col, r); bool transpose = true; h = f.RevSparseHes(n_col, s, transpose); // for(size_t i = 0; i < n; i++) { for(size_t j = 0; j < n_col; j++) if( j_col + j < n ) pattern[ i * n + j_col + j ] = h[ i * n_col + j]; } } ok &= check_hes(pattern, n); // return ok; } } // driver for all of the cases above bool bool_sparsity(void) { bool ok = true; // // record the function size_t n = 100; size_t m = n + 1; vector< AD > x(n), y(m); for(size_t j = 0; j < n; j++) x[j] = AD(j+1); CppAD::Independent(x); y = fun(x); ADFun f(x, y); // // run the three example / tests ok &= for_sparse_jac(f); ok &= rev_sparse_jac(f); ok &= rev_sparse_hes(f); return ok; } // END C++ ================================================ FILE: test_more/general/check_simple_vector.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # include namespace { template void Case(const Scalar& x, const Scalar& y) { using CppAD::CheckSimpleVector; CheckSimpleVector > (x, y); CheckSimpleVector > (x, y); CheckSimpleVector > (x, y); typedef CPPAD_TESTVECTOR(Scalar) testvector; CheckSimpleVector (x, y); } } bool check_simple_vector(void) { // Unusual test in that CheckSimpleVector will abort if an error occurs Case(float(0), float(1)); Case(double(0), double(1)); // std::set x, y; x.insert(1); y.insert(2); Case(x, y); // return true; } ================================================ FILE: test_more/general/chkpoint_one.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace { using CppAD::AD; typedef CPPAD_TESTVECTOR(AD) ADVector; // ------------------------------------------------------------------------ bool f_algo(const ADVector& x, ADVector& y) { size_t m = y.size(); assert( size_t(x.size()) == m + 1); for(size_t i = 0; i < m; i++) y[i] = x[i] * x[i+1]; return true; } bool g_algo(const ADVector& y, ADVector& z) { size_t m = z.size(); assert( size_t(y.size()) + 1 == m ); z[0] = 0.0; for(size_t i = 1; i < m; i++) { z[0] += y[i-1]; z[i] = y[i-1]; } return true; } bool test_one(void) { bool ok = true; using CppAD::checkpoint; using CppAD::ADFun; using CppAD::NearEqual; size_t i, j, k, n = 4, ell = n-1 , m = ell + 1; double eps = 10. * std::numeric_limits::epsilon(); // checkpoint version of the function F(x) ADVector ax(n), ay(ell), az(m); for(j = 0; j < n; j++) ax[j] = double(j); checkpoint f_check("f_check", f_algo, ax, ay); checkpoint g_check("g_check", g_algo, ay, az); // Record a version of z = g[f(x)] without checkpointing Independent(ax); f_algo(ax, ay); g_algo(ay, az); ADFun check_not(ax, az); // Record a version of z = g[f(x)] with checkpointing Independent(ax); f_check(ax, ay); g_check(ay, az); ADFun check_yes(ax, az); // compare forward mode results for orders 0, 1, 2 size_t p = 2; CPPAD_TESTVECTOR(double) x_p(n*(p+1)), z_not(m*(p+1)), z_yes(m*(p+1)); for(j = 0; j < n; j++) { for(k = 0; k <= p; k++) x_p[ j * (p+1) + k ] = 1.0 / double(p + 1 - k); } z_not = check_not.Forward(p, x_p); z_yes = check_yes.Forward(p, x_p); for(i = 0; i < m; i++) { for(k = 0; k <= p; k++) { double zik_not = z_not[ i * (p+1) + k]; double zik_yes = z_yes[ i * (p+1) + k]; ok &= NearEqual(zik_not, zik_yes, eps, eps); } } // compare reverse mode results CPPAD_TESTVECTOR(double) w(m*(p+1)), dw_not(n*(p+1)), dw_yes(n*(p+1)); for(i = 0; i < m; i++) { for(k = 0; k <= p; k++) w[ i * (p+1) + k ] = 2.0 / double(p + 1 - k ); } dw_not = check_not.Reverse(p+1, w); dw_yes = check_yes.Reverse(p+1, w); for(j = 0; j < n; j++) { for(k = 0; k <= p; k++) { double dwjk_not = dw_not[ j * (p+1) + k]; double dwjk_yes = dw_yes[ j * (p+1) + k]; ok &= NearEqual(dwjk_not, dwjk_yes, eps, eps); } } // mix sparsity so test both cases f_check.option( CppAD::atomic_base::bool_sparsity_enum ); g_check.option( CppAD::atomic_base::set_sparsity_enum ); // compare forward mode Jacobian sparsity patterns size_t q = n - 1; CppAD::vector< std::set > r(n), s_not(m), s_yes(m); for(j = 0; j < n; j++) { if( j < q ) r[j].insert(j); else { r[j].insert(0); r[j].insert(1); } } s_not = check_not.ForSparseJac(q, r); s_yes = check_yes.ForSparseJac(q, r); for(i = 0; i < m; i++) ok &= s_not[i] == s_yes[i]; // compare reverse mode Jacobian sparsity patterns CppAD::vector< std::set > s(m), r_not(m), r_yes(m); for(i = 0; i < m; i++) s[i].insert(i); r_not = check_not.RevSparseJac(m, s); r_yes = check_yes.RevSparseJac(m, s); for(i = 0; i < m; i++) ok &= s_not[i] == s_yes[i]; // compare reverse mode Hessian sparsity patterns CppAD::vector< std::set > s_one(1), h_not(q), h_yes(q); for(i = 0; i < m; i++) s_one[0].insert(i); h_not = check_not.RevSparseHes(q, s_one); h_yes = check_yes.RevSparseHes(q, s_one); for(i = 0; i < q; i++) ok &= h_not[i] == h_yes[i]; checkpoint::clear(); return ok; } // ------------------------------------------------------------------------ bool h_algo(const ADVector& ax, ADVector& ay) { ay[0] = ax[0]; ay[1] = ax[1] + ax[2]; return true; } bool test_two(void) { bool ok = true; using CppAD::checkpoint; using CppAD::ADFun; using CppAD::NearEqual; // checkpoint version of H(x) size_t m = 2; size_t n = 3; ADVector ax(n), ay(m); for(size_t j = 0; j < n; j++) ax[j] = double(j); checkpoint h_check("h_check", h_algo, ax, ay); // record function using h_check Independent(ax); h_check(ax, ay); ADFun h(ax, ay); // // -------------------------------------------------------------------- // sparsity pattern // -------------------------------------------------------------------- for(size_t k = 0; k < 3; k++) { if( k == 0 ) h_check.option(CppAD::atomic_base::pack_sparsity_enum); if( k == 1 ) h_check.option(CppAD::atomic_base::bool_sparsity_enum); if( k == 2 ) h_check.option(CppAD::atomic_base::set_sparsity_enum); // compute sparsity pattern h_1(x) = x[1] + x[2] CppAD::vector< std::set > r(1), s(1); r[0].insert(1); s = h.RevSparseJac(1, r); // check result std::set check; check.insert(1); check.insert(2); ok &= s[0] == check; } // -------------------------------------------------------------------- // base2ad // -------------------------------------------------------------------- ADFun< AD , double > ah; ah = h.base2ad(); // // forward mode ADVector au(n), av(m); for(size_t j = 0; j < n; j++) ax[j] = au[j] = double(j + 1); av = ah.Forward(0, au); h_algo(ax, ay); for(size_t i = 0; i < m; ++i) ok &= av[i] == ay[i]; // // reverse mode ADVector adw(n), aw(m); for(size_t i = 0; i < m; ++i) aw[i] = 1.0; adw = ah.Reverse(1, aw); ok &= Value( adw[0] ) == 1.0; ok &= Value( adw[1] ) == 1.0; ok &= Value( adw[2] ) == 1.0; // return ok; } } bool chkpoint_one(void) { bool ok = true; ok &= test_one(); ok &= test_two(); return ok; } // END C++ ================================================ FILE: test_more/general/chkpoint_two.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace { using CppAD::AD; using CppAD::vector; using CppAD::chkpoint_two; // ----------------------------------------------------------------------- template chkpoint_two checkpoint_two( std::string name , Algo& algo , vector< AD >& ax , vector< AD >& ay ) { CppAD::Independent(ax); algo(ax, ay); CppAD::ADFun g_fun(ax, ay); bool internal_bool = false; bool use_hes_sparsity = true; bool use_base2ad = true; bool use_in_parallel = false; return chkpoint_two(g_fun, name, internal_bool, use_hes_sparsity, use_base2ad, use_in_parallel ); } // ---------------------------------------------------------------- // Test for bug where checkpoint function did not depend on // the operands in the logical comparison because of the CondExp // sparsity pattern. void j_algo( const CppAD::vector< CppAD::AD >& ax , CppAD::vector< CppAD::AD >& ay ) { ay[0] = CondExpGt(ax[0], ax[1], ax[2], ax[3]); } bool test_one(void) { bool ok = true; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); // Create a checkpoint version of the function g vector< AD > au(4), av(1); for(size_t i = 0; i < 4; i++) au[i] = AD(i); chkpoint_two j_check = checkpoint_two("j_check", j_algo, au, av); // independent variable vector vector< AD > ax(2), ay(1); ax[0] = 1.; ax[1] = 1.; Independent(ax); // call atomic function that does not get used for(size_t i = 0; i < 4; i++) au[i] = ax[0] + AD(i + 1) * ax[1]; j_check(au, ay); // create function object f : ax -> ay CppAD::ADFun f(ax, ay); // now optimize the operation sequence f.optimize(); // check result where true case is used; i.e., au[0] > au[1] vector x(2), y(1); x[0] = 1.; x[1] = -1; y = f.Forward(0, x); ok &= NearEqual(y[0], x[0] + double(3) * x[1], eps10, eps10); // check result where false case is used; i.e., au[0] <= au[1] x[0] = 1.; x[1] = 1; y = f.Forward(0, x); ok &= NearEqual(y[0], x[0] + double(4) * x[1], eps10, eps10); return ok; } // ------------------------------------------------------------------- // Test conditional optimizing out call to an atomic function call void k_algo( const CppAD::vector< CppAD::AD >& x , CppAD::vector< CppAD::AD >& y ) { y[0] = x[0] + x[1]; } void h_algo( const CppAD::vector< CppAD::AD >& x , CppAD::vector< CppAD::AD >& y ) { y[0] = x[0] - x[1]; } bool test_two(void) { bool ok = true; typedef vector< AD > ADVector; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); // Create a checkpoint version of the function g ADVector ax(2), ag(1), ah(1), ay(1); ax[0] = 0.; ax[1] = 1.; chkpoint_two k_check = checkpoint_two("k_check", k_algo, ax, ag); chkpoint_two h_check = checkpoint_two("h_check", h_algo, ax, ah); // independent variable vector Independent(ax); // atomic function calls that get conditionally used k_check(ax, ag); h_check(ax, ah); // conditional expression ay[0] = CondExpLt(ax[0], ax[1], ag[0], ah[0]); // create function object f : ax -> ay CppAD::ADFun f; f.Dependent(ax, ay); // use zero order to evaluate when condition is true CppAD::vector x(2), dx(2); CppAD::vector y(1), dy(1), w(1); x[0] = 3.; x[1] = 4.; y = f.Forward(0, x); ok &= NearEqual(y[0], x[0] + x[1], eps10, eps10); // before optimize ok &= f.number_skip() == 0; // now optimize the operation sequence f.optimize(); // optimized zero order forward when condition is false x[0] = 4.; x[1] = 3.; y = f.Forward(0, x); ok &= NearEqual(y[0], x[0] - x[1], eps10, eps10); // after optimize can skip either call to g or call to h ok &= f.number_skip() == 1; // optimized first order forward dx[0] = 2.; dx[1] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], dx[0] - dx[1], eps10, eps10); // optimized first order reverse w[0] = 1.; dx = f.Reverse(1, w); ok &= NearEqual(dx[0], 1., eps10, eps10); ok &= NearEqual(dx[1], -1., eps10, eps10); return ok; } // ------------------------------------------------------------------- // Test of optimizing out arguments to an atomic function void g_algo( const CppAD::vector< CppAD::AD >& ax , CppAD::vector< CppAD::AD >& ay ) { ay = ax; } bool test_three(void) { bool ok = true; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); // Create a checkpoint version of the function g vector< AD > ax(2), ay(2), az(1); ax[0] = 0.; ax[1] = 1.; chkpoint_two g_check = checkpoint_two("g_check", g_algo, ax, ay); // independent variable vector Independent(ax); // call atomic function that does not get used g_check(ax, ay); // conditional expression az[0] = CondExpLt(ax[0], ax[1], ax[0] + ax[1], ax[0] - ax[1]); // create function object f : ax -> az CppAD::ADFun f(ax, az); // number of variables before optimization // (include ay[0] and ay[1]) size_t n_before = f.size_var(); // now optimize the operation sequence f.optimize(); // number of variables after optimization // (does not include ay[0] and ay[1]) size_t n_after = f.size_var(); ok &= n_after + 2 == n_before; // check optimization works ok vector x(2), z(1); x[0] = 4.; x[1] = 3.; z = f.Forward(0, x); ok &= NearEqual(z[0], x[0] - x[1], eps10, eps10); return ok; } bool test_four(void) { bool ok = true; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); vector< AD > au(2), aw(2), ax(2), ay(1); // create atomic function corresponding to g_algo au[0] = 1.0; au[1] = 2.0; chkpoint_two g_check = checkpoint_two("g_algo", g_algo, au, ax); // start recording a new function CppAD::Independent(ax); // now use g_check during the recording au[0] = ax[0] + ax[1]; // this argument requires a new variable au[1] = ax[0] - ax[1]; // this argument also requires a new variable g_check(au, aw); // now create f(x) = x_0 + x_1 ay[0] = aw[0]; CppAD::ADFun f(ax, ay); // number of variables before optimization // ax[0], ax[1], ax[0] + ax[1], ax[0] - ax[1], g[0], g[1] // and phantom variable at index 0 size_t n_before = f.size_var(); ok &= n_before == 7; // now optimize f so that the calculation of au[1] is removed f.optimize(); // number of variables after optimization // ax[0], ax[1], ax[0] + ax[1], g[0] // and phantom variable at index 0 size_t n_after = f.size_var(); ok &= n_after == 5; // now compute and check a forward mode calculation vector x(2), y(1); x[0] = 5.0; x[1] = 6.0; y = f.Forward(0, x); ok &= NearEqual(y[0], x[0] + x[1], eps10, eps10); return ok; } // ----------------------------------------------------------------------- void i_algo( const CppAD::vector< CppAD::AD >& ax , CppAD::vector< CppAD::AD >& ay ) { ay[0] = 1.0 / ax[0]; } // // Test bug where atomic functions were not properly conditionally skipped. bool test_five(bool conditional_skip) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); using CppAD::vector; // Create a checkpoint version of the function i_algo vector< AD > au(1), av(1), aw(1); au[0] = 1.0; chkpoint_two i_check = checkpoint_two("i_check", i_algo, au, av); // independent variable vector vector< AD > ax(2), ay(1); ax[0] = 1.0; ax[1] = 2.0; Independent(ax); // call atomic function that does not get used au[0] = ax[0]; i_check(au, av); au[0] = ax[1]; i_check(au, aw); AD zero = 0.0; ay[0] = CondExpGt(av[0], zero, av[0], aw[0]); // create function object f : ax -> ay CppAD::ADFun f(ax, ay); // run case that skips the second call to afun // (can use trace in sweep/forward_0.hpp to see this). vector x(2), y_before(1), y_after(1); x[0] = 1.0; x[1] = 2.0; y_before = f.Forward(0, x); if( conditional_skip ) f.optimize(); else f.optimize("no_conditional_skip"); y_after = f.Forward(0, x); ok &= NearEqual(y_before[0], y_after[0], eps10, eps10); return ok; } // ----------------------------------------------------------------------- void m_algo( const CppAD::vector< CppAD::AD >& ax , CppAD::vector< CppAD::AD >& ay ) { ay[0] = 0.0; for(size_t j = 0; j < ax.size(); ++j) ay[0] += ax[j] * ax[j]; } // // Test bug where select_y[i] should be select_x[i] bool test_six(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; using CppAD::vector; // Create a checkpoint version of the function m_algo size_t n = 3, m = 1; vector< AD > ax(n), ay(m); for(size_t j = 0; j < n; ++j) ax[j] = 1.0; chkpoint_two m_check = checkpoint_two("m_check", m_algo, ax, ay); // independent variable vector Independent(ax); // call atomic function that does not get used m_check(ax, ay); // create function object f : ax -> ay CppAD::ADFun f(ax, ay); // Evaluate Hessian sparsity vector select_domain(n), select_range(m); select_range[0] = true; for(size_t j = 0; j < n; ++j) select_domain[j] = true; bool internal_bool = true; CppAD::sparse_rc< vector > pattern_out; // f.for_hes_sparsity( select_domain, select_range, internal_bool, pattern_out ); size_t nnz = pattern_out.nnz(); const vector& row( pattern_out.row() ); const vector& col( pattern_out.col() ); vector row_major = pattern_out.row_major(); // ok &= nnz == n; for(size_t k = 0; k < nnz; ++k) { ok &= row[ row_major[k] ] == k; ok &= col[ row_major[k] ] == k; } return ok; } } bool chkpoint_two(void) { bool ok = true; // ok &= test_one(); ok &= test_two(); ok &= test_three(); ok &= test_four(); ok &= test_five(true); ok &= test_five(false); ok &= test_six(); // return ok; } // END C++ ================================================ FILE: test_more/general/compare.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Check comparison operators between AD< AD > and Base, int */ # include namespace { // --------------------------------------------------------------------- bool compare_parameter(bool optimize) { bool ok = true; using CppAD::AD; // // f CPPAD_TESTVECTOR( AD ) ax(1), ap(1), ay(1); ax[0] = 1.0; ap[0] = 3.0; CppAD::Independent( ax, ap ); if( ap[0] < 2.0 ) ay[0] = ax[0] + ax[0]; else ay[0] = ax[0] * ax[0]; CppAD::ADFun f(ax, ay); if( optimize ) f.optimize(); // // x, y, ok CPPAD_TESTVECTOR(double) x(1), y(1); x[0] = 4.0; y = f.Forward(0, x); ok &= y[0] == x[0] * x[0]; ok &= f.compare_change_number() == 0; // // p, ok CPPAD_TESTVECTOR(double) p(1); p[0] = 1.0; f.new_dynamic(p); y = f.Forward(0, x); ok &= y[0] == x[0] * x[0]; ok &= f.compare_change_number() == 1; // return ok; } // --------------------------------------------------------------------- template bool compare_type(void) { bool ok = true; using CppAD::AD; Type middle = 4; AD three = 3; AD four = 4; AD five = 5; // AD > Type ok &= ! (three > middle); ok &= ! (four > middle); ok &= (five > middle); // Type > AD ok &= (middle > three ); ok &= ! (middle > four ); ok &= ! (middle > five ); // AD >= Type ok &= ! (three >= middle); ok &= (four >= middle); ok &= (five >= middle); // Type > AD ok &= (middle >= three ); ok &= (middle >= four ); ok &= ! (middle >= five ); // AD < Type ok &= (three < middle); ok &= ! (four < middle); ok &= ! (five < middle); // Type > AD ok &= ! (middle < three ); ok &= ! (middle < four ); ok &= (middle < five ); // AD <= Type ok &= (three <= middle); ok &= (four <= middle); ok &= ! (five <= middle); // Type > AD ok &= ! (middle <= three ); ok &= (middle <= four ); ok &= (middle <= five ); return ok; } } bool Compare(void) { bool ok = true; ok &= compare_type(); ok &= compare_type(); ok &= compare_type< CppAD::AD >(); ok &= compare_parameter(true); ok &= compare_parameter(false); return ok; } ================================================ FILE: test_more/general/compare_change.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old CompareChange examples and tests, now just used for validation testing */ # include namespace { // ---------------------------------------------------------------------- bool CompareChange_one(void) { bool ok = true; using namespace CppAD; // ------------------------------- < ---------------------------- // create independent variables CPPAD_TESTVECTOR(AD) X(2); X[0] = 3.; X[1] = 4.; Independent(X); // create dependent variables CPPAD_TESTVECTOR(AD) Y(6); // CondExp would never require retaping if( X[0] < X[1] ) // True variable < variable Y[0] = X[0]; else Y[0] = X[1]; if( X[1] < X[0] ) // False variable < variable Y[1] = X[0]; else Y[1] = X[1]; if( 3.5 < X[1] ) // True parameter < variable Y[2] = X[0]; else Y[2] = X[1]; if( 3.5 < X[0] ) // False parameter < variable Y[3] = X[0]; else Y[3] = X[1]; if( X[0] < 4. ) // True variable < parameter Y[4] = X[0]; else Y[4] = X[1]; if( X[1] < 4. ) // False variable < parameter Y[5] = X[0]; else Y[5] = X[1]; // f : X -> Y ADFun *f; f = new ADFun(X, Y); // new argument value CPPAD_TESTVECTOR(double) x( X.size() ); x[0] = 4.; x[1] = 3.; // evaluate the function at new argument CPPAD_TESTVECTOR(double) y( Y.size() ); y = f->Forward(0, x); // check results ok &= (y[0] == x[0]); // this is what the was taped ok &= (y[1] == x[1]); ok &= (y[2] == x[0]); ok &= (y[3] == x[1]); ok &= (y[4] == x[0]); ok &= (y[5] == x[1]); ok &= (f->CompareChange() == 6); // all comparisons have changed // done with this function delete f; // ------------------------------- > ---------------------------- // create independent variables Independent(X); if( X[0] > X[1] ) // False variable > variable Y[0] = X[0]; else Y[0] = X[1]; if( X[1] > X[0] ) // True variable > variable Y[1] = X[0]; else Y[1] = X[1]; if( 3.5 > X[1] ) // False parameter > variable Y[2] = X[0]; else Y[2] = X[1]; if( 3.5 > X[0] ) // True parameter > variable Y[3] = X[0]; else Y[3] = X[1]; if( X[0] > 3. ) // False variable > parameter Y[4] = X[0]; else Y[4] = X[1]; if( X[1] > 3. ) // True variable > parameter Y[5] = X[0]; else Y[5] = X[1]; // f : X -> Y f = new ADFun (X, Y); // evaluate the function at new argument y = f->Forward(0, x); // check results ok &= (y[0] == x[1]); // this is what the was taped ok &= (y[1] == x[0]); ok &= (y[2] == x[1]); ok &= (y[3] == x[0]); ok &= (y[4] == x[1]); ok &= (y[5] == x[0]); ok &= (f->CompareChange() == 6); // all comparisons have changed // done with this function delete f; // ------------------------------- <= ---------------------------- // create independent variables Independent(X); if( X[0] <= X[1] ) // True variable <= variable Y[0] = X[0]; else Y[0] = X[1]; if( X[1] <= X[0] ) // False variable <= variable Y[1] = X[0]; else Y[1] = X[1]; if( 4. <= X[1] ) // True parameter <= variable Y[2] = X[0]; else Y[2] = X[1]; if( 4. <= X[0] ) // False parameter <= variable Y[3] = X[0]; else Y[3] = X[1]; if( X[0] <= 3.5 ) // True variable <= parameter Y[4] = X[0]; else Y[4] = X[1]; if( X[1] <= 3.5 ) // False variable <= parameter Y[5] = X[0]; else Y[5] = X[1]; // f : X -> Y f = new ADFun (X, Y); // evaluate the function at new argument y = f->Forward(0, x); // check results ok &= (y[0] == x[0]); // this is what the was taped ok &= (y[1] == x[1]); ok &= (y[2] == x[0]); ok &= (y[3] == x[1]); ok &= (y[4] == x[0]); ok &= (y[5] == x[1]); ok &= (f->CompareChange() == 6); // all comparisons have changed // done with this function delete f; // ------------------------------- >= ---------------------------- // create independent variables Independent(X); if( X[0] >= X[1] ) // False variable >= variable Y[0] = X[0]; else Y[0] = X[1]; if( X[1] >= X[0] ) // True variable >= variable Y[1] = X[0]; else Y[1] = X[1]; if( 3.5 >= X[1] ) // False parameter >= variable Y[2] = X[0]; else Y[2] = X[1]; if( 3.5 >= X[0] ) // True parameter >= variable Y[3] = X[0]; else Y[3] = X[1]; if( X[0] >= 4. ) // False variable >= parameter Y[4] = X[0]; else Y[4] = X[1]; if( X[1] >= 4. ) // True variable >= parameter Y[5] = X[0]; else Y[5] = X[1]; // f : X -> Y f = new ADFun (X, Y); // evaluate the function at new argument y = f->Forward(0, x); // check results ok &= (y[0] == x[1]); // this is what the was taped ok &= (y[1] == x[0]); ok &= (y[2] == x[1]); ok &= (y[3] == x[0]); ok &= (y[4] == x[1]); ok &= (y[5] == x[0]); ok &= (f->CompareChange() == 6); // all comparisons have changed // done with this function delete f; // ------------------------------- == ---------------------------- // create independent variables Independent(X); if( X[0] == X[1] ) // False variable == variable Y[0] = X[0]; else Y[0] = X[1]; if( X[0] == X[0] ) // True variable == variable Y[1] = X[0]; else Y[1] = X[1]; if( 3. == X[1] ) // False parameter == variable Y[2] = X[0]; else Y[2] = X[1]; if( 3. == X[0] ) // True parameter == variable Y[3] = X[0]; else Y[3] = X[1]; if( X[0] == 4. ) // False variable == parameter Y[4] = X[0]; else Y[4] = X[1]; if( X[1] == 4. ) // True variable == parameter Y[5] = X[0]; else Y[5] = X[1]; // f : X -> Y f = new ADFun (X, Y); // evaluate the function at new argument y = f->Forward(0, x); // check results ok &= (y[0] == x[1]); // this is what the was taped ok &= (y[1] == x[0]); ok &= (y[2] == x[1]); ok &= (y[3] == x[0]); ok &= (y[4] == x[1]); ok &= (y[5] == x[0]); // the first two comparisons do not change ok &= (f->CompareChange() == 4); // done with this function delete f; // ------------------------------- != ---------------------------- // create independent variables Independent(X); if( X[0] != X[1] ) // True variable != variable Y[0] = X[0]; else Y[0] = X[1]; if( X[0] != X[0] ) // False variable != variable Y[1] = X[0]; else Y[1] = X[1]; if( 3. != X[1] ) // True parameter != variable Y[2] = X[0]; else Y[2] = X[1]; if( 3. != X[0] ) // False parameter != variable Y[3] = X[0]; else Y[3] = X[1]; if( X[0] != 4. ) // True variable != parameter Y[4] = X[0]; else Y[4] = X[1]; if( X[1] != 4. ) // False variable != parameter Y[5] = X[0]; else Y[5] = X[1]; // f : X -> Y f = new ADFun (X, Y); // evaluate the function at new argument y = f->Forward(0, x); // check results ok &= (y[0] == x[0]); // this is what the was taped ok &= (y[1] == x[1]); ok &= (y[2] == x[0]); ok &= (y[3] == x[1]); ok &= (y[4] == x[0]); ok &= (y[5] == x[1]); // the first two comparisons do not change ok &= (f->CompareChange() == 4); // done with this function delete f; return ok; } // ---------------------------------------------------------------------- template Type Minimum(const Type &x, const Type &y) { // Use a comparison to compute the min(x, y) // (note that CondExp would never require retaping). if( x < y ) return x; return y; } bool CompareChange_two(void) { bool ok = true; using CppAD::AD; using CppAD::ADFun; using CppAD::Independent; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) X(n); X[0] = 3.; X[1] = 4.; // declare independent variables and start tape recording CppAD::Independent(X); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = Minimum(X[0], X[1]); // create f: x -> y and stop tape recording ADFun f(X, Y); // evaluate zero mode Forward where conditional has the same result // note that f.CompareChange is not defined when NDEBUG is true CPPAD_TESTVECTOR(double) x(n); CPPAD_TESTVECTOR(double) y(m); x[0] = 3.5; x[1] = 4.; y = f.Forward(0, x); ok &= (y[0] == x[0]); ok &= (y[0] == Minimum(x[0], x[1])); ok &= (f.CompareChange() == 0); // evaluate zero mode Forward where conditional has different result x[0] = 4.; x[1] = 3.; y = f.Forward(0, x); ok &= (y[0] == x[0]); ok &= (y[0] != Minimum(x[0], x[1])); ok &= (f.CompareChange() == 1); // re-tape to obtain the new AD operation sequence X[0] = 4.; X[1] = 3.; Independent(X); Y[0] = Minimum(X[0], X[1]); // stop tape and store result in f f.Dependent(Y); // evaluate the function at new argument values y = f.Forward(0, x); ok &= (y[0] == x[1]); ok &= (y[0] == Minimum(x[0], x[1])); ok &= (f.CompareChange() == 0); return ok; } } bool compare_change(void) { bool ok = true; ok &= CompareChange_one(); ok &= CompareChange_two(); return ok; } // END C++ ================================================ FILE: test_more/general/cond_exp.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Comprehensive test built on 08/07 for new user interface to CondExp */ // BEGIN C++ # include namespace { // Begin empty namespace bool CondExp_ppvv(void) { bool ok = true; using CppAD::AD; // independent variable vector CPPAD_TESTVECTOR(AD) ax(2), ap(2); ap[0] = 0.; ap[1] = 1.; ax[0] = 2.; ax[1] = 3.; size_t abort_op_index = 0; bool record_compare = false; CppAD::Independent(ax, abort_op_index, record_compare, ap); // dependent variable vector CPPAD_TESTVECTOR(AD) ay(5); // CondExp(parameter, variable, variable, variable) ay[0] = CondExpLt(ap[0], ap[1], ax[0], ax[1]); ay[1] = CondExpLt(ap[0], ap[1], ax[0], ax[1]); ay[2] = CondExpEq(ap[0], ap[1], ax[0], ax[1]); ay[3] = CondExpGe(ap[0], ap[1], ax[0], ax[1]); ay[4] = CondExpGt(ap[0], ap[1], ax[0], ax[1]); // create f: X -> Y CppAD::ADFun f(ax, ay); // vectors for function values CPPAD_TESTVECTOR(double) x( f.Domain() ); CPPAD_TESTVECTOR(double) y( f.Range() ); // vectors for derivative values CPPAD_TESTVECTOR(double) dx( f.Domain() ); CPPAD_TESTVECTOR(double) dy( f.Range() ); // check original function values // ap[0] < ap[1] ok &= ay[0] == ax[0]; ok &= ay[1] == ax[0]; ok &= ay[2] == ax[1]; ok &= ay[3] == ax[1]; ok &= ay[4] == ax[1]; // function values x[0] = 2.; x[1] = 3.; y = f.Forward(0, x); ok &= ( y[0] == x[0] ); ok &= ( y[1] == x[0] ); ok &= ( y[2] == x[1] ); ok &= ( y[3] == x[1] ); ok &= ( y[4] == x[1] ); // forward mode derivative values dx[0] = 1.; dx[1] = 2.; dy = f.Forward(1, dx); ok &= (dy[0] == dx[0] ); ok &= (dy[1] == dx[0] ); ok &= (dy[2] == dx[1] ); ok &= (dy[3] == dx[1] ); ok &= (dy[4] == dx[1] ); // reverse mode derivative values dy[0] = 1.; dy[1] = 2.; dy[2] = 3.; dy[3] = 4.; dy[4] = 5.; dx = f.Reverse(1, dy); ok &= dx[0] == dy[0] + dy[1]; ok &= dx[1] == dy[2] + dy[3] + dy[4]; // now change the dynamic parameter so the results are reversed CPPAD_TESTVECTOR(double) p( f.size_dyn_ind() ); p[0] = 1.0; p[1] = 0.0; f.new_dynamic(p); // function values y = f.Forward(0, x); ok &= ( y[0] == x[1] ); ok &= ( y[1] == x[1] ); ok &= ( y[2] == x[1] ); ok &= ( y[3] == x[0] ); ok &= ( y[4] == x[0] ); // forward mode derivative values dx[0] = 1.; dx[1] = 2.; dy = f.Forward(1, dx); ok &= (dy[0] == dx[1] ); ok &= (dy[1] == dx[1] ); ok &= (dy[2] == dx[1] ); ok &= (dy[3] == dx[0] ); ok &= (dy[4] == dx[0] ); // reverse mode derivative values dy[0] = 1.; dy[1] = 2.; dy[2] = 3.; dy[3] = 4.; dy[4] = 5.; dx = f.Reverse(1, dy); ok &= dx[0] == dy[3] + dy[4]; ok &= dx[1] == dy[0] + dy[1] + dy[2]; return ok; } bool CondExp_pvvv(void) { bool ok = true; using namespace CppAD; // independent variable vector CPPAD_TESTVECTOR(AD) X(3); X[0] = 0.; X[1] = 1.; X[2] = 2.; Independent(X); // parameter value AD one = 1.; // dependent variable vector CPPAD_TESTVECTOR(AD) Y(5); // CondExp(parameter, variable, variable, variable) Y[0] = CondExpLt(one, X[0], X[1], X[2]); Y[1] = CondExpLe(one, X[0], X[1], X[2]); Y[2] = CondExpEq(one, X[0], X[1], X[2]); Y[3] = CondExpGe(one, X[0], X[1], X[2]); Y[4] = CondExpGt(one, X[0], X[1], X[2]); // create f: X -> Y ADFun f(X, Y); // vectors for function values CPPAD_TESTVECTOR(double) v( f.Domain() ); CPPAD_TESTVECTOR(double) w( f.Range() ); // vectors for derivative values CPPAD_TESTVECTOR(double) dv( f.Domain() ); CPPAD_TESTVECTOR(double) dw( f.Range() ); // check original function values ok &= Y[0] == X[2]; ok &= Y[1] == X[2]; ok &= Y[2] == X[2]; ok &= Y[3] == X[1]; ok &= Y[4] == X[1]; // function values v[0] = 2.; v[1] = 1.; v[2] = 0.; w = f.Forward(0, v); ok &= ( w[0] == v[1] ); ok &= ( w[1] == v[1] ); ok &= ( w[2] == v[2] ); ok &= ( w[3] == v[2] ); ok &= ( w[4] == v[2] ); // forward mode derivative values dv[0] = 1.; dv[1] = 2.; dv[2] = 3.; dw = f.Forward(1, dv); ok &= (dw[0] == dv[1] ); ok &= (dw[1] == dv[1] ); ok &= (dw[2] == dv[2] ); ok &= (dw[3] == dv[2] ); ok &= (dw[4] == dv[2] ); // reverse mode derivative values dw[0] = 1.; dw[1] = 2.; dw[2] = 3.; dw[3] = 4.; dw[4] = 5.; dv = f.Reverse(1, dw); ok &= (dv[0] == 0.); ok &= (dv[1] == dw[0] + dw[1] ); ok &= (dv[2] == dw[2] + dw[3] + dw[4] ); return ok; } bool CondExp_vpvv(void) { bool ok = true; using namespace CppAD; // independent variable vector CPPAD_TESTVECTOR(AD) X(3); X[0] = 0.; X[1] = 1.; X[2] = 2.; Independent(X); // parameter value AD one = 1.; // dependent variable vector CPPAD_TESTVECTOR(AD) Y(5); // CondExp(variable, parameter, variable, variable) Y[0] = CondExpLt(X[0], one, X[1], X[2]); Y[1] = CondExpLe(X[0], one, X[1], X[2]); Y[2] = CondExpEq(X[0], one, X[1], X[2]); Y[3] = CondExpGe(X[0], one, X[1], X[2]); Y[4] = CondExpGt(X[0], one, X[1], X[2]); // create f: X -> Y ADFun f(X, Y); // vectors for function values CPPAD_TESTVECTOR(double) v( f.Domain() ); CPPAD_TESTVECTOR(double) w( f.Range() ); // vectors for derivative values CPPAD_TESTVECTOR(double) dv( f.Domain() ); CPPAD_TESTVECTOR(double) dw( f.Range() ); // check original function values ok &= Y[0] == X[1]; ok &= Y[1] == X[1]; ok &= Y[2] == X[2]; ok &= Y[3] == X[2]; ok &= Y[4] == X[2]; // function values v[0] = 2.; v[1] = 1.; v[2] = 0.; w = f.Forward(0, v); ok &= ( w[0] == v[2] ); ok &= ( w[1] == v[2] ); ok &= ( w[2] == v[2] ); ok &= ( w[3] == v[1] ); ok &= ( w[4] == v[1] ); // forward mode derivative values dv[0] = 1.; dv[1] = 2.; dv[2] = 3.; dw = f.Forward(1, dv); ok &= (dw[0] == dv[2] ); ok &= (dw[1] == dv[2] ); ok &= (dw[2] == dv[2] ); ok &= (dw[3] == dv[1] ); ok &= (dw[4] == dv[1] ); // reverse mode derivative values dw[0] = 1.; dw[1] = 2.; dw[2] = 3.; dw[3] = 4.; dw[4] = 5.; dv = f.Reverse(1, dw); ok &= (dv[0] == 0.); ok &= (dv[1] == dw[3] + dw[4] ); ok &= (dv[2] == dw[0] + dw[1] + dw[2] ); return ok; } bool CondExp_vvpv(void) { bool ok = true; using namespace CppAD; // independent variable vector CPPAD_TESTVECTOR(AD) X(3); X[0] = 0.; X[1] = 1.; X[2] = 2.; Independent(X); // parameter value AD three = 3.; // dependent variable vector CPPAD_TESTVECTOR(AD) Y(5); // CondExp(variable, variable, parameter, variable) Y[0] = CondExpLt(X[0], X[1], three, X[2]); Y[1] = CondExpLe(X[0], X[1], three, X[2]); Y[2] = CondExpEq(X[0], X[1], three, X[2]); Y[3] = CondExpGe(X[0], X[1], three, X[2]); Y[4] = CondExpGt(X[0], X[1], three, X[2]); // create f: X -> Y ADFun f(X, Y); // vectors for function values CPPAD_TESTVECTOR(double) v( f.Domain() ); CPPAD_TESTVECTOR(double) w( f.Range() ); // vectors for derivative values CPPAD_TESTVECTOR(double) dv( f.Domain() ); CPPAD_TESTVECTOR(double) dw( f.Range() ); // check original function values ok &= Y[0] == three; ok &= Y[1] == three; ok &= Y[2] == X[2]; ok &= Y[3] == X[2]; ok &= Y[4] == X[2]; // function values v[0] = 2.; v[1] = 1.; v[2] = 0.; w = f.Forward(0, v); ok &= ( w[0] == v[2] ); ok &= ( w[1] == v[2] ); ok &= ( w[2] == v[2] ); ok &= ( w[3] == three ); ok &= ( w[4] == three ); // forward mode derivative values dv[0] = 1.; dv[1] = 2.; dv[2] = 3.; dw = f.Forward(1, dv); ok &= (dw[0] == dv[2] ); ok &= (dw[1] == dv[2] ); ok &= (dw[2] == dv[2] ); ok &= (dw[3] == 0. ); ok &= (dw[4] == 0. ); // reverse mode derivative values dw[0] = 1.; dw[1] = 2.; dw[2] = 3.; dw[3] = 4.; dw[4] = 5.; dv = f.Reverse(1, dw); ok &= (dv[0] == 0.); ok &= (dv[1] == 0.); ok &= (dv[2] == dw[0] + dw[1] + dw[2] ); return ok; } bool CondExp_vvvp(void) { bool ok = true; using namespace CppAD; // independent variable vector CPPAD_TESTVECTOR(AD) X(3); X[0] = 0.; X[1] = 1.; X[2] = 2.; Independent(X); // parameter value AD three = 3.; // dependent variable vector CPPAD_TESTVECTOR(AD) Y(5); // CondExp(variable, variable, variable, parameter) Y[0] = CondExpLt(X[0], X[1], X[2], three); Y[1] = CondExpLe(X[0], X[1], X[2], three); Y[2] = CondExpEq(X[0], X[1], X[2], three); Y[3] = CondExpGe(X[0], X[1], X[2], three); Y[4] = CondExpGt(X[0], X[1], X[2], three); // create f: X -> Y ADFun f(X, Y); // vectors for function values CPPAD_TESTVECTOR(double) v( f.Domain() ); CPPAD_TESTVECTOR(double) w( f.Range() ); // vectors for derivative values CPPAD_TESTVECTOR(double) dv( f.Domain() ); CPPAD_TESTVECTOR(double) dw( f.Range() ); // check original function values ok &= Y[0] == X[2]; ok &= Y[1] == X[2]; ok &= Y[2] == three; ok &= Y[3] == three; ok &= Y[4] == three; // function values v[0] = 2.; v[1] = 1.; v[2] = 0.; w = f.Forward(0, v); ok &= ( w[0] == three ); ok &= ( w[1] == three ); ok &= ( w[2] == three ); ok &= ( w[3] == v[2] ); ok &= ( w[4] == v[2] ); // forward mode derivative values dv[0] = 1.; dv[1] = 2.; dv[2] = 3.; dw = f.Forward(1, dv); ok &= (dw[0] == 0. ); ok &= (dw[1] == 0. ); ok &= (dw[2] == 0. ); ok &= (dw[3] == dv[2] ); ok &= (dw[4] == dv[2] ); // reverse mode derivative values dw[0] = 1.; dw[1] = 2.; dw[2] = 3.; dw[3] = 4.; dw[4] = 5.; dv = f.Reverse(1, dw); ok &= (dv[0] == 0.); ok &= (dv[1] == 0.); ok &= (dv[2] == dw[3] + dw[4] ); return ok; } # include bool SecondOrderReverse(void) { // Bradley M. Bell 2009-07-04 // Reverse mode for CExpOp was only modifying the highest order partial // This test demonstrated the bug bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * std::numeric_limits::epsilon(); size_t n = 1; CPPAD_TESTVECTOR(AD) X(n); X[0] = 2.; CppAD::Independent(X); size_t m = 2; CPPAD_TESTVECTOR(AD) Y(m); AD left = X[0]; AD right = X[0] * X[0]; AD exp_if_true = left; AD exp_if_false = right; // Test of reverse mode using exp_if_true case // For this value of X, should be the same as Z = X[0] AD Z = CondExpLt(left, right, exp_if_true, exp_if_false); Y[0] = Z * Z; // Test of reverse mode using exp_if_false case exp_if_false = left; exp_if_true = right; Z = CondExpGt(left, right, exp_if_true, exp_if_false); Y[1] = Z * Z; CppAD::ADFun f(X, Y); // first order forward CPPAD_TESTVECTOR(double) dx(n); size_t p = 1; dx[0] = 1.; f.Forward(p, dx); // second order reverse (test exp_if_true case) CPPAD_TESTVECTOR(double) w(m), dw(2 * n); w[0] = 1.; w[1] = 0.; p = 2; dw = f.Reverse(p, w); // check first derivative in dw double check = 2. * Value( X[0] ); ok &= NearEqual(dw[0], check, eps, eps); // check second derivative in dw check = 2.; ok &= NearEqual(dw[1], check, eps, eps); // test exp_if_false case w[0] = 0.; w[1] = 1.; p = 2; dw = f.Reverse(p, w); // check first derivative in dw check = 2. * Value( X[0] ); ok &= NearEqual(dw[0], check, eps, eps); // check second derivative in dw check = 2.; ok &= NearEqual(dw[1], check, eps, eps); return ok; } double Infinity(double zero) { return 1. / zero; } bool OldExample(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; using CppAD::log; double eps = 100. * std::numeric_limits::epsilon(); // domain space vector size_t n = 5; CPPAD_TESTVECTOR(AD) X(n); size_t j; for(j = 0; j < n; j++) X[j] = 1.; // declare independent variables and start tape recording CppAD::Independent(X); // sum with respect to j of log of absolute value of X[j] // should be - infinity if any of the X[j] are zero AD MinusInfinity = - Infinity(0.); AD Sum = 0.; AD Zero(0); for(j = 0; j < n; j++) { // if X[j] > 0 Sum += CppAD::CondExpGt(X[j], Zero, log(X[j]), Zero); // if X[j] < 0 Sum += CppAD::CondExpLt(X[j], Zero, log(-X[j]), Zero); // if X[j] == 0 Sum += CppAD::CondExpEq(X[j], Zero, MinusInfinity, Zero); } // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = Sum; // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // vectors for arguments to the function object f CPPAD_TESTVECTOR(double) x(n); // argument values CPPAD_TESTVECTOR(double) y(m); // function values CPPAD_TESTVECTOR(double) w(m); // function weights CPPAD_TESTVECTOR(double) dw(n); // derivative of weighted function // a case where fabs( x[j] ) > 0 for all j double check = 0.; double sign = 1.; for(j = 0; j < n; j++) { sign *= -1.; x[j] = sign * double(j + 1); check += log( fabs( x[j] ) ); } // function value y = f.Forward(0, x); ok &= ( y[0] == check ); // compute derivative of y[0] w[0] = 1.; dw = f.Reverse(1, w); for(j = 0; j < n; j++) { if( x[j] > 0. ) ok &= NearEqual(dw[j], 1./fabs( x[j] ), eps, eps); else ok &= NearEqual(dw[j], -1./fabs( x[j] ), eps, eps); } // a case where x[0] is equal to zero sign = 1.; for(j = 0; j < n; j++) { sign *= -1.; x[j] = sign * double(j); } // function value y = f.Forward(0, x); ok &= ( y[0] == -Infinity(0.) ); // compute derivative of y[0] f.check_for_nan(false); w[0] = 1.; dw = f.Reverse(1, w); for(j = 0; j < n; j++) { if( x[j] > 0. ) ok &= NearEqual(dw[j], 1./fabs( x[j] ), eps, eps); else if( x[j] < 0. ) ok &= NearEqual(dw[j], -1./fabs( x[j] ), eps, eps); else ok &= NearEqual(dw[j], 0.0, eps, eps); } return ok; } } // end empty namespace bool CondExp(void) { bool ok = true; ok &= CondExp_ppvv(); ok &= CondExp_pvvv(); ok &= CondExp_vpvv(); ok &= CondExp_vvpv(); ok &= CondExp_vvvp(); ok &= SecondOrderReverse(); ok &= OldExample(); return ok; } // END C++ ================================================ FILE: test_more/general/cond_exp_ad.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Test of CondExp with AD< AD< Base > > types */ # include typedef CppAD::AD< double > ADdouble; typedef CppAD::AD< ADdouble > ADADdouble; namespace { // BEGIN empty namespace bool CondExpADOne(void) { bool ok = true; using namespace CppAD; size_t n = 3; size_t m = 8; // ADdouble independent variable vector CPPAD_TESTVECTOR( ADdouble ) Xa(n); Xa[0] = -1.; Xa[1] = 0.; Xa[2] = 1.; Independent(Xa); // ADdouble independent variable vector CPPAD_TESTVECTOR( ADADdouble ) Xaa(n); Xaa[0] = Xa[0]; Xaa[1] = Xa[1]; Xaa[2] = Xa[2]; Independent(Xaa); // ADADdouble parameter ADADdouble p = ADADdouble(Xa[0]); ADADdouble q = ADADdouble(Xa[1]); ADADdouble r = ADADdouble(Xa[2]); // ADADdouble dependent variable vector CPPAD_TESTVECTOR( ADADdouble ) Yaa(m); // CondExp(parameter, parameter, parameter) Yaa[0] = CondExp(p, q, r); // CondExp(parameter, parameter, variable) Yaa[1] = CondExp(p, q, Xaa[2]); // CondExp(parameter, variable, parameter) Yaa[2] = CondExp(p, Xaa[1], r); // CondExp(parameter, variable, variable) Yaa[3] = CondExp(p, Xaa[1], Xaa[2]); // CondExp(variable, variable, variable) Yaa[5] = CondExp(Xaa[0], Xaa[1], Xaa[2]); // CondExp(variable, variable, parameter) Yaa[4] = CondExp(Xaa[0], Xaa[1], r); // CondExp(variable, parameter, variable) Yaa[6] = CondExp(Xaa[0], q, Xaa[2]); // CondExp(variable, parameter, parameter) Yaa[7] = CondExp(Xaa[0], q, r); // create fa: Xaa -> Yaa function object ADFun< ADdouble > fa(Xaa, Yaa); // function values CPPAD_TESTVECTOR( ADdouble ) Ya(m); Ya = fa.Forward(0, Xa); // create f: Xa -> Ya function object ADFun f(Xa, Ya); // check result of function evaluation CPPAD_TESTVECTOR(double) x(n); CPPAD_TESTVECTOR(double) y(m); x[0] = 1.; x[1] = 0.; x[2] = -1.; y = f.Forward(0, x); size_t i; for(i = 0; i < m; i++) { // y[i] = CondExp(x[0], x[1], x[2]) if( x[0] > 0 ) ok &= (y[i] == x[1]); else ok &= (y[i] == x[2]); } // check forward mode derivatives CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dx[1] = 2.; dx[2] = 3.; dy = f.Forward(1, dx); for(i = 0; i < m; i++) { if( x[0] > 0. ) ok &= (dy[i] == dx[1]); else ok &= (dy[i] == dx[2]); } // calculate Jacobian CPPAD_TESTVECTOR(double) J(m * n); size_t j; for(i = 0; i < m; i++) { for(j = 0; j < n; j++) J[i * n + j] = 0.; if( x[0] > 0. ) J[i * n + 1] = 1.; else J[i * n + 2] = 1.; } // check reverse mode derivatives for(i = 0; i < m; i++) dy[i] = double(i); dx = f.Reverse(1, dy); double sum; for(j = 0; j < n; j++) { sum = 0; for(i = 0; i < m; i++) sum += dy[i] * J[i * n + j]; ok &= (sum == dx[j]); } // forward mode computation of sparsity pattern CPPAD_TESTVECTOR(bool) Px(n * n); for(i = 0; i < n; i++) { for(j = 0; j < n; j++) Px[i * n + j] = false; Px[i * n + i] = true; } CPPAD_TESTVECTOR(bool) Py(m * n); Py = f.ForSparseJac(n, Px); for(i = 0; i < m; i++) { ok &= Py[ i * n + 0 ] == false; ok &= Py[ i * n + 1 ] == true; ok &= Py[ i * n + 2 ] == true; } // reverse mode computation of sparsity pattern Py.resize(m * m); for(i = 0; i < m; i++) { for(j = 0; j < m; j++) Py[i * m + j] = false; Py[i * m + i] = true; } Px.resize(m * n); Px = f.RevSparseJac(m, Py); for(i = 0; i < m; i++) { for(j = 0; j < n; j++) ok &= ( Px[i * n + j] == ( j > 0 ) ); } return ok; } bool CondExpADTwo(void) { bool ok = true; using namespace CppAD; size_t n = 3; size_t m = 8; // ADdouble independent variable vector CPPAD_TESTVECTOR( ADdouble ) Xa(n); Xa[0] = -1.; Xa[1] = 0.; Xa[2] = 1.; Independent(Xa); // use VecAD so that sparsity results are local VecAD Va(1); ADdouble zero = 0.; Va[zero] = Xa[0]; // ADdouble independent variable vector CPPAD_TESTVECTOR( ADADdouble ) Xaa(n); Xaa[0] = ADdouble( Va[zero] ); Xaa[1] = Xa[1]; Xaa[2] = Xa[2]; Independent(Xaa); // ADADdouble parameter ADADdouble p = ADADdouble(Xa[0]); ADADdouble q = ADADdouble(Xa[1]); ADADdouble r = ADADdouble(Xa[2]); // ADADdouble dependent variable vector CPPAD_TESTVECTOR( ADADdouble ) Yaa(m); // CondExp(parameter, parameter, parameter) Yaa[0] = CondExp(p, q, r); // CondExp(parameter, parameter, variable) Yaa[1] = CondExp(p, q, Xaa[2]); // CondExp(parameter, variable, parameter) Yaa[2] = CondExp(p, Xaa[1], r); // CondExp(parameter, variable, variable) Yaa[3] = CondExp(p, Xaa[1], Xaa[2]); // CondExp(variable, variable, variable) Yaa[5] = CondExp(Xaa[0], Xaa[1], Xaa[2]); // CondExp(variable, variable, parameter) Yaa[4] = CondExp(Xaa[0], Xaa[1], r); // CondExp(variable, parameter, variable) Yaa[6] = CondExp(Xaa[0], q, Xaa[2]); // CondExp(variable, parameter, parameter) Yaa[7] = CondExp(Xaa[0], q, r); // create fa: Xaa -> Yaa function object ADFun< ADdouble > fa(Xaa, Yaa); // function values CPPAD_TESTVECTOR( ADdouble ) Ya(m); Ya = fa.Forward(0, Xa); // create f: Xa -> Ya function object ADFun f(Xa, Ya); // check use_VecAD ok &= f.use_VecAD(); // check result of function evaluation CPPAD_TESTVECTOR(double) x(n); CPPAD_TESTVECTOR(double) y(m); x[0] = 1.; x[1] = 0.; x[2] = -1.; y = f.Forward(0, x); size_t i; for(i = 0; i < m; i++) { // y[i] = CondExp(x[0], x[1], x[2]) if( x[0] > 0 ) ok &= (y[i] == x[1]); else ok &= (y[i] == x[2]); } // check forward mode derivatives CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dx[1] = 2.; dx[2] = 3.; dy = f.Forward(1, dx); for(i = 0; i < m; i++) { if( x[0] > 0. ) ok &= (dy[i] == dx[1]); else ok &= (dy[i] == dx[2]); } // calculate Jacobian CPPAD_TESTVECTOR(double) J(m * n); size_t j; for(i = 0; i < m; i++) { for(j = 0; j < n; j++) J[i * n + j] = 0.; if( x[0] > 0. ) J[i * n + 1] = 1.; else J[i * n + 2] = 1.; } // check reverse mode derivatives for(i = 0; i < m; i++) dy[i] = double(i); dx = f.Reverse(1, dy); double sum; for(j = 0; j < n; j++) { sum = 0; for(i = 0; i < m; i++) sum += dy[i] * J[i * n + j]; ok &= (sum == dx[j]); } // forward mode computation of sparsity pattern CPPAD_TESTVECTOR(bool) Px(n * n); for(i = 0; i < n; i++) { for(j = 0; j < n; j++) Px[i * n + j] = false; Px[i * n + i] = true; } CPPAD_TESTVECTOR(bool) Py(m * n); Py = f.ForSparseJac(n, Px); for(i = 0; i < m; i++) { for(j = 0; j < n; j++) // sparsity pattern works for both true and false cases. ok &= ( Py[i * n + j] == (j > 0) ); } // reverse mode computation of sparsity pattern Py.resize(m * m); for(i = 0; i < m; i++) { for(j = 0; j < m; j++) Py[i * m + j] = false; Py[i * m + i] = true; } Px.resize(m * n); Px = f.RevSparseJac(m, Py); for(i = 0; i < m; i++) { for(j = 0; j < n; j++) ok &= ( Px[i * n + j] == (j > 0) ); } return ok; } } // END empty namespace bool CondExpAD(void) { bool ok = true; ok &= CondExpADOne(); ok &= CondExpADTwo(); return ok; } ================================================ FILE: test_more/general/cond_exp_rev.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // Test that reverse mode handles conditional expressions properly // in that infinity and nans do not propagate thouh un-used case. # include bool cond_exp_rev(void) { bool ok = true; using CppAD::vector; using CppAD::AD; AD anan = std::numeric_limits::quiet_NaN(); AD azero = 0.0; size_t n = 2; vector< AD > ax(n), ay; ax[0] = 1.0; ax[1] = anan; Independent(ax); // AbsOp ay.push_back( CondExpGt(ax[0], azero, ax[0], fabs(ax[1]) )); // AcosOp ay.push_back( CondExpGt(ax[0], azero, ax[0], acos(ax[1]) )); // AddvvOp ay.push_back( CondExpGt(ax[0], azero, ax[0], ax[0] + ax[1] )); // AddpvOp ay.push_back( CondExpGt(ax[0], azero, ax[0], 1.0 + ax[1] )); // AsinOp ay.push_back( CondExpGt(ax[0], azero, ax[0], asin(ax[1]) )); // AtanOp ay.push_back( CondExpGt(ax[0], azero, ax[0], atan(ax[1]) )); // CosOp ay.push_back( CondExpGt(ax[0], azero, ax[0], cos(ax[1]) )); // CoshOp ay.push_back( CondExpGt(ax[0], azero, ax[0], cosh(ax[1]) )); // DivvvOp ay.push_back( CondExpGt(ax[0], azero, ax[0], ax[0] / ax[1] )); // DivpvOp ay.push_back( CondExpGt(ax[0], azero, ax[0], 1.0 / ax[1] )); // DivvpOp ay.push_back( CondExpGt(ax[0], azero, ax[0], ax[1] / 2.0 )); // ErfOp ay.push_back( CondExpGt(ax[0], azero, ax[0], erf(ax[1]) )); // ExpOp ay.push_back( CondExpGt(ax[0], azero, ax[0], exp(ax[1]) )); // LogOp ay.push_back( CondExpGt(ax[0], azero, ax[0], log(ax[1]) )); // MulvvOp ay.push_back( CondExpGt(ax[0], azero, ax[0], ax[0] * ax[1] )); // MulpvOp ay.push_back( CondExpGt(ax[0], azero, ax[0], 2.0 * ax[1] )); // PowvvOP // uses check in log, mul, and exp ay.push_back( CondExpGt(ax[0], azero, ax[0], pow(ax[1], ax[1]) )); // PowvpOP // uses check in log, mul, and exp ay.push_back( CondExpGt(ax[0], azero, ax[0], pow(ax[1], 2.0) )); // PowpvOP // uses check in log, mul, and exp ay.push_back( CondExpGt(ax[0], azero, ax[0], pow(2.0, ax[1]) )); // SignOp ay.push_back( CondExpGt(ax[0], azero, ax[0], sign(ax[1]) )); // SinOp ay.push_back( CondExpGt(ax[0], azero, ax[0], sin(ax[1]) )); // SinhOp ay.push_back( CondExpGt(ax[0], azero, ax[0], sinh(ax[1]) )); // SqrtOp ay.push_back( CondExpGt(ax[0], azero, ax[0], sqrt(ax[1]) )); // SubvvOp ay.push_back( CondExpGt(ax[0], azero, ax[0], ax[0] - ax[1] )); // SubpvOp ay.push_back( CondExpGt(ax[0], azero, ax[0], 1.0 - ax[1] )); // SubvpOp ay.push_back( CondExpGt(ax[0], azero, ax[0], ax[1] - 1.0 )); // TanOp ay.push_back( CondExpGt(ax[0], azero, ax[0], tan(ax[1]) )); // TanhOp ay.push_back( CondExpGt(ax[0], azero, ax[0], tanh(ax[1]) )); // create f : x -> y size_t m = ay.size(); CppAD::ADFun f(ax, ay); // weighting vector and reverse mode derivative vector w(m), dw(n); for(size_t i = 0; i < m; i++) w[i] = 0.0; // check DivvOp for(size_t i = 0; i < m; i++) { w[i] = 1.0; dw = f.Reverse(1, w); ok &= dw[0] == 1.0; ok &= dw[1] == 0.0; w[i] = 0.0; } return ok; } ================================================ FILE: test_more/general/copy.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old Copy example now used just for valiadation testing */ # include namespace { // begin empty namespace bool copy_older(void) { bool ok = true; using namespace CppAD; // independent variable vector, indices, values, and declaration CPPAD_TESTVECTOR(AD) U(1); size_t is = 0; U[is] = 2.; Independent(U); // create an AD that does not depend on s AD t = 3.; // use copy constructor AD x(U[is]); AD y(t); // check which are parameters ok &= ! Parameter(x); ok &= Parameter(y); // dependent variable vector, indices, and values CPPAD_TESTVECTOR(AD) Z(2); size_t ix = 0; size_t iy = 1; Z[ix] = x; Z[iy] = y; // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v( f.Domain() ); CPPAD_TESTVECTOR(double) w( f.Range() ); // check parameters flags ok &= ! f.Parameter(ix); ok &= f.Parameter(iy); // check function values ok &= ( Z[ix] == 2. ); ok &= ( Z[iy] == 3. ); // forward computation of partials w.r.t. s v[is] = 1.; w = f.Forward(1, v); ok &= ( w[ix] == 1. ); // dx/ds ok &= ( w[iy] == 0. ); // dy/ds return ok; } bool copy_ad(void) { bool ok = true; // initialize test result flag using CppAD::AD; // so can use AD in place of CppAD::AD // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) x(n); x[0] = 2.; // declare independent variables and start tape recording CppAD::Independent(x); // create an AD that does not depend on x AD b = 3.; // use copy constructor AD u(x[0]); AD v = b; // check which are parameters ok &= Variable(u); ok &= Parameter(v); // range space vector size_t m = 2; CPPAD_TESTVECTOR(AD) y(m); y[0] = u; y[1] = v; // create f: x -> y and vectors used for derivative calculations CppAD::ADFun f(x, y); CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); // check parameters flags ok &= ! f.Parameter(0); ok &= f.Parameter(1); // check function values ok &= ( y[0] == 2. ); ok &= ( y[1] == 3. ); // forward computation of partials w.r.t. x[0] dx[0] = 1.; dy = f.Forward(1, dx); ok &= ( dy[0] == 1. ); // du / dx ok &= ( dy[1] == 0. ); // dv / dx return ok; } bool copy_base(void) { bool ok = true; // initialize test result flag using CppAD::AD; // so can use AD in place of CppAD::AD // construct directly from Base where Base is double AD x(1.); // construct from a type that converts to Base where Base is double AD y = 2; // construct from a type that converts to Base where Base = AD AD< AD > z(3); // check that resulting objects are parameters ok &= Parameter(x); ok &= Parameter(y); ok &= Parameter(z); // check values of objects (compare AD with double) ok &= ( x == 1.); ok &= ( y == 2.); ok &= ( Value(z) == 3.); // user constructor through the static_cast template function x = static_cast < AD >( 4 ); z = static_cast < AD< AD > >( 5 ); ok &= ( x == 4. ); ok &= ( Value(z) == 5. ); return ok; } bool default_ctor(void) { bool ok = true; using CppAD::AD; // default AD constructor AD x, y; // check that they are parameters ok &= Parameter(x); ok &= Parameter(y); // assign them values x = 3.; y = 4.; // just check a simple operation ok &= (x + y == 7.); return ok; } // END PROGRAM } // end empty namespace bool copy(void) { bool ok = true; ok &= copy_older(); ok &= copy_ad(); ok &= copy_base(); ok &= default_ctor(); return ok; } ================================================ FILE: test_more/general/cos.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old example now just used for validation testing */ # include bool Cos(void) { bool ok = true; using CppAD::sin; using CppAD::cos; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector CPPAD_TESTVECTOR(AD) U(1); U[0] = 1.; Independent(U); // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); Z[0] = cos(U[0]); // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v(1); CPPAD_TESTVECTOR(double) w(1); // check value double sin_u = sin( Value(U[0]) ); double cos_u = cos( Value(U[0]) ); ok &= NearEqual(cos_u, Value(Z[0]), eps99 , eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; v[0] = 1.; for(j = 1; j < p; j++) { w = f.Forward(j, v); double value; if( j % 4 == 1 ) value = -sin_u; else if( j % 4 == 2 ) value = -cos_u; else if( j % 4 == 3 ) value = sin_u; else value = cos_u; jfac *= double(j); ok &= NearEqual(w[0], value/jfac, eps99, eps99); // d^jz/du^j v[0] = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r(p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; for(j = 0; j < p; j++) { double value; if( j % 4 == 0 ) value = -sin_u; else if( j % 4 == 1 ) value = -cos_u; else if( j % 4 == 2 ) value = sin_u; else value = cos_u; ok &= NearEqual(r[j], value/jfac, eps99, eps99); // d^jz/du^j jfac *= double(j + 1); } return ok; } ================================================ FILE: test_more/general/cosh.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old example now just used for validation testing. */ # include bool Cosh(void) { bool ok = true; using CppAD::sinh; using CppAD::cosh; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector CPPAD_TESTVECTOR(AD) U(1); U[0] = 1.; Independent(U); // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); Z[0] = cosh(U[0]); // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v(1); CPPAD_TESTVECTOR(double) w(1); // check value double sinh_u = sinh( Value(U[0]) ); double cosh_u = cosh( Value(U[0]) ); ok &= NearEqual(cosh_u, Value(Z[0]), eps99 , eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; v[0] = 1.; for(j = 1; j < p; j++) { w = f.Forward(j, v); double value; if( j % 2 == 1 ) value = sinh_u; else value = cosh_u; jfac *= double(j); ok &= NearEqual(w[0], value/jfac, eps99, eps99); // d^jz/du^j v[0] = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r(p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; for(j = 0; j < p; j++) { double value; if( j % 2 == 0 ) value = sinh_u; else value = cosh_u; ok &= NearEqual(r[j], value/jfac, eps99, eps99); // d^jz/du^j jfac *= double(j + 1); } return ok; } ================================================ FILE: test_more/general/cpp_graph.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin graph_unary_op.cpp$$ $spell sin $$ $section Graph Unary Operator: Example and Test$$ $head Source Code$$ $srcthisfile%0%// BEGIN C++%// END C++%1%$$ $end */ // BEGIN C++ # include namespace { // BEGIN_EMPTY_NAMESPACE typedef double (*unary_fun_t)(double); bool test_unary_fun(unary_fun_t fun, CppAD::graph::graph_op_enum op_enum) { bool ok = true; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // C++ graph object CppAD::cpp_graph graph_obj; // // value of constant in function CPPAD_TESTVECTOR(double) p(1), x(1), c(1); c[0] = 0.1; p[0] = 0.2; x[0] = 0.3; if( std::isnan( fun( c[0] ) ) ) c[0] = 1.0; if( std::isnan( fun( p[0] ) ) ) p[0] = 2.0; if( std::isnan( fun( x[0] ) ) ) x[0] = 3.0; // // set scalars graph_obj.function_name_set("unary_op example"); size_t n_dynamic_ind = 1; graph_obj.n_dynamic_ind_set(n_dynamic_ind); size_t n_variable_ind = 1; graph_obj.n_variable_ind_set(n_variable_ind); graph_obj.constant_vec_push_back( c[0] ); // // node_4 : sin(p[0]) graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(1); // // node_5 : sin(x[0]) graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(2); // // node_6 : sin(c[0]) graph_obj.operator_vec_push_back(op_enum); graph_obj.operator_arg_push_back(3); // // y[0] = sin(p[0]) graph_obj.dependent_vec_push_back(4); // y[1] = sin(x[0]) graph_obj.dependent_vec_push_back(5); // y[2] = sin(c[0]) graph_obj.dependent_vec_push_back(6); // // f(p, x) = y CppAD::ADFun f; f.from_graph(graph_obj); ok &= f.Domain() == 1; ok &= f.size_dyn_ind() == 1; ok &= f.Range() == 3; // // // compute y = f(p, x) f.new_dynamic(p); CPPAD_TESTVECTOR(double) y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], fun(p[0]), eps99, eps99); ok &= CppAD::NearEqual(y[1], fun(x[0]), eps99, eps99); ok &= CppAD::NearEqual(y[2], fun(c[0]), eps99, eps99); // ------------------------------------------------------------------ // Convert to Graph graph and back again f.to_graph(graph_obj); f.from_graph(graph_obj); // ------------------------------------------------------------------- // // compute y = f(p, x) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], fun(p[0]), eps99, eps99); ok &= CppAD::NearEqual(y[1], fun(x[0]), eps99, eps99); ok &= CppAD::NearEqual(y[2], fun(c[0]), eps99, eps99); // return ok; } double sign(double x) { return CppAD::sign(x); } double neg(double x) { return - x; } } // END_EMPTY_NAMESPACE bool cpp_graph(void) { bool ok = true; ok &= test_unary_fun(std::fabs, CppAD::graph::abs_graph_op); ok &= test_unary_fun(std::acos, CppAD::graph::acos_graph_op); ok &= test_unary_fun(std::acosh, CppAD::graph::acosh_graph_op); ok &= test_unary_fun(std::asinh, CppAD::graph::asinh_graph_op); ok &= test_unary_fun(std::atanh, CppAD::graph::atanh_graph_op); ok &= test_unary_fun(std::erf, CppAD::graph::erf_graph_op); ok &= test_unary_fun(std::erfc, CppAD::graph::erfc_graph_op); ok &= test_unary_fun(std::expm1, CppAD::graph::expm1_graph_op); ok &= test_unary_fun(std::log1p, CppAD::graph::log1p_graph_op); ok &= test_unary_fun(neg, CppAD::graph::neg_graph_op); ok &= test_unary_fun(sign, CppAD::graph::sign_graph_op); ok &= test_unary_fun(std::sinh, CppAD::graph::sinh_graph_op); ok &= test_unary_fun(std::sin, CppAD::graph::sin_graph_op); ok &= test_unary_fun(std::sqrt, CppAD::graph::sqrt_graph_op); ok &= test_unary_fun(std::tanh, CppAD::graph::tanh_graph_op); ok &= test_unary_fun(std::tan, CppAD::graph::tan_graph_op); ok &= test_unary_fun(std::acosh, CppAD::graph::acosh_graph_op); ok &= test_unary_fun(std::acosh, CppAD::graph::acosh_graph_op); ok &= test_unary_fun(std::sin, CppAD::graph::sin_graph_op); // return ok; } ================================================ FILE: test_more/general/cppad_eigen.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Test of Eigen Interface to CppAD Scalar Types $end */ # include bool cppad_eigen(void) { bool ok = true; using CppAD::AD; using Eigen::Dynamic; using Eigen::Matrix; typedef Eigen::NumTraits > traits; ok &= traits::IsComplex == 0; ok &= traits::IsInteger == 0; ok &= traits::IsSigned == 1; ok &= traits::RequireInitialization == 1; ok &= traits::ReadCost == 1; ok &= traits::AddCost == 2; ok &= traits::MulCost == 2; ok &= traits::epsilon() == std::numeric_limits::epsilon(); ok &= traits::dummy_precision() == 100.* std::numeric_limits::epsilon(); ok &= traits::highest() == std::numeric_limits::max(); ok &= traits::lowest() == std::numeric_limits::min(); ok &= std::isnan(traits::quiet_NaN()); ok &= std::isinf(traits::infinity()); AD x = 2.0; ok &= conj(x) == x; ok &= real(x) == x; ok &= imag(x) == 0.0; ok &= abs2(x) == 4.0; ok &= (!std::isinf(x)); ok &= (!std::isnan(x)); x = traits::quiet_NaN(); ok &= std::isnan(x); x = traits::infinity(); ok &= std::isinf(x); // Outputting a matrix used to fail before partial specialization of // struct significant_decimals_default_impl in cppad_eigen.hpp. Matrix< AD, 1, 1> X; X(0, 0) = AD(1); std::stringstream stream_out; stream_out << X; ok &= "1" == stream_out.str(); // multiplying three matrices together used to cause warning // before making ctor from arbitrary type to AD explicit. typedef CppAD::AD AScalar; Matrix A(1,1), B(1,1), C(1,1), D(1,1); A(0,0) = 1.0; B(0,0) = 2.0; C(0,0) = 3.0; D = A * B * C; ok &= D(0,0) == 6.0 ; // Multiplying Eigen objects (such as matrices) of element types used to // fail before appropriate "ScalarBinaryOpTraits" was added. const int nn = 3; Matrix d_matrix = Matrix::Zero(); Matrix a_matrix = Matrix::Zero(); a_matrix = a_matrix * d_matrix; a_matrix = d_matrix * a_matrix; ok &= a_matrix(0, 0) == AScalar(0); // Test multiply matrix elements of mixed types // (This worked before corresponding ScalarBinaryOpTraits was added.) AScalar a_scalar = a_matrix(0,0) * d_matrix(0,0); ok &= a_scalar == AScalar(0); a_scalar = d_matrix(0, 0) * a_matrix(0, 0); ok &= a_scalar == AScalar(0); return ok; } ================================================ FILE: test_more/general/cppad_vector.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- // CppAD::vector tests # include # include # include namespace { // BEGIN_EMPTY_NAMESPACE bool test_find(void) { // find requires an input iterator bool ok = true; typedef CppAD::vector::const_iterator const_iterator; // size_t n = 10; CppAD::vector vec(n); for(size_t i = 0; i < n; ++i) vec[i] = static_cast( '0' + i); const_iterator itr; // itr = std::find(vec.begin(), vec.end(), '3'); ok &= itr != vec.end(); ok &= *itr == '3'; // itr = std::find(vec.begin(), vec.end(), 'a'); ok &= itr == vec.end(); // return ok; } bool test_copy(void) { // copy requires a forward iterator bool ok = true; // size_t n = 10; CppAD::vector vec(n), des(n); for(size_t i = 0; i < n; ++i) vec[i] = static_cast( '0' + i); const CppAD::vector src(vec); // // src.begin(), src.end() are const_iterator // des.begin() is an iterator std::copy(src.begin(), src.end(), des.begin()); // for(size_t i = 0; i < n; ++i) ok &= src[i] == des[i]; // return ok; } bool test_reverse(void) { // copy requires a bidirectional iterator bool ok = true; // size_t n = 10; CppAD::vector src(n), des(n); for(size_t i = 0; i < n; ++i) src[i] = static_cast( '0' + i); // std::reverse(src.begin(), src.end()); // for(size_t i = 0; i < n; ++i) ok &= src[i] == static_cast( '0' + (n - i - 1) ); // return ok; } bool test_sort(void) { // copy requires a random access iterator bool ok = true; // size_t n = 10; CppAD::vector vec(n); for(size_t i = 0; i < n; ++i) vec[i] = static_cast( '0' + (n - i - 1) ); // std::sort(vec.begin(), vec.end()); // for(size_t i = 0; i < n; ++i) ok &= vec[i] == static_cast( '0' + i ); // return ok; } } // END_EMPTY_NAMESPACE bool cppad_vector(void) { bool ok = true; // ok &= test_find(); ok &= test_copy(); ok &= test_reverse(); ok &= test_sort(); // return ok; } ================================================ FILE: test_more/general/dbl_epsilon.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Check the value of machine epsilon is accurate enough for the correctness tests */ # include # include bool dbl_epsilon(void) { bool ok = true; // CppAD correctness tests assume machine epsilon is less than 1e-13 ok &= DBL_EPSILON < 1e-13; ok &= std::numeric_limits::digits10 >= 13; return ok; } ================================================ FILE: test_more/general/dependency.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old example for deprecated interface. $spell CppAD Jac $$ $section Computing Dependency: Example and Test$$ $head Discussion$$ The partial of an dependent variable with respect to an independent variable might always be zero even though the dependent variable depends on the value of the dependent variable. Consider the following case $latex \[ f(x) = {\rm sign} (x) = \left\{ \begin{array}{rl} +1 & {\rm if} \; x > 0 \\ 0 & {\rm if} \; x = 0 \\ -1 & {\rm if} \; x < 0 \end{array} \right. \] $$ In this case the value of $latex f(x)$$ depends on the value of $latex x$$ but CppAD always returns zero for the derivative of the $cref sign$$ function. $head Dependency Pattern$$ If the $th i$$ dependent variables depends on the value of the $th j$$ independent variable, the corresponding entry in the dependency pattern is non-zero (true). Otherwise it is zero (false). CppAD uses $cref/sparsity patterns/glossary/Sparsity Pattern/$$ to represent dependency matrices. The $icode dependency$$ argument to $cref/ForSparseJac/ForSparseJac/dependency/$$ and $cref/RevSparseJac/RevSparseJac/dependency/$$ is a flag that signals that the dependency pattern (instead of the sparsity pattern) is computed. $srcthisfile%0%// BEGIN C++%// END C++%1%$$ $end */ // BEGIN C++ # include namespace { double heavyside(const double& x) { if( x <= 0.0 ) return 0.0; return 1.0; } CPPAD_DISCRETE_FUNCTION(double, heavyside) } bool dependency(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; // VecAD object for use later CppAD::VecAD vec_ad(2); vec_ad[0] = 0.0; vec_ad[1] = 1.0; // domain space vector size_t n = 5; CPPAD_TESTVECTOR(AD) ax(n); for(size_t j = 0; j < n; j++) ax[j] = AD(j + 1); // declare independent variables and start tape recording CppAD::Independent(ax); // some AD constants AD azero(0.0), aone(1.0); // range space vector size_t m = n; size_t m1 = n - 1; CPPAD_TESTVECTOR(AD) ay(m); ay[m1-0] = sign( ax[0] ); ay[m1-1] = CondExpLe( ax[1], azero, azero, aone); ay[m1-2] = CondExpLe( azero, ax[2], azero, aone); ay[m1-3] = heavyside( ax[3] ); ay[m1-4] = vec_ad[ ax[4] - AD(4.0) ]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // ----------------------------------------------------------- // ForSparseJac and bool dependency bool transpose = false; bool dependency; // could replace CppAD::vectorBooll by CPPAD_TESTVECTOR CppAD::vectorBool eye_bool(n * n), depend_bool(m * n); for(size_t i = 0; i < n; i++) { for(size_t j = 0; j < n; j++) eye_bool[i * n + j] = (i == j); } dependency = true; depend_bool = f.ForSparseJac(n, eye_bool, transpose, dependency); for(size_t i = 0; i < m; i++) { for(size_t j = 0; j < n; j++) ok &= depend_bool[i * n + j] == (i == (m1-j)); } dependency = false; depend_bool = f.ForSparseJac(n, eye_bool, transpose, dependency); for(size_t i = 0; i < m; i++) { for(size_t j = 0; j < n; j++) ok &= depend_bool[i * n + j] == false; } // ----------------------------------------------------------- // RevSparseJac and set dependency CppAD::vector< std::set > eye_set(m), depend_set(m); for(size_t i = 0; i < m; i++) { ok &= eye_set[i].empty(); eye_set[i].insert(i); } dependency = true; depend_set = f.RevSparseJac(n, eye_set, transpose, dependency); for(size_t i = 0; i < m; i++) { std::set check; check.insert(m1 - i); ok &= depend_set[i] == check; } dependency = false; depend_set = f.RevSparseJac(n, eye_set, transpose, dependency); for(size_t i = 0; i < m; i++) ok &= depend_set[i].empty(); return ok; } // END C++ ================================================ FILE: test_more/general/div.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Two old Div examples now used just for valiadation testing */ # include namespace { // BEGIN empty namespace bool DivTestOne(void) { bool ok = true; using namespace CppAD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // assign some parameters AD zero = 0.; AD one = 1.; // independent variable vector, indices, values, and declaration CPPAD_TESTVECTOR(AD) U(2); size_t s = 0; size_t t = 1; U[s] = 2.; U[t] = 3.; Independent(U); // dependent variable vector and indices CPPAD_TESTVECTOR(AD) Z(6); size_t x = 0; size_t y = 1; size_t z = 2; size_t u = 3; size_t v = 4; size_t w = 5; // dependent variables Z[x] = U[s] / U[t]; // AD / AD Z[y] = Z[x] / 4.; // AD / double Z[z] = 5. / Z[y]; // double / AD Z[u] = Z[z] / one; // division by a parameter equal to one Z[v] = Z[z] / 1.; // division by a double equal to one Z[w] = zero / Z[z]; // division into a parameter equal to zero // check division into a zero valued parameter results in a parameter // (must do this before creating f because it erases the tape) ok &= Parameter(Z[w]); // create f : U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) q( f.Domain() ); CPPAD_TESTVECTOR(double) r( f.Range() ); // check parameter flag ok &= f.Parameter(w); // check values ok &= NearEqual( Z[x] , 2. / 3. , eps99, eps99); ok &= NearEqual( Z[y] , 2. / ( 3. * 4. ) , eps99, eps99); ok &= NearEqual( Z[z] , 5. * 3. * 4. / 2. , eps99, eps99); ok &= ( Z[w] == 0. ); ok &= ( Z[u] == Z[z] ); // forward computation of partials w.r.t. s q[s] = 1.; q[t] = 0.; r = f.Forward(1, q); ok &= NearEqual(r[x], 1./U[t], eps99, eps99); // dx/ds ok &= NearEqual(r[y], 1./(U[t]*4.), eps99, eps99); // dy/ds ok &= NearEqual(r[z], -5.*U[t]*4./(U[s]*U[s]), eps99, eps99); // dz/ds ok &= ( r[u] == r[z] ); // du/ds ok &= ( r[v] == r[z] ); // dv/ds ok &= ( r[w] == 0. ); // dw/ds // forward computation in the direction (1, 1) q[s] = 1.; q[t] = 1.; r = f.Forward(1, q); ok &= NearEqual(r[x], 1./U[t] - U[s]/(U[t] * U[t]), eps99, eps99); // second order reverse mode computation CPPAD_TESTVECTOR(double) Q( f.Domain() * 2 ); r[x] = 1.; r[y] = r[z] = r[u] = r[v] = r[w] = 0.; Q = f.Reverse(2, r); ok &= NearEqual( Q[s * f.Domain() + 1], - 1. / (U[t] * U[t]), eps99, eps99 ); return ok; } bool DivTestTwo(void) { bool ok = true; using namespace CppAD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector double u0 = .5; CPPAD_TESTVECTOR(AD) U(1); U[0] = u0; Independent(U); AD a = U[0] / 1.; // AD / double AD b = a / 2; // AD / int AD c = 3. / b; // double / AD AD d = 4 / c; // int / AD // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); Z[0] = U[0] * U[0] / d; // AD / AD // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v(1); CPPAD_TESTVECTOR(double) w(1); // check value ok &= NearEqual(Value(Z[0]) , u0*u0/(4/(3/(u0/2))), eps99, eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; v[0] = 1.; double value = 6. / 4.; for(j = 1; j < p; j++) { jfac *= double(j); w = f.Forward(j, v); ok &= NearEqual(w[0], value/jfac, eps99, eps99); // d^jz/du^j v[0] = 0.; value = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r(p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; value = 6. / 4.; for(j = 0; j < p; j++) { ok &= NearEqual(r[j], value/jfac, eps99, eps99); // d^jz/du^j jfac *= double(j + 1); value = 0.; } return ok; } bool DivTestThree(void) { bool ok = true; using namespace CppAD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // more testing of variable / variable case double x0 = 2.; double x1 = 3.; size_t n = 2; CPPAD_TESTVECTOR(AD) X(n); X[0] = x0; X[1] = x1; Independent(X); size_t m = 1; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = X[0] / X[1]; ADFun f(X, Y); CPPAD_TESTVECTOR(double) dx(n), dy(m); double check; dx[0] = 1.; dx[1] = 1.; dy = f.Forward(1, dx); check = 1. / x1 - x0 / (x1 * x1); ok &= NearEqual(dy[0], check, eps99, eps99); CPPAD_TESTVECTOR(double) w(m), dw(n); w[0] = 1.; dw = f.Reverse(1, w); check = 1. / x1; ok &= NearEqual(dw[0], check, eps99, eps99); check = - x0 / (x1 * x1); ok &= NearEqual(dw[1], check, eps99, eps99); return ok; } } // END empty namespace bool Div(void) { bool ok = true; ok &= DivTestOne(); ok &= DivTestTwo(); ok &= DivTestThree(); return ok; } ================================================ FILE: test_more/general/div_eq.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old DivEq example now used just for valiadation testing */ # include namespace { // BEGIN empty namespace bool DivEqTestOne(void) { bool ok = true; using namespace CppAD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector, indices, values, and declaration CPPAD_TESTVECTOR(AD) U(2); size_t s = 0; size_t t = 1; U[s] = 3.; U[t] = 2.; Independent(U); // dependent variable vector and indices CPPAD_TESTVECTOR(AD) Z(2); size_t x = 0; size_t y = 1; // some constants AD zero = 0.; AD one = 1.; // dependent variable values Z[x] = U[s]; Z[y] = U[t]; Z[x] /= U[t]; // AD *= AD Z[y] /= 5.; // AD *= double zero /= Z[y]; // divide into a parameter equal to zero Z[y] /= one; // divide by a parameter equal to one Z[y] /= 1.; // divide by a double equal to one // check that zero is still a parameter // (must do this before creating f because it erases the tape) ok &= Parameter(zero); // create f : U -> Z and vectors for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v( f.Domain() ); CPPAD_TESTVECTOR(double) w( f.Range() ); // check that none of the components of f are parameters size_t i; for(i = 0; i < f.Range(); i++) ok &= ! f.Parameter(i); // check function values ok &= NearEqual(Z[x] , 3. / 2. , eps99, eps99); ok &= NearEqual(Z[y] , 2. / 5. , eps99, eps99); // forward computation of partials w.r.t. t v[s] = 0.; v[t] = 1.; w = f.Forward(1, v); ok &= NearEqual(w[x] , -1.*U[s]/(U[t]*U[t]) , eps99, eps99); // dx/dt ok &= NearEqual(w[y] , 1. / 5. , eps99, eps99); // dy/dt // reverse computation of second partials of x CPPAD_TESTVECTOR(double) r( f.Domain() * 2 ); w[x] = 1.; w[y] = 0.; r = f.Reverse(2, w); ok &= NearEqual(r[2 * s + 1] // d^2 x / (dt ds) , - 1. / (U[t] * U[t]) , eps99 , eps99 ); ok &= NearEqual(r[2 * t + 1] // d^2 x / (dt dt) , 2. * U[s] / (U[t] * U[t] * U[t]) , eps99 , eps99 ); return ok; } bool DivEqTestTwo(void) { bool ok = true; using namespace CppAD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector double u0 = .5; CPPAD_TESTVECTOR(AD) U(1); U[0] = u0; Independent(U); // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); Z[0] = U[0] * U[0]; // initial value Z[0] /= 2; // AD /= int Z[0] /= 4.; // AD /= double Z[0] /= U[0]; // AD /= AD // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v(1); CPPAD_TESTVECTOR(double) w(1); // check value ok &= NearEqual(Z[0] , u0*u0/(2*4*u0), eps99, eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; double value = 1./8.; v[0] = 1.; for(j = 1; j < p; j++) { jfac *= double(j); w = f.Forward(j, v); ok &= NearEqual(w[0], value/jfac, eps99, eps99); // d^jz/du^j v[0] = 0.; value = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r(p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; value = 1./8.; for(j = 0; j < p; j++) { ok &= NearEqual(r[j], value/jfac, eps99, eps99); // d^jz/du^j jfac *= double(j + 1); value = 0.; } return ok; } } // END empty namespace bool DivEq(void) { bool ok = true; ok &= DivEqTestOne(); ok &= DivEqTestTwo(); return ok; } ================================================ FILE: test_more/general/div_zero_one.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Test the use of the special parameters zero and one with the multiply operator */ # include typedef CppAD::AD ADdouble; typedef CppAD::AD< ADdouble > ADDdouble; bool DivZeroOne(void) { bool ok = true; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); size_t i; for(i = 0; i < 3; i++) { // run through the cases x = 0, 1, 2 size_t j; for(j = 0; j < 3; j++) { // run through the cases y = 0, 1, 2 CPPAD_TESTVECTOR( ADdouble ) x(1); x[0] = double(i); Independent(x); CPPAD_TESTVECTOR( ADDdouble ) y(1); y[0] = ADDdouble(j); Independent(y); CPPAD_TESTVECTOR( ADDdouble ) z(2); if( j == 0 ) z[0] = ADDdouble(0); else z[0] = x[0] / y[0]; if( i == 0 ) z[1] = ADDdouble(0); else { z[1] = y[0] / x[0]; z[1] /= x[0]; } // f(y) = z = { x / y , y / (x * x) } ADFun< ADdouble > f(y, z); CPPAD_TESTVECTOR( ADdouble ) u( f.Domain() ); CPPAD_TESTVECTOR( ADdouble ) v( f.Range() ); // v = f'(y) u[0] = ADdouble(1.); v = f.Forward(1, u); // check derivatives of f ADdouble check = - double(i) / double(j * j); if( j != 0 ) ok &= NearEqual( v[0], check, eps99, eps99 ); check = 1. / double(i * i); if( i != 0 ) ok &= NearEqual( v[1], check, eps99, eps99); // g(x) = f'(y) = {-x/y^2 , 1/(x * x)} ADFun g(x, v); CPPAD_TESTVECTOR( double ) a( g.Domain() ); CPPAD_TESTVECTOR( double ) b( g.Range() ); // b = g'(x) a[0] = 1.; b = g.Forward(1, a); // check derivatives of g if( j != 0 ) ok &= NearEqual( b[0], - 1./double(j*j), eps99, eps99 ); if( i != 0 ) ok &= NearEqual( b[1], -2./double(i*i*i), eps99, eps99); } } return ok; } ================================================ FILE: test_more/general/eigen_mat_inv.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include namespace { // BEGIN_EMPTY_NAMESPACE typedef double scalar; typedef CppAD::AD ad_scalar; typedef atomic_eigen_mat_inv::ad_matrix ad_matrix; scalar eps = 10. * std::numeric_limits::epsilon(); using CppAD::NearEqual; // -------------------------------------------------------------------------- /* Test atomic_eigen_mat_inv using a non-symmetric matrix f(x) = [ x[0] -1 ]^{-1} [ 2 x[1] ] = [ x[1] 1 ] / (x[0] * x[1] + 2) [ -2 x[0] ] y[0] = x[1] / (x[0] * x[1] + 2) y[1] = 1.0 / (x[0] * x[1] + 2) y[2] = -2.0 / (x[0] * x[1] + 2) y[3] = x[0] / (x[0] * x[1] + 2) */ bool non_symmetric(void) { bool ok = true; using Eigen::Index; // ------------------------------------------------------------------- // object that computes inverse of a 2x2 matrix atomic_eigen_mat_inv mat_inv; // ------------------------------------------------------------------- // declare independent variable vector x size_t n = 2; CPPAD_TESTVECTOR(ad_scalar) ad_x(n); for(size_t j = 0; j < n; j++) ad_x[j] = ad_scalar(j); CppAD::Independent(ad_x); // ------------------------------------------------------------------- // arg = [ x[0] -1 ] // [ 2 x[1] ] size_t nr = 2; ad_matrix ad_arg(nr, nr); ad_arg(0, 0) = ad_x[0]; ad_arg(0, 1) = ad_scalar(-1.0); ad_arg(1, 0) = ad_scalar(2.0); ad_arg(1, 1) = ad_x[1]; // ------------------------------------------------------------------- // use atomic operation to compute arg^{-1} ad_matrix ad_result = mat_inv.op(ad_arg); // ------------------------------------------------------------------- // declare the dependent variable vector y size_t m = 4; CPPAD_TESTVECTOR(ad_scalar) ad_y(4); for(size_t i = 0; i < nr; i++) for(size_t j = 0; j < nr; j++) ad_y[ i * nr + j ] = ad_result( Index(i), Index(j) ); /* Used to test hand calculated derivaives CppAD::AD ad_dinv = 1.0 / (ad_x[0] * ad_x[1] + 2.0); ad_y[0] = ad_x[1] * ad_dinv; ad_y[1] = 1.0 * ad_dinv; ad_y[2] = -2.0 * ad_dinv; ad_y[3] = ad_x[0] * ad_dinv; */ CppAD::ADFun f(ad_x, ad_y); // ------------------------------------------------------------------- // check zero order forward mode CPPAD_TESTVECTOR(scalar) x(n), y(m); for(size_t i = 0; i < n; i++) x[i] = scalar(i + 2); scalar dinv = 1.0 / (x[0] * x[1] + 2.0); y = f.Forward(0, x); ok &= NearEqual(y[0], x[1] * dinv, eps, eps); ok &= NearEqual(y[1], 1.0 * dinv, eps, eps); ok &= NearEqual(y[2], -2.0 * dinv, eps, eps); ok &= NearEqual(y[3], x[0] * dinv, eps, eps); // ------------------------------------------------------------------- // check first order forward mode CPPAD_TESTVECTOR(scalar) x1(n), y1(m); scalar dinv_x0 = - x[1] * dinv * dinv; x1[0] = 1.0; x1[1] = 0.0; y1 = f.Forward(1, x1); ok &= NearEqual(y1[0], x[1] * dinv_x0, eps, eps); ok &= NearEqual(y1[1], 1.0 * dinv_x0, eps, eps); ok &= NearEqual(y1[2], -2.0 * dinv_x0, eps, eps); ok &= NearEqual(y1[3], dinv + x[0] * dinv_x0, eps, eps); // scalar dinv_x1 = - x[0] * dinv * dinv; x1[0] = 0.0; x1[1] = 1.0; y1 = f.Forward(1, x1); ok &= NearEqual(y1[0], dinv + x[1] * dinv_x1, eps, eps); ok &= NearEqual(y1[1], 1.0 * dinv_x1, eps, eps); ok &= NearEqual(y1[2], -2.0 * dinv_x1, eps, eps); ok &= NearEqual(y1[3], x[0] * dinv_x1, eps, eps); // ------------------------------------------------------------------- // check second order forward mode CPPAD_TESTVECTOR(scalar) x2(n), y2(m); scalar dinv_x1_x1 = 2.0 * x[0] * x[0] * dinv * dinv * dinv; x2[0] = 0.0; x2[1] = 0.0; y2 = f.Forward(2, x2); ok &= NearEqual(2.0*y2[0], 2.0*dinv_x1 + x[1]*dinv_x1_x1, eps, eps); ok &= NearEqual(2.0*y2[1], 1.0 * dinv_x1_x1, eps, eps); ok &= NearEqual(2.0*y2[2], -2.0 * dinv_x1_x1, eps, eps); ok &= NearEqual(2.0*y2[3], x[0] * dinv_x1_x1, eps, eps); // ------------------------------------------------------------------- // check first order reverse CPPAD_TESTVECTOR(scalar) w(m), d1w(n); for(size_t i = 0; i < m; i++) w[i] = 0.0; w[0] = 1.0; d1w = f.Reverse(1, w); ok &= NearEqual(d1w[0], x[1] * dinv_x0, eps, eps); ok &= NearEqual(d1w[1], dinv + x[1] * dinv_x1, eps, eps); w[0] = 0.0; w[1] = 1.0; d1w = f.Reverse(1, w); ok &= NearEqual(d1w[0], 1.0 * dinv_x0, eps, eps); ok &= NearEqual(d1w[1], 1.0 * dinv_x1, eps, eps); w[1] = 0.0; w[2] = 1.0; d1w = f.Reverse(1, w); ok &= NearEqual(d1w[0], -2.0 * dinv_x0, eps, eps); ok &= NearEqual(d1w[1], -2.0 * dinv_x1, eps, eps); w[2] = 0.0; w[3] = 1.0; d1w = f.Reverse(1, w); ok &= NearEqual(d1w[0], dinv + x[0] * dinv_x0, eps, eps); ok &= NearEqual(d1w[1], x[0] * dinv_x1, eps, eps); // ------------------------------------------------------------------- // check second order reverse CPPAD_TESTVECTOR(scalar) d2w(2 * n); // dinv_x1 = - x[0] * dinv * dinv; scalar dinv_x1_x0 = 2.0 * x[0] * x[1] * dinv * dinv * dinv - dinv * dinv; d2w = f.Reverse(2, w); // partial f_3 w.r.t x_0 ok &= NearEqual(d2w[0 * 2 + 0], dinv + x[0] * dinv_x0, eps, eps); // partial f_3 w.r.t x_1 ok &= NearEqual(d2w[1 * 2 + 0], x[0] * dinv_x1, eps, eps); // partial f_3 w.r.t. x_1, x_0 ok &= NearEqual(d2w[0 * 2 + 1], dinv_x1 + x[0] * dinv_x1_x0, eps, eps); // partial f_3 w.r.t. x_1, x_1 ok &= NearEqual(d2w[1 * 2 + 1], x[0] * dinv_x1_x1, eps, eps); // ------------------------------------------------------------------- return ok; } } // END_EMPTY_NAMESPACE bool eigen_mat_inv(void) { bool ok = true; ok &= non_symmetric(); return ok; } ================================================ FILE: test_more/general/erf.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace { // --------------------------------------------------------------------- bool old_example(void) { bool ok = true; using namespace CppAD; using CppAD::atan; using CppAD::exp; using CppAD::sqrt; double eps = 100.0 * std::numeric_limits::epsilon(); // Construct function object corresponding to erf CPPAD_TESTVECTOR(AD) ax(1); CPPAD_TESTVECTOR(AD) ay(1); ax[0] = 0.; Independent(ax); ay[0] = erf(ax[0]); ADFun f(ax, ay); // Construct function object corresponding to derivative of erf Independent(ax); double pi = 4.0 * atan(1.0); ay[0] = exp( - ax[0] * ax[0] ) * 2.0 / sqrt(pi); ADFun df(ax, ay); // vectors to use with function object CPPAD_TESTVECTOR(double) x0(1), y0(1), x1(1), y1(1), check(1); // check value at zero x0[0] = 1.5; y0 = f.Forward(0, x0); check[0] = 0.96611; ok &= std::fabs(check[0] - y0[0]) <= 4e-4; // check the derivative of error function x1[0] = 1.0; y1 = f.Forward(1, x1); check = df.Forward(0, x0); ok &= NearEqual(check[0], y1[0], 0., 2e-3); ok &= NearEqual(check[0], y1[0], eps, eps); // check second derivative CPPAD_TESTVECTOR(double) x2(1), y2(1); x2[0] = 0.0; y2 = f.Forward(2, x2); check = df.Forward(1, x1); ok &= NearEqual(check[0] / 2.0, y2[0], 0., 2e-3); ok &= NearEqual(check[0] / 2.0, y2[0], eps, eps); // check third derivative CPPAD_TESTVECTOR(double) x3(1), y3(1); x3[0] = 0.0; y3 = f.Forward(3, x3); check = df.Forward(2, x2); ok &= NearEqual(check[0] / 3.0, y3[0], 0., 2e-3); ok &= NearEqual(check[0] / 3.0, y3[0], eps, eps); // check 4-th order of reverse mode CPPAD_TESTVECTOR(double) w(1), dy(4), x4(1), y4(1); x4[0] = 0.0; w[0] = 1.0; dy = f.Reverse(4, w); y4 = f.Forward(4, x4); // ok &= NearEqual(dy[0], y1[0], 0., 2e-3); ok &= NearEqual(dy[0], y1[0], eps, eps); // ok &= NearEqual(dy[1], 2.0 * y2[0], 0., 2e-3); ok &= NearEqual(dy[1], 2.0 * y2[0], eps, eps); // ok &= NearEqual(dy[2], 3.0 * y3[0], 0., 2e-3); ok &= NearEqual(dy[2], 3.0 * y3[0], eps, eps); // ok &= NearEqual(dy[3], 4.0 * y4[0], 0., 2e-3); ok &= NearEqual(dy[3], 4.0 * y4[0], eps, eps); return ok; } // --------------------------------------------------------------------- bool hessian(void) { bool ok = true; double eps = 1.0 * std::numeric_limits::epsilon(); using CppAD::vector; using CppAD::AD; size_t n = 2; size_t m = 1; vector x(n), w(m); w[0] = 1.0; vector< AD > ax(n), ay(m); ax[0] = x[0] = 0.5; ax[1] = x[1] = 0.0; // construct function CppAD::Independent(ax); ay[0] = erf( 2.0 * ax[0] ); CppAD::ADFun f(ax, ay); // dense hessian vector dense_hess = f.Hessian(x, 0); // sparse_hessian vector sparse_hess = f.SparseHessian(x, w); // Define g(u) = erf(2 * u) // g'(u) = 2 * erf'(2 * u) // = 2 * exp( - 2 * u * 2 * u ) * 2 / sqrt(pi) // = exp( - 4 * u * u ) * 4 / sqrt(pi) // g''(u) = - exp( - 4 * u * u ) * 32 * u / sqrt(pi) double root_pi = std::sqrt( 4.0 * atan(1.0) ); double check = -std::exp(-4.0 * x[0] * x[0]) * 32.0 * x[0] / root_pi; ok &= CppAD::NearEqual(dense_hess[0], check, eps, eps); ok &= CppAD::NearEqual(sparse_hess[0], check, eps, eps); for(size_t k = 1; k < n * n; k++) { ok &= CppAD::NearEqual(dense_hess[k], 0.0, eps, eps); ok &= CppAD::NearEqual(sparse_hess[k], 0.0, eps, eps); } return ok; } // --------------------------------------------------------------------- bool mul_dir(void) { bool ok = true; using namespace CppAD; using CppAD::atan; using CppAD::exp; using CppAD::sqrt; double eps = 100.0 * std::numeric_limits::epsilon(); // Construct function object corresponding to erf CPPAD_TESTVECTOR(AD) ax(1); CPPAD_TESTVECTOR(AD) ay(1); ax[0] = 0.; Independent(ax); ay[0] = erf(ax[0]); ADFun f(ax, ay); // Construct function object corresponding to derivative of erf Independent(ax); double pi = 4.0 * atan(1.0); ay[0] = exp( - ax[0] * ax[0] ) * 2.0 / sqrt(pi); ADFun df(ax, ay); // number of directions size_t r = 1; // vectors to use with objects CPPAD_TESTVECTOR(double) x0(1), y0(1), x1(1), y1(1), y2(1), y3(1); CPPAD_TESTVECTOR(double) zero(1), check(1); CPPAD_TESTVECTOR(double) xq(r), yq(r), checkq(r), zeroq(r); // check function value x0[0] = 1.5; y0 = f.Forward(0, x0); check[0] = 0.9661051464753108; double tmp = std::max(1e-15, eps); ok &= NearEqual(check[0], y0[0], 0.0, tmp); // check first order derivative x1[0] = 1.0; y1 = f.Forward(1, x1); check = df.Forward(0, x0); ok &= NearEqual(check[0], y1[0], eps, eps); for(size_t ell = 0; ell < r; ell++) { xq[ell] = x1[ell] / double(ell + 1); zeroq[ell] = 0.0; } yq = f.Forward(1, r, xq); for(size_t ell = 0; ell < r; ell++) { checkq[ell] = check[0] * xq[ell]; ok &= NearEqual(checkq[ell], yq[ell], eps, eps); } // check second order derivative zero[0] = 0.0; y2 = f.Forward(2, zero); check = df.Forward(1, x1); check[0] /= 2.0; ok &= NearEqual(check[0], y2[0], eps, eps); yq = f.Forward(2, r, zeroq); for(size_t ell = 0; ell < r; ell++) { checkq[ell] = check[0] * xq[ell]; ok &= NearEqual(checkq[ell], yq[ell], eps, eps); } // check third order derivative zero[0] = 0.0; y3 = f.Forward(3, zero); check = df.Forward(2, zero); check[0] /= 3.0; ok &= NearEqual(check[0], y3[0], eps, eps); yq = f.Forward(3, r, zeroq); for(size_t ell = 0; ell < r; ell++) { checkq[ell] = check[0] * xq[ell]; ok &= NearEqual(checkq[ell], yq[ell], eps, eps); } return ok; } // ------------------------------------------------------------------- } bool erf(void) { bool ok = true; ok &= old_example(); ok &= hessian(); ok &= mul_dir(); return ok; } ================================================ FILE: test_more/general/exp.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Two old exp example now used just for validation testing. */ # include # include namespace { // BEGIN empty namespace bool ExpTestOne(void) { bool ok = true; using CppAD::exp; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector, indices, values, and declaration CPPAD_TESTVECTOR(AD) U(1); size_t s = 0; U[s] = 1.; Independent(U); // dependent variable vector, indices, and values CPPAD_TESTVECTOR(AD) Z(2); size_t x = 0; size_t y = 1; Z[x] = exp(U[s]); Z[y] = exp(Z[x]); // define f : U -> Z and vectors for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v( f.Domain() ); CPPAD_TESTVECTOR(double) w( f.Range() ); // check values ok &= NearEqual(Z[x] , exp(1.), eps99 , eps99); ok &= NearEqual(Z[y] , exp( exp(1.) ), eps99 , eps99); // forward computation of partials w.r.t. s v[s] = 1.; w = f.Forward(1, v); ok &= NearEqual(w[x], Z[x], eps99 , eps99); // dx/ds ok &= NearEqual(w[y], Z[y] * Z[x], eps99 , eps99); // dy/ds // reverse computation of partials of y w[x] = 0.; w[y] = 1.; v = f.Reverse(1,w); ok &= NearEqual(v[s], Z[y] * Z[x], eps99 , eps99); // dy/ds // forward computation of second partials w.r.t s v[s] = 1.; w = f.Forward(1, v); v[s] = 0.; w = f.Forward(2, v); ok &= NearEqual( // d^2 y / (ds ds) 2. * w[y] , Z[y] * Z[x] * Z[x] + Z[y] * Z[x], eps99 , eps99 ); // reverse computation of second partials of y CPPAD_TESTVECTOR(double) r( f.Domain() * 2 ); w[x] = 0.; w[y] = 1.; r = f.Reverse(2, w); ok &= NearEqual( // d^2 y / (ds ds) r[2 * s + 1] , Z[y] * Z[x] * Z[x] + Z[y] * Z[x], eps99 , eps99 ); return ok; } bool ExpTestTwo(void) { bool ok = true; using CppAD::exp; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector CPPAD_TESTVECTOR(AD) U(1); U[0] = 1.; Independent(U); // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); Z[0] = exp(U[0]); // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v(1); CPPAD_TESTVECTOR(double) w(1); // check value double exp_u = exp( Value(U[0]) ); ok &= NearEqual(exp_u, Value(Z[0]), eps99 , eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; v[0] = 1.; for(j = 1; j < p; j++) { w = f.Forward(j, v); jfac *= double(j); ok &= NearEqual(jfac*w[0], exp_u, eps99 , eps99); // d^jz/du^j v[0] = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r(p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; for(j = 0; j < p; j++) { ok &= NearEqual(jfac*r[j], exp_u, eps99 , eps99); // d^jz/du^j jfac *= double(j + 1); } return ok; } } // END empty namespace bool Exp(void) { bool ok = true; ok &= ExpTestOne(); ok &= ExpTestTwo(); return ok; } ================================================ FILE: test_more/general/expm1.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include bool expm1(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; // 10 times machine epsilon double eps = 10. * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = x0; // declare independent variables and start tape recording CppAD::Independent(ax); // a temporary value AD log_p1 = CppAD::log(ax[0] + 1.0); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = CppAD::expm1(log_p1); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // check value ok &= NearEqual(ay[0] , x0, eps, eps); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 1., eps, eps); // forward computation of higher order partials w.r.t. x[0] size_t n_order = 5; for(size_t order = 2; order < n_order; order++) { dx[0] = 0.; dy = f.Forward(order, dx); ok &= NearEqual(dy[0], 0., eps, eps); } // reverse computation of derivatives CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n_order * n); w[0] = 1.; dw = f.Reverse(n_order, w); ok &= NearEqual(dw[0], 1., eps, eps); for(size_t order = 1; order < n_order; order++) ok &= NearEqual(dw[order * n + 0], 0., eps, eps); return ok; } // END C++ ================================================ FILE: test_more/general/extern_value.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include "extern_value.hpp" # define INSTANTIATE(Type) template class extern_value< Type > template extern_value::extern_value(Type value) { value_ = value; } template void extern_value::set(Type value) { value_ = value; } template Type extern_value::get(void) { return value_; } INSTANTIATE( float ); INSTANTIATE( double ); INSTANTIATE( std::complex ); INSTANTIATE( std::complex ); // INSTANTIATE( CppAD::AD< float > ); INSTANTIATE( CppAD::AD< double > ); INSTANTIATE( CppAD::AD< std::complex > ); INSTANTIATE( CppAD::AD< std::complex > ); ================================================ FILE: test_more/general/extern_value.hpp ================================================ # ifndef CPPAD_TEST_MORE_GENERAL_EXTERN_VALUE_HPP # define CPPAD_TEST_MORE_GENERAL_EXTERN_VALUE_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- template class extern_value { private: Type value_; public: extern_value(Type value); void set(Type value); Type get(void); }; # endif ================================================ FILE: test_more/general/fabs.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Test of directional derivative in AD< AD< double > > case. */ # include bool fabs(void) { // test if CppAD::abs uses if statement during forward computations bool ok = true; using CppAD::Independent; using CppAD::ADFun; typedef CppAD::AD ADdouble; typedef CppAD::AD< ADdouble > ADDdouble; // af(x) = |x| CPPAD_TESTVECTOR( ADDdouble ) aax(1), aay(1); aax[0] = ADDdouble(0.); CppAD::Independent(aax); aay[0] = fabs(aax[0]); CppAD::ADFun< ADdouble > af(aax, aay); // f(x) = |x| CPPAD_TESTVECTOR( ADdouble ) ax(1), ay(1); ax[0] = ADdouble(0.); CppAD::Independent(ax); ay = af.Forward(0, ax); CppAD::ADFun f(ax, ay); // compute derivative of af at a positive argument CPPAD_TESTVECTOR( ADdouble ) adx(1), ady(1); ax[0] = 1.; ay = af.Forward(0, ax); adx[0] = 1; ady = af.Forward(1, adx); ok &= (ady[0] == 1.); // compute derivative of af at a zero argument ax[0] = 0.; ay = af.Forward(0, ax); adx[0] = 1; ady = af.Forward(1, adx); ok &= (ady[0] == 0.); // compute derivative of f at zero argument CPPAD_TESTVECTOR(double) x(1), y(1), dx(1), dy(1); x[0] = 0.; y = f.Forward(0, x); dx[0] = 1; dy = f.Forward(1, dx); ok &= (dy[0] == 0.); // compute derivative of af at a negative argument x[0] = -1.; y = f.Forward(0, x); dx[0] = 1; dy = f.Forward(1, dx); ok &= (dy[0] == -1.); return ok; } ================================================ FILE: test_more/general/for_hes_sparsity.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace { // Begin empty namespace // -------------------------------------------------------------------------- bool test_old_interface() { volatile bool ok = true; using namespace CppAD; // dimension of the domain space size_t n = 14; // dimension of the range space size_t m = 1; // temporary indices size_t i, j; // for testing load and store operations CppAD::VecAD ad_vec(2); ad_vec[0] = 3.0; ad_vec[1] = 4.0; // initialize check values to false CPPAD_TESTVECTOR(bool) check(n * n); for(j = 0; j < n * n; j++) check[j] = false; // independent variable vector CPPAD_TESTVECTOR(AD) ax(n); for(j = 0; j < n; j++) ax[j] = AD(j); Independent(ax); // accumulate sum here AD sum(0.); // first operand size_t F = 0; // ad_vec[variable] when ad_vec is a parameter sum += ad_vec[ax[F]]; // use fact ax[F] is zero F += 1; // ad_vec[parameter] when ad_vec depends on a variable // (CppAD sparsity does not separate elements of ad_vec) ad_vec[ AD(0) ] = ax[F] * ax[F]; sum += ad_vec[ ax[F] ]; // user fact that ax[F] is one check[F * n + F] = true; F += 1; // parameter / variable sum += 2.0 / ax[F]; check[F * n + F] = true; F += 1; // erf(variable) sum += erf( ax[F] ); check[F * n + F] = true; F += 1; // pow(parameter, variable) sum += pow( 2.0 , ax[F] ); check[F * n + F] = true; F += 1; // pow(variable, parameter) sum += pow( ax[F] , 2.0 ); check[F * n + F] = true; F += 1; // second operand size_t S = F + 1; // variable * variable sum += ax[F] * ax[S]; check[F * n + S] = check[S * n + F] = true; F += 2; S += 2; // azmul(variable, variable) sum += azmul(ax[F], ax[S]); check[F * n + S] = check[S * n + F] = true; F += 2; S += 2; // variable / variable sum += ax[F] / ax[S]; check[F * n + S] = check[S * n + F] = check[S * n + S] = true; F += 2; S += 2; // pow( variable , variable ) sum += pow( ax[F] , ax[S] ); check[F * n + F] = check[S * n + S] = true; check[F * n + S] = check[S * n + F] = true; F += 2; S += 2; ok &= F == n; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = sum; // create function object f : x -> y ADFun f(ax, ay); // ------------------------------------------------------------------ // compute sparsity CPPAD_TESTVECTOR(bool) r(n), s(m), h(n * n); for(j = 0; j < n; j++) r[j] = true; for(i = 0; i < m; i++) s[i] = true; h = f.ForSparseHes(r, s); // check result for(i = 0; i < n; i++) for(j = 0; j < n; j++) { if(h[i * n + j] != check[i * n + j]) { std::cout << "i: " << i << std::endl; std::cout << "j: " << j << std::endl; std::cout << "h[i * n + j]: " << h[i * n + j] << std::endl; std::cout << "check[i * n + j]: " << check[i * n + j] << std::endl; } ok &= h[i * n + j] == check[i * n + j]; } // ------------------------------------------------------------------ return ok; } // -------------------------------------------------------------------------- // This case demonstrated a bug that was fixed on 2024-11-16. bool test_csum(void) { // ok bool ok = true; // // n, ax size_t n = 5; CPPAD_TESTVECTOR( CppAD::AD ) ax(n); for(size_t j = 0; j < n; ++j) ax[j] = double(j + 1); CppAD::Independent(ax); // // ay size_t m = 1; CPPAD_TESTVECTOR( CppAD::AD ) ay(m); CppAD::AD sum = ax[0]; for(size_t j = 1; j < n; ++j) sum += ax[j]; ay[0] = sum * sum; // // f // the optimize step should create a CSumOp operation CppAD::ADFun f(ax, ay); f.optimize(); // // sparse_rc typedef CppAD::sparse_rc< CppAD::vector > sparse_rc; // // pattern_hes sparse_rc pattern_hes; CPPAD_TESTVECTOR(bool) select_domain(n), select_range(m); for(size_t j = 0; j < n; ++j) select_domain[j] = true; select_range[0] = true; bool internal_bool = false; f.for_hes_sparsity( select_domain, select_range, internal_bool, pattern_hes ); // // pattern_check size_t nr = n, nc = n, nnz = n * n; sparse_rc pattern_check(nr, nc, nnz); size_t k = 0; for(size_t i = 0; i < n; ++i) { for(size_t j = 0; j < n; ++j) { pattern_check.set(k, i, j); ++k; } } // // ok ok &= pattern_hes == pattern_check; // return ok; } } // End of empty namespace // --------------------------------------------------------------------------- bool for_hes_sparsity(void) { bool ok = true; // ok &= test_old_interface(); ok &= test_csum(); // return ok; } ================================================ FILE: test_more/general/for_hess.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old ForHess example now used just for valiadation testing */ # include bool ForHess(void) { bool ok = true; using namespace CppAD; using CppAD::exp; using CppAD::sin; using CppAD::cos; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); size_t i; // create independent variable vector with assigned values CPPAD_TESTVECTOR(double) u0(3); CPPAD_TESTVECTOR(AD) U(3); for(i = 0; i < 3; i++) U[i] = u0[i] = double(i+1); Independent( U ); // define the function CPPAD_TESTVECTOR(AD) Y(2); Y[0] = U[0] * exp( U[1] ); Y[1] = U[1] * sin( U[2] ); // create the function y = F(u) ADFun F(U, Y); // formulas for the upper triangle of Hessian of F_0 CPPAD_TESTVECTOR(double) H0(9); H0[0] = 0.; // d^2 y[0] / d_u[0] d_u[0] H0[1] = exp( u0[1] ); // d^2 y[0] / d_u[0] d_u[1] H0[2] = 0.; // d^2 y[0] / d_u[0] d_u[2] H0[4] = u0[0] * exp( u0[1] ); // d^2 y[0] / d_u[1] d_u[1] H0[5] = 0.; // d^2 y[0] / d_u[1] d_u[2] H0[8] = 0.; // d^2 y[0] / d_u[2] d_u[2] // formulas for the upper triangle of Hessian of F_1 CPPAD_TESTVECTOR(double) H1(9); H1[0] = 0.; // d^2 Y[1] / d_U[0] d_U[0] H1[1] = 0.; // d^2 Y[1] / d_U[0] d_U[1] H1[2] = 0.; // d^2 Y[1] / d_U[0] d_U[2] H1[4] = 0.; // d^2 Y[1] / d_U[1] d_U[1] H1[5] = cos( u0[2] ); // d^2 Y[1] / d_U[1] d_U[2] H1[8] = - u0[1] * sin( u0[2] );// d^2 Y[1] / d_U[2] d_U[2] // Define U(t) = u0 + u1 t + u2 t^2 / 2 CPPAD_TESTVECTOR(double) u1(3); CPPAD_TESTVECTOR(double) u2(3); for(i = 0; i < 3; i++) u1[i] = u2[i] = 0.; size_t j; for(i = 0; i < 3; i++) { // diagonal of Hessians in i-th coordinate direction u1[i] = 1.; F.Forward(1, u1); CPPAD_TESTVECTOR(double) Di = F.Forward(2, u2); ok &= NearEqual( 2. * Di[0] , H0[ i + 3 * i], eps99, eps99); ok &= NearEqual( 2. * Di[1] , H1[ i + 3 * i], eps99, eps99); // for(j = i+1; j < 3; j++) { // cross term in i and j direction u1[j] = 1.; F.Forward(1, u1); CPPAD_TESTVECTOR(double) Cij = F.Forward(2, u2); // diagonal of Hessian in j-th coordinate direction u1[i] = 0.; F.Forward(1, u1); CPPAD_TESTVECTOR(double) Dj = F.Forward(2, u2); // (i, j) elements of the Hessians double H0ij = Cij[0] - Di[0] - Dj[0]; ok &= NearEqual( H0ij, H0[j + 3 * i], eps99, eps99); double H1ij = Cij[1] - Di[1] - Dj[1]; ok &= NearEqual( H1ij, H1[j + 3 * i], eps99, eps99); // reset all components of u1 to zero u1[j] = 0.; } } return ok; } ================================================ FILE: test_more/general/for_jac_sparsity.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # define CheckOp(Op) \ Y[index] = X[0] Op 2.; \ Check[index * n + 0] = true; \ Check[index * n + 1] = false; \ Check[index * n + 2] = false; \ index++; \ Y[index] = X[0] Op X[1]; \ Check[index * n + 0] = true; \ Check[index * n + 1] = true; \ Check[index * n + 2] = false; \ index++; \ Y[index] = 3. Op X[1]; \ Check[index * n + 0] = false; \ Check[index * n + 1] = true; \ Check[index * n + 2] = false; \ index++; # define CheckUnaryFun(Fun) \ Y[index] = Fun(X[0]); \ Check[index * n + 0] = true; \ Check[index * n + 1] = false; \ Check[index * n + 2] = false; \ index++; \ Y[index] = Fun(X[0] + X[1]); \ Check[index * n + 0] = true; \ Check[index * n + 1] = true; \ Check[index * n + 2] = false; \ index++; \ Y[index] = Fun(X[1]); \ Check[index * n + 0] = false; \ Check[index * n + 1] = true; \ Check[index * n + 2] = false; \ index++; # define CheckBinaryFun(Fun) \ Y[index] = Fun( X[0] , 2.); \ Check[index * n + 0] = true; \ Check[index * n + 1] = false; \ Check[index * n + 2] = false; \ index++; \ Y[index] = Fun( X[0] , X[1]); \ Check[index * n + 0] = true; \ Check[index * n + 1] = true; \ Check[index * n + 2] = false; \ index++; \ Y[index] = Fun( 3. , X[1]); \ Check[index * n + 0] = false; \ Check[index * n + 1] = true; \ Check[index * n + 2] = false; \ index++; namespace { // Begin empty namespace bool case_one() { bool ok = true; using namespace CppAD; // dimension of the domain space size_t n = 3; // dimension of the range space size_t m = (4 + 11 + 1) * 3 + 4; // independent variable vector CPPAD_TESTVECTOR(AD) X(n); X[0] = .1; X[1] = .2; X[2] = .3; Independent(X); // dependent variable vector CPPAD_TESTVECTOR(AD) Y(m); // check results vector CPPAD_TESTVECTOR( bool ) Check(m * n); // initialize index into Y size_t index = 0; // 4 binary operators CheckOp(+); CheckOp(-); CheckOp(*); CheckOp(/); // 11 unary functions CheckUnaryFun(abs); CheckUnaryFun(acos); CheckUnaryFun(asin); CheckUnaryFun(atan); CheckUnaryFun(cos); CheckUnaryFun(cosh); CheckUnaryFun(exp); CheckUnaryFun(log); CheckUnaryFun(sin); CheckUnaryFun(sinh); CheckUnaryFun(sqrt); // 1 binary function CheckBinaryFun(pow); // conditional expression (value of comparison does not matter) Y[index] = CondExpLt(X[0], X[1], X[0], AD(2.)); Check[index * n + 0] = true; Check[index * n + 1] = false; Check[index * n + 2] = false; index++; Y[index] = CondExpLt(X[0], X[1], X[0], X[1]); Check[index * n + 0] = true; Check[index * n + 1] = true; Check[index * n + 2] = false; index++; Y[index] = CondExpLt(X[0], X[1], AD(3.), X[1]); Check[index * n + 0] = false; Check[index * n + 1] = true; Check[index * n + 2] = false; index++; // non-trivial composition Y[index] = X[0] * X[1] + X[1] * X[2]; Check[index * n + 0] = true; Check[index * n + 1] = true; Check[index * n + 2] = true; index++; // check final index assert( index == m ); // create function object F : X -> Y ADFun F(X, Y); // --------------------------------------------------------- // dependency matrix for the identity function W(x) = x CPPAD_TESTVECTOR( bool ) Px(n * n); size_t i, j; for(i = 0; i < n; i++) { for(j = 0; j < n; j++) Px[ i * n + j ] = false; Px[ i * n + i ] = true; } // evaluate the dependency matrix for F(X(x)) CPPAD_TESTVECTOR( bool ) Py(m * n); Py = F.ForSparseJac(n, Px); // check values for(i = 0; i < m; i++) { for(j = 0; j < n; j++) ok &= (Py[i * n + j] == Check[i * n + j]); } // --------------------------------------------------------- // dependency matrix for the identity function W(x) = x CPPAD_TESTVECTOR(std::set) Sx(n); for(i = 0; i < n; i++) { assert( Sx[i].empty() ); Sx[i].insert(i); } // evaluate the dependency matrix for F(X(x)) CPPAD_TESTVECTOR(std::set) Sy(m); Sy = F.ForSparseJac(n, Sx); // check values bool found; for(i = 0; i < m; i++) { for(j = 0; j < n; j++) { found = Sy[i].find(j) != Sy[i].end(); ok &= (found == Check[i * n + j]); } } return ok; } bool case_two() { bool ok = true; using namespace CppAD; // dimension of the domain space size_t n = 3; // dimension of the range space size_t m = 3; // initialize the vector as zero CppAD::VecAD Z(n - 1); size_t k; for(k = 0; k < n-1; k++) Z[k] = 0.; // independent variable vector CPPAD_TESTVECTOR(AD) X(n); X[0] = 0.; X[1] = 1.; X[2] = 2.; Independent(X); // VecAD vector is going to depend on X[1] and X[2] Z[ X[0] ] = X[1]; Z[ X[1] ] = X[2]; // dependent variable vector CPPAD_TESTVECTOR(AD) Y(m); // check results vector CPPAD_TESTVECTOR( bool ) Check(m * n); // initialize index into Y size_t index = 0; // First component only depends on X[0]; Y[index] = X[0]; Check[index * n + 0] = true; Check[index * n + 1] = false; Check[index * n + 2] = false; index++; // Second component depends on the vector Z AD zero(0); Y[index] = Z[zero]; // Load by a parameter Check[index * n + 0] = false; Check[index * n + 1] = true; Check[index * n + 2] = true; index++; // Third component depends on the vector Z Y[index] = Z[ X[0] ]; // Load by a variable Check[index * n + 0] = false; Check[index * n + 1] = true; Check[index * n + 2] = true; index++; // check final index assert( index == m ); // create function object F : X -> Y ADFun F(X, Y); // ----------------------------------------------------------------- // dependency matrix for the identity function W(x) = x CPPAD_TESTVECTOR( bool ) Px(n * n); size_t i, j; for(i = 0; i < n; i++) { for(j = 0; j < n; j++) Px[ i * n + j ] = false; Px[ i * n + i ] = true; } // evaluate the dependency matrix for F(X(x)) CPPAD_TESTVECTOR( bool ) Py(m * n); Py = F.ForSparseJac(n, Px); // check values for(i = 0; i < m; i++) { for(j = 0; j < n; j++) ok &= (Py[i * n + j] == Check[i * n + j]); } // --------------------------------------------------------- // dependency matrix for the identity function W(x) = x CPPAD_TESTVECTOR(std::set) Sx(n); for(i = 0; i < n; i++) { assert( Sx[i].empty() ); Sx[i].insert(i); } // evaluate the dependency matrix for F(X(x)) CPPAD_TESTVECTOR(std::set) Sy(m); Sy = F.ForSparseJac(n, Sx); // check values bool found; for(i = 0; i < m; i++) { for(j = 0; j < n; j++) { found = Sy[i].find(j) != Sy[i].end(); ok &= (found == Check[i * n + j]); } } return ok; } bool case_three() { bool ok = true; using namespace CppAD; // dimension of the domain space size_t n = 2; // dimension of the range space size_t m = 3; // independent variable vector CPPAD_TESTVECTOR(AD) X(n); X[0] = 2.; X[1] = 3.; Independent(X); // dependent variable vector CPPAD_TESTVECTOR(AD) Y(m); // check results vector CPPAD_TESTVECTOR( bool ) Check(m * n); // initialize index into Y size_t index = 0; // Y[0] only depends on X[0]; Y[index] = pow(X[0], 2.); Check[index * n + 0] = true; Check[index * n + 1] = false; index++; // Y[1] depends on X[1] Y[index] = pow(2., X[1]); Check[index * n + 0] = false; Check[index * n + 1] = true; index++; // Y[2] depends on X[0] and X[1] Y[index] = pow(X[0], X[1]); Check[index * n + 0] = true; Check[index * n + 1] = true; index++; // check final index assert( index == m ); // create function object F : X -> Y ADFun F(X, Y); // ----------------------------------------------------------------- // dependency matrix for the identity function CPPAD_TESTVECTOR( bool ) Px(n * n); size_t i, j; for(i = 0; i < n; i++) { for(j = 0; j < n; j++) Px[ i * n + j ] = false; Px[ i * n + i ] = true; } // evaluate the dependency matrix for F(X(x)) CPPAD_TESTVECTOR( bool ) Py(m * n); Py = F.ForSparseJac(n, Px); // check values for(i = 0; i < m; i++) { for(j = 0; j < n; j++) ok &= (Py[i * n + j] == Check[i * n + j]); } // --------------------------------------------------------- // dependency matrix for the identity function CPPAD_TESTVECTOR(std::set) Sx(n); for(i = 0; i < n; i++) { assert( Sx[i].empty() ); Sx[i].insert(i); } // evaluate the dependency matrix for F(X(x)) CPPAD_TESTVECTOR(std::set) Sy(m); Sy = F.ForSparseJac(n, Sx); // check values bool found; for(i = 0; i < m; i++) { for(j = 0; j < n; j++) { found = Sy[i].find(j) != Sy[i].end(); ok &= (found == Check[i * n + j]); } } return ok; } bool case_four() { bool ok = true; using namespace CppAD; // dimension of the domain space size_t n = 2; // dimension of the range space size_t m = 3; // independent variable vector CPPAD_TESTVECTOR(AD) X(n); X[0] = 2.; X[1] = 3.; Independent(X); // dependent variable vector CPPAD_TESTVECTOR(AD) Y(m); // check results vector CPPAD_TESTVECTOR( bool ) Check(m * n); // initialize index into Y size_t index = 0; // Y[0] only depends on X[0]; Y[index] = pow(X[0], 2.); Check[index * n + 0] = true; Check[index * n + 1] = false; index++; // Y[1] depends on X[1] Y[index] = pow(2., X[1]); Check[index * n + 0] = false; Check[index * n + 1] = true; index++; // Y[2] depends on X[0] and X[1] Y[index] = pow(X[0], X[1]); Check[index * n + 0] = true; Check[index * n + 1] = true; index++; // check final index assert( index == m ); // create function object F : X -> Y ADFun F(X, Y); // ----------------------------------------------------------------- // dependency matrix for the identity function CPPAD_TESTVECTOR( bool ) Px(n * n); size_t i, j; for(i = 0; i < n; i++) { for(j = 0; j < n; j++) Px[ i * n + j ] = false; Px[ i * n + i ] = true; } // evaluate the dependency matrix for F(X(x)) bool transpose = true; CPPAD_TESTVECTOR( bool ) Py(n * m); Py = F.ForSparseJac(n, Px, transpose); // check values for(i = 0; i < m; i++) { for(j = 0; j < n; j++) ok &= (Py[j * m + i] == Check[i * n + j]); } // --------------------------------------------------------- // dependency matrix for the identity function CPPAD_TESTVECTOR(std::set) Sx(n); for(i = 0; i < n; i++) { assert( Sx[i].empty() ); Sx[i].insert(i); } // evaluate the dependency matrix for F(X(x)) CPPAD_TESTVECTOR(std::set) Sy(n); Sy = F.ForSparseJac(n, Sx, transpose); // check values bool found; for(i = 0; i < m; i++) { for(j = 0; j < n; j++) { found = Sy[j].find(i) != Sy[j].end(); ok &= (found == Check[i * n + j]); } } return ok; } } // End empty namespace bool for_jac_sparsity(void) { bool ok = true; ok &= case_one(); ok &= case_two(); ok &= case_three(); ok &= case_four(); return ok; } ================================================ FILE: test_more/general/forward.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Two old Forward example now used just for valiadation testing */ # include namespace { // Begin empty namespace template // vector class, elements of type double bool ForwardCases(void) { bool ok = true; using namespace CppAD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector CPPAD_TESTVECTOR(AD) X(2); X[0] = 0.; X[1] = 1.; Independent(X); // compute product of elements in X CPPAD_TESTVECTOR(AD) Y(1); Y[0] = X[0] * X[0] * X[1]; // create function object F : X -> Y ADFun F(X, Y); // use zero order to evaluate F[ (3, 4) ] DoubleVector x0( F.Domain() ); DoubleVector y0( F.Range() ); x0[0] = 3.; x0[1] = 4.; y0 = F.Forward(0, x0); ok &= NearEqual(y0[0] , x0[0]*x0[0]*x0[1], eps99, eps99); // evaluate derivative of F in X[0] direction DoubleVector x1( F.Domain() ); DoubleVector y1( F.Range() ); x1[0] = 1.; x1[1] = 0.; y1 = F.Forward(1, x1); ok &= NearEqual(y1[0] , 2.*x0[0]*x0[1], eps99, eps99); // evaluate second derivative of F in X[0] direction DoubleVector x2( F.Domain() ); DoubleVector y2( F.Range() ); x2[0] = 0.; x2[1] = 0.; y2 = F.Forward(2, x2); double F_00 = 2. * y2[0]; ok &= NearEqual(F_00, 2.*x0[1], eps99, eps99); // evaluate derivative of F in X[1] direction x1[0] = 0.; x1[1] = 1.; y1 = F.Forward(1, x1); ok &= NearEqual(y1[0] , x0[0]*x0[0], eps99, eps99); // evaluate second derivative of F in X[1] direction y2 = F.Forward(2, x2); double F_11 = 2. * y2[0]; ok &= NearEqual(F_11, 0., eps99, eps99); // evaluate derivative of F in X[0] + X[1] direction x1[0] = 1.; x1[1] = 1.; y1 = F.Forward(1, x1); ok &= NearEqual(y1[0], 2.*x0[0]*x0[1] + x0[0]*x0[0], eps99, eps99); // use second derivative of F in X[0] direction to // compute second partial of F w.r.t X[1] w.r.t X[2] y2 = F.Forward(2, x2); double F_01 = y2[0] - F_00 / 2. - F_11 / 2.; ok &= NearEqual(F_01 , 2.*x0[0], eps99, eps99); return ok; } bool ForwardOlder(void) { bool ok = true; using namespace CppAD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector CPPAD_TESTVECTOR(AD) U(3); U[0] = 0.; U[1] = 1.; U[2] = 2.; Independent(U); // compute sum and product of elements in U AD sum = 0.; AD prod = 1.; size_t i; for(i = 0; i < 3; i++) { sum += U[i]; prod *= U[i]; } // dependent variable vector CPPAD_TESTVECTOR(AD) V(2); V[0] = sum; V[1] = prod; // V = f(U) ADFun f(U, V); // use ADFun object to evaluate f[ (1, 2, 3)^T ] ----------------- CPPAD_TESTVECTOR(double) u0( f.Domain() ); CPPAD_TESTVECTOR(double) v0( f.Range() ); size_t p; p = 0; u0[0] = 1.; u0[1] = 2.; u0[2] = 3.; v0 = f.Forward(p, u0); // direct evaluation of f[ u0 ] CPPAD_TESTVECTOR(double) f0(2); f0[0] = u0[0] + u0[1] + u0[2]; f0[1] = u0[0] * u0[1] * u0[2]; // compare values ok &= NearEqual(v0[0] , f0[0], eps99, eps99); ok &= NearEqual(v0[1] , f0[1], eps99, eps99); // use ADFun object to evaluate f^(1) [ u0 ] * u1 ----------------- CPPAD_TESTVECTOR(double) u1( f.Domain() ); CPPAD_TESTVECTOR(double) v1( f.Range() ); p = 1; u1[0] = 1.; u1[1] = 1.; u1[2] = 1.; v1 = f.Forward(p, u1); // direct evaluation of gradients of components of f CPPAD_TESTVECTOR(double) g0(3), g1(3); g0[0] = 1.; g0[1] = 1.; g0[2] = 1.; g1[0] = u0[1]*u0[2]; g1[1] = u0[0]*u0[2]; g1[2] = u0[0]*u0[1]; // compare values ok &= NearEqual(v1[0] , g0[0]*u1[0] + g0[1]*u1[1] + g0[2]*u1[2] , eps99, eps99); ok &= NearEqual(v1[1] , g1[0]*u1[0] + g1[1]*u1[1] + g1[2]*u1[2] , eps99, eps99); // use ADFun object to evaluate ------------------------------------ // (1/2) * { f^(1)[ u0 ] * u2 + u1^T * f^(2)[ u0 ] * u1 } CPPAD_TESTVECTOR(double) u2( f.Domain() ); CPPAD_TESTVECTOR(double) v2( f.Range() ); p = 2; u2[0] = .5; u2[1] = .4; u2[2] = .3; v2 = f.Forward(p, u2); // direct evaluation of Hessian of second components of f // (the Hessian of the first component is zero) CPPAD_TESTVECTOR(double) H1(9); H1[0] = 0.; H1[1] = u0[2]; H1[2] = u0[1]; H1[3] = u0[2]; H1[4] = 0.; H1[5] = u0[0]; H1[6] = u0[1]; H1[7] = u0[0]; H1[8] = 0.; // compare values ok &= NearEqual(v2[0] , g0[0]*u2[0] + g0[1]*u2[1] + g0[2]*u2[2] , eps99, eps99); size_t j; double v2_1 = 0.; for(i = 0; i < 3; i++) { v2_1 += g1[i] * u2[i]; for(j = 0; j < 3; j++) v2_1 += .5 * u1[i] * H1[i * 3 + j] * u1[j]; } ok &= NearEqual(v2[1], v2_1, eps99, eps99); return ok; } # ifndef NDEBUG # if ! CPPAD_DEBUG_AND_RELEASE void my_error_handler( bool known , int line , const char *file , const char *exp , const char *msg ) { // error handler must not return, so throw an exception std::string message = msg; throw message; } bool forward_nan(void) { using CppAD::vector; using CppAD::AD; size_t n = 2, m = 1; vector< AD > a_x(n), a_y(m); a_x[0] = 1.; a_x[1] = 2.; Independent(a_x); a_y[0] = a_x[0] / a_x[1]; CppAD::ADFun f(a_x, a_y); // vector x(n), y(m); x[0] = 0.; x[1] = 0.; // replace the default CppAD error handler CppAD::ErrorHandler info(my_error_handler); bool ok = false; try { y = f.Forward(0, x); } catch( std::string msg ) { // check that the message contains // "vector_size = " and "file_name = " ok = msg.find("vector_size = ") != std::string::npos; ok = msg.find("file_name = ") != std::string::npos; } return ok; } # endif # endif } // END empty namespace # include # include bool Forward(void) { bool ok = true; ok &= ForwardCases< CppAD::vector >(); ok &= ForwardCases< std::vector >(); ok &= ForwardCases< std::valarray >(); ok &= ForwardOlder(); # ifndef NDEBUG # if ! CPPAD_DEBUG_AND_RELEASE // CppAD does not check for nan when NDEBUG is defined ok &= forward_nan(); # endif # endif return ok; } ================================================ FILE: test_more/general/forward_dir.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // test multiple directions operators // MulvvOp is tested by example/forward_dir.cpp # include # include # include namespace { using CppAD::AD; using CppAD::NearEqual; // --------------------------------------------------------------------- // Used the check that fun is an identity function typedef AD (*adfun)(const AD&); bool check_identity(adfun identity, double argument) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = argument; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = identity(ax[0]); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 2); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // Y_0 (t) = F[X_0(t)] = X_0(t) // = 0.5 + 1t + 2t^2 double y_1_0 = 1.0; double y_2_0 = 2.0; // // Y_1 (t) = F[X_1(t)] = X_1(t) // = 0.5 + 2t + 3t^2 double y_1_1 = 2.0; double y_2_1 = 3.0; // ok &= NearEqual(y1[0] , y_1_0, eps, eps); ok &= NearEqual(y1[1] , y_1_1, eps, eps); ok &= NearEqual(y2[0] , y_2_0, eps, eps); ok &= NearEqual(y2[1] , y_2_1, eps, eps); // return ok; } // --------------------------------------------------------------------- // AbsOp bool abs_op(void) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; ax[1] = -1.0; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = fabs( ax[0] ) + fabs( 2.0 * ax[1] ); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 2); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // Y_0 (t) = F[X_0(t)] // = fabs(0.5 + 1t + 2t^2) + fabs( 2*(-1.0 + 2t + 3t^2 ) ) double y_1_0 = -3.0; double y_2_0 = -4.0; // // Y_1 (t) = F[X_1(t)] // = fabs(0.5 + 2t + 3t^2) + fabs( 2*(-1.0 + 3t + 4t^2 ) ) double y_1_1 = -4.0; double y_2_1 = -5.0; // ok &= NearEqual(y1[0] , y_1_0, eps, eps); ok &= NearEqual(y1[1] , y_1_1, eps, eps); ok &= NearEqual(y2[0] , y_2_0, eps, eps); ok &= NearEqual(y2[1] , y_2_1, eps, eps); // return ok; } // --------------------------------------------------------------------- // AddpvOp bool addpv_op(void) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = 2.0 + ax[0]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 3); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // Y_0 (t) = F[X_0(t)] // = 2.0 + (0.5 + 1t + 3t^2) double y_1_0 = 1.0; double y_2_0 = 3.0; // // Y_1 (t) = F[X_1(t)] // = 2.0 + (0.5 + 2t + 4t^2) double y_1_1 = 2.0; double y_2_1 = 4.0; // ok &= NearEqual(y1[0] , y_1_0, eps, eps); ok &= NearEqual(y1[1] , y_1_1, eps, eps); ok &= NearEqual(y2[0] , y_2_0, eps, eps); ok &= NearEqual(y2[1] , y_2_1, eps, eps); // return ok; } // --------------------------------------------------------------------- // AddvvOp bool addvv_op(void) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; ax[1] = 2.0; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = ax[0] + ax[1]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 2); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // Y_0 (t) = F[X_0(t)] // = (0.5 + 1t + 2t^2) + (2.0 + 2t + 3t^2) double y_1_0 = 1.0 + 2.0; double y_2_0 = 2.0 + 3.0; // // Y_1 (t) = F[X_1(t)] // = (2.0 + 2t + 3t^2) + (2.0 + 3t + 4t^2) double y_1_1 = 2.0 + 3.0; double y_2_1 = 3.0 + 4.0; // ok &= NearEqual(y1[0] , y_1_0, eps, eps); ok &= NearEqual(y1[1] , y_1_1, eps, eps); ok &= NearEqual(y2[0] , y_2_0, eps, eps); ok &= NearEqual(y2[1] , y_2_1, eps, eps); // return ok; } // --------------------------------------------------------------------- // CosOp bool cos_op(void) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = cos( ax[0] ); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 2); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // Y_0 (t) = F[X_0(t)] // = cos( 0.5 + 1t + 2t^2 ) // Y_0' (t) = -sin( 0.5 + 1t + 2t^2) * (1 + 4t) double y_1_0 = - sin(0.5); double y_2_0 = - ( cos(0.5) + 4.0 * sin(0.5) ) / 2.0; // // Y_1 (t) = F[X_1(t)] // = cos( 0.5 + 2t + 3t^2 ) // Y_1' (t) = -sin( 0.5 + 2t + 3t^2) * (2 + 6t) double y_1_1 = - sin(0.5) * 2.0; double y_2_1 = - ( cos(0.5) * 4.0 + 6.0 * sin(0.5) ) / 2.0; // ok &= NearEqual(y1[0] , y_1_0, eps, eps); ok &= NearEqual(y1[1] , y_1_1, eps, eps); ok &= NearEqual(y2[0] , y_2_0, eps, eps); ok &= NearEqual(y2[1] , y_2_1, eps, eps); // return ok; } // --------------------------------------------------------------------- // CoshOp bool cosh_op(void) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = cosh( ax[0] ); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 2); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // Y_0 (t) = F[X_0(t)] // = cosh( 0.5 + 1t + 2t^2 ) // Y_0' (t) = sinh( 0.5 + 1t + 2t^2) * (1 + 4t) double y_1_0 = sinh(0.5); double y_2_0 = ( sinh(0.5) * 4.0 + cosh(0.5) ) / 2.0; // // Y_1 (t) = F[X_1(t)] // = cosh( 0.5 + 2t + 3t^2 ) // Y_1' (t) = sinh( 0.5 + 2t + 3t^2) * (2 + 6t) double y_1_1 = sinh(0.5) * 2.0; double y_2_1 = ( sinh(0.5) * 6.0 + cosh(0.5) * 4.0 ) / 2.0; // ok &= NearEqual(y1[0] , y_1_0, eps, eps); ok &= NearEqual(y1[1] , y_1_1, eps, eps); ok &= NearEqual(y2[0] , y_2_0, eps, eps); ok &= NearEqual(y2[1] , y_2_1, eps, eps); // return ok; } // --------------------------------------------------------------------- // CExpOp bool cexp_op(void) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 4; CPPAD_TESTVECTOR(AD) ax(n); for(j = 0; j < n; j++) ax[j] = double(j); // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 2; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = CondExpLt(ax[0], ax[1], ax[2], ax[3]); ay[1] = CondExpGt(ax[0], ax[1], ax[2], ax[3]); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 2); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // Y0_0 (t) = X2_0(t) // = 2.0 + 3t + 4t^2 double y0_1_0 = 3.0; double y0_2_0 = 4.0; // // Y1_0 (t) = X3_0(t) // = 3.0 + 4t + 5t^2 double y1_1_0 = 4.0; double y1_2_0 = 5.0; // // Y0_1 (t) = X2_1(t) // = 2.0 + 4t + 5t^2 double y0_1_1 = 4.0; double y0_2_1 = 5.0; // // Y1_1 (t) = X3_0(t) // = 3.0 + 5t + 6t^2 double y1_1_1 = 5.0; double y1_2_1 = 6.0; // ok &= NearEqual(y1[0*r+0] , y0_1_0, eps, eps); ok &= NearEqual(y1[1*r+0] , y1_1_0, eps, eps); ok &= NearEqual(y1[0*r+1] , y0_1_1, eps, eps); ok &= NearEqual(y1[1*r+1] , y1_1_1, eps, eps); // ok &= NearEqual(y2[0*r+0] , y0_2_0, eps, eps); ok &= NearEqual(y2[1*r+0] , y1_2_0, eps, eps); ok &= NearEqual(y2[0*r+1] , y0_2_1, eps, eps); ok &= NearEqual(y2[1*r+1] , y1_2_1, eps, eps); // return ok; } // --------------------------------------------------------------------- // CSumOp bool csum_op(void) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 3; CPPAD_TESTVECTOR(AD) ax(n); for(j = 0; j < n; j++) ax[j] = double(j); // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = 0.0; for(j = 0; j < n; j++) ay[0] += ax[j]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // optimize the tape so converts summation to on CSumOp operator f.optimize(); // zero order CPPAD_TESTVECTOR(double) x0(n); for(j = 0; j < n; j++) x0[j] = double(j); f.Forward(0, x0); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 3); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // double check = 0.0; for(j = 0; j < n; j++) check += x1[ r * j + 0]; ok &= NearEqual(y1[0] , check, eps, eps); // check = 0.0; for(j = 0; j < n; j++) check += x1[ r * j + 1]; ok &= NearEqual(y1[1] , check, eps, eps); // check = 0.0; for(j = 0; j < n; j++) check += x2[ r * j + 0]; ok &= NearEqual(y2[0] , check, eps, eps); // check = 0.0; for(j = 0; j < n; j++) check += x2[ r * j + 1]; ok &= NearEqual(y2[1] , check, eps, eps); // return ok; } // --------------------------------------------------------------------- // DisOp (test assuming that AddvvOp is correct) double round_off(const double& x) { // std::round(x); is C++11, so we avoid using it return std::floor( x + 0.5 ); } CPPAD_DISCRETE_FUNCTION(double, round_off) bool dis_op(void) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = round_off( ax[0] ) + ax[0]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // zero order CPPAD_TESTVECTOR(double) x0(n), y0; x0[0] = 2.2; y0 = f.Forward(0, x0); ok &= size_t( y0.size() ) == m; ok &= NearEqual(y0[0], round_off(x0[0]) + x0[0], eps, eps); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 2); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // // Y_0 (t) = F[X_0(t)] // = 2.0 + (2.2 + 1t + 2t^2) double y_1_0 = 1.0; double y_2_0 = 2.0; // // Y_1 (t) = F[X_1(t)] // = 2.0 + (2.2 + 2t + 3t^2) double y_1_1 = 2.0; double y_2_1 = 3.0; // ok &= NearEqual(y1[0] , y_1_0, eps, eps); ok &= NearEqual(y1[1] , y_1_1, eps, eps); ok &= NearEqual(y2[0] , y_2_0, eps, eps); ok &= NearEqual(y2[1] , y_2_1, eps, eps); // return ok; } // --------------------------------------------------------------------- // DivpvOp (testing assumping MulpvOp is correct) bool divpv_op(void) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = (2.0 / ax[0]) * (ax[0] * ax[0]); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 2); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // Y_0 (t) = F[X_0(t)] // = 2.0 * (0.5 + 1t + 2t^2) double y_1_0 = 2.0; double y_2_0 = 4.0; // // Y_1 (t) = F[X_1(t)] // = 2.0 * (0.5 + 2t + 3t^2)/2.0 double y_1_1 = 4.0; double y_2_1 = 6.0; // ok &= NearEqual(y1[0] , y_1_0, eps, eps); ok &= NearEqual(y1[1] , y_1_1, eps, eps); ok &= NearEqual(y2[0] , y_2_0, eps, eps); ok &= NearEqual(y2[1] , y_2_1, eps, eps); // return ok; } // --------------------------------------------------------------------- // DivvpOp bool divvp_op(void) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = ax[0] / 2.0; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 3); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // Y_0 (t) = F[X_0(t)] // = (0.5 + 1t + 3t^2)/2.0 double y_1_0 = 1.0 / 2.0; double y_2_0 = 3.0 / 2.0; // // Y_1 (t) = F[X_1(t)] // = (0.5 + 2t + 4t^2)/2.0 double y_1_1 = 2.0 / 2.0; double y_2_1 = 4.0 / 2.0; // ok &= NearEqual(y1[0] , y_1_0, eps, eps); ok &= NearEqual(y1[1] , y_1_1, eps, eps); ok &= NearEqual(y2[0] , y_2_0, eps, eps); ok &= NearEqual(y2[1] , y_2_1, eps, eps); // return ok; } // --------------------------------------------------------------------- // ExpOp bool exp_op(void) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = exp( ax[0] ); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 2); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // Y_0 (t) = F[X_0(t)] // = exp(0.5 + 1t + 2t^2) // Y_0' (t) = exp(0.5 + 1t + 2t^2)*(1 + 4t) double y_1_0 = exp(0.5); double y_2_0 = ( exp(0.5)*4.0 + exp(0.5) ) / 2.0; // // Y_1 (t) = F[X_1(t)] // = exp(0.5 + 2t + 3t^2) // Y_1' (t) = exp(0.5 + 2t + 3t^2)*(2 + 6t) double y_1_1 = exp(0.5)*2.0; double y_2_1 = ( exp(0.5)*6.0 + exp(0.5)*2.0*2.0 ) / 2.0; // ok &= NearEqual(y1[0] , y_1_0, eps, eps); ok &= NearEqual(y1[1] , y_1_1, eps, eps); ok &= NearEqual(y2[0] , y_2_0, eps, eps); ok &= NearEqual(y2[1] , y_2_1, eps, eps); // return ok; } // --------------------------------------------------------------------- // LdpOp and LdvOp (test assuming AdvvOp is correct) bool load_op(void) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.0; ax[1] = 1.0; // declare independent variables and starting recording CppAD::Independent(ax); // Store operations CppAD::VecAD avec(3); avec[ AD(0) ] = ax[0]; // store a variable avec[ AD(1) ] = ax[1]; // store a variable avec[ AD(2) ] = 5.0; // store a parameter // range space vector size_t m = 2; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = avec[ AD(0) ]; // load using parameter index ay[1] = avec[ ax[0] ]; // load using variable index // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // zero order Taylor coefficients CPPAD_TESTVECTOR(double) x0(n), y0; x0[0] = 2; x0[1] = 3; y0 = f.Forward(0, x0); ok &= size_t( y0.size() ) == m; // y[0] = avec[0] = x[0] ok &= y0[0] == x0[0]; // y[1] = avec[ x[0] ] = avec[2] = 5.0 ok &= y0[1] == 5.0; // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 2); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // Y0_0 (t) = 2.0 + 1t + 2t^2 double y0_1_0 = 1.0; double y0_2_0 = 2.0; // // Y1_0 (t) = 5.0 double y1_1_0 = 0.0; double y1_2_0 = 0.0; // // Y0_1 (t) = 2.0 + 2t + 3t^2 double y0_1_1 = 2.0; double y0_2_1 = 3.0; // // Y1_1 (t) = 5.0 double y1_1_1 = 0.0; double y1_2_1 = 0.0; // ok &= NearEqual(y1[0*r+0] , y0_1_0, eps, eps); ok &= NearEqual(y1[1*r+0] , y1_1_0, eps, eps); ok &= NearEqual(y1[0*r+1] , y0_1_1, eps, eps); ok &= NearEqual(y1[1*r+1] , y1_1_1, eps, eps); // ok &= NearEqual(y2[0*r+0] , y0_2_0, eps, eps); ok &= NearEqual(y2[1*r+0] , y1_2_0, eps, eps); ok &= NearEqual(y2[0*r+1] , y0_2_1, eps, eps); ok &= NearEqual(y2[1*r+1] , y1_2_1, eps, eps); // return ok; } // --------------------------------------------------------------------- // MulpvOp bool mulpv_op(void) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = 2.0 * ax[0]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 3); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // Y_0 (t) = F[X_0(t)] // = 2.0 * (0.5 + 1t + 3t^2) double y_1_0 = 2.0 * 1.0; double y_2_0 = 2.0 * 3.0; // // Y_1 (t) = F[X_1(t)] // = 2.0 * (0.5 + 2t + 4t^2) double y_1_1 = 2.0 * 2.0; double y_2_1 = 2.0 * 4.0; // ok &= NearEqual(y1[0] , y_1_0, eps, eps); ok &= NearEqual(y1[1] , y_1_1, eps, eps); ok &= NearEqual(y2[0] , y_2_0, eps, eps); ok &= NearEqual(y2[1] , y_2_1, eps, eps); // return ok; } // --------------------------------------------------------------------- // ParOp bool par_op(void) { bool ok = true; size_t j; // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = 0.0 * ax[0]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 2); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // Y_0 (t) = 0.0 for(ell = 0; ell < r; ell++) { ok &= y1[ell] == 0.0; ok &= y2[ell] == 0.0; } return ok; } // --------------------------------------------------------------------- // PowvvOp (test assuming LogOp, ExpOp and DivvvOp are correct) bool powvv_op(void) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; ax[1] = 2.0; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 2; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = log( pow( exp(ax[0]) , ax[1] ) ) / ax[1] ; ay[1] = log( pow( exp(ax[0]) , ax[1] ) ) / ax[0] ; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 2); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // Y0_0 (t) = 0.5 + 1t + 2t^2 double y0_1_0 = 1.0; double y0_2_0 = 2.0; // // Y0_1 (t) = 0.5 + 2t + 3t^2 double y0_1_1 = 2.0; double y0_2_1 = 3.0; // // Y1_0 (t) = 2.0 + 2t + 3t^2 double y1_1_0 = 2.0; double y1_2_0 = 3.0; // // Y1_1 (t) = 2.0 + 3t + 4t^2 double y1_1_1 = 3.0; double y1_2_1 = 4.0; // ok &= NearEqual(y1[0*r+0] , y0_1_0, eps, eps); ok &= NearEqual(y1[1*r+0] , y1_1_0, eps, eps); ok &= NearEqual(y1[0*r+1] , y0_1_1, eps, eps); ok &= NearEqual(y1[1*r+1] , y1_1_1, eps, eps); // ok &= NearEqual(y2[0*r+0] , y0_2_0, eps, eps); ok &= NearEqual(y2[1*r+0] , y1_2_0, eps, eps); ok &= NearEqual(y2[0*r+1] , y0_2_1, eps, eps); ok &= NearEqual(y2[1*r+1] , y1_2_1, eps, eps); // return ok; } // --------------------------------------------------------------------- // SignOp (test assuming that MulvvOp is correct) bool sign_op(void) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = sign( ax[0] ) * ax[0]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // zero order CPPAD_TESTVECTOR(double) x0(n), y0; x0[0] = -3.0; y0 = f.Forward(0, x0); ok &= size_t( y0.size() ) == m; ok &= NearEqual(y0[0], fabs(x0[0]), eps, eps); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 2); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // // Y_0 (t) = F[X_0(t)] // = -(-3.0 + 1t + 2t^2) double y_1_0 = -1.0; double y_2_0 = -2.0; // // Y_1 (t) = F[X_1(t)] // = -(-3.0 + 2t + 3t^2) double y_1_1 = -2.0; double y_2_1 = -3.0; // ok &= NearEqual(y1[0] , y_1_0, eps, eps); ok &= NearEqual(y1[1] , y_1_1, eps, eps); ok &= NearEqual(y2[0] , y_2_0, eps, eps); ok &= NearEqual(y2[1] , y_2_1, eps, eps); // return ok; } // --------------------------------------------------------------------- // SinOp bool sin_op(void) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = sin( ax[0] ); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 2); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // Y_0 (t) = F[X_0(t)] // = sin( 0.5 + 1t + 2t^2 ) // Y_0' (t) = cos( 0.5 + 1t + 2t^2) * (1 + 4t) double y_1_0 = cos(0.5); double y_2_0 = ( cos(0.5) * 4.0 - sin(0.5) ) / 2.0; // // Y_1 (t) = F[X_1(t)] // = sin( 0.5 + 2t + 3t^2 ) // Y_1' (t) = cos( 0.5 + 2t + 3t^2) * (2 + 6t) double y_1_1 = cos(0.5) * 2.0; double y_2_1 = ( cos(0.5) * 6.0 - sin(0.5) * 4.0 ) / 2.0; // ok &= NearEqual(y1[0] , y_1_0, eps, eps); ok &= NearEqual(y1[1] , y_1_1, eps, eps); ok &= NearEqual(y2[0] , y_2_0, eps, eps); ok &= NearEqual(y2[1] , y_2_1, eps, eps); // return ok; } // --------------------------------------------------------------------- // SinhOp bool sinh_op(void) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = sinh( ax[0] ); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 2); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // Y_0 (t) = F[X_0(t)] // = sinh( 0.5 + 1t + 2t^2 ) // Y_0' (t) = cosh( 0.5 + 1t + 2t^2) * (1 + 4t) double y_1_0 = cosh(0.5); double y_2_0 = ( cosh(0.5) * 4.0 + sinh(0.5) ) / 2.0; // // Y_1 (t) = F[X_1(t)] // = sinh( 0.5 + 2t + 3t^2 ) // Y_1' (t) = cosh( 0.5 + 2t + 3t^2) * (2 + 6t) double y_1_1 = cosh(0.5) * 2.0; double y_2_1 = ( cosh(0.5) * 6.0 + sinh(0.5) * 4.0 ) / 2.0; // ok &= NearEqual(y1[0] , y_1_0, eps, eps); ok &= NearEqual(y1[1] , y_1_1, eps, eps); ok &= NearEqual(y2[0] , y_2_0, eps, eps); ok &= NearEqual(y2[1] , y_2_1, eps, eps); // return ok; } // --------------------------------------------------------------------- // SubpvOp bool subpv_op(void) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = 2.0 - ax[0]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 3); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // Y_0 (t) = F[X_0(t)] // = 2.0 - (0.5 + 1t + 3t^2)/2.0 double y_1_0 = - 1.0; double y_2_0 = - 3.0; // // Y_1 (t) = F[X_1(t)] // = 3.0 - (0.5 + 2t + 4t^2)/2.0 double y_1_1 = - 2.0; double y_2_1 = - 4.0; // ok &= NearEqual(y1[0] , y_1_0, eps, eps); ok &= NearEqual(y1[1] , y_1_1, eps, eps); ok &= NearEqual(y2[0] , y_2_0, eps, eps); ok &= NearEqual(y2[1] , y_2_1, eps, eps); // return ok; } // --------------------------------------------------------------------- // SubvvOp bool subvv_op(void) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 2; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; ax[1] = 2.0; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = ax[0] - 2.0 * ax[1]; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 2); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // Y_0 (t) = F[X_0(t)] // = (0.5 + 1t + 2t^2) - 2.0 * (2.0 + 2t + 3t^2) double y_1_0 = 1.0 - 4.0; double y_2_0 = 2.0 - 6.0; // // Y_1 (t) = F[X_1(t)] // = (2.0 + 2t + 3t^2) - 2.0 * (2.0 + 3t + 4t^2) double y_1_1 = 2.0 - 6.0; double y_2_1 = 3.0 - 8.0; // ok &= NearEqual(y1[0] , y_1_0, eps, eps); ok &= NearEqual(y1[1] , y_1_1, eps, eps); ok &= NearEqual(y2[0] , y_2_0, eps, eps); ok &= NearEqual(y2[1] , y_2_1, eps, eps); // return ok; } // --------------------------------------------------------------------- // TanOp bool tan_op(void) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = tan( ax[0] ); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 2); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // Y_0 (t) = F[X_0(t)] // = tan(0.5 + 1t + 2t^2) // Y_0' (t) = cos(0.5 + 1t + 2t^2)^(-2)*(1 + 4t) // Y_0''(0) = 2*cos(0.5)^(-3)*sin(0.5) + 4*cos(0.5)^(-2) double sec_sq = 1.0 / ( cos(0.5) * cos(0.5) ); double y_1_0 = sec_sq; double y_2_0 = (2.0 * tan(0.5) + 4.0) * sec_sq / 2.0; // // Y_1 (t) = F[X_1(t)] // = tan(0.5 + 2t + 3t^2) // Y_1' (t) = cos(0.5 + 2t + 3t^2)^(-2)*(2 + 6t) // Y_1''(0) = 2*cos(0.5)^(-3)*sin(0.5)*2*2 + 6*cos(0.5)^(-2) double y_1_1 = 2.0 * sec_sq; double y_2_1 = (8.0 * tan(0.5) + 6.0) * sec_sq / 2.0; // ok &= NearEqual(y1[0] , y_1_0, eps, eps); ok &= NearEqual(y1[1] , y_1_1, eps, eps); ok &= NearEqual(y2[0] , y_2_0, eps, eps); ok &= NearEqual(y2[1] , y_2_1, eps, eps); // return ok; } // --------------------------------------------------------------------- // Usr*Op typedef CPPAD_TESTVECTOR(AD) avector; void usr_algo(const avector& x, avector& z) { z[0] = ( x[0] + x[1] ) / 2.0; z[1] = x[0] * x[1]; z[2] = ( x[0] - x[1] ) / 2.0; return; } bool usr_op(void) { bool ok = true; double eps = 10. * std::numeric_limits::epsilon(); size_t j; // define checkpoint function size_t n = 2; avector ax(n), az(3); ax[0] = 0.5; ax[1] = 2.0; CppAD::checkpoint usr_check("usr_check", usr_algo, ax, az); // declare independent variables and starting recording CppAD::Independent(ax); // record checkpoint function usr_check(ax, az); // range space vector size_t m = 2; avector ay(m); ay[0] = az[0] + az[2]; // = ax[0] ay[1] = az[0] - az[2]; // = ax[1] // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // first order Taylor coefficients size_t r = 2, ell; CPPAD_TESTVECTOR(double) x1(r*n), y1; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x1[ r * j + ell ] = double(j + ell + 1); } y1 = f.Forward(1, r, x1); ok &= size_t( y1.size() ) == r*m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(r*n), y2; for(ell = 0; ell < r; ell++) { for(j = 0; j < n; j++) x2[ r * j + ell ] = double(j + ell + 2); } y2 = f.Forward(2, r, x2); ok &= size_t( y2.size() ) == r*m; // // Y0_0 (t) = 0.5 + 1t + 2t^2 double y0_1_0 = 1.0; double y0_2_0 = 2.0; // // Y0_1 (t) = 0.5 + 2t + 3t^2 double y0_1_1 = 2.0; double y0_2_1 = 3.0; // // Y1_0 (t) = 2.0 + 2t + 3t^2 double y1_1_0 = 2.0; double y1_2_0 = 3.0; // // Y1_1 (t) = 2.0 + 3t + 4t^2 double y1_1_1 = 3.0; double y1_2_1 = 4.0; // ok &= NearEqual(y1[0*r+0] , y0_1_0, eps, eps); ok &= NearEqual(y1[1*r+0] , y1_1_0, eps, eps); ok &= NearEqual(y1[0*r+1] , y0_1_1, eps, eps); ok &= NearEqual(y1[1*r+1] , y1_1_1, eps, eps); // ok &= NearEqual(y2[0*r+0] , y0_2_0, eps, eps); ok &= NearEqual(y2[1*r+0] , y1_2_0, eps, eps); ok &= NearEqual(y2[0*r+1] , y0_2_1, eps, eps); ok &= NearEqual(y2[1*r+1] , y1_2_1, eps, eps); // return ok; } // --------------------------------------------------------------------- // Inverse functions assume the following already tested: // CosOp, SinOp, TanOp, ExpOp, MulvvOp, DivvpOp, AddpvOp // // AcosOp AD acos_fun(const AD& x) { return acos( cos(x) ); } bool acos_op(void) { return check_identity(acos_fun, 0.5); } // // AcoshOp AD acosh_fun(const AD& x) { return acosh( cosh(x) ); } bool acosh_op(void) { return check_identity(acosh_fun, 0.5); } // // AsinOp AD asin_fun(const AD& x) { return asin( sin(x) ); } bool asin_op(void) { return check_identity(asin_fun, 0.5); } // // AsinhOp AD asinh_fun(const AD& x) { return asinh( sinh(x) ); } bool asinh_op(void) { return check_identity(asinh_fun, 0.5); } // // AtanOp AD atan_fun(const AD& x) { return atan( tan(x) ); } bool atan_op(void) { return check_identity(atan_fun, 0.5); } // // AtanhOp AD atanh_fun(const AD& x) { return atanh( tanh(x) ); } bool atanh_op(void) { return check_identity(atanh_fun, 0.5); } // // LogOp AD log_fun(const AD& x) { return log( exp(x) ); } bool log_op(void) { return check_identity(log_fun, 0.5); } // // DivvvOp AD divvv_fun(const AD& x) { return (x * x) / x; } bool divvv_op(void) { return check_identity(divvv_fun, 0.5); } // // PowpvOp AD powpv_fun(const AD& x ) { return log( pow( exp(3.0) , x ) ) / 3.0; } bool powpv_op(void) { return check_identity(powpv_fun, 0.5); } // // PowvpOp AD powvp_fun(const AD& x ) { return log( pow( exp(x) , 3.0 ) ) / 3.0; } bool powvp_op(void) { return check_identity(powvp_fun, 0.5); } // // SqrtOp AD sqrt_fun(const AD& x ) { return sqrt( x * x ); } bool sqrt_op(void) { return check_identity(sqrt_fun, 0.5); } // // SubvpOp AD subvp_fun(const AD& x ) { return 3.0 + ( x - 3.0 ); } bool subvp_op(void) { return check_identity(subvp_fun, 0.5); } // // TanhOp AD tanh_fun(const AD& x ) { AD z = tanh(x); return log( (1.0 + z) / (1.0 - z) ) / 2.0; } bool tanh_op(void) { return check_identity(tanh_fun, 0.5); } } bool forward_dir(void) { bool ok = true; // ok &= abs_op(); ok &= acos_op(); ok &= acosh_op(); ok &= asin_op(); ok &= asinh_op(); ok &= atan_op(); ok &= atanh_op(); ok &= addpv_op(); ok &= addvv_op(); ok &= cexp_op(); ok &= cosh_op(); ok &= cos_op(); ok &= csum_op(); ok &= dis_op(); ok &= divpv_op(); ok &= divvp_op(); ok &= divvv_op(); ok &= exp_op(); ok &= load_op(); ok &= log_op(); ok &= mulpv_op(); ok &= par_op(); ok &= powpv_op(); ok &= powvp_op(); ok &= powvv_op(); ok &= sign_op(); ok &= sin_op(); ok &= sinh_op(); ok &= subpv_op(); ok &= subvp_op(); ok &= subvv_op(); ok &= sqrt_op(); ok &= tan_op(); ok &= tanh_op(); ok &= usr_op(); // return ok; } ================================================ FILE: test_more/general/forward_order.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* */ // BEGIN C++ # include namespace { double my_discrete(const double& x) { return static_cast ( x ); } CPPAD_DISCRETE_FUNCTION(double, my_discrete) } bool forward_order(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; size_t j, k; double eps = 10. * CppAD::numeric_limits::epsilon(); // domain space vector size_t n = 23, m = n; CPPAD_TESTVECTOR(AD) X(n), Y(m); for(j = 0; j < n; j++) X[j] = 0.0; // declare independent variables and starting recording CppAD::Independent(X); // identity function values size_t i = 0; size_t identity_begin = i; Y[i] = cos( acos( X[i] ) ); i++; // AcosOp, CosOp Y[i] = sin( asin( X[i] ) ); i++; // AsinOp, SinOp Y[i] = tan( atan( X[i] ) ); i++; // AtanOp, TanOp Y[i] = CondExpGt(X[i], X[i-1], X[i], X[i-2]); i++; // CExpOp Y[i] = X[i-1] * X[i] / X[i-1]; i++; // DivvvOp, MulvvOp Y[i] = X[i] * X[i] * 1.0 / X[i]; i++; // DivpvOp Y[i] = 5.0 * X[i] / 5.0; i++; // DivvpOp, MulpvOp Y[i] = exp( log( X[i] ) ); i++; // ExpOp, LogOp Y[i] = pow( sqrt( X[i] ), 2.0); i++; // PowvpOp, SqrtOp Y[i] = log( pow( std::exp(1.), X[i] ) ); i++; // PowpvOp Y[i] = log( pow( X[i], X[i] ) ) / log( X[i]); i++; // PowvvOp Y[i] = -2. - ((X[i-1] - X[i]) - 2.) + X[i-1]; i++; // Sub*Op: pv, vv, vp size_t identity_end = i; // other functions Y[i] = fabs( X[i] ); i++; // AbsOp Y[i] = X[i-1] + X[i] + 2.0; i++; // AddvvOp, AddvpOp Y[i] = cosh( X[i] ); i++; // CoshOp Y[i] = my_discrete( X[i] ); i++; // DisOp Y[i] = 4.0; i++; // ParOp Y[i] = sign( X[i] ); i++; // SignOp Y[i] = sinh( X[i] ); i++; // SinhOp Y[i] = tanh(X[i]); i++; // TanhOp // VecAD operations CppAD::VecAD V(n); AD index = 1.; V[index] = 3.0; Y[i] = V[index]; i++; // StppOp, LdpOp V[index] = X[0]; Y[i] = V[index]; i++; // StpvOp, LdpOp index = double(n) * X[3]; V[index] = X[1]; Y[i] = V[index]; i++; // StvvOp, LdvOp // create f: X -> Y and stop tape recording assert( i == m ); CppAD::ADFun f; f.Dependent(X, Y); // initially, no values stored in f ok &= f.size_order() == 0; // Set X_j (t) = x + t size_t p = 2, p1 = p+1; CPPAD_TESTVECTOR(double) x(n), x_p(n * p1), y_p(m * p1); for(j = 0; j < n; j++) { x[j] = double(j) / double(n); x_p[j * p1 + 0] = x[j]; // order 0 x_p[j * p1 + 1] = 1.; // order 1 x_p[j * p1 + 2] = 0.; // order 2 } // compute orders 0, 1, 2 y_p = f.Forward(p, x_p); // identity functions CPPAD_TESTVECTOR(double) y(p1); i = 0; for(j = identity_begin; j != identity_end; j++) { y[0] = x[j]; y[1] = 1.0; y[2] = 0.0; for(k = 0; k < p1; k++) ok &= NearEqual(y[k] , y_p[i * p1 + k], eps, eps); i++; } // y_i = fabs( x_i ) y[0] = fabs( x[i] ); y[1] = CppAD::sign( x[i] ); y[2] = 0.0; for(k = 0; k < p1; k++) ok &= NearEqual(y[k] , y_p[i * p1 + k], eps, eps); // y_i = x_[i-1] + x_i + 2 i++; y[0] = x[i-1] + x[i] + 2.0; y[1] = 2.0; y[2] = 0.0; for(k = 0; k < p1; k++) ok &= NearEqual(y[k] , y_p[i * p1 + k], eps, eps); // y_i = cosh( x_i ) i++; y[0] = CppAD::cosh( x[i] ); y[1] = CppAD::sinh( x[i] ); y[2] = CppAD::cosh( x[i] ) / 2.0; for(k = 0; k < p1; k++) ok &= NearEqual(y[k] , y_p[i * p1 + k], eps, eps); // y_i = my_discrete( x_i ) i++; y[0] = my_discrete( x[i] ); y[1] = 0.0; y[2] = 0.0; for(k = 0; k < p1; k++) ok &= NearEqual(y[k] , y_p[i * p1 + k], eps, eps); // y_i = 4 i++; y[0] = 4.0; y[1] = 0.0; y[2] = 0.0; for(k = 0; k < p1; k++) ok &= NearEqual(y[k] , y_p[i * p1 + k], eps, eps); // y_i = sign( x_i ) i++; y[0] = CppAD::sign( x[i] ); y[1] = 0.0; y[2] = 0.0; for(k = 0; k < p1; k++) ok &= NearEqual(y[k] , y_p[i * p1 + k], eps, eps); // y_i = sinh( x_i ) i++; y[0] = CppAD::sinh( x[i] ); y[1] = CppAD::cosh( x[i] ); y[2] = CppAD::sinh( x[i] ) / 2.0; for(k = 0; k < p1; k++) ok &= NearEqual(y[k] , y_p[i * p1 + k], eps, eps); // y_i = tanh( x_i ) i++; y[0] = CppAD::tanh( x[i] ); y[1] = 1.0 - y[0] * y[0]; y[2] = - 2.0 * y[0] * y[1] / 2.0; for(k = 0; k < p1; k++) ok &= NearEqual(y[k] , y_p[i * p1 + k], eps, eps); // y_i = 3.0; i++; y[0] = 3.0; y[1] = 0.0; y[2] = 0.0; for(k = 0; k < p1; k++) ok &= NearEqual(y[k] , y_p[i * p1 + k], eps, eps); // y_i = x_0 i++; y[0] = x[0]; y[1] = 1.0; y[2] = 0.0; for(k = 0; k < p1; k++) ok &= NearEqual(y[k] , y_p[i * p1 + k], eps, eps); // y_i = x_1 i++; y[0] = x[1]; y[1] = 1.0; y[2] = 0.0; for(k = 0; k < p1; k++) ok &= NearEqual(y[k] , y_p[i * p1 + k], eps, eps); return ok; } // END C++ ================================================ FILE: test_more/general/from_base.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old FromBase example now used just for valiadation testing */ # include bool FromBase(void) { bool ok = true; using namespace CppAD; // construct directly form Base where Base = double AD x(1.); AD y = 2.; // construct from a type that can be converted to Base // where Base = AD AD< AD > X(1.); AD< AD > Y(2); // check that resulting objects are parameters ok &= Parameter(x); ok &= Parameter(y); ok &= Parameter(X); ok &= Parameter(Y); // check values of objects ok &= (x == 1.); ok &= (X == x); ok &= (y == 2.); ok &= (Y == y); // user constructor through the static_cast template function x = static_cast < AD >( 4 ); X = static_cast < AD< AD > >( 4 ); ok &= (x == 4.); ok &= (X == x); return ok; } // END PROGRAM ================================================ FILE: test_more/general/fun_check.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* old example, now just used for testing deprecated syntax f.Dependent(y) */ // BEGIN C++ # include namespace { // ----------------------------------------------------------- // define the template function object Fun in empty namespace template class Fun { private: size_t n; public: // function constructor Fun(size_t n_) : n(n_) { } // function evaluator Vector operator() (const Vector &x) { Vector y(n); size_t i; for(i = 0; i < n; i++) { // This operation sequence depends on x if( x[i] >= 0 ) y[i] = exp(x[i]); else y[i] = exp(-x[i]); } return y; } }; // template function FunCheckCases in empty namespace template bool FunCheckCases(void) { bool ok = true; using CppAD::AD; using CppAD::ADFun; using CppAD::Independent; double eps99 = 99.0 * std::numeric_limits::epsilon(); // use the ADFun default constructor ADFun f; // domain space vector size_t n = 2; ADVector X(n); X[0] = -1.; X[1] = 1.; // declare independent variables and starting recording Independent(X); // create function object to use with AD Fun< AD, ADVector > G(n); // range space vector size_t m = n; ADVector Y(m); Y = G(X); // stop tape and store operation sequence in f : X -> Y f.Dependent(Y); ok &= (f.size_order() == 0); // no implicit forward operation // create function object to use with double Fun g(n); // function values should agree when the independent variable // values are the same as during recording Vector x(n); size_t j; for(j = 0; j < n; j++) x[j] = Value(X[j]); double r = eps99; double a = eps99; ok &= FunCheck(f, g, x, a, r); // function values should not agree when the independent variable // values are the negative of values during recording for(j = 0; j < n; j++) x[j] = - Value(X[j]); ok &= ! FunCheck(f, g, x, a, r); // re-tape to obtain the new AD of double operation sequence for(j = 0; j < n; j++) X[j] = x[j]; Independent(X); Y = G(X); // stop tape and store operation sequence in f : X -> Y f.Dependent(Y); ok &= (f.size_order() == 0); // no implicit forward with this x // function values should agree now ok &= FunCheck(f, g, x, a, r); return ok; } } // End empty namespace # include # include bool FunCheck(void) { bool ok = true; typedef CppAD::vector Vector1; typedef CppAD::vector< CppAD::AD > ADVector1; typedef std::vector Vector2; typedef std::vector< CppAD::AD > ADVector2; typedef std::valarray Vector3; typedef std::valarray< CppAD::AD > ADVector3; // Run with Vector and ADVector equal to three different cases // all of which are Simple Vectors with elements of type // double and AD respectively. ok &= FunCheckCases< Vector1, ADVector2 >(); ok &= FunCheckCases< Vector2, ADVector3 >(); ok &= FunCheckCases< Vector3, ADVector1 >(); return ok; } // END C++ ================================================ FILE: test_more/general/general.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- // CPPAD_HAS_* defines # include // system include files used for I/O # include // memory leak checker # include // test runner # include // BEGIN_SORT_THIS_LINE_PLUS_1 extern bool Add(void); extern bool AddEq(void); extern bool AddZero(void); extern bool Compare(void); extern bool CondExp(void); extern bool CondExpAD(void); extern bool Cos(void); extern bool Cosh(void); extern bool Div(void); extern bool DivEq(void); extern bool DivZeroOne(void); extern bool Exp(void); extern bool ForHess(void); extern bool Forward(void); extern bool FromBase(void); extern bool FunCheck(void); extern bool Mul(void); extern bool MulEq(void); extern bool MulZeroOne(void); extern bool Neg(void); extern bool Poly(void); extern bool Pow(void); extern bool PowInt(void); extern bool RevTwo(void); extern bool RombergOne(void); extern bool Rosen34(void); extern bool Runge45(void); extern bool SimpleVector(void); extern bool Sin(void); extern bool SinCos(void); extern bool Sinh(void); extern bool Sqrt(void); extern bool Sub(void); extern bool SubEq(void); extern bool SubZero(void); extern bool Value(void); extern bool VecAD(void); extern bool VecADPar(void); extern bool VecUnary(void); extern bool abs_normal(void); extern bool acos(void); extern bool acosh(void); extern bool adfun(void); extern bool alloc_openmp(void); extern bool asin(void); extern bool asinh(void); extern bool assign(void); extern bool atan(void); extern bool atan2(void); extern bool atanh(void); extern bool atomic_four(void); extern bool atomic_three(void); extern bool azmul(void); extern bool base2ad(void); extern bool base_adolc(void); extern bool base_alloc_test(void); extern bool base_complex(void); extern bool bool_sparsity(void); extern bool check_simple_vector(void); extern bool chkpoint_one(void); extern bool chkpoint_two(void); extern bool compare_change(void); extern bool cond_exp_rev(void); extern bool copy(void); extern bool cpp_graph(void); extern bool cppad_eigen(void); extern bool cppad_vector(void); extern bool dbl_epsilon(void); extern bool dependency(void); extern bool eigen_mat_inv(void); extern bool erf(void); extern bool expm1(void); extern bool fabs(void); extern bool for_hes_sparsity(void); extern bool for_jac_sparsity(void); extern bool forward_dir(void); extern bool forward_order(void); extern bool hes_sparsity(void); extern bool ipopt_solve(void); extern bool jacobian(void); extern bool json_graph(void); extern bool log(void); extern bool log10(void); extern bool log1p(void); extern bool mul_cond_rev(void); extern bool mul_cskip(void); extern bool mul_level(void); extern bool mul_zdouble(void); extern bool near_equal_ext(void); extern bool new_dynamic(void); extern bool num_limits(void); extern bool ode_err_control(void); extern bool optimize(void); extern bool parameter(void); extern bool print_for(void); extern bool rev_sparse_jac(void); extern bool reverse(void); extern bool sparse_hessian(void); extern bool sparse_jac_work(void); extern bool sparse_jacobian(void); extern bool sparse_sub_hes(void); extern bool sparse_vec_ad(void); extern bool std_math(void); extern bool subgraph_1(void); extern bool subgraph_2(void); extern bool subgraph_hes2jac(void); extern bool tan(void); extern bool to_csrc(void); extern bool to_string(void); // END_SORT_THIS_LINE_MINUS_1 // tests in local subdirectory extern bool is_pod(void); extern bool json_lexer(void); extern bool json_parser(void); extern bool temp_file(void); extern bool vector_set(void); // main program that runs all the tests int main(void) { std::string group = "test_more/general"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // BEGIN_SORT_THIS_LINE_PLUS_1 Run( Add, "Add" ); Run( AddEq, "AddEq" ); Run( AddZero, "AddZero" ); Run( Compare, "Compare" ); Run( CondExp, "CondExp" ); Run( CondExpAD, "CondExpAD" ); Run( Cos, "Cos" ); Run( Cosh, "Cosh" ); Run( Div, "Div" ); Run( DivEq, "DivEq" ); Run( DivZeroOne, "DivZeroOne" ); Run( Exp, "Exp" ); Run( ForHess, "ForHess" ); Run( Forward, "Forward" ); Run( FromBase, "FromBase" ); Run( FunCheck, "FunCheck" ); Run( Mul, "Mul" ); Run( MulEq, "MulEq" ); Run( MulZeroOne, "MulZeroOne" ); Run( Neg, "Neg" ); Run( Poly, "Poly" ); Run( Pow, "Pow" ); Run( PowInt, "PowInt" ); Run( RevTwo, "RevTwo" ); Run( RombergOne, "RombergOne" ); Run( Rosen34, "Rosen34" ); Run( Runge45, "Runge45" ); Run( SimpleVector, "SimpleVector" ); Run( Sin, "Sin" ); Run( SinCos, "SinCos" ); Run( Sinh, "Sinh" ); Run( Sqrt, "Sqrt" ); Run( Sub, "Sub" ); Run( SubEq, "SubEq" ); Run( SubZero, "SubZero" ); Run( Value, "Value" ); Run( VecAD, "VecAD" ); Run( VecADPar, "VecADPar" ); Run( VecUnary, "VecUnary" ); Run( abs_normal, "abs_normal" ); Run( acos, "acos" ); Run( acosh, "acosh" ); Run( adfun, "adfun" ); Run( asin, "asin" ); Run( asinh, "asinh" ); Run( assign, "assign" ); Run( atan, "atan" ); Run( atan2, "atan2" ); Run( atanh, "atanh" ); Run( atomic_four, "atomic_four" ); Run( atomic_three, "atomic_three" ); Run( azmul, "azmul" ); Run( base2ad, "base2ad" ); Run( base_complex, "base_complex" ); Run( bool_sparsity, "bool_sparsity" ); Run( check_simple_vector, "check_simple_vector" ); Run( chkpoint_one, "chkpoint_one" ); Run( chkpoint_two, "chkpoint_two" ); Run( compare_change, "compare_change" ); Run( cond_exp_rev, "cond_exp_rev" ); Run( copy, "copy" ); Run( cpp_graph, "cpp_graph" ); Run( cppad_vector, "cppad_vector" ); Run( dbl_epsilon, "dbl_epsilon" ); Run( dependency, "dependency" ); Run( erf, "erf" ); Run( expm1, "expm1" ); Run( fabs, "fabs" ); Run( for_hes_sparsity, "for_hes_sparsity" ); Run( for_jac_sparsity, "for_jac_sparsity" ); Run( forward_dir, "forward_dir" ); Run( forward_order, "forward_order" ); Run( hes_sparsity, "hes_sparsity" ); Run( jacobian, "jacobian" ); Run( json_graph, "json_graph" ); Run( log, "log" ); Run( log10, "log10" ); Run( log1p, "log1p" ); Run( mul_cond_rev, "mul_cond_rev" ); Run( mul_cskip, "Mul_cskip" ); Run( mul_level, "mul_level" ); Run( mul_zdouble, "mul_zdouble" ); Run( near_equal_ext, "near_equal_ext" ); Run( new_dynamic, "new_dynamic" ); Run( num_limits, "num_limits" ); Run( ode_err_control, "ode_err_control"); Run( optimize, "optimize" ); Run( parameter, "parameter" ); Run( print_for, "print_for" ); Run( rev_sparse_jac, "rev_sparse_jac" ); Run( reverse, "reverse" ); Run( sparse_hessian, "sparse_hessian" ); Run( sparse_jac_work, "sparse_jac_work"); Run( sparse_jacobian, "sparse_jacobian"); Run( sparse_sub_hes, "sparse_sub_hes" ); Run( sparse_vec_ad, "sparse_vec_ad" ); Run( std_math, "std_math" ); Run( subgraph_1, "subgraph_1" ); Run( subgraph_2, "subgraph_2" ); Run( subgraph_hes2jac, "subgraph_hes2jac" ); Run( tan, "tan" ); Run( to_string, "to_string" ); // END_SORT_THIS_LINE_MINUS_1 # if CPPAD_C_COMPILER_GNU_FLAGS || CPPAD_C_COMPILER_MSVC_FLAGS # if ! CPPAD_LINK_FLAGS_HAS_M32 Run( to_csrc, "to_csrc" ); # endif # endif #if CPPAD_HAS_ADOLC Run( base_adolc, "base_adolc" ); # endif #if CPPAD_HAS_IPOPT Run( ipopt_solve, "ipopt_solve" ); # endif # ifdef CPPAD_OPENMP_TEST Run( alloc_openmp, "alloc_openmp" ); # endif # if CPPAD_HAS_EIGEN Run( cppad_eigen, "cppad_eigen" ); Run( eigen_mat_inv, "eigen_mat_inv" ); # endif // local sub-directory Run( is_pod, "is_pod" ); Run( json_lexer, "json_lexer" ); Run( json_parser, "json_parser" ); Run( temp_file, "temp_file" ); Run( vector_set, "vector_set" ); // // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // // Run base_alloc after memory leak check because base_alloc.hpp uses // thread_alloc to allocate memory for static copies of nan. Run( base_alloc_test, "base_alloc" ); // // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } // END PROGRAM ================================================ FILE: test_more/general/hes_sparsity.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace { // Begin empty namespace void forward_sparse_jacobian_bool(CppAD::ADFun& f) { size_t n = f.Domain(); CPPAD_TESTVECTOR(bool) eye(n * n); for(size_t i = 0; i < n; i++) { for(size_t j = 0; j < n; j++) eye[ i * n + j] = (i == j); } f.ForSparseJac(n, eye); return; } void forward_sparse_jacobian_set(CppAD::ADFun& f) { size_t n = f.Domain(); CPPAD_TESTVECTOR(std::set) eye(n); for(size_t i = 0; i < n; i++) eye[i].insert(i); f.ForSparseJac(n, eye); return; } bool sparse_hessian_test( CppAD::ADFun& f , size_t index , CPPAD_TESTVECTOR(bool)& check ) { bool ok = true; size_t n = f.Domain(); size_t m = f.Range(); // boolean sparsity patterns CPPAD_TESTVECTOR(bool) r_bool(n), s_bool(m), h_bool(n * n); for(size_t j = 0; j < n; j++) r_bool[j] = true; for(size_t i = 0; i < m; i++) s_bool[i] = i == index; // // bool ForSparseHes h_bool = f.ForSparseHes(r_bool, s_bool); for(size_t i = 0; i < n * n; i++) ok &= h_bool[i] == check[i]; // // bool RevSparseHes forward_sparse_jacobian_bool(f); h_bool = f.RevSparseHes(n, s_bool); for(size_t i = 0; i < n * n; i++) ok &= h_bool[i] == check[i]; // // set sparsity patterns CPPAD_TESTVECTOR( std::set ) r_set(1), s_set(1), h_set(n); for(size_t j = 0; j < n; j++) r_set[0].insert(j); s_set[0].insert(index); // // set ForSparseHes h_set = f.ForSparseHes(r_set, s_set); for(size_t i = 0; i < n; i++) { for(size_t j = 0; j < n; j++) { bool found = h_set[i].find(j) != h_set[i].end(); ok &= found == check[i * n + j]; } } // // set RevSparseHes forward_sparse_jacobian_set(f); h_set = f.RevSparseHes(n, s_set); for(size_t i = 0; i < n; i++) { for(size_t j = 0; j < n; j++) { bool found = h_set[i].find(j) != h_set[i].end(); ok &= found == check[i * n + j]; } } // return ok; } bool case_one() { bool ok = true; using namespace CppAD; // dimension of the domain space size_t n = 10; // dimension of the range space size_t m = 2; // temporary indices // initialize check values to false CPPAD_TESTVECTOR(bool) Check(n * n); for(size_t j = 0; j < n * n; j++) Check[j] = false; // independent variable vector CPPAD_TESTVECTOR(AD) X(n); for(size_t j = 0; j < n; j++) X[j] = AD(j); Independent(X); // accumulate sum here AD sum(0.); // variable * variable sum += X[2] * X[3]; Check[2 * n + 3] = Check[3 * n + 2] = true; // variable / variable sum += X[4] / X[5]; Check[4 * n + 5] = Check[5 * n + 4] = Check[5 * n + 5] = true; // CondExpLt(variable, variable, variable, variable) sum += CondExpLt(X[1], X[2], sin(X[6]), cos(X[7]) ); Check[6 * n + 6] = true; Check[7 * n + 7] = true; // pow(variable, variable) sum += pow(X[8], X[9]); Check[8 * n + 8] = Check[8 * n + 9] = true; Check[9 * n + 8] = Check[9 * n + 9] = true; // dependent variable vector CPPAD_TESTVECTOR(AD) Y(m); Y[0] = sum; // variable - variable Y[1] = X[0] - X[1]; // create function object F : X -> Y ADFun F(X, Y); // check Hessian of F_0 ok &= sparse_hessian_test(F, 0, Check); // check Hessian of F_1 for(size_t j = 0; j < n * n; j++) Check[j] = false; ok &= sparse_hessian_test(F, 1, Check); // ----------------------------------------------------------------------- return ok; } bool case_two() { bool ok = true; using namespace CppAD; // dimension of the domain space size_t n = 4; // dimension of the range space size_t m = 1; // initialize check values to false CPPAD_TESTVECTOR(bool) Check(n * n); for(size_t j = 0; j < n * n; j++) Check[j] = false; // independent variable vector CPPAD_TESTVECTOR(AD) X(n); for(size_t j = 0; j < n; j++) X[j] = AD(j); Independent(X); // Test the case where dependent variable is a non-linear function // of the result of a conditional expression. CPPAD_TESTVECTOR(AD) Y(m); Y[0] = CondExpLt(X[0], X[1], X[2], X[3]); Y[0] = cos(Y[0]) + X[0] + X[1]; // Hessian with respect to x[0] and x[1] is zero. // Hessian with respect to x[2] and x[3] is full // (although we know that there are no cross terms, this is an // inefficiency of the conditional expression operator). Check[2 * n + 2] = Check[ 2 * n + 3 ] = true; Check[3 * n + 2] = Check[ 3 * n + 3 ] = true; // create function object F : X -> Y ADFun F(X, Y); // ----------------------------------------------------------------- sparse_hessian_test(F, 0, Check); // return ok; } bool case_three() { bool ok = true; using CppAD::AD; // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) X(n); X[0] = 0.; // declare independent variables and start recording CppAD::Independent(X); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) Y(m); // make sure reverse jacobian is propagating dependency to // intermediate values (not just final ones). Y[0] = X[0] * X[0] + 2; // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // ------------------------------------------------------------------ CPPAD_TESTVECTOR(bool) check(n * n); check[0] = true; sparse_hessian_test(f, 0, check); // return ok; } bool case_four() { bool ok = true; using namespace CppAD; // dimension of the domain space size_t n = 3; // dimension of the range space size_t m = 1; // initialize the vector as zero CppAD::VecAD Z(n - 1); size_t k; for(k = 0; k < n-1; k++) Z[k] = 0.; // independent variable vector CPPAD_TESTVECTOR(AD) X(n); X[0] = 0.; X[1] = 1.; X[2] = 2.; Independent(X); // VecAD vector z depends on both x[1] and x[2] // (component indices do not matter because they can change). Z[ X[0] ] = X[1] * X[2]; Z[ X[1] ] = 0.; // dependent variable vector CPPAD_TESTVECTOR(AD) Y(m); // check results vector CPPAD_TESTVECTOR( bool ) Check(n * n); // y = z[j] where j might be zero or one. Y[0] = Z[ X[1] ]; Check[0 * n + 0] = false; // partial w.r.t x[0], x[0] Check[0 * n + 1] = false; // partial w.r.t x[0], x[1] Check[0 * n + 2] = false; // partial w.r.t x[0], x[2] Check[1 * n + 0] = false; // partial w.r.t x[1], x[0] Check[1 * n + 1] = false; // partial w.r.t x[1], x[1] Check[1 * n + 2] = true; // partial w.r.t x[1], x[2] Check[2 * n + 0] = false; // partial w.r.t x[2], x[0] Check[2 * n + 1] = true; // partial w.r.t x[2], x[1] Check[2 * n + 2] = false; // partial w.r.t x[2], x[2] // create function object F : X -> Y ADFun F(X, Y); // ----------------------------------------------------- sparse_hessian_test(F, 0, Check); // return ok; } bool case_five(void) { bool ok = true; using CppAD::AD; size_t n = 2; CPPAD_TESTVECTOR(AD) X(n); X[0] = 1.; X[1] = 2.; CppAD::Independent(X); size_t m = 2; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = pow(X[0], 2.); Y[1] = pow(2., X[1]); // create function object F : X -> Y CppAD::ADFun F(X, Y); // Test F_0 and F_1 for(size_t index = 0; index < n; index++) { CPPAD_TESTVECTOR(bool) check(n * n); for(size_t i = 0; i < n; i++) for(size_t j = 0; j < n; j++) check[i * n + j] = (i == index) && (j == index); sparse_hessian_test(F, index, check); } // return ok; } // Note ForSparseHes does not work for this case because R not diagonal. bool case_six() { bool ok = true; using namespace CppAD; // dimension of the domain space size_t n = 3; // dimension of the range space size_t m = 1; // independent variable vector CPPAD_TESTVECTOR(AD) X(n); X[0] = 0.; X[1] = 1.; X[2] = 2.; Independent(X); // y = z[j] where j might be zero or one. CPPAD_TESTVECTOR(AD) Y(m); Y[0] = X[1] * X[2]; // create function object F : X -> Y ADFun F(X, Y); // sparsity pattern for hessian of F^2 CPPAD_TESTVECTOR(bool) F2(n * n); F2[0 * n + 0] = false; // partial w.r.t x[0], x[0] F2[0 * n + 1] = false; // partial w.r.t x[0], x[1] F2[0 * n + 2] = false; // partial w.r.t x[0], x[2] F2[1 * n + 0] = false; // partial w.r.t x[1], x[0] F2[1 * n + 1] = false; // partial w.r.t x[1], x[1] F2[1 * n + 2] = true; // partial w.r.t x[1], x[2] F2[2 * n + 0] = false; // partial w.r.t x[2], x[0] F2[2 * n + 1] = true; // partial w.r.t x[2], x[1] F2[2 * n + 2] = false; // partial w.r.t x[2], x[2] // choose a non-symmetric sparsity pattern for R CPPAD_TESTVECTOR( bool ) r(n * n); size_t i, j, k; for(i = 0; i < n; i++) { for(j = 0; j < n; j++) r[ i * n + j ] = false; j = n - i - 1; r[ j * n + j ] = true; } // sparsity pattern for H^T CPPAD_TESTVECTOR(bool) Check(n * n); for(i = 0; i < n; i++) { for(j = 0; j < n; j++) { Check[ i * n + j] = false; for(k = 0; k < n; k++) { // some gcc versions std::vector do not support |= // on elements (because they pack the bits). bool tmp = Check[i * n + j]; Check[i * n + j] = tmp | (F2[i * n + k] && r[ k * n + j]); } } } // compute the reverse Hessian sparsity pattern for F^2 * R F.ForSparseJac(n, r); CPPAD_TESTVECTOR( bool ) s(m), h(n * n); s[0] = 1.; bool transpose = true; h = F.RevSparseHes(n, s, transpose); // check values for(i = 0; i < n; i++) { for(j = 0; j < n; j++) ok &= (h[i * n + j] == Check[i * n + j]); } // compute the reverse Hessian sparsity pattern for R^T * F^2 transpose = false; h = F.RevSparseHes(n, s, transpose); // check values for(i = 0; i < n; i++) { for(j = 0; j < n; j++) ok &= (h[j * n + i] == Check[i * n + j]); } return ok; } // bug in cppad/local/sweep/for_hes.hpp fixed on 2022-05-15 bool case_seven() { bool ok = true; using namespace CppAD; typedef CPPAD_TESTVECTOR(size_t) size_vector; // dimension of the domain space size_t n = 3; // dimension of the range space size_t m = 2; // independent variable vector CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.; ax[1] = 1.; ax[2] = 2.; Independent(ax); // dependent variable vector CPPAD_TESTVECTOR(AD) ay(m); ay[0] = ax[0] * ax[1]; ay[1] = ax[1] * ax[2]; // create function object f : x -> y ADFun f(ax, ay); // sparsity pattern for Hessian of y[1] CppAD::sparse_rc pattern_out; bool internal_bool = false; CPPAD_TESTVECTOR(bool) select_domain(n), select_range(m); select_domain[0] = false; select_domain[1] = true; select_domain[2] = true; select_range[0] = false; select_range[1] = true; f.for_hes_sparsity( select_domain, select_range, internal_bool, pattern_out ); // ok &= pattern_out.nnz() == 2; for(size_t k = 0; k < pattern_out.nnz(); ++k) { size_t i = pattern_out.row()[k]; size_t j = pattern_out.col()[k]; if( i == 1 ) ok &= j == 2; else if( i == 2 ) ok &= j == 1; else ok = false; } // return ok; } } // End of empty namespace bool hes_sparsity(void) { bool ok = true; ok &= case_one(); ok &= case_two(); ok &= case_three(); ok &= case_four(); ok &= case_five(); ok &= case_six(); ok &= case_seven(); return ok; } ================================================ FILE: test_more/general/ipopt_solve.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Testing ipopt::solve */ // CPPAD_HAS_* defines # include # if CPPAD_HAS_IPOPT # include namespace { using CppAD::AD; class FG_eval { public: typedef CPPAD_TESTVECTOR( AD ) ADvector; void operator()(ADvector& fg, const ADvector& x) { assert( fg.size() == 3 ); assert( x.size() == 4 ); // Fortran style indexing AD x1 = x[0]; AD x2 = x[1]; AD x3 = x[2]; AD x4 = x[3]; // f(x) fg[0] = x1 * x4 * (x1 + x2 + x3) + x3; // g_1 (x) fg[1] = x1 * x2 * x3 * x4; // g_2 (x) fg[2] = x1 * x1 + x2 * x2 + x3 * x3 + x4 * x4; // return; } }; } bool ipopt_solve(void) { bool ok = true; size_t i, j; typedef CPPAD_TESTVECTOR( double ) Dvector; // number of independent variables (domain dimension for f and g) size_t nx = 4; // number of constraints (range dimension for g) size_t ng = 2; // initial value of the independent variables Dvector xi(nx); xi[0] = 1.0; xi[1] = 5.0; xi[2] = 5.0; xi[3] = 1.0; // lower and upper limits for x Dvector xl(nx), xu(nx); for(j = 0; j < nx; j++) { xl[j] = 1.0; xu[j] = 5.0; } // lower and upper limits for g Dvector gl(ng), gu(ng); gl[0] = 25.0; gu[0] = 1.0e19; gl[1] = 40.0; gu[1] = 40.0; // object that computes objective and constraints FG_eval fg_eval; // options std::string base_options; // turn off any printing base_options += "Integer print_level 0\n"; base_options += "String sb yes\n"; // maximum number of iterations base_options += "Integer max_iter 10\n"; // approximate accuracy in first order necessary conditions; // see Mathematical Programming, Volume 106, Number 1, // Pages 25-57, Equation (6) base_options += "Numeric tol 1e-6\n"; // derivative testing base_options += "String derivative_test second-order\n"; // maximum amount of random perturbation; e.g., // when evaluation finite diff base_options += "Numeric point_perturbation_radius 0.\n"; // place to return solution CppAD::ipopt::solve_result solution; // solution values and tolerances double check_x[] = { 1.000000, 4.743000, 3.82115, 1.379408 }; double check_zl[] = { 1.087871, 0., 0., 0. }; double check_zu[] = { 0., 0., 0., 0. }; double rel_tol = 1e-6; // relative tolerance double abs_tol = 1e-6; // absolute tolerance for(i = 0; i < 3; i++) { std::string options( base_options ); if( i == 1 ) options += "Sparse true forward\n"; if( i == 2 ) options += "Sparse true reverse\n"; // solve the problem CppAD::ipopt::solve( options, xi, xl, xu, gl, gu, fg_eval, solution ); ok &= solution.status==CppAD::ipopt::solve_result::success; // // Check some of the solution values for(j = 0; j < nx; j++) { ok &= CppAD::NearEqual( check_x[j], solution.x[j], rel_tol, abs_tol ); ok &= CppAD::NearEqual( check_zl[j], solution.zl[j], rel_tol, abs_tol ); ok &= CppAD::NearEqual( check_zu[j], solution.zu[j], rel_tol, abs_tol ); } } return ok; } # endif ================================================ FILE: test_more/general/jacobian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace { // --------------------------------------------------------- template Vector eval_g(const Vector& x) { Vector g(2); g[0] = x[0] * x[1] * x[2] * x[3]; g[1] = x[0] * x[0] + x[1] * x[1] + x[2] * x[2] + x[3] * x[3]; return g; } template Vector eval_jac_g(const Vector& x) { Vector jac_g(8); // g[0] = x[0] * x[1] * x[2] * x[3]; jac_g[0] = x[1] * x[2] * x[3]; jac_g[1] = x[0] * x[2] * x[3]; jac_g[2] = x[0] * x[1] * x[3]; jac_g[3] = x[0] * x[1] * x[2]; // g[1] = x[0] * x[0] + x[1] * x[1] + x[2] * x[2] + x[3] * x[3]; jac_g[4+0] = 2. * x[0]; jac_g[4+1] = 2. * x[1]; jac_g[4+2] = 2. * x[2]; jac_g[4+3] = 2. * x[3]; return jac_g; } } // End empty namespace bool jacobian(void) { bool ok = true; using CppAD::vector; size_t i, j, k; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); size_t n = 4; size_t m = 2; vector< CppAD::AD > ad_x(n); vector< CppAD::AD > ad_g(m); vector x(n); x[0] = 1.; x[1] = 5.0; x[2] = 5.0; x[3] = 1.0; for(j = 0; j < n; j++) ad_x[j] = x[j]; // CppAD::Independent(ad_x); ad_g = eval_g(ad_x); CppAD::ADFun fun_g(ad_x, ad_g); vector check(m * n); check = eval_jac_g(x); // regular jacobian vector jac_g = fun_g.Jacobian(x); for(k = 0; k < m *n; k++) ok &= NearEqual(jac_g[k], check[k], eps99, eps99); // one argument sparse jacobian jac_g = fun_g.SparseJacobian(x); for(k = 0; k < m *n; k++) ok &= NearEqual(jac_g[k], check[k], eps99, eps99); // sparse jacobian using bool vectors CPPAD_TESTVECTOR(bool) p_b(m * n) , r_b(n * n); for(i = 0; i < n; i++) for(j = 0; j < n; j++) r_b[i * n + j] = (i == j); p_b = fun_g.ForSparseJac(n, r_b); jac_g = fun_g.SparseJacobian(x, p_b); for(k = 0; k < m *n; k++) ok &= NearEqual(jac_g[k], check[k], eps99, eps99); // sparse jacobian using vectors of sets std::vector< std::set > p_s(m) , r_s(n); for(i = 0; i < n; i++) for(j = 0; j < n; j++) r_s[i].insert(j); p_s = fun_g.ForSparseJac(n, r_s); jac_g = fun_g.SparseJacobian(x, p_s); for(k = 0; k < m *n; k++) ok &= NearEqual(jac_g[k], check[k], eps99, eps99); return ok; } // END PROGRAM ================================================ FILE: test_more/general/json_graph.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace { // BEGIN_EMPTY_NAMESPACE // --------------------------------------------------------------------------- bool comp_op_dyn_dyn(void) { bool ok = true; using CppAD::vector; using CppAD::AD; // // AD graph example // node[0:4] : p[0], p[1], p[2], p[3], p[4] // node_5 : x[0] // : p[0] == p[1] // : p[0] <= p[2] // : p[0] < p[3] // : p[0] != p[4] // y[0] = p[0] std::string json = "{\n" " 'function_name' : 'comp_op example',\n" " 'op_define_vec' : [ 4, [\n" " { 'op_code':1, 'name':'comp_eq' } ,\n" " { 'op_code':2, 'name':'comp_le' } ,\n" " { 'op_code':3, 'name':'comp_lt' } ,\n" " { 'op_code':4, 'name':'comp_ne' } ]\n" " ],\n" " 'n_dynamic_ind' : 5,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 0, [ ] ],\n" " 'op_usage_vec' : [ 4, [\n" " [ 1, 0, 2, [1, 2 ] ] ,\n" // p[0] == p[1] " [ 2, 0, 2, [1, 3 ] ] ,\n" // p[0] <= p[2] " [ 3, 0, 2, [1, 4 ] ] ,\n" // p[0] < p[3] " [ 4, 0, 2, [1, 5 ] ] ]\n" // p[0] != p[4] " ],\n" " 'dependent_vec' : [ 1, [1] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = p[0] CppAD::ADFun f; f.from_json(json); // ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 5; // // set independent variables and parameters so all comparisons true vector x(1), p(5); x[0] = 1.0; p[0] = 3.0; p[1] = p[0]; p[2] = p[0] + 1.0; p[3] = p[0] + 1.0; p[4] = p[0] + 1.0; // // compute y = f(x; p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check that all comparisons are true ok &= f.compare_change_number() == 0; // // case where all the comparisons are false p[1] = p[0] - 1.0; p[2] = p[0] - 1.0; p[3] = p[0] - 1.0; p[4] = p[0]; f.new_dynamic(p); y = f.Forward(0, x); y = f.Forward(0, x); ok &= f.compare_change_number() == 4; // // ----------------------------------------------------------------------- // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // ----------------------------------------------------------------------- // ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 5; // // set independent variables and parameters so all comparisons true p[0] = 3.0; p[1] = p[0]; p[2] = p[0] + 1.0; p[3] = p[0] + 1.0; p[4] = p[0] + 1.0; // // compute y = f(x) f.new_dynamic(p); y = f.Forward(0, x); // // check that all comparisons are true ok &= f.compare_change_number() == 0; // // case where all the comparisons are false p[1] = p[0] - 1.0; p[2] = p[0] - 1.0; p[3] = p[0] - 1.0; p[4] = p[0]; f.new_dynamic(p); y = f.Forward(0, x); ok &= f.compare_change_number() == 4; // return ok; } // --------------------------------------------------------------------------- bool comp_op_var_var(void) { bool ok = true; using CppAD::vector; using CppAD::AD; // // AD graph example // node[1:5] : x[0], x[1], x[2], x[3], x[4] // : x[0] == x[1] // : x[0] <= x[2] // : x[0] < x[3] // : x[0] != x[4] // y[0] = x[0] std::string json = "{\n" " 'function_name' : 'comp_op example',\n" " 'op_define_vec' : [ 4, [\n" " { 'op_code':1, 'name':'comp_eq' } ,\n" " { 'op_code':2, 'name':'comp_le' } ,\n" " { 'op_code':3, 'name':'comp_lt' } ,\n" " { 'op_code':4, 'name':'comp_ne' } ]\n" " ],\n" " 'n_dynamic_ind' : 0,\n" " 'n_variable_ind' : 5,\n" " 'constant_vec' : [ 0, [ ] ],\n" " 'op_usage_vec' : [ 4, [\n" " [ 1, 0, 2, [1, 2 ] ] ,\n" // x[0] == x[1] " [ 2, 0, 2, [1, 3 ] ] ,\n" // x[0] <= x[2] " [ 3, 0, 2, [1, 4 ] ] ,\n" // x[0] < x[3] " [ 4, 0, 2, [1, 5 ] ] ]\n" // x[0] != x[4] " ],\n" " 'dependent_vec' : [ 1, [1] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = p[0] CppAD::ADFun f; f.from_json(json); // ok &= f.Domain() == 5; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 0; // // set independent variables and parameters so all comparisons true vector x(5); x[0] = 3.0; x[1] = x[0]; x[2] = x[0] + 1.0; x[3] = x[0] + 1.0; x[4] = x[0] + 1.0; // // compute y = f(x) vector y = f.Forward(0, x); // // check that all comparisons are true ok &= f.compare_change_number() == 0; // // case where all the comparisons are false x[1] = x[0] - 1.0; x[2] = x[0] - 1.0; x[3] = x[0] - 1.0; x[4] = x[0]; y = f.Forward(0, x); ok &= f.compare_change_number() == 4; // // ----------------------------------------------------------------------- // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // ----------------------------------------------------------------------- // ok &= f.Domain() == 5; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 0; // // set independent variables and parameters so all comparisons true x[0] = 3.0; x[1] = x[0]; x[2] = x[0] + 1.0; x[3] = x[0] + 1.0; x[4] = x[0] + 1.0; // // compute y = f(x) y = f.Forward(0, x); // // check that all comparisons are true ok &= f.compare_change_number() == 0; // // case where all the comparisons are false x[1] = x[0] - 1.0; x[2] = x[0] - 1.0; x[3] = x[0] - 1.0; x[4] = x[0]; y = f.Forward(0, x); ok &= f.compare_change_number() == 4; // return ok; } // --------------------------------------------------------------------------- bool comp_op_dyn_var(void) { bool ok = true; using CppAD::vector; using CppAD::AD; // // AD graph example // node_1 : p[0] // node[2:5] : x[0], x[1], x[2], x[3] // : p[0] == x[0] // : p[0] <= x[1] // : p[0] < x[2] // : p[0] != x[3] // y[0] = p[0] std::string json = "{\n" " 'function_name' : 'comp_op example',\n" " 'op_define_vec' : [ 4, [\n" " { 'op_code':1, 'name':'comp_eq' } ,\n" " { 'op_code':2, 'name':'comp_le' } ,\n" " { 'op_code':3, 'name':'comp_lt' } ,\n" " { 'op_code':4, 'name':'comp_ne' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 4,\n" " 'constant_vec' : [ 0, [ ] ],\n" " 'op_usage_vec' : [ 4, [\n" " [ 1, 0, 2, [1, 2 ] ] ,\n" // p[0] == x[0] " [ 2, 0, 2, [1, 3 ] ] ,\n" // p[0] <= x[1] " [ 3, 0, 2, [1, 4 ] ] ,\n" // p[0] < x[2] " [ 4, 0, 2, [1, 5 ] ] ]\n" // p[0] != x[3] " ],\n" " 'dependent_vec' : [ 1, [1] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = p[0] CppAD::ADFun f; f.from_json(json); // ok &= f.Domain() == 4; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // set independent variables and parameters so all comparisons true vector x(4), p(1); p[0] = 3.0; x[0] = p[0]; x[1] = p[0] + 1.0; x[2] = p[0] + 1.0; x[3] = p[0] + 1.0; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check that all comparisons are true ok &= f.compare_change_number() == 0; // // case where all the comparisons are false x[0] = p[0] - 1.0; x[1] = p[0] - 1.0; x[2] = p[0] - 1.0; x[3] = p[0]; f.new_dynamic(p); y = f.Forward(0, x); ok &= f.compare_change_number() == 4; // // ----------------------------------------------------------------------- // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // ----------------------------------------------------------------------- // ok &= f.Domain() == 4; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // set independent variables and parameters so all comparisons true p[0] = 3.0; x[0] = p[0]; x[1] = p[0] + 1.0; x[2] = p[0] + 1.0; x[3] = p[0] + 1.0; // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check that all comparisons are true ok &= f.compare_change_number() == 0; // // case where all the comparisons are false x[0] = p[0] - 1.0; x[1] = p[0] - 1.0; x[2] = p[0] - 1.0; x[3] = p[0]; f.new_dynamic(p); y = f.Forward(0, x); ok &= f.compare_change_number() == 4; // return ok; } // --------------------------------------------------------------------------- bool comp_op_var_dyn(void) { bool ok = true; using CppAD::vector; using CppAD::AD; // // AD graph example // node[1:4] : p[0], p[1], p[2], p[3] // node_5 : x[0] // : x[0] == p[0] // : x[0] <= p[1] // : x[0] < p[2] // : x[0] != p[3] // y[0] = p[0] std::string json = "{\n" " 'function_name' : 'comp_op example',\n" " 'op_define_vec' : [ 4, [\n" " { 'op_code':1, 'name':'comp_eq' } ,\n" " { 'op_code':2, 'name':'comp_le' } ,\n" " { 'op_code':3, 'name':'comp_lt' } ,\n" " { 'op_code':4, 'name':'comp_ne' } ]\n" " ],\n" " 'n_dynamic_ind' : 4,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 0, [ ] ],\n" " 'op_usage_vec' : [ 4, [\n" " [ 1, 0, 2, [5, 1 ] ] ,\n" // x[0] == p[0] " [ 2, 0, 2, [5, 2 ] ] ,\n" // x[0] <= p[1] " [ 3, 0, 2, [5, 3 ] ] ,\n" // x[0] < p[2] " [ 4, 0, 2, [5, 4 ] ] ]\n" // x[0] != p[3] " ],\n" " 'dependent_vec' : [ 1, [1] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = p[0] CppAD::ADFun f; f.from_json(json); // ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 4; // // set independent variables and parameters so all comparisons true vector x(1), p(4); x[0] = 3.0; p[0] = x[0]; p[1] = x[0] + 1.0; p[2] = x[0] + 1.0; p[3] = x[0] + 1.0; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check that all comparisons are true ok &= f.compare_change_number() == 0; // // case where all the comparisons are false p[0] = x[0] - 1.0; p[1] = x[0] - 1.0; p[2] = x[0] - 1.0; p[3] = x[0]; f.new_dynamic(p); y = f.Forward(0, x); ok &= f.compare_change_number() == 4; // // ----------------------------------------------------------------------- // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // ----------------------------------------------------------------------- // ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 4; // // set independent variables and parameters so all comparisons true x[0] = 3.0; p[0] = x[0]; p[1] = x[0] + 1.0; p[2] = x[0] + 1.0; p[3] = x[0] + 1.0; // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check that all comparisons are true ok &= f.compare_change_number() == 0; // // case where all the comparisons are false p[0] = x[0] - 1.0; p[1] = x[0] - 1.0; p[2] = x[0] - 1.0; p[3] = x[0]; f.new_dynamic(p); y = f.Forward(0, x); ok &= f.compare_change_number() == 4; // return ok; } // =========================================================================== bool acosh_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : acosh(p[0]) // node_5 : acosh(x[0]) // node_6 : acosh(c[0]) // node_7 : acosh(p[0]) + acosh(x[0]) + acosh(c[0]) // y[0] = acosh(p[0]) + acosh(x[0]) + acosh(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'acosh_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'acosh', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ 1.3 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // acosh(p0) " [ 1, 2] ,\n" // acosh(x0) " [ 1, 3] ,\n" // acosh(c0) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // acosh(p0)+acosh(x0)+acosh(c0) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = acosh(p0) + acosh(x0) + acosh(c0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function vector c(1); c[0] = 1.3; // // set independent variables and parameters vector p(1), x(1); p[0] = 1.1; x[0] = 1.2; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = CppAD::acosh(p[0]) + CppAD::acosh(x[0]) + CppAD::acosh(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // --------------------------------------------------------------------------- bool log1p_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : log1p(p[0]) // node_5 : log1p(x[0]) // node_6 : log1p(c[0]) // node_7 : log1p(p[0]) + log1p(x[0]) + log1p(c[0]) // y[0] = log1p(p[0]) + log1p(x[0]) + log1p(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'log1p_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'log1p', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ 0.3 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // log1p(p0) " [ 1, 2] ,\n" // log1p(x0) " [ 1, 3] ,\n" // log1p(c0) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // log1p(p0)+log1p(x0)+log1p(c0) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = log1p(p0) + log1p(x0) + log1p(c0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function vector c(1); c[0] = 0.3; // // set independent variables and parameters vector p(1), x(1); p[0] = -0.1; x[0] = 0.2; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = CppAD::log1p(p[0]) + CppAD::log1p(x[0]) + CppAD::log1p(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // --------------------------------------------------------------------------- bool expm1_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : expm1(p[0]) // node_5 : expm1(x[0]) // node_6 : expm1(c[0]) // node_7 : expm1(p[0]) + expm1(x[0]) + expm1(c[0]) // y[0] = expm1(p[0]) + expm1(x[0]) + expm1(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'expm1_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'expm1', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ 0.3 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // expm1(p0) " [ 1, 2] ,\n" // expm1(x0) " [ 1, 3] ,\n" // expm1(c0) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // expm1(p0)+expm1(x0)+expm1(c0) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = expm1(p0) + expm1(x0) + expm1(c0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function vector c(1); c[0] = 0.3; // // set independent variables and parameters vector p(1), x(1); p[0] = -0.1; x[0] = 0.2; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = CppAD::expm1(p[0]) + CppAD::expm1(x[0]) + CppAD::expm1(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // --------------------------------------------------------------------------- bool erfc_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : erfc(p[0]) // node_5 : erfc(x[0]) // node_6 : erfc(c[0]) // node_7 : erfc(p[0]) + erfc(x[0]) + erfc(c[0]) // y[0] = erfc(p[0]) + erfc(x[0]) + erfc(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'erfc_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'erfc', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ 0.3 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // erfc(p0) " [ 1, 2] ,\n" // erfc(x0) " [ 1, 3] ,\n" // erfc(c0) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // erfc(p0)+erfc(x0)+erfc(c0) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = erfc(p0) + erfc(x0) + erfc(c0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function vector c(1); c[0] = 0.3; // // set independent variables and parameters vector p(1), x(1); p[0] = -0.1; x[0] = 0.2; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = CppAD::erfc(p[0]) + CppAD::erfc(x[0]) + CppAD::erfc(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // --------------------------------------------------------------------------- bool erf_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : erf(p[0]) // node_5 : erf(x[0]) // node_6 : erf(c[0]) // node_7 : erf(p[0]) + erf(x[0]) + erf(c[0]) // y[0] = erf(p[0]) + erf(x[0]) + erf(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'erf_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'erf', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ 0.3 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // erf(p0) " [ 1, 2] ,\n" // erf(x0) " [ 1, 3] ,\n" // erf(c0) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // erf(p0)+erf(x0)+erf(c0) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = erf(p0) + erf(x0) + erf(c0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function vector c(1); c[0] = 0.3; // // set independent variables and parameters vector p(1), x(1); p[0] = -0.1; x[0] = 0.2; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = CppAD::erf(p[0]) + CppAD::erf(x[0]) + CppAD::erf(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // --------------------------------------------------------------------------- bool atanh_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : atanh(p[0]) // node_5 : atanh(x[0]) // node_6 : atanh(c[0]) // node_7 : atanh(p[0]) + atanh(x[0]) + atanh(c[0]) // y[0] = atanh(p[0]) + atanh(x[0]) + atanh(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'atanh_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'atanh', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ 0.3 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // atanh(p0) " [ 1, 2] ,\n" // atanh(x0) " [ 1, 3] ,\n" // atanh(c0) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // atanh(p0)+atanh(x0)+atanh(c0) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = atanh(p0) + atanh(x0) + atanh(c0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function vector c(1); c[0] = 0.3; // // set independent variables and parameters vector p(1), x(1); p[0] = -0.1; x[0] = 0.2; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = CppAD::atanh(p[0]) + CppAD::atanh(x[0]) + CppAD::atanh(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // --------------------------------------------------------------------------- bool asinh_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : asinh(p[0]) // node_5 : asinh(x[0]) // node_6 : asinh(c[0]) // node_7 : asinh(p[0]) + asinh(x[0]) + asinh(c[0]) // y[0] = asinh(p[0]) + asinh(x[0]) + asinh(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'asinh_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'asinh', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ 0.3 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // asinh(p0) " [ 1, 2] ,\n" // asinh(x0) " [ 1, 3] ,\n" // asinh(c0) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // asinh(p0)+asinh(x0)+asinh(c0) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = asinh(p0) + asinh(x0) + asinh(c0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function vector c(1); c[0] = 0.3; // // set independent variables and parameters vector p(1), x(1); p[0] = -0.1; x[0] = 0.2; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = CppAD::asinh(p[0]) + CppAD::asinh(x[0]) + CppAD::asinh(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // =========================================================================== bool tan_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : tan(p[0]) // node_5 : tan(x[0]) // node_6 : tan(c[0]) // node_7 : tan(p[0]) + tan(x[0]) + tan(c[0]) // y[0] = tan(p[0]) + tan(x[0]) + tan(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'tan_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'tan', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ -0.1 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // tan(p[0]) " [ 1, 2] ,\n" // tan(x[0]) " [ 1, 3] ,\n" // tan(c[0]) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // tan(p[0])+tan(x[0])+tan(c[0]) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = tan(p_0) + tan(x_0) + tan(c_0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant that is in function vector c(1); c[0] = -0.1; // // set independent variables and parameters vector p(1), x(1); p[0] = 0.2; x[0] = 0.3; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = std::tan(p[0]) + std::tan(x[0]) + std::tan(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // --------------------------------------------------------------------------- bool tanh_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : tanh(p[0]) // node_5 : tanh(x[0]) // node_6 : tanh(c[0]) // node_7 : tanh(p[0]) + tanh(x[0]) + tanh(c[0]) // y[0] = tanh(p[0]) + tanh(x[0]) + tanh(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'tanh_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'tanh', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ -0.1 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // tanh(p[0]) " [ 1, 2] ,\n" // tanh(x[0]) " [ 1, 3] ,\n" // tanh(c[0]) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // tanh(p[0])+tanh(x[0])+tanh(c[0]) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = tanh(p_0) + tanh(x_0) + tanh(c_0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function vector c(1); c[0] = -0.1; // // set independent variables and parameters vector p(1), x(1); p[0] = 0.2; x[0] = 0.3; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = std::tanh(p[0]) + std::tanh(x[0]) + std::tanh(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // --------------------------------------------------------------------------- bool sqrt_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : sqrt(p[0]) // node_5 : sqrt(x[0]) // node_6 : sqrt(c[0]) // node_7 : sqrt(p[0]) + sqrt(x[0]) + sqrt(c[0]) // y[0] = sqrt(p[0]) + sqrt(x[0]) + sqrt(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'sqrt_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'sqrt', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ +0.1 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // sqrt(p[0]) " [ 1, 2] ,\n" // sqrt(x[0]) " [ 1, 3] ,\n" // sqrt(c[0]) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // sqrt(p[0])+sqrt(x[0])+sqrt(c[0]) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = sqrt(p_0) + sqrt(x_0) + sqrt(c_0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function vector c(1); c[0] = +0.1; // // set independent variables and parameters vector p(1), x(1); p[0] = 0.2; x[0] = 0.3; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = std::sqrt(p[0]) + std::sqrt(x[0]) + std::sqrt(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // --------------------------------------------------------------------------- bool sin_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : sin(p[0]) // node_5 : sin(x[0]) // node_6 : sin(c[0]) // node_7 : sin(p[0]) + sin(x[0]) + sin(c[0]) // y[0] = sin(p[0]) + sin(x[0]) + sin(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'sin_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'sin', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ -0.1 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // sin(p[0]) " [ 1, 2] ,\n" // sin(x[0]) " [ 1, 3] ,\n" // sin(c[0]) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // sin(p[0])+sin(x[0])+sin(c[0]) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = sin(p_0) + sin(x_0) + sin(c_0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function vector c(1); c[0] = -0.1; // // set independent variables and parameters vector p(1), x(1); p[0] = 0.2; x[0] = 0.3; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = std::sin(p[0]) + std::sin(x[0]) + std::sin(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // --------------------------------------------------------------------------- bool sinh_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : sinh(p[0]) // node_5 : sinh(x[0]) // node_6 : sinh(c[0]) // node_7 : sinh(p[0]) + sinh(x[0]) + sinh(c[0]) // y[0] = sinh(p[0]) + sinh(x[0]) + sinh(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'sinh_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'sinh', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ -0.1 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // sinh(p[0]) " [ 1, 2] ,\n" // sinh(x[0]) " [ 1, 3] ,\n" // sinh(c[0]) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // sinh(p[0])+sinh(x[0])+sinh(c[0]) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = sinh(p_0) + sinh(x_0) + sinh(c_0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function vector c(1); c[0] = -0.1; // // set independent variables and parameters vector p(1), x(1); p[0] = 0.2; x[0] = 0.3; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = std::sinh(p[0]) + std::sinh(x[0]) + std::sinh(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // --------------------------------------------------------------------------- bool sign_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : sign(p[0]) // node_5 : sign(x[0]) // node_6 : sign(c[0]) // node_7 : sign(p[0]) + sign(x[0]) + sign(c[0]) // y[0] = sign(p[0]) + sign(x[0]) + sign(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'sign_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'sign', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ -0.1 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // sign(p[0]) " [ 1, 2] ,\n" // sign(x[0]) " [ 1, 3] ,\n" // sign(c[0]) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // sign(p[0])+sign(x[0])+sign(c[0]) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = sign(p_0) + sign(x_0) + sign(c_0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function vector c(1); c[0] = -0.1; // // set independent variables and parameters vector p(1), x(1); p[0] = 0.2; x[0] = 0.3; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = CppAD::sign(p[0]) + CppAD::sign(x[0]) + CppAD::sign(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // --------------------------------------------------------------------------- bool log_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : log(p[0]) // node_5 : log(x[0]) // node_6 : log(c[0]) // node_7 : log(p[0]) + log(x[0]) + log(c[0]) // y[0] = log(p[0]) + log(x[0]) + log(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'log_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'log', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ +0.1 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // log(p[0]) " [ 1, 2] ,\n" // log(x[0]) " [ 1, 3] ,\n" // log(c[0]) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // log(p[0])+log(x[0])+log(c[0]) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = log(p_0) + log(x_0) + log(c_0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function vector c(1); c[0] = +0.1; // // set independent variables and parameters vector p(1), x(1); p[0] = 0.2; x[0] = 0.3; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = std::log(p[0]) + std::log(x[0]) + std::log(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // --------------------------------------------------------------------------- bool exp_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : exp(p[0]) // node_5 : exp(x[0]) // node_6 : exp(c[0]) // node_7 : exp(p[0]) + exp(x[0]) + exp(c[0]) // y[0] = exp(p[0]) + exp(x[0]) + exp(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'exp_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'exp', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ -0.1 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // exp(p[0]) " [ 1, 2] ,\n" // exp(x[0]) " [ 1, 3] ,\n" // exp(c[0]) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // exp(p[0])+exp(x[0])+exp(c[0]) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = exp(p_0) + exp(x_0) + exp(c_0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function vector c(1); c[0] = -0.1; // // set independent variables and parameters vector p(1), x(1); p[0] = 0.2; x[0] = 0.3; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = std::exp(p[0]) + std::exp(x[0]) + std::exp(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // --------------------------------------------------------------------------- bool cos_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : cos(p[0]) // node_5 : cos(x[0]) // node_6 : cos(c[0]) // node_7 : cos(p[0]) + cos(x[0]) + cos(c[0]) // y[0] = cos(p[0]) + cos(x[0]) + cos(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'cos_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'cos', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ -0.1 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // cos(p[0]) " [ 1, 2] ,\n" // cos(x[0]) " [ 1, 3] ,\n" // cos(c[0]) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // cos(p[0])+cos(x[0])+cos(c[0]) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = cos(p_0) + cos(x_0) + cos(c_0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function vector c(1); c[0] = -0.1; // // set independent variables and parameters vector p(1), x(1); p[0] = 0.2; x[0] = 0.3; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = std::cos(p[0]) + std::cos(x[0]) + std::cos(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // --------------------------------------------------------------------------- bool cosh_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : cosh(p[0]) // node_5 : cosh(x[0]) // node_6 : cosh(c[0]) // node_7 : cosh(p[0]) + cosh(x[0]) + cosh(c[0]) // y[0] = cosh(p[0]) + cosh(x[0]) + cosh(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'cosh_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'cosh', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ -0.1 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // cosh(p[0]) " [ 1, 2] ,\n" // cosh(x[0]) " [ 1, 3] ,\n" // cosh(c[0]) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // cosh(p[0])+cosh(x[0])+cosh(c[0]) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = cosh(p_0) + cosh(x_0) + cosh(c_0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function vector c(1); c[0] = -0.1; // // set independent variables and parameters vector p(1), x(1); p[0] = 0.2; x[0] = 0.3; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = std::cosh(p[0]) + std::cosh(x[0]) + std::cosh(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // --------------------------------------------------------------------------- bool atan_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : atan(p[0]) // node_5 : atan(x[0]) // node_6 : atan(c[0]) // node_7 : atan(p[0]) + atan(x[0]) + atan(c[0]) // y[0] = atan(p[0]) + atan(x[0]) + atan(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'atan_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'atan', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ -0.1 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // atan(p[0]) " [ 1, 2] ,\n" // atan(x[0]) " [ 1, 3] ,\n" // atan(c[0]) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // atan(p[0])+atan(x[0])+atan(c[0]) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = atan(p_0) + atan(x_0) + atan(c_0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function vector c(1); c[0] = -0.1; // // set independent variables and parameters vector p(1), x(1); p[0] = 0.2; x[0] = 0.3; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = std::atan(p[0]) + std::atan(x[0]) + std::atan(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // --------------------------------------------------------------------------- bool asin_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : asin(p[0]) // node_5 : asin(x[0]) // node_6 : asin(c[0]) // node_7 : asin(p[0]) + asin(x[0]) + asin(c[0]) // y[0] = asin(p[0]) + asin(x[0]) + asin(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'asin_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'asin', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ -0.1 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // asin(p[0]) " [ 1, 2] ,\n" // asin(x[0]) " [ 1, 3] ,\n" // asin(c[0]) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // asin(p[0])+asin(x[0])+asin(c[0]) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = asin(p_0) + asin(x_0) + asin(c_0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function vector c(1); c[0] = -0.1; // // set independent variables and parameters vector p(1), x(1); p[0] = 0.2; x[0] = 0.3; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = std::asin(p[0]) + std::asin(x[0]) + std::asin(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // --------------------------------------------------------------------------- bool acos_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : acos(p[0]) // node_5 : acos(x[0]) // node_6 : acos(c[0]) // node_7 : acos(p[0]) + acos(x[0]) + acos(c[0]) // y[0] = acos(p[0]) + acos(x[0]) + acos(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'acos_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'acos', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ -0.1 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // acos(p[0]) " [ 1, 2] ,\n" // acos(x[0]) " [ 1, 3] ,\n" // acos(c[0]) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // acos(p[0])+acos(x[0])+acos(c[0]) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = acos(p_0) + acos(x_0) + acos(c_0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function vector c(1); c[0] = -0.1; // // set independent variables and parameters vector p(1), x(1); p[0] = 0.2; x[0] = 0.3; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = std::acos(p[0]) + std::acos(x[0]) + std::acos(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // Convert to Json graph and back again json = f.to_json(); f.from_json(json); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // --------------------------------------------------------------------------- bool abs_op(void) { bool ok = true; using CppAD::vector; using CppAD::AD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // // AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : c[0] // node_4 : abs(p[0]) // node_5 : abs(x[0]) // node_6 : abs(c[0]) // node_7 : abs(p[0]) + abs(x[0]) + abs(c[0]) // y[0] = abs(p[0]) + abs(x[0]) + abs(c[0]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'abs_op example',\n" " 'op_define_vec' : [ 2, [\n" " { 'op_code':1, 'name':'abs', 'n_arg':1 } ,\n" " { 'op_code':2, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 1, [ -0.1 ] ],\n" // c[0] " 'op_usage_vec' : [ 4, [\n" " [ 1, 1] ,\n" // abs(p[0]) " [ 1, 2] ,\n" // abs(x[0]) " [ 1, 3] ,\n" // abs(c[0]) " [ 2, 1, 3, [4, 5, 6] ] ]\n" // abs(p[0])+abs(x[0])+abs(c[0]) " ],\n" " 'dependent_vec' : [ 1, [7] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // f(x, p) = abs(p_0) + abs(x_0) + abs(c_0) CppAD::ADFun f; f.from_json(json); ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 1; // // value of constant in function vector c(1); c[0] = -0.1; // // set independent variables and parameters vector p(1), x(1); p[0] = 0.2; x[0] = 0.3; // // compute y = f(x, p) f.new_dynamic(p); vector y = f.Forward(0, x); // // check result double check = std::fabs(p[0]) + std::fabs(x[0]) + std::fabs(c[0]); ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // // Convert to Json graph and back again json = f.to_json(); // std::cout << graph; f.from_json(json); // // compute y = f(x, p) f.new_dynamic(p); y = f.Forward(0, x); // // check result ok &= CppAD::NearEqual(y[0], check, eps99, eps99); // return ok; } // --------------------------------------------------------------------------- // Test conditional expression bool cexp_lt_variable(void) { bool ok = true; using CppAD::vector; // // An AD graph example // node_1 : x[0] // node_2 : x[1] // node_3 : cexp_lt(x[0], x[1], x[1], x[0]) // y[0] = max(x[0], x[1]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'cexp_lt test',\n" " 'op_define_vec' : [ 1, [\n" " { 'op_code':1, 'name':'cexp_lt', 'n_arg':4 } ]\n" " ],\n" " 'n_dynamic_ind' : 0,\n" " 'n_variable_ind' : 2,\n" " 'constant_vec' : [ 0, [] ],\n" " 'op_usage_vec' : [ 1, [\n" " [ 1, 1, 2, 2, 1 ] ] \n" " ],\n" " 'dependent_vec' : [ 1, [3] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // CppAD::ADFun f; f.from_json(json); // --------------------------------------------------------------------- ok &= f.Domain() == 2; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 0; // vector x(2), y(1); x[0] = 2.0; x[1] = 3.0; y = f.Forward(0, x); ok &= y[0] == std::max(x[0], x[1]); // x[0] = 3.0; x[1] = 2.0; y = f.Forward(0, x); ok &= y[0] == std::max(x[0], x[1]); // --------------------------------------------------------------------- json = f.to_json(); f.from_json(json); // --------------------------------------------------------------------- ok &= f.Domain() == 2; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 0; // x[0] = 2.0; x[1] = 3.0; y = f.Forward(0, x); ok &= y[0] == std::max(x[0], x[1]); // x[0] = 3.0; x[1] = 2.0; y = f.Forward(0, x); ok &= y[0] == std::max(x[0], x[1]); // // std::cout << graph; return ok; } // Test conditional expression bool cexp_lt_constant(void) { bool ok = true; using CppAD::vector; // // An AD graph example // node_1 : x[0] // node_2 : c[0] // node_3 : c[1] // node_4 : cexp_lt(c[0], c[1], c[1], c[0]) // y[0] = max(c[0], c[1]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'cexp_lt test',\n" " 'op_define_vec' : [ 1, [\n" " { 'op_code':1, 'name':'cexp_lt', 'n_arg':4 } ]\n" " ],\n" " 'n_dynamic_ind' : 0,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 2, [ 5.0, -5.0 ] ],\n" " 'op_usage_vec' : [ 1, [\n" " [ 1, 2, 3, 3, 2 ] ] \n" " ],\n" " 'dependent_vec' : [ 1, [4] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // CppAD::ADFun f; f.from_json(json); // --------------------------------------------------------------------- ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 0; // vector c(2), x(1), y(1); c[0] = 5.0; c[1] = -5.0; y = f.Forward(0, x); ok &= y[0] == std::max(c[0], c[1]); // --------------------------------------------------------------------- json = f.to_json(); f.from_json(json); // --------------------------------------------------------------------- ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 0; // ok &= y[0] == std::max(c[0], c[1]); // --------------------------------------------------------------------- // std::cout << graph; return ok; } bool cexp_lt_dynamic(void) { bool ok = true; using CppAD::vector; // // An AD graph example // node_1 : p[0] // node_2 : p[1] // node_3 : x[0] // node_4 : cexp_lt(p[0], p[1], p[1], p[0]) // y[0] = max(p[0], p[1]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'cexp_lt test',\n" " 'op_define_vec' : [ 1, [\n" " { 'op_code':1, 'name':'cexp_lt', 'n_arg':4 } ]\n" " ],\n" " 'n_dynamic_ind' : 2,\n" " 'n_variable_ind' : 1,\n" " 'constant_vec' : [ 0, [ ] ],\n" " 'op_usage_vec' : [ 1, [\n" " [ 1, 1, 2, 2, 1 ] ] \n" " ],\n" " 'dependent_vec' : [ 1, [4] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // CppAD::ADFun f; f.from_json(json); // --------------------------------------------------------------------- ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 2; // vector p(2), x(1), y(1); p[0] = 3.0; p[1] = 2.0; f.new_dynamic(p); y = f.Forward(0, x); ok &= y[0] == std::max(p[0], p[1]); // --------------------------------------------------------------------- json = f.to_json(); // std::cout << graph; f.from_json(json); // --------------------------------------------------------------------- ok &= f.Domain() == 1; ok &= f.Range() == 1; ok &= f.size_dyn_ind() == 2; // p[0] = 2.0; p[1] = 3.0; f.new_dynamic(p); y = f.Forward(0, x); ok &= y[0] == std::max(p[0], p[1]); // --------------------------------------------------------------------- // std::cout << graph; return ok; } // --------------------------------------------------------------------------- // Test atomic function that gets passed both variables and dynamic parameters bool atomic_both(void) { bool ok = true; using CppAD::vector; using CppAD::AD; // // y[0] = p[0] * x[0] vector< AD > ap(1), ax(1), ay(1); ap[0] = 2.0; ax[0] = 3.0; size_t abort_op_index = 0; bool record_compare = false; CppAD::Independent(ax, abort_op_index, record_compare, ap); ay[0] = ap[0] * ax[0]; CppAD::ADFun f(ax, ay); // // Create a ckhpoint_two with name f(x; p). // (This also creates an atomic_three function with same name.) bool internal_bool = false; bool use_hes_sparsity = false; bool use_base2ad = false; bool use_in_parallel = false; CppAD::chkpoint_two chk_f(f, "f(x; p)", internal_bool, use_hes_sparsity, use_base2ad, use_in_parallel ); // ----------------------------------------------------------------------- // g(u; p) vector< AD > au(2), av(1); au[0] = 5.0; au[1] = 6.0; CppAD::Independent(au); ax[0] = au[0]; chk_f(ax, av); // v[0] = p[0] * u[0] ay[0] = au[1] + av[0]; // y[0] = u[1] + p[0] * u[0] CppAD::ADFun g(au, ay); // --------------------------------------------------------------------- ok &= g.Domain() == 2; ok &= g.Range() == 1; ok &= g.size_dyn_ind() == 0; // // evaluate g(u; p) vector p(1), u(2), y(1); p[0] = 3.0; u[0] = 4.0; u[1] = 5.0; chk_f.new_dynamic(p); y = g.Forward(0, u); // // check value double check = u[1] + p[0] * u[0]; ok &= y[0] == check; // --------------------------------------------------------------------- std::string json = g.to_json(); // std::cout << json; g.from_json(json); // --------------------------------------------------------------------- ok &= g.Domain() == 2; ok &= g.Range() == 1; ok &= g.size_dyn_ind() == 0; // // evaluate g(u; p) p[0] = 4.0; u[0] = 5.0; u[1] = 6.0; chk_f.new_dynamic(p); y = g.Forward(0, u); // // check value check = u[1] + p[0] * u[0]; ok &= y[0] == check; // ----------------------------------------------------------------------- // std::cout << graph; return ok; } // --------------------------------------------------------------------------- // Test atomic function that only gets passed dynamic parameters bool atomic_dynamic(void) { bool ok = true; using CppAD::vector; using CppAD::AD; // // y[0] = x[0] * x[1] vector< AD > ax(2), ay(1); ax[0] = 2.0; ax[1] = 3.0; CppAD::Independent(ax); ay[0] = ax[0] * ax[1]; CppAD::ADFun f(ax, ay); // // Create a ckhpoint_two with name f(x). // (This also creates an atomic_three function with same name.) bool internal_bool = false; bool use_hes_sparsity = false; bool use_base2ad = false; bool use_in_parallel = false; CppAD::chkpoint_two chk_f(f, "f(x)", internal_bool, use_hes_sparsity, use_base2ad, use_in_parallel ); // ----------------------------------------------------------------------- vector< AD > au(1), aq(2), av(1); aq[0] = 4.0; aq[1] = 5.0; au[0] = 6.0; size_t abort_op_index = 0; bool record_compare = false; CppAD::Independent(au, abort_op_index, record_compare, aq); chk_f(aq, av); // v[0] = q[0] * q[1] ay[0] = au[0] + av[0]; // y[0] = u[0] + q[0] * q[1] CppAD::ADFun g(au, ay); // // --------------------------------------------------------------------- ok &= g.Domain() == 1; ok &= g.Range() == 1; ok &= g.size_dyn_ind() == 2; // // set q in g(u; q) vector q(2); q[0] = 2.0; q[1] = 3.0; g.new_dynamic(q); // // evaluate g(u; q) vector u(1), y(1); u[0] = 4.0; y = g.Forward(0, u); // // check value double check = u[0] + q[0] * q[1]; ok &= y[0] == check; // --------------------------------------------------------------------- std::string json = g.to_json(); // std::cout << graph; g.from_json(json); // ok &= g.Domain() == 1; ok &= g.Range() == 1; ok &= g.size_dyn_ind() == 2; // // set q in g(u; q) q[0] = 3.0; q[1] = 4.0; g.new_dynamic(q); // // evaluate g(u; q) u[0] = 5.0; y = g.Forward(0, u); // // check value check = u[0] + q[0] * q[1]; ok &= y[0] == check; // ---------------------------------------------------------------------- // std::cout << graph; return ok; } // --------------------------------------------------------------------------- // Test transforming to Json and back to a function bool to_json_and_back(void) { bool ok = true; using CppAD::vector; // // An AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : x[1] // node_4 : -2.0 // node_5 : p[0] + x[0] + x[1] // node_6 : (p[0] + x[0] + x[1]) * (p[0] + x[0] + x[1]) // y[0] = (p[0] + x[0] + x[1]) * (p[0] + x[0] + x[1]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'to_json_and_back test',\n" " 'op_define_vec' : [ 3, [\n" " { 'op_code':1, 'name':'add', 'n_arg':2 } ,\n" " { 'op_code':2, 'name':'mul', 'n_arg':2 } ,\n" " { 'op_code':3, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 2,\n" " 'constant_vec' : [ 1, [ -2.0 ] ],\n" " 'op_usage_vec' : [ 2, [\n" " [ 3, 1, 3, [1, 2, 3 ] ] ,\n" " [ 2, 5, 5 ] ] \n" " ],\n" " 'dependent_vec' : [ 1, [6] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // CppAD::ADFun fun; fun.from_json(json); json = fun.to_json(); // For debugging // std::cout << "json = " << json; fun.from_json(json); // // Compute function value vector p(1), x(2); p[0] = 1.0; x[0] = 2.0; x[1] = 3.0; fun.new_dynamic(p); vector y = fun.Forward(0, x); ok &= y[0] == (p[0] + x[0] + x[1]) * (p[0] + x[0] + x[1]); // // Conpute derivative value vector jac = fun.Jacobian(x); ok &= jac[0] == 2.0 * (p[0] + x[0] + x[1]); ok &= jac[1] == 2.0 * (p[0] + x[0] + x[1]); // // Uncomment statement below to see the graph // std::cout << graph; // return ok; } // --------------------------------------------------------------------------- // Test binary operators that should be implemented bool binary_operators(void) { bool ok = true; using CppAD::AD; // size_t np = 1; size_t nx = 2; size_t ny = 10; CPPAD_TESTVECTOR(double) p(np), x(nx); CPPAD_TESTVECTOR( AD ) ap(np), ax(nx), ay(ny); for(size_t i = 0; i < np; ++i) { ap[i] = 0.5; p[i] = double(i + 1); } for(size_t i = 0; i < nx; ++i) { ax[i] = 0.25; x[i] = double(2 * i + 1); } CppAD::Independent(ax, ap); // size_t j = 0; ay[j++] = ap[0] + 2.0; // dynamic + constant (and ParOp) ay[j++] = 2.0 + ap[0]; // constant + dynamic (and ParOp) ay[j++] = ax[0] + ap[0]; // variable + dynamic ay[j++] = ap[0] + ax[0]; // dynamic + variable ay[j++] = ax[0] + ax[1]; // variable + variable // ay[j++] = ap[0] * 2.0; // dynamic * constant (and ParOp) ay[j++] = 2.0 * ap[0]; // constant * dynamic (and ParOp) ay[j++] = ax[0] * ap[0]; // variable * dynamic ay[j++] = ap[0] * ax[0]; // dynamic * variable ay[j++] = ax[0] * ax[1]; // variable * variable // ok &= j == ny; // // Create function CppAD::ADFun f(ax, ay); // // Evaluate function at x before f.new_dynamic(p); CPPAD_TESTVECTOR(double) y_before = f.Forward(0, x); // // Convert to Json and back again std::string json = f.to_json(); // std::cout << graph; f.from_json(json); // // Evaluate function at x after f.new_dynamic(p); CPPAD_TESTVECTOR(double) y_after = f.Forward(0, x); // double eps99 = 99.0 * std::numeric_limits::epsilon(); for(size_t i = 0; i < ny; ++i) ok &= CppAD::NearEqual( y_before[i], y_after[i], eps99, eps99 ); // // Uncomment statement below to see the graph // std::cout << graph; return ok; } // --------------------------------------------------------------------------- // Test cumulative sum operator bool cumulative_sum(void) { bool ok = true; using CppAD::AD; // size_t np = 2; size_t nx = 2; size_t ny = 1; CPPAD_TESTVECTOR(double) p(np), x(nx); CPPAD_TESTVECTOR( AD ) ap(np), ax(nx), ay(ny); for(size_t i = 0; i < np; ++i) { ap[i] = 0.5; p[i] = double(i + 1); } for(size_t i = 0; i < nx; ++i) { ax[i] = 0.25; x[i] = double(2 * i + 1); } CppAD::Independent(ax, ap); // AD asum = 0.0; asum += 1.0 + ap[0]; asum += ap[1] + 1.0; asum -= ap[1] + ap[0]; // asum += 1.0 + ax[0]; asum += ax[1] + 1.0; asum -= ax[1] + ax[0]; // asum += ap[0] + ax[0]; asum += ax[1] + ap[1]; // ay[0] = asum; // // Create function CppAD::ADFun f(ax, ay); f.optimize(); // // Evaluate function at x before f.new_dynamic(p); CPPAD_TESTVECTOR(double) y_before = f.Forward(0, x); // // Convert to Json and back again std::string json = f.to_json(); // std::cout << graph; f.from_json(json); // // Evaluate function at x after f.new_dynamic(p); CPPAD_TESTVECTOR(double) y_after = f.Forward(0, x); // double eps99 = 99.0 * std::numeric_limits::epsilon(); for(size_t i = 0; i < ny; ++i) ok &= CppAD::NearEqual( y_before[i], y_after[i], eps99, eps99 ); // // Uncomment statement below to see the graph // std::cout << graph; return ok; } // --------------------------------------------------------------------------- // Test unary operators bool unary(bool p_first) { bool ok = true; using CppAD::AD; // size_t np = 11; size_t nx = 11; size_t ny = np + nx; CPPAD_TESTVECTOR(double) p(np), x(nx), y(ny); CPPAD_TESTVECTOR( AD ) ap(np), ax(nx), ay(ny); for(size_t i = 0; i < np; ++i) { p[i] = double(i + 1) / double( np + nx + 1 ); ap[i] = p[i] / 2.0; } for(size_t i = 0; i < nx; ++i) { x[i] = double(i + 1 + np) / double( np + nx + 1); ax[i] = x[i] / 2.0; } CppAD::Independent(ax, ap); // CPPAD_TESTVECTOR( AD ) atmp(np + nx); if( p_first ) { for(size_t i = 0; i < np; ++i) atmp[i] = ap[i]; for(size_t i = 0; i < nx; ++i) atmp[i + np] = ax[i]; } else { for(size_t i = 0; i < np; ++i) atmp[i + nx] = ap[i]; for(size_t i = 0; i < nx; ++i) atmp[i] = ax[i]; } // ay[0] = fabs(atmp[0]); ay[1] = acos(atmp[1]); ay[2] = acosh(atmp[2] + 1.0); ay[3] = asin(atmp[3]); ay[4] = asinh(atmp[4]); ay[5] = atan(atmp[5]); ay[6] = atanh(atmp[6]); ay[7] = cos(atmp[7]); ay[8] = cosh(atmp[8]); ay[9] = erf(atmp[9]); ay[10] = erfc(atmp[10]); ay[11] = exp(atmp[11]); ay[12] = expm1(atmp[12]); ay[13] = log(atmp[13]); ay[14] = log1p(atmp[14]); ay[15] = - atmp[15]; ay[16] = sign(atmp[16]); ay[17] = sin(atmp[17]); ay[18] = sinh(atmp[18]); ay[19] = sqrt(atmp[19]); ay[20] = tan(atmp[20]); ay[21] = sin(atmp[21]); // Create function CppAD::ADFun f(ax, ay); f.optimize(); // // Evaluate function at x before f.new_dynamic(p); CPPAD_TESTVECTOR(double) y_before = f.Forward(0, x); // // Convert to Json and back again std::string json = f.to_json(); // std::cout << graph; f.from_json(json); // // Evaluate function at x after f.new_dynamic(p); CPPAD_TESTVECTOR(double) y_after = f.Forward(0, x); // double eps99 = 99.0 * std::numeric_limits::epsilon(); for(size_t i = 0; i < ny; ++i) ok &= CppAD::NearEqual( y_before[i], y_after[i], eps99, eps99 ); // return ok; } // --------------------------------------------------------------------------- } // END_EMPTY_NAMESPACE bool json_graph(void) { bool ok = true; ok &= comp_op_dyn_dyn(); ok &= comp_op_var_var(); ok &= comp_op_dyn_var(); ok &= comp_op_var_dyn(); ok &= acosh_op(); ok &= log1p_op(); ok &= expm1_op(); ok &= erfc_op(); ok &= erf_op(); ok &= atanh_op(); ok &= asinh_op(); ok &= tan_op(); ok &= tanh_op(); ok &= sqrt_op(); ok &= sin_op(); ok &= sinh_op(); ok &= sign_op(); ok &= log_op(); ok &= exp_op(); ok &= cos_op(); ok &= cosh_op(); ok &= atan_op(); ok &= asin_op(); ok &= acos_op(); ok &= abs_op(); ok &= cexp_lt_variable(); ok &= cexp_lt_constant(); ok &= cexp_lt_dynamic(); ok &= atomic_both(); ok &= atomic_dynamic(); ok &= to_json_and_back(); ok &= binary_operators(); ok &= cumulative_sum(); ok &= unary(true); ok &= unary(false); // return ok; } ================================================ FILE: test_more/general/local/is_pod.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include bool is_pod(void) { bool ok = true; using CppAD::local::is_pod; // // Check all the cases that are the same as short int on // https://en.cppreference.com/w/cpp/language/types on 2020-12-04. ok &= is_pod(); ok &= is_pod(); ok &= is_pod(); ok &= is_pod(); // return ok; } ================================================ FILE: test_more/general/local/json_lexer.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include bool json_lexer(void) { bool ok = true; typedef CppAD::graph::graph_op_enum graph_op_enum; using CppAD::local::graph::op_name2enum; // // match_any_string std::string match_any_string = ""; // // An AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : x[1] // node_4 : -2.0 // node_5 : p[0] + x[0] + x[1] // node_6 : (p[0] + x[0] + x[1]) * (p[0] + x[0] + x[1]) // y[0] = (p[0] + x[0] + x[1]) * (p[0] + x[0] + x[1]) // use single quote to avoid having to escape double quote std::string graph = "{\n" " 'op_define_vec' : [ 3, [\n" " { 'op_code':1, 'name':'add', 'n_arg':2 } ,\n" " { 'op_code':2, 'name':'mul', 'n_arg':2 } ,\n" " { 'op_code':3, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 2,\n" " 'constant_vec' : [ 1, [ -2.0 ] ],\n" " 'op_usage_vec' : [ 2, [\n" " [ 3, 1, 3, [1, 2, 3] ] ,\n" " [ 2, 5, 5 ] ] \n" " ],\n" " 'dependent_vec' : [ 1, [6] ]\n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < graph.size(); ++i) if( graph[i] == '\'' ) graph[i] = '"'; // // json_lexer constructor checks for { at beginning CppAD::local::graph::json_lexer json_lexer(graph); // ----------------------------------------------------------------------- // op_define_vec json_lexer.check_next_string("op_define_vec"); json_lexer.check_next_char(':'); json_lexer.check_next_char('['); // // n_define json_lexer.next_non_neg_int(); size_t n_define = json_lexer.token2size_t(); json_lexer.check_next_char(','); json_lexer.check_next_char('['); CppAD::vector op_code2enum(1); for(size_t i = 0; i < n_define; ++i) { json_lexer.check_next_char('{'); // // op_code json_lexer.check_next_string("op_code"); json_lexer.check_next_char(':'); json_lexer.next_non_neg_int(); # ifndef NDEBUG size_t op_code = json_lexer.token2size_t(); assert( op_code == op_code2enum.size() ); # endif json_lexer.check_next_char(','); // // name json_lexer.check_next_string("name"); json_lexer.check_next_char(':'); json_lexer.check_next_string(match_any_string); std::string name = json_lexer.token(); graph_op_enum op_enum = op_name2enum[name]; // // op_code2enum op_code2enum.push_back(op_enum); // if( op_enum != CppAD::graph::sum_graph_op ) { json_lexer.check_next_char(','); // // n_arg json_lexer.check_next_string("n_arg"); json_lexer.check_next_char(':'); json_lexer.next_non_neg_int(); ok &= json_lexer.token2size_t() == 2; } // json_lexer.check_next_char('}'); if( i + 1 == n_define ) json_lexer.check_next_char(']'); else json_lexer.check_next_char(','); } json_lexer.check_next_char(']'); json_lexer.check_next_char(','); // ----------------------------------------------------------------------- // n_dynamic_ind json_lexer.check_next_string("n_dynamic_ind"); json_lexer.check_next_char(':'); json_lexer.next_non_neg_int(); size_t n_dynamic_ind = json_lexer.token2size_t(); json_lexer.check_next_char(','); // ok &= n_dynamic_ind == 1; // ----------------------------------------------------------------------- // n_variable_ind json_lexer.check_next_string("n_variable_ind"); json_lexer.check_next_char(':'); json_lexer.next_non_neg_int(); size_t n_variable_ind = json_lexer.token2size_t(); json_lexer.check_next_char(','); // ok &= n_variable_ind == 2; // ----------------------------------------------------------------------- // constant_vec json_lexer.check_next_string("constant_vec"); json_lexer.check_next_char(':'); json_lexer.check_next_char('['); json_lexer.next_non_neg_int(); size_t n_constant = json_lexer.token2size_t(); CppAD::vector constant_vec(n_constant); json_lexer.check_next_char(','); // // [ first_constant, ... , last_constant ] json_lexer.check_next_char('['); for(size_t i = 0; i < n_constant; ++i) { json_lexer.next_float(); constant_vec[i] = json_lexer.token2double(); // if( i + 1 == n_constant ) json_lexer.check_next_char(']'); else json_lexer.check_next_char(','); } // json_lexer.check_next_char(']'); json_lexer.check_next_char(','); // ok &= constant_vec.size() == 1; ok &= constant_vec[0] == -2.0; // ----------------------------------------------------------------------- // op_usage_vec json_lexer.check_next_string("op_usage_vec"); // json_lexer.check_next_char(':'); json_lexer.check_next_char('['); // json_lexer.next_non_neg_int(); size_t n_usage = json_lexer.token2size_t(); CppAD::vector operator_vec(n_usage); // json_lexer.check_next_char(','); // // [ first_operator, ... , last_operator ] json_lexer.check_next_char('['); for(size_t i = 0; i < n_usage; ++i) { // start next operator json_lexer.check_next_char('['); graph_op_enum op_usage; // // op_code json_lexer.next_non_neg_int(); size_t op_code = json_lexer.token2size_t(); // // op_enum op_usage = op_code2enum[op_code]; json_lexer.check_next_char(','); // size_t n_result, n_arg; if( op_usage != CppAD::graph::sum_graph_op ) { n_result = 1; n_arg = 2; } else { // n_result json_lexer.next_non_neg_int(); n_result = json_lexer.token2size_t(); json_lexer.check_next_char(','); ok &= n_result == 1; // // n_arg json_lexer.next_non_neg_int(); n_arg = json_lexer.token2size_t(); json_lexer.check_next_char(','); json_lexer.check_next_char('['); ok &= n_arg == 3; // } ok &= n_result == 1; CppAD::vector arg_node(0); // first_arg_node, ... , last_arg_node for(size_t j = 0; j < n_arg; ++j) { // next argument node json_lexer.next_non_neg_int(); size_t argument_node = json_lexer.token2size_t(); arg_node.push_back( argument_node ); // if( j + 1 == n_arg ) json_lexer.check_next_char(']'); else json_lexer.check_next_char(','); } if( op_usage == CppAD::graph::sum_graph_op ) { json_lexer.check_next_char(']'); ok &= arg_node.size() == 3; ok &= arg_node[0] == 1; ok &= arg_node[1] == 2; ok &= arg_node[2] == 3; } else { ok &= arg_node.size() == 2; ok &= arg_node[0] == 5; ok &= arg_node[1] == 5; } // // end of this operator operator_vec[i] = op_usage; // if( i + 1 == n_usage ) json_lexer.check_next_char(']'); else json_lexer.check_next_char(','); } json_lexer.check_next_char(']'); // json_lexer.check_next_char(','); // ok &= operator_vec.size() == 2; // graph_op_enum op_enum = operator_vec[0]; ok &= op_enum == CppAD::graph::sum_graph_op; // op_enum = operator_vec[1]; ok &= op_enum == CppAD::graph::mul_graph_op; // ----------------------------------------------------------------------- // dependent_vec json_lexer.check_next_string("dependent_vec"); json_lexer.check_next_char(':'); json_lexer.check_next_char('['); json_lexer.next_non_neg_int(); size_t n_dependent = json_lexer.token2size_t(); CppAD::vector dependent_vec(n_dependent); // json_lexer.check_next_char(','); // // [ first_dependent, ... , last_dependent ] json_lexer.check_next_char('['); for(size_t i = 0; i < n_dependent; ++i) { json_lexer.next_float(); dependent_vec[i] = json_lexer.token2size_t(); // if( i + 1 == n_dependent ) json_lexer.check_next_char(']'); else json_lexer.check_next_char(','); } // json_lexer.check_next_char(']'); // ok &= dependent_vec.size() == 1; ok &= dependent_vec[0] == 6; // ----------------------------------------------------------------------- // } json_lexer.check_next_char('}'); // return ok; } ================================================ FILE: test_more/general/local/json_parser.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include bool json_parser(void) { bool ok = true; using CppAD::cpp_graph; using CppAD::vector; // // An AD graph example // node_1 : p[0] // node_2 : x[0] // node_3 : x[1] // node_4 : -2.0 // node_5 : p[0] + x[0] + x[1] // node_6 : (p[0] + x[0] + x[1]) * (p[0] + x[0] + x[1]) // y[0] = (p[0] + x[0] + x[1]) * (p[0] + x[0] + x[1]) // use single quote to avoid having to escape double quote std::string json = "{\n" " 'function_name' : 'json_parser test',\n" " 'op_define_vec' : [ 3, [\n" " { 'op_code':1, 'name':'add', 'n_arg':2 } ,\n" " { 'op_code':2, 'name':'mul', 'n_arg':2 } ,\n" " { 'op_code':3, 'name':'sum' } ]\n" " ],\n" " 'n_dynamic_ind' : 1,\n" " 'n_variable_ind' : 2,\n" " 'constant_vec' : [ 1, [ -2.0 ] ],\n" " 'op_usage_vec' : [ 2, [\n" " [ 3, 1, 3, [1, 2, 3 ] ] ,\n" " [ 2, 5, 5 ] ] \n" " ],\n" " 'dependent_vec' : [ 1, [6] ] \n" "}\n"; // Convert the single quote to double quote for(size_t i = 0; i < json.size(); ++i) if( json[i] == '\'' ) json[i] = '"'; // // C++ graph object cpp_graph graph_obj; // const std::string& function_name( graph_obj.function_name_get() ); const size_t& n_dynamic_ind( graph_obj.n_dynamic_ind_get() ); const size_t& n_variable_ind( graph_obj.n_variable_ind_get() ); // // call parser CppAD::local::graph::json_parser( json, graph_obj ); // ok &= function_name == "json_parser test"; ok &= n_dynamic_ind == 1; ok &= n_variable_ind == 2; ok &= graph_obj.atomic_name_vec_size() == 0; // ok &= graph_obj.constant_vec_size() == 1; ok &= graph_obj.constant_vec_get(0) == -2.0; // ok &= graph_obj.operator_vec_size() == 2; // // vector arg_node; cpp_graph::const_iterator graph_itr = graph_obj.begin(); cpp_graph::const_iterator::value_type itr_value = *graph_itr; arg_node = *(itr_value.arg_node_ptr); ok &= itr_value.op_enum == CppAD::graph::sum_graph_op; ok &= arg_node.size() == 3; ok &= arg_node[0] == 1; ok &= arg_node[1] == 2; ok &= arg_node[2] == 3; // itr_value = *++graph_itr; ok &= itr_value.op_enum == CppAD::graph::mul_graph_op; arg_node.resize(0); // to avoid CppAD::vector assignment error arg_node = *(itr_value.arg_node_ptr); ok &= arg_node.size() == 2; ok &= arg_node[0] == 5; ok &= arg_node[1] == 5; // ok &= graph_obj.dependent_vec_size() == 1; ok &= graph_obj.dependent_vec_get(0) == 6; // ----------------------------------------------------------------------- // return ok; } ================================================ FILE: test_more/general/local/temp_file.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include bool temp_file(void) { bool ok = true; // // file_name // create file std::string file_name = CppAD::local::temp_file(); // // ifs std::ifstream ifs; ifs.open(file_name); // // ok ok &= ifs.good(); int c = ifs.get(); ok &= c == EOF; // // ok // remove file ifs.close(); int flag = std::remove(file_name.c_str()); ok &= flag == 0; // return ok; } ================================================ FILE: test_more/general/local/vector_set.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace { // BEGIN empty namespace template bool test_no_other(void) { bool ok = true; SetVector vec_set; size_t n_set = 4; size_t end = 5; // // set size of vec_set vec_set.resize(n_set, end); ok &= end == vec_set.end(); ok &= n_set == vec_set.n_set(); // // test resizing to zero vec_set.resize(0, 0); ok &= 0 == vec_set.n_set(); ok &= 0 == vec_set.end(); // // set size of vec_set vec_set.resize(n_set, end); ok &= end == vec_set.end(); ok &= n_set == vec_set.n_set(); // // add the element i+1 to set i for(size_t i = 1; i < n_set; i++) vec_set.add_element(i, i+1); // // check for element i and i+1 in set i for(size_t i = 0; i < n_set; i++) { ok &= ! vec_set.is_element(i, i); if( i == 0 ) ok &= ! vec_set.is_element(i, i+1); else ok &= vec_set.is_element(i, i+1); } // // set an empty set to value of set 2 size_t target = 0; size_t source = 2; vec_set.assignment(target, source, vec_set); ok &= ! vec_set.is_element(target, source); ok &= vec_set.is_element(target, source+1); // // set a non-empty set to the value of set 2 target = 1; vec_set.assignment(target, source, vec_set); ok &= ! vec_set.is_element(target, source); ok &= vec_set.is_element(target, source+1); // // add an element to set 1, one of the three vectors equal to set 2 target = 1; vec_set.add_element(target, source); ok &= vec_set.is_element(target, source); ok &= vec_set.is_element(target, source+1); ok &= ! vec_set.is_element(source, source); ok &= vec_set.is_element(source, source+1); // // now take the union of set 2 and set 3 and place in set 0 // (which is sharing a list with set 2) target = 0; vec_set.binary_union(target, source, source+1, vec_set); ok &= vec_set.is_element(target, source+1); ok &= vec_set.is_element(target, source+2); ok &= vec_set.is_element(source, source+1); ok &= ! vec_set.is_element(source, source+2); // // now check the elements in set 0 by iterating over them typename SetVector::const_iterator itr(vec_set, target); ok &= *itr == source+1; ok &= *(++itr) == source+2; ok &= *(++itr) == end; // // now test clear vec_set.clear(1); ok &= ! vec_set.is_element(1, source+1); ok &= vec_set.is_element(0, source+1); // // now force list_setvec garbage collection by setting all sets // equal to set 0 for(size_t i = 1; i < n_set; i++) { vec_set.assignment(i, 0, vec_set); ok &= vec_set.is_element(i, source+1); ok &= vec_set.is_element(i, source+2); } // return ok; } template bool test_yes_other(void) { bool ok = true; SetVector vec_set, other_vec; size_t n_set = 4; size_t end = 5; vec_set.resize(n_set, end); other_vec.resize(n_set, end); // // add element i to set i in vec_set // add element i+1 to set i in other for(size_t i = 1; i < n_set; i++) { vec_set.add_element(i, i); other_vec.add_element(i, i+1); } // // assignment of one set from other size_t target = 0; size_t source = 1; vec_set.assignment(target, source, other_vec); ok &= ! vec_set.is_element(target, source); ok &= vec_set.is_element(target, source+1); // // now take the union of a set from vec_set and from other_vec target = 2; // where result goes in vec_set size_t left = 2; // left operand in vec_set size_t right = 2; // right operand in other vec_set.binary_union(target, left, right, other_vec); ok &= vec_set.is_element(target, left); ok &= vec_set.is_element(target, right+1); // // now use assignment for entire vector of sets vec_set = other_vec; ok &= ! vec_set.is_element(0, 0); ok &= ! vec_set.is_element(0, 1); for(size_t i = 1; i < n_set; i++) { ok &= ! vec_set.is_element(i, i); ok &= vec_set.is_element(i, i+1); } return ok; } template bool test_intersection(void) { bool ok = true; // SetVector vec_set; size_t n_set = 3; size_t end = 5; vec_set.resize(n_set, end); // // set[0] = {1, 2} vec_set.add_element(0, 1); vec_set.add_element(0, 2); // // set[1] = {2, 3} vec_set.add_element(1, 2); vec_set.add_element(1, 3); // // set[2] = set[0] intersect set[1] size_t target = 2; size_t left = 0; size_t right = 1; vec_set.binary_intersection(target, left, right, vec_set); // typename SetVector::const_iterator itr1(vec_set, target); ok &= *itr1 == 2; ok &= *(++itr1) == end; // // other[1] = set[1] SetVector other; other.resize(n_set, end); target = 1; size_t source = 1; other.assignment(target, source, vec_set); // // set[2] = set[0] intersect other[1] target = 2; left = 0; right = 1; vec_set.binary_intersection(target, left, right, other); // typename SetVector::const_iterator itr2(vec_set, target); ok &= *itr2 == 2; ok &= *(++itr2) == end; // return ok; } template bool test_post(void) { bool ok = true; // SetVector vec_set; size_t n_set = 3; size_t end = 5; vec_set.resize(n_set, end); // // set[1] = {1, 2} vec_set.add_element(1, 1); vec_set.add_element(1, 2); // // set[1] = {1, 2} union (2, 4, 4) = {1, 2, 4} size_t target = 1; vec_set.post_element(target, 2); vec_set.post_element(target, 4); vec_set.post_element(target, 4); vec_set.process_post(target); // typename SetVector::const_iterator itr1(vec_set, target); ok &= *itr1 == 1; ok &= *(++itr1) == 2; ok &= *(++itr1) == 4; ok &= *(++itr1) == end; // // set[1] = {1, 2, 4} union (1, 2) target = 1; vec_set.post_element(target, 1); vec_set.post_element(target, 2); vec_set.process_post(target); // typename SetVector::const_iterator itr2(vec_set, target); ok &= *itr2 == 1; ok &= *(++itr2) == 2; ok &= *(++itr2) == 4; ok &= *(++itr2) == end; // return ok; } } // END empty namespace bool vector_set(void) { bool ok = true; // ok &= test_no_other(); ok &= test_no_other(); ok &= test_no_other(); // ok &= test_yes_other(); ok &= test_yes_other(); ok &= test_yes_other(); // ok &= test_intersection(); ok &= test_intersection(); ok &= test_intersection(); // ok &= test_post(); ok &= test_post(); # ifdef CPPAD_DO_NOT_RUN_THIS_TEST // 2DO: This class tested below is not currently being used. // This test is failing due to a bug. To be specific, push_back on a vector // is invalidating some pointers. // We need to use a different temporary vector for these push_backs. ok &= test_post(); # endif // return ok; } ================================================ FILE: test_more/general/log.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Two old log examples now used just for validation testing */ # include namespace { // BEGIN empty namespace bool LogTestOne(void) { bool ok = true; using CppAD::log; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector, indices, values, and declaration CPPAD_TESTVECTOR(AD) U(1); size_t s = 0; U[s] = 2.; Independent(U); // dependent variable vector, indices, and values CPPAD_TESTVECTOR(AD) Z(2); size_t x = 0; size_t y = 1; Z[x] = log(U[s]); Z[y] = log(Z[x]); // define f : U -> Z and vectors for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v( f.Domain() ); CPPAD_TESTVECTOR(double) w( f.Range() ); // check values ok &= NearEqual(Z[x] , log(2.), eps99 , eps99); ok &= NearEqual(Z[y] , log( log(2.) ), eps99 , eps99); // forward computation of partials w.r.t. s v[s] = 1.; w = f.Forward(1, v); ok &= NearEqual(w[x], 1. / U[s], eps99 , eps99); // dx/ds ok &= NearEqual(w[y], 1. / (U[s] * Z[x]), eps99 , eps99); // dy/ds // reverse computation of partials of y w[x] = 0.; w[y] = 1.; v = f.Reverse(1,w); ok &= NearEqual(v[s], 1. / (U[s] * Z[x]), eps99 , eps99); // dy/ds // forward computation of second partials w.r.t. s v[s] = 1.; w = f.Forward(1, v); v[s] = 0.; w = f.Forward(2, v); ok &= NearEqual( 2. * w[y] , - 1. / (Z[x]*Z[x]*U[s]*U[s]) - 1. / (Z[x]*U[s]*U[s]), eps99 , eps99 ); // reverse computation of second partials of y CPPAD_TESTVECTOR(double) r( f.Domain() * 2 ); w[x] = 0.; w[y] = 1.; r = f.Reverse(2, w); ok &= NearEqual( r[2 * s + 1] , - 1. / (Z[x]*Z[x]*U[s]*U[s]) - 1. / (Z[x]*U[s]*U[s]), eps99 , eps99 ); return ok; } bool LogTestTwo(void) { bool ok = true; using CppAD::log; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector CPPAD_TESTVECTOR(AD) U(1); U[0] = 1.; Independent(U); // a temporary values AD x = exp(U[0]); // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); Z[0] = log(x); // log( exp(u) ) // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v(1); CPPAD_TESTVECTOR(double) w(1); // check value ok &= NearEqual(U[0] , Z[0], eps99 , eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; double value = 1.; v[0] = 1.; for(j = 1; j < p; j++) { jfac *= double(j); w = f.Forward(j, v); ok &= NearEqual(w[0], value/jfac, eps99, eps99); // d^jz/du^j v[0] = 0.; value = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r(p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; value = 1.; for(j = 0; j < p; j++) { ok &= NearEqual(r[j], value/jfac, eps99, eps99); // d^jz/du^j jfac *= double(j + 1); value = 0.; } return ok; } } // END empty namespace bool log(void) { bool ok = true; ok &= LogTestOne(); ok &= LogTestTwo(); return ok; } ================================================ FILE: test_more/general/log10.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old example now used just for validation testing. */ # include bool log10(void) { bool ok = true; using CppAD::log10; using CppAD::log; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector, indices, values, and declaration CPPAD_TESTVECTOR(AD) U(1); size_t s = 0; U[s] = 10.; Independent(U); // dependent variable vector, indices, and values CPPAD_TESTVECTOR(AD) Z(2); size_t x = 0; size_t y = 1; Z[x] = log10(U[s]); Z[y] = log10(Z[x]); // define f : U -> Z and vectors for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v( f.Domain() ); CPPAD_TESTVECTOR(double) w( f.Range() ); // check values ok &= NearEqual(Z[x] , 1., eps99 , eps99); ok &= NearEqual(Z[y] , 0., eps99 , eps99); // forward computation of partials w.r.t. s double l10 = log(10.); v[s] = 1.; w = f.Forward(1, v); ok &= NearEqual(w[x], 1./(U[s]*l10) , eps99 , eps99); // dx/ds ok &= NearEqual(w[y], 1./(U[s]*Z[x]*l10*l10), eps99 , eps99); // dy/ds // reverse computation of partials of y w[x] = 0.; w[y] = 1.; v = f.Reverse(1,w); ok &= NearEqual(v[s], 1./(U[s]*Z[x]*l10*l10), eps99 , eps99); // dy/ds return ok; } ================================================ FILE: test_more/general/log1p.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include bool log1p(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; // 10 times machine epsilon double eps = 10. * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 0.5; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = x0; // declare independent variables and start tape recording CppAD::Independent(ax); // a temporary value AD expm1_of_x0 = CppAD::expm1(ax[0]); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = CppAD::log1p(expm1_of_x0); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // check value ok &= NearEqual(ay[0] , x0, eps, eps); // forward computation of first partial w.r.t. x[0] CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 1., eps, eps); // forward computation of higher order partials w.r.t. x[0] size_t n_order = 5; for(size_t order = 2; order < n_order; order++) { dx[0] = 0.; dy = f.Forward(order, dx); ok &= NearEqual(dy[0], 0., eps, eps); } // reverse computation of derivatives CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n_order * n); w[0] = 1.; dw = f.Reverse(n_order, w); ok &= NearEqual(dw[0], 1., eps, eps); for(size_t order = 1; order < n_order; order++) ok &= NearEqual(dw[order * n + 0], 0., eps, eps); return ok; } // END C++ ================================================ FILE: test_more/general/mul.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Two old Mul examples now used just for valiadation testing */ # include namespace { // BEGIN empty namespace bool zero_times_nan(void) { bool ok = true; CPPAD_TESTVECTOR( CppAD::AD ) ax(2), ay(3); ax[0] = 0.0; ax[1] = 0.0; CppAD::Independent(ax); CppAD::AD adiv = ax[0] / ax[1]; // // the result for each of these cases should be identically zero ay[0] = 0.0 * adiv; ay[1] = adiv * 0.0; ay[2] = 0.0 / adiv; // CppAD::ADFun f(ax, ay); // CPPAD_TESTVECTOR(double) x(2), y(3); x[0] = 1.0; x[1] = 1.0; y = f.Forward(0, x); ok &= y[0] == 0.0; ok &= y[1] == 0.0; ok &= y[2] == 0.0; return ok; } bool MulTestOne(void) { bool ok = true; using namespace CppAD; // independent variable vector, indices, values, and declaration CPPAD_TESTVECTOR(AD) U(2); size_t s = 0; size_t t = 1; U[s] = 3.; U[t] = 2.; Independent(U); // assign some parameters AD zero = 0.; AD one = 1.; // dependent variable vector and indices CPPAD_TESTVECTOR(AD) Z(5); size_t x = 0; size_t y = 1; size_t z = 2; size_t u = 3; size_t v = 4; // assign the dependent variables Z[x] = U[s] * U[t]; // AD * AD Z[y] = Z[x] * 4.; // AD * double Z[z] = 4. * Z[y]; // double * AD Z[u] = one * Z[z]; // multiplication by parameter equal to one Z[v] = zero * Z[z]; // multiplication by parameter equal to zero // check multipilcation by zero results in a parameter ok &= Parameter(Z[v]); // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) q( f.Domain() ); CPPAD_TESTVECTOR(double) r( f.Range() ); // check parameter flag ok &= f.Parameter(v); // check values ok &= ( Z[x] == 3. * 2. ); ok &= ( Z[y] == 3. * 2. * 4. ); ok &= ( Z[z] == 4. * 3. * 2. * 4. ); ok &= ( Z[u] == Z[z] ); ok &= ( Z[v] == 0. ); // forward computation of partials w.r.t. s q[s] = 1.; q[t] = 0.; r = f.Forward(1, q); ok &= ( r[x] == U[t] ); // dx/ds ok &= ( r[y] == U[t] * 4. ); // dy/ds ok &= ( r[z] == 4. * U[t] * 4. ); // dz/ds ok &= ( r[u] == r[z] ); // du/ds ok &= ( r[v] == 0. ); // dv/ds // reverse computation of second partials of z CPPAD_TESTVECTOR(double) d2( f.Domain() * 2 ); r[x] = 0.; r[y] = 0.; r[z] = 1.; r[u] = 0.; r[v] = 0.; d2 = f.Reverse(2, r); // check second order partials ok &= ( d2[2 * s + 1] == 0. ); // d^2 z / (ds ds) ok &= ( d2[2 * t + 1] == 4. * 4. ); // d^2 z / (ds dt) return ok; } bool MulTestTwo(void) { bool ok = true; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector double u0 = .5; CPPAD_TESTVECTOR(AD) U(1); U[0] = u0; Independent(U); AD a = U[0] * 1.; // AD * double AD b = a * 2; // AD * int AD c = 3. * b; // double * AD AD d = 4 * c; // int * AD // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); Z[0] = U[0] * d; // AD * AD // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v(1); CPPAD_TESTVECTOR(double) w(1); // check value ok &= NearEqual(Value(Z[0]) , u0*4*3*2*u0, eps99 , eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; v[0] = 1.; for(j = 1; j < p; j++) { double value; if( j == 1 ) value = 48. * u0; else if( j == 2 ) value = 48.; else value = 0.; jfac *= double(j); w = f.Forward(j, v); ok &= NearEqual(w[0], value/jfac, eps99, eps99); // d^jz/du^j v[0] = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r(p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; for(j = 0; j < p; j++) { double value; if( j == 0 ) value = 48. * u0; else if( j == 1 ) value = 48.; else value = 0.; ok &= NearEqual(r[j], value/jfac, eps99, eps99); // d^jz/du^j jfac *= double(j + 1); } return ok; } } // END empty namespace bool Mul(void) { bool ok = true; ok &= MulTestOne(); ok &= MulTestTwo(); ok &= zero_times_nan(); return ok; } ================================================ FILE: test_more/general/mul_cond_rev.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Test of multi-level conditional expressions reverse mode */ # include bool mul_cond_rev(void) { bool ok = true; using CppAD::vector; using CppAD::NearEqual; double eps = 10. * std::numeric_limits::epsilon(); // typedef CppAD::AD a1double; typedef CppAD::AD a2double; // a1double a1zero = 0.0; a2double a2zero = a1zero; a1double a1one = 1.0; a2double a2one = a1one; // // -------------------------------------------------------------------- // create a1f = f(x) size_t n = 1; size_t m = 25; // vector a2x(n), a2y(m); a2x[0] = a2double( 5.0 ); Independent(a2x); // size_t i = 0; // variable that is greater than one when x[0] is zero // and less than one when x[0] is 1.0 or greater a2double a2switch = a2one / (a2x[0] + a2double(0.5)); // variable that is infinity when x[0] is zero // and a normal number when x[0] is 1.0 or greater a2double a2inf_var = a2one / a2x[0]; // variable that is nan when x[0] is zero // and a normal number when x[0] is 1.0 or greater a2double a2nan_var = ( a2one / a2inf_var ) / a2x[0]; // variable that is one when x[0] is zero // and less then one when x[0] is 1.0 or greater a2double a2one_var = a2one / ( a2one + a2x[0] ); // div a2y[i++] = CondExpGt(a2x[0], a2zero, a2nan_var, a2zero); // abs a2y[i++] = CondExpGt(a2x[0], a2zero, fabs( a2y[0] ), a2zero); // add a2y[i++] = CondExpGt(a2x[0], a2zero, a2nan_var + a2nan_var, a2zero); // acos a2y[i++] = CondExpGt(a2x[0], a2zero, acos(a2switch), a2zero); // asin a2y[i++] = CondExpGt(a2x[0], a2zero, asin(a2switch), a2zero); // atan a2y[i++] = CondExpGt(a2x[0], a2zero, atan(a2nan_var), a2zero); // cos a2y[i++] = CondExpGt(a2x[0], a2zero, cos(a2nan_var), a2zero); // cosh a2y[i++] = CondExpGt(a2x[0], a2zero, cosh(a2nan_var), a2zero); // exp a2y[i++] = CondExpGt(a2x[0], a2zero, exp(a2nan_var), a2zero); // log a2y[i++] = CondExpGt(a2x[0], a2zero, log(a2x[0]), a2zero); // mul a2y[i++] = CondExpGt(a2x[0], a2zero, a2x[0] * a2inf_var, a2zero); // pow a2y[i++] = CondExpGt(a2x[0], a2zero, pow(a2inf_var, a2x[0]), a2zero); // sin a2y[i++] = CondExpGt(a2x[0], a2zero, sin(a2nan_var), a2zero); // sinh a2y[i++] = CondExpGt(a2x[0], a2zero, sinh(a2nan_var), a2zero); // sqrt a2y[i++] = CondExpGt(a2x[0], a2zero, sqrt(a2x[0]), a2zero); // sub a2y[i++] = CondExpGt(a2x[0], a2zero, a2inf_var - a2nan_var, a2zero); // tan a2y[i++] = CondExpGt(a2x[0], a2zero, tan(a2nan_var), a2zero); // tanh a2y[i++] = CondExpGt(a2x[0], a2zero, tanh(a2nan_var), a2zero); // azmul a2y[i++] = CondExpGt(a2x[0], a2zero, azmul(a2x[0], a2inf_var), a2zero); // // Operations that are C+11 atomic // // acosh a2y[i++] = CondExpGt(a2x[0], a2zero, acosh( a2x[0] ), a2zero); // asinh a2y[i++] = CondExpGt(a2x[0], a2zero, asinh( a2nan_var ), a2zero); // atanh a2y[i++] = CondExpGt(a2x[0], a2zero, atanh( a2one_var ), a2zero); // erf a2y[i++] = CondExpGt(a2x[0], a2zero, erf( a2nan_var ), a2zero); // expm1 a2y[i++] = CondExpGt(a2x[0], a2zero, expm1(a2nan_var), a2zero); // log1p a2y[i++] = CondExpGt(a2x[0], a2zero, log1p(- a2one_var ), a2zero); // ok &= i == m; CppAD::ADFun a1f; a1f.Dependent(a2x, a2y); // -------------------------------------------------------------------- // create h = f(x) vector a1x(n), a1y(m); a1x[0] = 5.0; // Independent(a1x); i = 0; a1double a1switch = a1one / (a1x[0] + a1double(0.5)); a1double a1inf_var = a1one / a1x[0]; a1double a1nan_var = ( a1one / a1inf_var ) / a1x[0]; a1double a1one_var = a1one / ( a1one + a1x[0] ); // div a1y[i++] = CondExpGt(a1x[0], a1zero, a1nan_var, a1zero); // abs a1y[i++] = CondExpGt(a1x[0], a1zero, fabs( a1y[0] ), a1zero); // add a1y[i++] = CondExpGt(a1x[0], a1zero, a1nan_var + a1nan_var, a1zero); // acos a1y[i++] = CondExpGt(a1x[0], a1zero, acos(a1switch), a1zero); // asin a1y[i++] = CondExpGt(a1x[0], a1zero, asin(a1switch), a1zero); // atan a1y[i++] = CondExpGt(a1x[0], a1zero, atan(a1nan_var), a1zero); // cos a1y[i++] = CondExpGt(a1x[0], a1zero, cos(a1nan_var), a1zero); // cosh a1y[i++] = CondExpGt(a1x[0], a1zero, cosh(a1nan_var), a1zero); // exp a1y[i++] = CondExpGt(a1x[0], a1zero, exp(a1nan_var), a1zero); // log a1y[i++] = CondExpGt(a1x[0], a1zero, log(a1x[0]), a1zero); // mul a1y[i++] = CondExpGt(a1x[0], a1zero, a1x[0] * a1inf_var, a1zero); // pow a1y[i++] = CondExpGt(a1x[0], a1zero, pow(a1inf_var, a1x[0]), a1zero); // sin a1y[i++] = CondExpGt(a1x[0], a1zero, sin(a1nan_var), a1zero); // sinh a1y[i++] = CondExpGt(a1x[0], a1zero, sinh(a1nan_var), a1zero); // sqrt a1y[i++] = CondExpGt(a1x[0], a1zero, sqrt(a1x[0]), a1zero); // sub a1y[i++] = CondExpGt(a1x[0], a1zero, a1inf_var - a1nan_var, a1zero); // tan a1y[i++] = CondExpGt(a1x[0], a1zero, tan(a1nan_var), a1zero); // tanh a1y[i++] = CondExpGt(a1x[0], a1zero, tanh(a1nan_var), a1zero); // azmul a1y[i++] = CondExpGt(a1x[0], a1zero, azmul(a1x[0], a1inf_var), a1zero); // // Operations that are C+11 atomic // // acosh a1y[i++] = CondExpGt(a1x[0], a1zero, acosh( a1x[0] ), a1zero); // asinh a1y[i++] = CondExpGt(a1x[0], a1zero, asinh( a1nan_var ), a1zero); // atanh a1y[i++] = CondExpGt(a1x[0], a1zero, atanh( a1one_var ), a1zero); // erf a1y[i++] = CondExpGt(a1x[0], a1zero, erf( a1nan_var ), a1zero); // expm1 a1y[i++] = CondExpGt(a1x[0], a1zero, expm1(a1nan_var), a1zero); // log1p a1y[i++] = CondExpGt(a1x[0], a1zero, log1p(- a1one_var ), a1zero); // ok &= i == m; CppAD::ADFun h; h.Dependent(a1x, a1y); // -------------------------------------------------------------------- // create g = f'(x) vector a1dy(m), a1w(m); a1x[0] = 2.0; for(i = 0; i < m; i++) a1w[i] = 0.0; // Independent(a1x); a1f.Forward(0, a1x); // for(i = 0; i < m; i++) { a1w[i] = 1.0; vector dyi_dx = a1f.Reverse(1, a1w); a1dy[i] = dyi_dx[0]; a1w[i] = 0.0; } CppAD::ADFun g; // g uses reverse mode derivatives g.Dependent(a1x, a1dy); // -------------------------------------------------------------------- // check case where x[0] > 0 vector x(1), dx(1), dg(m), dh(m); x[0] = 2.0; dx[0] = 1.0; h.Forward(0, x); dh = h.Forward(1, dx); // dh uses forward mode derivatives dg = g.Forward(0, x); for(i = 0; i < m; i++) ok &= NearEqual(dg[i], dh[i], eps, eps); // -------------------------------------------------------------------- // check case where x[0] = 0 x[0] = 0.0; dg = g.Forward(0, x); h.Forward(0, x); dh = h.Forward(1, dx); for(i = 0; i < m; i++) { ok &= dg[i] == 0.0; ok &= dh[i] == 0.0; } // -------------------------------------------------------------------- return ok; } ================================================ FILE: test_more/general/mul_cskip.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include // Test multiple level conditional skip where value of comparison is // uncertain during forward mode base Base value can be a variable. bool mul_cskip(void) { bool ok = true; using namespace CppAD; using CppAD::vector; typedef AD a1type; typedef AD a2type; size_t n = 2; size_t m = 1; vector x(n), y(m); x[0] = 0.0; x[1] = 1.0; // start recording a2type operations vector a2x(n), a2y(m); for (size_t j = 0; j < n; j++) a2x[j] = a2type( a1type(x[j]) ); Independent(a2x); // a1f(x) = x_0 * x_1 if x[0] == 1 // 0.0 otherwise a2type a2zero = a2type(0.0); a2type a2one = a2type(1.0); a2type a2p = a2x[0] * a2x[1]; a2y[0] = CondExpEq(a2x[0], a2one, a2p, a2zero); ADFun a1f(a2x, a2y); // Optimization will check to see if we can skip part of conditional // expression that is not used. a1f.optimize(); // f(x) = x_0 * x_1 if x[0] == 1 // 0.0 otherwise vector a1x(n), a1y(m); for (size_t j = 0; j < n; j++) a1x[j] = a1type(x[j]); Independent(a1x); a1y = a1f.Forward(0, a1x); CppAD::ADFun f(a1x, a1y); // check case where x[0] == 1 x[0] = 1.0; x[1] = 2.0; y = f.Forward(0, x); ok &= y[0] == x[1]; // check case where x[0] != 1 x[0] = 3.0; x[1] = 2.0; y = f.Forward(0, x); ok &= y[0] == 0.0; return ok; } ================================================ FILE: test_more/general/mul_eq.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Two old MulEq examples now used just for valiadation testing */ # include namespace { // BEGIN empty namespace bool MulEqTestOne(void) { bool ok = true; using namespace CppAD; // independent variable vector, indices, values, and declaration CPPAD_TESTVECTOR(AD) U(2); size_t s = 0; size_t t = 1; U[s] = 3.; U[t] = 2.; Independent(U); // dependent variable vector and indices CPPAD_TESTVECTOR(AD) Z(2); size_t x = 0; size_t y = 1; // some initial values AD zero = 0.; AD one = 1.; // dependent variable values Z[x] = U[s]; Z[y] = U[t]; Z[x] *= U[t]; // AD *= AD Z[y] *= 5.; // AD *= double zero *= Z[x]; one *= Z[x]; // check that zero is a parameter ok &= Parameter(zero); // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v( f.Domain() ); CPPAD_TESTVECTOR(double) w( f.Range() ); // check values ok &= ( Z[x] == 3. * 2. ); ok &= ( Z[y] == 2. * 5. ); ok &= ( zero == 0. ); ok &= ( one == Z[x] ); // forward computation of partials w.r.t. t v[s] = 0.; v[t] = 1.; w = f.Forward(1, v); ok &= ( w[x] == U[s] ); // dx/dt ok &= ( w[y] == 5. ); // dy/dt // reverse computation of second partials of x CPPAD_TESTVECTOR(double) r( f.Domain() * 2 ); w[x] = 1.; w[y] = 0.; r = f.Reverse(2, w); ok &= ( r[2 * s + 1] == 1. ); ok &= ( r[2 * t + 1] == 0. ); return ok; } bool MulEqTestTwo(void) { bool ok = true; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector double u0 = .5; CPPAD_TESTVECTOR(AD) U(1); U[0] = u0; Independent(U); // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); Z[0] = U[0]; // initial value Z[0] *= 2; // AD *= int Z[0] *= 4.; // AD *= double Z[0] *= U[0]; // AD *= AD // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v(1); CPPAD_TESTVECTOR(double) w(1); // check value ok &= NearEqual(Z[0] , u0*2*4*u0, eps99 , eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; v[0] = 1.; for(j = 1; j < p; j++) { double value; if( j == 1 ) value = 16. * u0; else if( j == 2 ) value = 16.; else value = 0.; jfac *= double(j); w = f.Forward(j, v); ok &= NearEqual(w[0], value/jfac, eps99, eps99); // d^jz/du^j v[0] = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r(p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; for(j = 0; j < p; j++) { double value; if( j == 0 ) value = 16. * u0; else if( j == 1 ) value = 16.; else value = 0.; ok &= NearEqual(r[j], value/jfac, eps99, eps99); // d^jz/du^j jfac *= double(j + 1); } return ok; } } // END empty namespace bool MulEq(void) { bool ok = true; ok &= MulEqTestOne(); ok &= MulEqTestTwo(); return ok; } ================================================ FILE: test_more/general/mul_level.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // CPPAD_HAS_* defines and CPPAD_COMPILER_IS_GNUCXX # include # if CPPAD_HAS_ADOLC // adolc examples should suppress conversion warnings # include // # include # include # include // adouble definitions not in Adolc distribution and // required in order to use CppAD::AD # include # endif # include # include namespace { // BEGIN empty namespace bool One(void) { bool ok = true; // initialize test result using CppAD::NearEqual; double eps = 10. * std::numeric_limits::epsilon(); typedef CppAD::AD ADdouble; // for one level of taping typedef CppAD::AD ADDdouble; // for two levels of taping size_t n = 2; // dimension for example // value of the independent variables CPPAD_TESTVECTOR(ADDdouble) aa_x(n); aa_x[0] = 1.; aa_x[1] = 3.; // test conversion double to AD< AD > aa_x[0] = 2. * aa_x[0]; // test double * AD< AD > CppAD::Independent(aa_x); // compute the function f(x) = 2 * x[0] * x[1] CPPAD_TESTVECTOR(ADDdouble) aa_f(1); aa_f[0] = 2. * aa_x[0] * aa_x[1]; CppAD::ADFun F(aa_x, aa_f); // value of the independent variables CPPAD_TESTVECTOR(ADdouble) a_x(n); a_x[0] = 2.; a_x[1] = 3.; Independent(a_x); // re-evaluate f(2, 3) (must get deepedence on a_x). size_t p = 0; CPPAD_TESTVECTOR(ADdouble) a_fp(1); a_fp = F.Forward(p, a_x); ok &= NearEqual(a_fp[0], 2. * a_x[0] * a_x[1], eps, eps); // compute the function g(x) = 2 * partial_x[0] f(x) = 4 * x[1] p = 1; CPPAD_TESTVECTOR(ADdouble) a_dx(n), a_g(1); a_dx[0] = 1.; a_dx[1] = 0.; a_fp = F.Forward(p, a_dx); a_g[0] = 2. * a_fp[0]; CppAD::ADFun G(a_x, a_g); // compute partial_x[1] g(x) CPPAD_TESTVECTOR(double) xp(n), gp(1); p = 0; xp[0] = 4.; xp[1] = 5.; gp = G.Forward(p, xp); ok &= NearEqual(gp[0], 4. * xp[1], eps, eps); p = 1; xp[0] = 0.; xp[1] = 1.; gp = G.Forward(p, xp); ok &= NearEqual(gp[0], 4., eps, eps); return ok; } // f(x) = |x|^2 = .5 * ( x[0]^2 + ... + x[n-1]^2 ) template Type f_Two(CPPAD_TESTVECTOR(Type) &x) { Type sum; // check assignment of AD< AD > = double sum = .5; sum += .5; size_t i = x.size(); while(i--) sum += x[i] * x[i]; // check compound assignment AD< AD > -= int sum -= 1; // check double * AD< AD > return .5 * sum; } bool Two(void) { bool ok = true; // initialize test result double eps99 = 99.0 * std::numeric_limits::epsilon(); typedef CppAD::AD ADdouble; // for one level of taping typedef CppAD::AD ADDdouble; // for two levels of taping size_t n = 5; // dimension for example size_t j; // a temporary index variable CPPAD_TESTVECTOR(double) x(n); CPPAD_TESTVECTOR(ADdouble) a_x(n); CPPAD_TESTVECTOR(ADDdouble) aa_x(n); // value of the independent variables for(j = 0; j < n; j++) a_x[j] = x[j] = double(j); // x[j] = j Independent(a_x); // a_x is indedendent for ADdouble for(j = 0; j < n; j++) aa_x[j] = a_x[j]; // track how aa_x depends on a_x CppAD::Independent(aa_x); // aa_x is independent for ADDdouble // compute function CPPAD_TESTVECTOR(ADDdouble) aa_f(1); // scalar valued function aa_f[0] = f_Two(aa_x); // has only one component // declare inner function (corresponding to ADDdouble calculation) CppAD::ADFun a_F(aa_x, aa_f); // compute f'(x) size_t p = 1; // order of derivative of a_F CPPAD_TESTVECTOR(ADdouble) a_w(1); // weight vector for a_F CPPAD_TESTVECTOR(ADdouble) a_df(n); // value of derivative a_w[0] = 1; // weighted function same as a_F a_df = a_F.Reverse(p, a_w); // gradient of f // declare outer function (corresponding to ADdouble calculation) CppAD::ADFun df(a_x, a_df); // compute the d/dx of f'(x) * v = f''(x) * v CPPAD_TESTVECTOR(double) v(n); CPPAD_TESTVECTOR(double) ddf_v(n); for(j = 0; j < n; j++) v[j] = double(n - j); ddf_v = df.Reverse(p, v); // f(x) = .5 * ( x[0]^2 + x[1]^2 + ... + x[n-1]^2 ) // f'(x) = (x[0], x[1], ... , x[n-1]) // f''(x) * v = ( v[0], v[1], ... , x[n-1] ) for(j = 0; j < n; j++) ok &= CppAD::NearEqual(ddf_v[j], v[j], eps99, eps99); return ok; } # if CPPAD_HAS_ADOLC bool adolc(void) { bool ok = true; // initialize test result double eps99 = 99.0 * std::numeric_limits::epsilon(); typedef adouble ADdouble; // for first level of taping typedef CppAD::AD ADDdouble; // for second level of taping size_t n = 5; // number independent variables CPPAD_TESTVECTOR(double) x(n); CPPAD_TESTVECTOR(ADdouble) a_x(n); CPPAD_TESTVECTOR(ADDdouble) aa_x(n); // value of the independent variables short tag = 0; // Adolc setup int keep = 1; trace_on(tag, keep); size_t j; for(j = 0; j < n; j++) { x[j] = double(j); // x[j] = j a_x[j] <<= x[j]; // a_x is independent for ADdouble } for(j = 0; j < n; j++) aa_x[j] = a_x[j]; // track how aa_x depends on a_x CppAD::Independent(aa_x); // aa_x is independent for ADDdouble // compute function CPPAD_TESTVECTOR(ADDdouble) aa_f(1); // scalar valued function aa_f[0] = f_Two(aa_x); // has only one component // declare inner function (corresponding to ADDdouble calculation) CppAD::ADFun a_F(aa_x, aa_f); // compute f'(x) size_t p = 1; // order of derivative of a_F CPPAD_TESTVECTOR(ADdouble) a_w(1); // weight vector for a_F CPPAD_TESTVECTOR(ADdouble) a_df(n); // value of derivative a_w[0] = 1; // weighted function same as a_F a_df = a_F.Reverse(p, a_w); // gradient of f // declare outer function // (corresponding to the tape of adouble operations) double df_j; for(j = 0; j < n; j++) a_df[j] >>= df_j; trace_off(); // compute the d/dx of f'(x) * v = f''(x) * v size_t m = n; // # dependent in f'(x) double *v = nullptr, *ddf_v = nullptr; v = CPPAD_TRACK_NEW_VEC(m, v); // track v = new double[m] ddf_v = CPPAD_TRACK_NEW_VEC(n, ddf_v); // track ddf_v = new double[n] for(j = 0; j < n; j++) v[j] = double(n - j); fos_reverse(tag, int(m), int(n), v, ddf_v); // f(x) = .5 * ( x[0]^2 + x[1]^2 + ... + x[n-1]^2 ) // f'(x) = (x[0], x[1], ... , x[n-1]) // f''(x) * v = ( v[0], v[1], ... , x[n-1] ) for(j = 0; j < n; j++) ok &= CppAD::NearEqual(ddf_v[j], v[j], eps99, eps99); CPPAD_TRACK_DEL_VEC(v); // check usage of delete CPPAD_TRACK_DEL_VEC(ddf_v); return ok; } # endif // CPPAD_HAS_ADOLC bool std_math(void) { bool ok = true; using CppAD::AD; using CppAD::Independent; using CppAD::ADFun; double eps = std::numeric_limits::epsilon(); typedef AD ADdouble; // for first level of taping typedef AD ADDdouble; // for second level of taping size_t n = 1; // number independent variables size_t m = 1; // number dependent and independent variables CPPAD_TESTVECTOR(double) x(n), y(m); CPPAD_TESTVECTOR(ADdouble) ax(n), ay(m); CPPAD_TESTVECTOR(ADDdouble) aax(n), aay(m); // create af(x) = tanh(x) aax[0] = 1.; Independent( aax ); aay[0] = tanh(aax[0]); ADFun af(aax, aay); // create g(x) = af(x) ax[0] = 1.; Independent( ax ); ay = af.Forward(0, ax); ADFun g(ax, ay); // evaluate h(x) = g(x) x[0] = 1.; y = g.Forward(0, x); // check result double check = tanh(x[0]); ok &= CppAD::NearEqual(y[0], check, eps, eps); return ok; } bool fabs(void) { bool ok = true; using CppAD::AD; using CppAD::Independent; using CppAD::ADFun; double eps = std::numeric_limits::epsilon(); typedef AD ADdouble; // for first level of taping typedef AD ADDdouble; // for second level of taping size_t n = 1; // number independent variables size_t m = 1; // number dependent and independent variables CPPAD_TESTVECTOR(double) x(n), y(m); CPPAD_TESTVECTOR(ADdouble) ax(n), ay(m); CPPAD_TESTVECTOR(ADDdouble) aax(n), aay(m); // create af(x) = fabs(x) aax[0] = 1.; Independent( aax ); aay[0] = fabs(aax[0]); ADFun af(aax, aay); // create g(x) = af'(x) ax[0] = 1.; Independent( ax ); ay = af.Jacobian(ax); ADFun g(ax, ay); // evaluate g(x) at same x as recording x[0] = 1.; y = g.Forward(0, x); // check result double check = 1.; ok &= CppAD::NearEqual(y[0], check, eps, eps); // evaluate g(x) at different x from recording // (but abs is an atomic operation so derivative should work) x[0] = -1.; y = g.Forward(0, x); // check result check = -1.; ok &= CppAD::NearEqual(y[0], check, eps, eps); return ok; } } // END empty namespace bool mul_level(void) { bool ok = true; ok &= One(); ok &= Two(); # if CPPAD_HAS_ADOLC ok &= adolc(); # endif ok &= std_math(); ok &= fabs(); return ok; } ================================================ FILE: test_more/general/mul_zdouble.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // Check that multilevel reverse with conditional expressions works properly // when using AD< AD >. # include namespace { using CppAD::AD; using CppAD::zdouble; using CppAD::ADFun; using CppAD::vector; typedef AD a1type; typedef AD a2type; typedef vector (*a2fun)(const vector& a2x); // zdouble eps = 10. * std::numeric_limits::epsilon(); size_t n_ = 2; size_t m_ = 1; // void record(a2fun fun, ADFun& g) { vector x(n_); vector a1x(n_), a1w(m_), a1z(m_ * n_); vector a2x(n_), a2y(m_); // for(size_t j = 0; j < n_; j++) { x[j] = 0.0; a1x[j] = a1type( x[j] ); a2x[j] = a2type( a1x[j] ); } Independent(a2x); // f(x) = x[0] / x[1] if x[1] > 0.0 else 0.0 a2y = fun(a2x); ADFun a1f; a1f.Dependent(a2x, a2y); // use reverse mode to calculate g(x) = f'(x) a1w[0] = a1type(1.0); Independent(a1x); a1f.Forward(0, a1x); a1z = a1f.Reverse(1, a1w); g.Dependent(a1x, a1z); // return; } // ---------------------------------------------------------------------- vector div(const vector& a2x) { vector a2y(m_); a2type a2zero = a2type(0.0); a2type a2four = a2type(4.0); a2y[0] = CondExpGt(a2x[1], a2zero, a2x[0] / a2x[1], a2zero); a2y[0] += CondExpGt(a2x[1], a2zero, a2four / a2x[1], a2zero); return a2y; } bool check_div(void) { bool ok = true; // record division operations ADFun g; record(div, g); vector x(n_), z(n_); // check result where x[1] <= 0.0 (would be nan without absolute zero) x[0] = 0.0; x[1] = 0.0; z = g.Forward(0, x); z = g.Forward(0, x); ok &= z[0] == 0.0; ok &= z[1] == 0.0; // check result where x[1] > 0.0 x[0] = 2.0; x[1] = 3.0; z = g.Forward(0, x); ok &= CppAD::NearEqual(z[0], 1.0/x[1], eps, eps); ok &= CppAD::NearEqual(z[1], - (x[0]+4.0)/(x[1]*x[1]), eps, eps); // return ok; } // ---------------------------------------------------------------------- vector mul(const vector& a2x) { vector a2y(m_); a2type a2zero = a2type(0.0); a2type a2four = a2type(4.0); a2y[0] = CondExpLt(a2x[0], a2four, a2x[0] * a2x[1], a2zero); a2y[0] += CondExpLt(a2x[0], a2four, a2four * a2x[1], a2zero); a2y[0] += CondExpLt(a2x[0], a2four, a2x[1] * a2four, a2zero); return a2y; } bool check_mul(void) { bool ok = true; // record multiplication operations ADFun g; record(mul, g); vector x(n_), z(n_); // check result where x[0] > 4 (would be nan without absolute zero) ok &= std::numeric_limits::has_infinity; x[0] = std::numeric_limits::infinity(); x[1] = 0.0; z = g.Forward(0, x); ok &= z[0] == 0.0; ok &= z[1] == 0.0; // check result where x[0] < 4 x[0] = 2.0; x[1] = 3.0; z = g.Forward(0, x); ok &= CppAD::NearEqual(z[0], x[1], eps, eps); ok &= CppAD::NearEqual(z[1], x[0]+8.0, eps, eps); // return ok; } // ---------------------------------------------------------------------- bool check_numeric_limits(void) { bool ok = true; // double double_eps = std::numeric_limits::epsilon(); zdouble zdouble_eps = CppAD::numeric_limits::epsilon(); ok &= double_eps == zdouble_eps; // double double_min = std::numeric_limits::min(); zdouble zdouble_min = CppAD::numeric_limits::min(); ok &= double_min == zdouble_min; // double double_max = std::numeric_limits::max(); zdouble zdouble_max = CppAD::numeric_limits::max(); ok &= double_max == zdouble_max; // return ok; } } bool mul_zdouble(void) { bool ok = true; ok &= check_div(); ok &= check_mul(); ok &= check_numeric_limits(); return ok; } ================================================ FILE: test_more/general/mul_zero_one.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Test the use of the special parameters zero and one with the multiply operator */ # include typedef CppAD::AD ADdouble; typedef CppAD::AD< ADdouble > ADDdouble; bool MulZeroOne(void) { using namespace CppAD; bool ok = true; size_t i; for(i = 0; i < 3; i++) { // run through the cases x = 0, 1, 2 size_t j; for(j = 0; j < 3; j++) { // run through the cases y = 0, 1, 2 CPPAD_TESTVECTOR( ADdouble ) x(1); x[0] = double(i); Independent(x); CPPAD_TESTVECTOR( ADDdouble ) y(1); y[0] = ADDdouble(j); Independent(y); CPPAD_TESTVECTOR( ADDdouble ) z(2); z[0] = x[0] * y[0]; z[1] = y[0] * x[0]; z[1] *= x[0]; // f(y) = z = { x * y , y * x * x } ADFun< ADdouble > f(y, z); CPPAD_TESTVECTOR( ADdouble ) u( f.Domain() ); CPPAD_TESTVECTOR( ADdouble ) v( f.Range() ); // v = f'(y) u[0] = ADdouble(1.); v = f.Forward(1, u); // check derivatives of f ok &= v[0] == x[0]; ok &= v[1] == x[0] * x[0]; // g(x) = f'(y) = {x , x * x} ADFun g(x, v); CPPAD_TESTVECTOR( double ) a( g.Domain() ); CPPAD_TESTVECTOR( double ) b( g.Range() ); // b = g'(x) a[0] = 1.; b = g.Forward(1, a); // check derivatives of g ok &= (b[0] == 1.); ok &= (b[1] == 2. * x[0]); } } return ok; } ================================================ FILE: test_more/general/near_equal_ext.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old example now just used for validation testing */ # include # include bool near_equal_ext(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; // double double x = 1.00000; double y = 1.00001; double a = .00005; double r = .00005; double zero = 0.; double inf = 1. / zero; double nan = 0. / zero; // AD AD X(x); AD Y(y); AD Inf(inf); AD Nan(nan); ok &= NearEqual(X, Y, zero, a); ok &= NearEqual(X, y, zero, a); ok &= NearEqual(x, Y, zero, a); ok &= ! NearEqual(X, Y, zero, a/25.); ok &= ! NearEqual(X, y, zero, a/25.); ok &= ! NearEqual(x, Y, zero, a/25.); ok &= NearEqual(X, Y, r, zero); ok &= NearEqual(X, y, r, zero); ok &= NearEqual(x, Y, r, zero); ok &= ! NearEqual(X, Y, r/25., zero); ok &= ! NearEqual(X, y, r/25., zero); ok &= ! NearEqual(x, Y, r/25., zero); ok &= ! NearEqual(Inf, Inf, r, a); ok &= ! NearEqual(Inf, inf, r, a); ok &= ! NearEqual(inf, Inf, r, a); ok &= ! NearEqual(-Inf, -Inf, r, a); ok &= ! NearEqual(-Inf, -inf, r, a); ok &= ! NearEqual(-inf, -Inf, r, a); ok &= ! NearEqual(Nan, Nan, r, a); ok &= ! NearEqual(Nan, nan, r, a); ok &= ! NearEqual(nan, Nan, r, a); return ok; } ================================================ FILE: test_more/general/neg.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* old example and test now only used for testing */ // BEGIN C++ # include bool Neg(void) { bool ok = true; using namespace CppAD; // independent variable vector, indices, values, and declaration CPPAD_TESTVECTOR(AD) U(2); size_t s = 0; size_t t = 1; U[s] = 3.; U[t] = 4.; Independent(U); // dependent variable vector and indices CPPAD_TESTVECTOR(AD) Z(1); size_t x = 0; // dependent variable values Z[x] = - U[t]; // - AD // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v( f.Domain() ); CPPAD_TESTVECTOR(double) w( f.Range() ); // check values ok &= ( Z[x] == -4. ); // forward computation of partials w.r.t. s v[s] = 1.; v[t] = 0.; w = f.Forward(1, v); ok &= ( w[x] == 0. ); // dx/ds // forward computation of partials w.r.t. t v[s] = 0.; v[t] = 1.; w = f.Forward(1, v); ok &= ( w[x] == -1. ); // dx/dt // reverse computation of second partials of z CPPAD_TESTVECTOR(double) r( f.Domain() * 2 ); w[x] = 1.; r = f.Reverse(2, w); ok &= ( r[2 * s + 1] == 0. ); // d^2 x / (ds ds) ok &= ( r[2 * t + 1] == 0. ); // d^2 x / (ds dt) return ok; } // END C++ ================================================ FILE: test_more/general/new_dynamic.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include namespace { // BEGIN_EMPTY_NAMESPACE // ---------------------------------------------------------------------------- // // pul_323 // Test that the with_no_op has the same number of dynamic parameters // (and variables) as without_no_op. bool pull_232_fun( bool with_no_op , CppAD::ADFun& f ) { // // ok bool ok = true; // // azero, aone CppAD::AD azero(0.0); CppAD::AD aone(1.0); // // ap, ax, ay CPPAD_TESTVECTOR( CppAD::AD ) ap(1), ax(1), ay(1); ap[0] = 2.0; ax[0] = 3.0; // // ax. ap CppAD::Independent(ax, ap); // // ay ay[0] = 0.0; if( with_no_op ) { ay[0] += ax[0] * ( ap[0] + azero ); ay[0] += ax[0] * ( ap[0] - azero ); ay[0] += ax[0] * ( ap[0] * aone ); ay[0] += ax[0] * ( ap[0] / aone ); ay[0] += azero; ay[0] -= azero; ay[0] *= aone; ay[0] /= aone; ay[0] += ax[0] * pow(ap[0] , azero); ay[0] += ax[0] + pow(azero, ap[0]); ay[0] += ax[0] + azmul(azero, ap[0]); ay[0] += ax[0] + azmul(ap[0], azero); } else { ay[0] += ax[0] * ap[0]; ay[0] += ax[0] * ap[0]; ay[0] += ax[0] * ap[0]; ay[0] += ax[0] * ap[0]; ay[0] += ax[0]; ay[0] += ax[0]; ay[0] += ax[0]; ay[0] += ax[0]; } // // f f.Dependent(ay); // // y, check CPPAD_TESTVECTOR(double) p(1), x(1), y(1); p[0] = 4.0; x[0] = 5.0; f.new_dynamic(p); y = f.Forward(0, x); double check = 0; check += x[0] * p[0]; check += x[0] * p[0]; check += x[0] * p[0]; check += x[0] * p[0]; check += x[0]; check += x[0]; check += x[0]; check += x[0]; // // ok ok &= y[0] == check; // return ok; } bool pull_232(void) { // // ok, no_op bool ok = true; // // with_no_op bool with_no_op; // // ok, f_without CppAD::ADFun f_without; with_no_op = false; ok &= pull_232_fun(with_no_op, f_without); // // ok, f_with CppAD::ADFun f_with; with_no_op = true; ok &= pull_232_fun(with_no_op, f_with); // // ok ok &= f_without.size_dyn_par() == f_with.size_dyn_par(); ok &= f_without.size_var() == f_with.size_var(); // return ok; } // ---------------------------------------------------------------------------- bool vecad(void) { bool ok = true; using CppAD::AD; // dynamic parameter vector size_t np = 2; size_t nx = 1; size_t ny = 2; CPPAD_TESTVECTOR(AD) ap(np), ax(nx), ay(ny); ap[0] = 0.0; ap[1] = 1.0; ax[0] = 2.0; // declare independent variables, dynamic parameters, start recording CppAD::Independent(ax, ap); // Create a VecAD object size_t nv = 2; CppAD::VecAD av(nv); // ap[0] is either 0 or 1 av[ ap[0] ] = ap[1]; av[ 1.0 - ap[0] ] = 2.0 * ap[1]; // create f: x -> y and stop tape recording CppAD::AD zero(0.0), one(1.0); ay[0] = av[zero]; ay[1] = av[one]; CppAD::ADFun f(ax, ay); // zero order forward mode with p[0] = 0 CPPAD_TESTVECTOR(double) p(np), x(nx), y(ny); p[0] = 0.0; p[1] = 1.0; x[0] = 2.0; f.new_dynamic(p); y = f.Forward(0, x); ok &= y[0] == p[1]; ok &= y[1] == 2.0 * p[1]; // zero order forward mode with p[0] = 1 p[0] = 1.0; p[1] = 2.0; x[0] = 3.0; f.new_dynamic(p); y = f.Forward(0, x); ok &= y[0] == 2.0 * p[1]; ok &= y[1] == p[1]; CPPAD_TESTVECTOR(double) dx(nx), dy(ny); dx[0] = 1.0; dy = f.Forward(1, dx); ok &= dy[0] == 0.0; ok &= dy[1] == 0.0; return ok; } // ---------------------------------------------------------------------------- bool operator_with_variable(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; using CppAD::azmul; using CppAD::CondExpLt; double eps99 = 99.0 * std::numeric_limits::epsilon(); size_t n = 11; // dynamic parameter vector CPPAD_TESTVECTOR(AD) adynamic(n); for(size_t j = 0; j < n; ++j) adynamic[j] = 2.0; // domain space vector CPPAD_TESTVECTOR(AD) ax(n); for(size_t j = 0; j < n; ++j) ax[j] = AD(j + 1); // declare independent variables, dynamic parameters, starting recording CppAD::Independent(ax, adynamic); // range space vector CPPAD_TESTVECTOR(AD) ay(n); size_t k = 0; ay[k] = -1.0*double(k+1)*(adynamic[k] + ax[k]) * (ax[k] + adynamic[k]); ++k; ay[k] = -1.0*double(k+1)*(adynamic[k] - ax[k]) * (ax[k] - adynamic[k]); ++k; ay[k] = -1.0*double(k+1)*(adynamic[k] * ax[k]) + (ax[k] * adynamic[k]); ++k; ay[k] = -1.0*double(k+1)*(adynamic[k] / ax[k]) + (ax[k] / adynamic[k]); ++k; ay[k] = ax[k]; ay[k] += adynamic[k]; ++k; ay[k] = adynamic[k]; ay[k] -= ax[k]; ++k; ay[k] = ax[k]; ay[k] *= adynamic[k]; ++k; ay[k] = adynamic[k]; ay[k] /= ax[k]; ++k; ay[k] = pow(ax[k], adynamic[k]) + pow(adynamic[k], ax[k]); ++k; ay[k] = azmul(ax[k], adynamic[k]) + azmul(adynamic[k], ax[k]); ++k; ay[k] = CondExpLt(ax[k], adynamic[k], ax[k], adynamic[k]); ++k; ok &= size_t(k) == n; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // zero order forward mode CPPAD_TESTVECTOR(double) x(n), y(n); for(size_t j = 0; j < n; ++j) x[j] = double(j + 1); y = f.Forward(0, x); double check; k = 0; check = ( Value(adynamic[k]) + x[k] ) * ( x[k] + Value(adynamic[k]) ); check *= -1.0*double(k+1); ok &= NearEqual(y[k] , check, eps99, eps99); ++k; check = ( Value(adynamic[k]) - x[k] ) * ( x[k] - Value(adynamic[k]) ); check *= -1.0*double(k+1); ok &= NearEqual(y[k] , check, eps99, eps99); ++k; check = -1.0*double(k+1)*( Value(adynamic[k]) * x[k] ); check += ( x[k] * Value(adynamic[k]) ); ok &= NearEqual(y[k] , check, eps99, eps99); ++k; check = -1.0*double(k+1)*( Value(adynamic[k]) / x[k] ); check += ( x[k] / Value(adynamic[k]) ); ok &= NearEqual(y[k] , check, eps99, eps99); ++k; check = x[k] + Value(adynamic[k]); ok &= NearEqual(y[k] , check, eps99, eps99); ++k; check = Value(adynamic[k]) - x[k]; ok &= NearEqual(y[k] , check, eps99, eps99); ++k; check = x[k] * Value(adynamic[k]); ok &= NearEqual(y[k] , check, eps99, eps99); ++k; check = Value(adynamic[k]) / x[k]; ok &= NearEqual(y[k] , check, eps99, eps99); ++k; check = std::pow(x[k], Value(adynamic[k])); check += std::pow(Value(adynamic[k]), x[k]); ok &= NearEqual(y[k] , check, eps99, eps99); ++k; check = azmul(x[k], Value(adynamic[k])) + azmul(Value(adynamic[k]), x[k]); ok &= NearEqual(y[k] , check, eps99, eps99); ++k; check = CondExpLt( x[k], Value(adynamic[k]), x[k], Value(adynamic[k]) ); ok &= NearEqual(y[k] , check, eps99, eps99); ++k; ok &= size_t(k) == n; // change the dynamic parameter values CPPAD_TESTVECTOR(double) dynamic(n); for(size_t j = 0; j < n; j++) dynamic[j] = double(2 * j + 1); f.new_dynamic(dynamic); // y = f.Forward(0, x); k = 0; check = -1.0*double(k+1)*( dynamic[k] + x[k] ) * ( x[k] + dynamic[k] ); ok &= NearEqual(y[k] , check, eps99, eps99); ++k; check = -1.0*double(k+1)*( dynamic[k] - x[k] ) * ( x[k] - dynamic[k] ); ok &= NearEqual(y[k] , check, eps99, eps99); ++k; check = -1.0*double(k+1)*( dynamic[k] * x[k] ) + ( x[k] * dynamic[k] ); ok &= NearEqual(y[k] , check, eps99, eps99); ++k; check = -1.0*double(k+1)*( dynamic[k] / x[k] ) + ( x[k] / dynamic[k] ); ok &= NearEqual(y[k] , check, eps99, eps99); ++k; check = x[k] + dynamic[k]; ok &= NearEqual(y[k] , check, eps99, eps99); ++k; check = dynamic[k] - x[k]; ok &= NearEqual(y[k] , check, eps99, eps99); ++k; check = x[k] * dynamic[k]; ok &= NearEqual(y[k] , check, eps99, eps99); ++k; check = dynamic[k] / x[k]; ok &= NearEqual(y[k] , check, eps99, eps99); ++k; check = std::pow(x[k], dynamic[k]); check += std::pow(dynamic[k], x[k]); ok &= NearEqual(y[k] , check, eps99, eps99); ++k; check = azmul(x[k], dynamic[k]) + CppAD::azmul(dynamic[k], x[k]); ok &= NearEqual(y[k] , check, eps99, eps99); ++k; check = CondExpLt(x[k], dynamic[k], x[k], dynamic[k]); ok &= NearEqual(y[k] , check, eps99, eps99); ++k; ok &= size_t(k) == n; // return ok; } // ---------------------------------------------------------------------------- bool dynamic_operator(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; using CppAD::azmul; using CppAD::sign; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent dynamic parameter vector size_t nd = 1; CPPAD_TESTVECTOR(AD) adynamic(nd); adynamic[0] = 0.5; // domain space vector size_t nx = 1; CPPAD_TESTVECTOR(AD) ax(nx); ax[0] = 0.25; // declare independent variables, dynamic parameters, starting recording CppAD::Independent(ax, adynamic); // range space vector size_t ny = 27; ny += 6; CPPAD_TESTVECTOR(AD) ay(ny); size_t k = 0; // ---------------------------------------------------------- // 98 standard math ay[k] = acos(adynamic[0]); ++k; ay[k] = asin(adynamic[0]); ++k; ay[k] = atan(adynamic[0]); ++k; ay[k] = cos(adynamic[0]); ++k; ay[k] = cosh(adynamic[0]); ++k; ay[k] = exp(adynamic[0]); ++k; ay[k] = acos(adynamic[0]); ++k; ay[k] = log(adynamic[0]); ++k; ay[k] = sin(adynamic[0]); ++k; ay[k] = sinh(adynamic[0]); ++k; ay[k] = sqrt(adynamic[0]); ++k; ay[k] = tan(adynamic[0]); ++k; ay[k] = tanh(adynamic[0]); ++k; // ---------------------------------------------------------- // 2011 standard math ay[k] = asinh(adynamic[0]); ++k; ay[k] = acosh(adynamic[0] + 1.0); ++k; ay[k] = atanh(adynamic[0]); ++k; ay[k] = expm1(adynamic[0]); ++k; ay[k] = erf(adynamic[0]); ++k; ay[k] = log1p(adynamic[0]); ++k; // ---------------------------------------------------------- // binary ay[k] = 2.0 + adynamic[0]; ++k; ay[k] = 2.0; ay[k] += adynamic[0]; // constant += dynamic ++k; ay[k] = adynamic[0] / 2.0; ++k; ay[k] = adynamic[0]; ay[k] /= 2.0; // dynamic /= constant ++k; ay[k] = 2.0 * adynamic[0]; ++k; ay[k] = 2.0; ay[k] *= adynamic[0]; // constant *= dynamic ++k; ay[k] = adynamic[0] - 2.0; ++k; ay[k] = adynamic[0]; ay[k] -= 2.0; // dynamic -= constant ++k; // ---------------------------------------------------------- // other ay[k] = - adynamic[0]; ++k; ay[k] = fabs(adynamic[0]); ++k; ay[k] = pow(adynamic[0], 2.0); ++k; ay[k] = sign(adynamic[0]); ++k; ay[k] = azmul(2.0, adynamic[0]); ++k; // if dynamic[0] == 0.5 then ay[k] = 1.0 else ay[k] = -1.0 ay[k] = CppAD::CondExpEq( adynamic[0], AD(0.5), AD(1.0), AD(-1.0) ); ++k; // ---------------------------------------------------------- ok &= size_t(k) == ny; // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // change the dynamic parameter values CPPAD_TESTVECTOR(double) dynamic(nd); dynamic[0] = 0.75; f.new_dynamic(dynamic); // CPPAD_TESTVECTOR(double) x(nx), y(ny); y = f.Forward(0, x); k = 0; // ---------------------------------------------------------- // 98 standard math double check = std::acos(dynamic[0]); ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = std::asin(dynamic[0]); ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = std::atan(dynamic[0]); ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = std::cos(dynamic[0]); ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = std::cosh(dynamic[0]); ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = std::exp(dynamic[0]); ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = std::acos(dynamic[0]); ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = std::log(dynamic[0]); ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = std::sin(dynamic[0]); ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = std::sinh(dynamic[0]); ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = std::sqrt(dynamic[0]); ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = std::tan(dynamic[0]); ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = std::tanh(dynamic[0]); ok &= NearEqual(y[k], check, eps99, eps99); ++k; // ---------------------------------------------------------- // 2011 standard math check = asinh(dynamic[0]); ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = std::acosh(dynamic[0] + 1.0); ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = std::atanh(dynamic[0]); ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = std::expm1(dynamic[0]); ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = std::erf(dynamic[0]); ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = std::log1p(dynamic[0]); ok &= NearEqual(y[k], check, eps99, eps99); ++k; // ---------------------------------------------------------- // binary check = 2.0 + dynamic[0]; ok &= NearEqual(y[k], check, eps99, eps99); ++k; ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = dynamic[0] / 2.0; ok &= NearEqual(y[k], check, eps99, eps99); ++k; ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = 2.0 * dynamic[0]; ok &= NearEqual(y[k], check, eps99, eps99); ++k; ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = dynamic[0] - 2.0; ok &= NearEqual(y[k], check, eps99, eps99); ++k; ok &= NearEqual(y[k], check, eps99, eps99); ++k; // ---------------------------------------------------------- // other check = - dynamic[0]; ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = std::fabs(dynamic[0]); ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = std::pow(dynamic[0], 2.0); ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = CppAD::sign(dynamic[0]); ok &= NearEqual(y[k], check, eps99, eps99); ++k; check = azmul(2.0, dynamic[0]); ok &= NearEqual(y[k], check, eps99, eps99); ++k; // if dynamic[0] == 0.5 then ay[k] = 1.0 else ay[k] = -1.0 check = CppAD::CondExpEq(dynamic[0], 0.5, 1.0, -1.0); ++k; // ---------------------------------------------------------- ok &= k == ny; // return ok; } // ---------------------------------------------------------------------------- bool dynamic_compare(void) { bool ok = true; using CppAD::AD; // independent dynamic parameter vector size_t nd = 1; CPPAD_TESTVECTOR(AD) adynamic(nd); // domain space vector size_t nx = 1; CPPAD_TESTVECTOR(AD) ax(nx); ax[0] = 0.0; // value does not matter for this example; // range space vector size_t ny = 2; CPPAD_TESTVECTOR(AD) ay(ny); // not using abort_op_index, are recording compare operators size_t abort_op_index = 0; bool record_compare = true; // Function object CppAD::ADFun f; // vectors used for new_dynamic and Forward. CPPAD_TESTVECTOR(double) dynamic(nd), x(nx), y(ny); // ---------------------------------------------------------- // operators: ==, != adynamic[0] = 0.5; CppAD::Independent(ax, abort_op_index, record_compare, adynamic); // // == if( adynamic[0] == 0.5 ) ay[0] = 1.0; else ay[0] = 0.0; // // != if( adynamic[0] != 0.5 ) ay[1] = 1.0; else ay[1] = 0.0; // create f: x -> y and stop tape recording f.Dependent(ax, ay); // dynamic[0] = Value( adynamic[0] ); f.new_dynamic(dynamic); y = f.Forward(0, x); ok = f.compare_change_number() == 0; // dynamic[0] = 1.0; f.new_dynamic(dynamic); y = f.Forward(0, x); ok = f.compare_change_number() == 2; // ---------------------------------------------------------- // operators: <, <= adynamic[0] = 0.5; CppAD::Independent(ax, abort_op_index, record_compare, adynamic); // // < if( adynamic[0] < 0.5 ) ay[0] = 0.0; else ay[0] = 1.0; // // <= if( adynamic[0] <= 0.5 ) ay[1] = 1.0; else ay[1] = 0.0; // create f: x -> y and stop tape recording f.Dependent(ax, ay); // dynamic[0] = Value( adynamic[0] ); f.new_dynamic(dynamic); y = f.Forward(0, x); ok = f.compare_change_number() == 0; // dynamic[0] = 1.0; f.new_dynamic(dynamic); y = f.Forward(0, x); ok = f.compare_change_number() == 1; // ---------------------------------------------------------- // operators: >, >= adynamic[0] = 0.5; CppAD::Independent(ax, abort_op_index, record_compare, adynamic); // // > if( adynamic[0] > 0.4 ) ay[0] = 1.0; else ay[0] = 0.0; // // >= if( adynamic[0] >= 0.4 ) ay[1] = 1.0; else ay[1] = 0.0; // create f: x -> y and stop tape recording f.Dependent(ax, ay); // dynamic[0] = Value( adynamic[0] ); f.new_dynamic(dynamic); y = f.Forward(0, x); ok = f.compare_change_number() == 0; // dynamic[0] = 0.3; f.new_dynamic(dynamic); y = f.Forward(0, x); ok = f.compare_change_number() == 2; // // ---------------------------------------------------------- return ok; } // ---------------------------------------------------------------------------- bool optimize_csum(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent dynamic parameter vector size_t num_dyn_ind = 2; CPPAD_TESTVECTOR(AD) adynamic(num_dyn_ind); // domain space vector size_t nx = 1; CPPAD_TESTVECTOR(AD) ax(nx); ax[0] = 0.0; // value does not matter for this example; // range space vector size_t ny = 2; CPPAD_TESTVECTOR(AD) ay(ny); // not using abort_op_index, are recording compare operators size_t abort_op_index = 0; bool record_compare = true; // Function object CppAD::ADFun f; // ---------------------------------------------------------- // record f adynamic[0] = 1.0; adynamic[1] = 2.0; CppAD::Independent(ax, abort_op_index, record_compare, adynamic); // // create three constant parameters that are only used in a cumulative // summation and will be optimized to just one constant parameter ay[0] = 3.0 - ax[0]; ay[0] = ay[0] - 4.0; ay[0] = ay[0] + 5.0; // // create a dependent dynamic parameter that is not used AD adp1 = adynamic[0] + adynamic[1]; // // create a dependent dynamic parameter that is used AD adp2 = adynamic[1] - adynamic[0]; ay[1] = ax[0] + adp2; // // create f: x -> y and stop tape recording f.Dependent(ax, ay); ok &= f.size_dyn_ind() == num_dyn_ind; ok &= f.size_dyn_par() == num_dyn_ind + 2; // constant parameters: nan, 3.0, 4.0, 5.0 ok &= f.size_par() == num_dyn_ind + 2 + 4; // ------------------------------------------------------------- // vectors used for new_dynamic and Forward. CPPAD_TESTVECTOR(double) dynamic(num_dyn_ind), x(nx), y(ny); dynamic[0] = 3.0; dynamic[1] = 4.0; f.new_dynamic(dynamic); x[0] = 5.0; y = f.Forward(0, x); double check = 3.0 - x[0] - 4.0 + 5.0; ok &= NearEqual(y[0], check, eps99, eps99); check = x[0] + dynamic[1] - dynamic[0]; ok &= NearEqual(y[1], check, eps99, eps99); // ------------------------------------------------------------- // optimize and re-test f.optimize(); // add the zero parameter ok &= f.size_dyn_ind() == num_dyn_ind; ok &= f.size_dyn_par() == num_dyn_ind + 1; // constant parameters: nan, zero, (3.0 - 4.0 + 5.0) ok &= f.size_par() == num_dyn_ind + 1 + 3; dynamic[0] = 0.3; dynamic[1] = 0.4; f.new_dynamic(dynamic); x[0] = 0.5; y = f.Forward(0, x); check = 3.0 - x[0] - 4.0 + 5.0; ok &= NearEqual(y[0], check, eps99, eps99); check = x[0] + dynamic[1] - dynamic[0]; ok &= NearEqual(y[1], check, eps99, eps99); // return ok; } // ---------------------------------------------------------------------------- typedef CPPAD_TESTVECTOR( CppAD::AD ) ADvector; void g_algo(const ADvector& ax, ADvector& ay) { ay[0] = ax[0] * ax[1]; } bool dynamic_atomic(void) { bool ok = true; // checkpoint version of g(x) = x[0] * x[1]; ADvector ax(2), ay(1); ax[0] = 2.0; ax[1] = 3.0; CppAD::checkpoint atom_g("g_algo", g_algo, ax, ay); // record the function f(x) = dynamic[0] * dynamic[1] // using atom_g to compute the product ADvector adynamic(2); adynamic[0] = 1.0; adynamic[1] = 2.0; size_t abort_op_index = 0; bool record_compare = true; Independent(ax, abort_op_index, record_compare, adynamic); atom_g(adynamic, ay); CppAD::ADFun f(ax, ay); // change the dynamic parameter values CPPAD_TESTVECTOR(double) x(2), y(1), dynamic(2); dynamic[0] = 4.0; dynamic[1] = 5.0; f.new_dynamic(dynamic); // check zero order forward x[0] = std::numeric_limits::quiet_NaN(); x[1] = std::numeric_limits::quiet_NaN(); y = f.Forward(0, x); ok &= y[0] == dynamic[0] * dynamic[1]; return ok; } // ---------------------------------------------------------------------------- double n_digits(const double& x) { double logx = std::log10(x); double ret = floor(logx) + 1.0; return ret; } CPPAD_DISCRETE_FUNCTION(double, n_digits) // bool dynamic_discrete(void) { bool ok = true; double eps99 = 99.0 * std::numeric_limits::epsilon(); // record the function f(x) = x[0] * n_digits( dynamic[0] ) ADvector ax(1); ax[0] = 2.0; ADvector adynamic(1); adynamic[0] = 3.0; size_t abort_op_index = 0; bool record_compare = true; Independent(ax, abort_op_index, record_compare, adynamic); ADvector ay(1); ay[0] = ax[0] * n_digits( adynamic[0] ); CppAD::ADFun f(ax, ay); // change the dynamic parameter value to 14 CPPAD_TESTVECTOR(double) x(1), y(1), dynamic(1); dynamic[0] = 14.0; f.new_dynamic(dynamic); // check zero order forward x[0] = 3.0; y = f.Forward(0, x); ok &= CppAD::NearEqual(y[0], x[0] * 2.0, eps99, eps99); return ok; } // ---------------------------------------------------------------------------- bool dynamic_optimize(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent dynamic parameter vector size_t nd = 1; CPPAD_TESTVECTOR(AD) adynamic(nd); // domain space vector size_t nx = 1; CPPAD_TESTVECTOR(AD) ax(nx); ax[0] = 0.0; // value does not matter for this example; // range space vector size_t ny = 2; CPPAD_TESTVECTOR(AD) ay(ny); // not using abort_op_index, are recording compare operators size_t abort_op_index = 0; bool record_compare = true; // Function object CppAD::ADFun f; // ---------------------------------------------------------- // record f adynamic[0] = 1.0; CppAD::Independent(ax, abort_op_index, record_compare, adynamic); // // Avoid cumulative summations because it adds constant parameters. // Use one constant parameter, namely 3.0. // Create five dynamic parameters where the second, fourth, and fifth, // should be optimized out. // // first extra dynamic parameter (also is in optimized tape) AD first = 3.0 + adynamic[0]; // // second that is the same as first because addition is communative AD second = adynamic[0] + 3.0; // // third extra dynamic parameter (also in optimized tape) AD third = first * first; // // fourth is same as third and uses second which gets optimized out AD fourth = second * second; // // fifth is not used and so should be optimized out AD fifth = first / 3.0; // ay[0] = ax[0] + third; ay[1] = ax[0] * fourth; // // create f: x -> y and stop tape recording f.Dependent(ax, ay); ok &= f.size_dyn_ind() == nd; ok &= f.size_dyn_par() == nd + 5; // constant parameters: nan, 3.0 ok &= f.size_par() == nd + 5 + 2; // ------------------------------------------------------------- // vectors used for new_dynamic and Forward. CPPAD_TESTVECTOR(double) dynamic(nd), x(nx), y(ny); dynamic[0] = 4.0; f.new_dynamic(dynamic); x[0] = 5.0; y = f.Forward(0, x); double check = x[0] + (3.0 + dynamic[0] ) * (3.0 + dynamic[0]); ok &= NearEqual(y[0], check, eps99, eps99); check = x[0] * (3.0 + dynamic[0] ) * (3.0 + dynamic[0]); ok &= NearEqual(y[1], check, eps99, eps99); // ------------------------------------------------------------- // optimize and re-test f.optimize(); // adds the zero parameter ok &= f.size_dyn_ind() == nd; ok &= f.size_dyn_par() == nd + 2; // constant parameters: nan, zero, 3.0 ok &= f.size_par() == nd + 2 + 3; dynamic[0] = 6.0; f.new_dynamic(dynamic); y = f.Forward(0, x); check = x[0] + (3.0 + dynamic[0]) * (3.0 + dynamic[0]); ok &= NearEqual(y[0], check, eps99, eps99); check = x[0] * (3.0 + dynamic[0]) * (3.0 + dynamic[0]); ok &= NearEqual(y[1], check, eps99, eps99); // return ok; } } // END_EMPTY_NAMESPACE // ---------------------------------------------------------------------------- bool new_dynamic(void) { bool ok = true; ok &= pull_232(); ok &= vecad(); ok &= operator_with_variable(); ok &= dynamic_operator(); ok &= dynamic_compare(); ok &= optimize_csum(); ok &= dynamic_atomic(); ok &= dynamic_discrete(); ok &= dynamic_optimize(); // return ok; } ================================================ FILE: test_more/general/num_limits.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* old num_limits.cpp example / test $spell $$ $section Numeric Limits: Example and Test$$ $index limits$$ $index example, limits$$ $index test, limits$$ $head Assumption$$ This code assumes that the decimal point is infront of the mantissa. Hence dividing the minimum normalized value looses precision, while multiplying the maximum normalized value results in infinity. $head Externals$$ This example using external routines to get and set values so that the compiler does not set the correspdong code and optimize it out. old verbatim%example/num_limits.cpp%0%// BEGIN C++%// END C++%1%$$ $end */ // BEGIN C++ // Complex examples should suppress conversion warnings # include # ifdef _MSC_VER // Suppress Microsoft compiler warning about possible loss of precision, // in the constructors (when converting to std::complex) // Type one = 1 // Type two = 2 // 1 and 2 are small enough so no loss of precision when converting to float. # pragma warning(disable:4244) # endif # include # include # include "extern_value.hpp" namespace { using CppAD::vector; using CppAD::abs_geq; template Type add_one(const Type& value) { return( Type(1) + value ); } // ----------------------------------------------------------------- template bool check_epsilon(void) { bool ok = true; typedef extern_value value; value eps( CppAD::numeric_limits::epsilon() ); value one( Type(1) ); value two( Type(2) ); value tmp( Type(0) ); // tmp.set( add_one( eps.get() / two.get() ) ); ok &= one.get() == tmp.get(); // tmp.set( add_one( eps.get() ) ); ok &= one.get() != tmp.get(); return ok; } // ----------------------------------------------------------------- template bool check_min(void) { bool ok = true; typedef extern_value value; value min( CppAD::numeric_limits::min() ); value eps3( Type(3) * CppAD::numeric_limits::epsilon() ); value one( Type(1) ); value hun( Type(100) ); value tmp( Type(0) ); // tmp.set( min.get() / hun.get() ); tmp.set( tmp.get() * hun.get() ); ok &= abs_geq(tmp.get()/min.get() - one.get(), eps3.get()); // tmp.set( min.get() * hun.get() ); tmp.set( tmp.get() / hun.get() ); ok &= ! abs_geq(tmp.get()/min.get() - one.get(), eps3.get()); return ok; } // ----------------------------------------------------------------- template bool check_max(void) { bool ok = true; typedef extern_value value; value max2( CppAD::numeric_limits::max() / Type(2) ); value eps3( Type(3) * CppAD::numeric_limits::epsilon() ); value one( Type(1) ); value hun( Type(100) ); value tmp( Type(0) ); // In complex case, this operation can result in (inf, 0) tmp.set( max2.get() * hun.get() ); // In complex case, this operaiotn can result in (inf,-nan) // (where nan corresponds to inf * 0) tmp.set( tmp.get() / hun.get() ); if( ! CppAD::isnan( tmp.get() ) ) ok &= abs_geq( tmp.get() / max2.get() - one.get(), eps3.get() ); // tmp.set( max2.get() / hun.get() ); tmp.set( tmp.get() * hun.get() ); ok &= ! abs_geq(tmp.get() / max2.get() - one.get(), eps3.get() ); return ok; } // ----------------------------------------------------------------- template bool check_quiet_NaN(void) { bool ok = true; typedef extern_value value; value nan( CppAD::numeric_limits::quiet_NaN() ); value same( nan.get() ); // ok &= nan.get() != same.get(); ok &= ! (nan.get() == same.get() ); // return ok; } // ----------------------------------------------------------------- template bool check_infinity(void) { bool ok = true; typedef extern_value value; value inf( CppAD::numeric_limits::infinity() ); value hun( Type(100) ); value tmp( Type(0) ); tmp.set( inf.get() + hun.get() ); ok &= inf.get() == tmp.get(); tmp.set( inf.get() - inf.get() ); ok &= CppAD::isnan( tmp.get() ); return ok; } } bool num_limits(void) { bool ok = true; using CppAD::AD; // ------------------------------------------------------------------- // epsilon for Base types defined by CppAD ok &= check_epsilon(); ok &= check_epsilon(); ok &= check_epsilon< std::complex >(); ok &= check_epsilon< std::complex >(); // epsilon for some AD types. ok &= check_epsilon< AD >(); ok &= check_epsilon< AD >(); ok &= check_epsilon< AD > >(); ok &= check_epsilon< AD > >(); // ------------------------------------------------------------------- // min for Base types defined by CppAD ok &= check_min(); ok &= check_min(); ok &= check_min< std::complex >(); ok &= check_min< std::complex >(); // min for some AD types. ok &= check_min< AD >(); ok &= check_min< AD >(); ok &= check_min< AD > >(); ok &= check_min< AD > >(); // ------------------------------------------------------------------- // max for Base types defined by CppAD ok &= check_max(); ok &= check_max(); ok &= check_max< std::complex >(); ok &= check_max< std::complex >(); // max for some AD types. ok &= check_max< AD >(); ok &= check_max< AD >(); ok &= check_max< AD< std::complex > >(); ok &= check_max< AD< std::complex > >(); // ------------------------------------------------------------------- // quiet_NaN for Base types defined by CppAD ok &= check_quiet_NaN(); ok &= check_quiet_NaN(); ok &= check_quiet_NaN< std::complex >(); ok &= check_quiet_NaN< std::complex >(); // quiet_NaN for some AD types. ok &= check_quiet_NaN< AD >(); ok &= check_quiet_NaN< AD >(); ok &= check_quiet_NaN< AD< std::complex > >(); ok &= check_quiet_NaN< AD< std::complex > >(); // ------------------------------------------------------------------- // infinity for Base types defined by CppAD ok &= check_infinity(); ok &= check_infinity(); ok &= check_infinity< std::complex >(); ok &= check_infinity< std::complex >(); // infinity for some AD types. ok &= check_infinity< AD >(); ok &= check_infinity< AD >(); ok &= check_infinity< AD< std::complex > >(); ok &= check_infinity< AD< std::complex > >(); return ok; } // END C++ ================================================ FILE: test_more/general/ode_err_control.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include // for size_t # include // for exp # include // CppAD::OdeErrControl # include // CppAD::NearEqual # include // CppAD::vector # include // CppAD::Runge45 /* ------------------------------------------------------------------------- Test relative error with zero initial conditions. (Uses minimum step size to integrate). */ namespace { // -------------------------------------------------------------- class Fun_one { private: size_t n; // dimension of the state space public: // constructor Fun_one(size_t n_) : n(n_) { } // given x(0) = 0 // solution is x_i (t) = t^(i+1) void Ode( const double &t, const CppAD::vector &x, CppAD::vector &f) { size_t i; f[0] = 1.; for(i = 1; i < n; i++) f[i] = double(i+1) * x[i-1]; } }; // -------------------------------------------------------------- class Method_one { private: Fun_one F; public: // constructor Method_one(size_t n_) : F(n_) { } void step( double ta, double tb, CppAD::vector &xa , CppAD::vector &xb , CppAD::vector &eb ) { xb = CppAD::Runge45(F, 1, ta, tb, xa, eb); } size_t order(void) { return 4; } }; } bool OdeErrControl_one(void) { bool ok = true; // initial return value using CppAD::NearEqual; // Runge45 should yield exact results for x_i (t) = t^(i+1), i < 4 size_t n = 6; // construct method for n component solution Method_one method(n); // inputs to OdeErrControl double ti = 0.; double tf = .9; double smin = 1e-2; double smax = 1.; double scur = .5; double erel = 1e-7; CppAD::vector xi(n); CppAD::vector eabs(n); size_t i; for(i = 0; i < n; i++) { xi[i] = 0.; eabs[i] = 0.; } // outputs from OdeErrControl CppAD::vector ef(n); CppAD::vector xf(n); xf = OdeErrControl(method, ti, tf, xi, smin, smax, scur, eabs, erel, ef); double check = 1.; for(i = 0; i < n; i++) { check *= tf; ok &= NearEqual(check, xf[i], erel, 0.); } return ok; } /* Old example now just a test Define $latex X : \B{R} \rightarrow \B{R}^2$$ by $latex \[ \begin{array}{rcl} X_0 (t) & = & - \exp ( - w_0 t ) \\ X_1 (t) & = & \frac{w_0}{w_1 - w_0} [ \exp ( - w_0 t ) - \exp( - w_1 t )] \end{array} \] $$ It follows that $latex X_0 (0) = 1$$, $latex X_1 (0) = 0$$ and $latex \[ \begin{array}{rcl} X_0^{(1)} (t) & = & - w_0 X_0 (t) \\ X_1^{(1)} (t) & = & + w_0 X_0 (t) - w_1 X_1 (t) \end{array} \] $$ */ namespace { // -------------------------------------------------------------- class Fun_two { private: CppAD::vector w; public: // constructor Fun_two(const CppAD::vector &w_) : w(w_) { } // set f = x'(t) void Ode( const double &t, const CppAD::vector &x, CppAD::vector &f) { f[0] = - w[0] * x[0]; f[1] = + w[0] * x[0] - w[1] * x[1]; } }; // -------------------------------------------------------------- class Method_two { private: Fun_two F; public: // constructor Method_two(const CppAD::vector &w_) : F(w_) { } void step( double ta, double tb, CppAD::vector &xa , CppAD::vector &xb , CppAD::vector &eb ) { xb = CppAD::Runge45(F, 1, ta, tb, xa, eb); } size_t order(void) { return 4; } }; } bool OdeErrControl_two(void) { bool ok = true; // initial return value using CppAD::NearEqual; CppAD::vector w(2); w[0] = 10.; w[1] = 1.; Method_two method(w); CppAD::vector xi(2); xi[0] = 1.; xi[1] = 0.; CppAD::vector eabs(2); eabs[0] = 1e-4; eabs[1] = 1e-4; // inputs double ti = 0.; double tf = 1.; double smin = 1e-4; double smax = 1.; double scur = .5; double erel = 0.; // outputs CppAD::vector ef(2); CppAD::vector xf(2); CppAD::vector maxabs(2); size_t nstep; xf = OdeErrControl(method, ti, tf, xi, smin, smax, scur, eabs, erel, ef, maxabs, nstep); double x0 = exp(-w[0]*tf); ok &= NearEqual(x0, xf[0], 1e-4, 1e-4); ok &= NearEqual(0., ef[0], 1e-4, 1e-4); double x1 = w[0] * (exp(-w[0]*tf) - exp(-w[1]*tf))/(w[1] - w[0]); ok &= NearEqual(x1, xf[1], 1e-4, 1e-4); ok &= NearEqual(0., ef[1], 1e-4, 1e-4); return ok; } /* Define $latex X : \B{R} \rightarrow \B{R}^2$$ by $latex \[ \begin{array}{rcl} X_0 (0) & = & 1 \\ X_1 (0) & = & 0 \\ X_0^{(1)} (t) & = & 2 \alpha t X_0 (t) \\ X_1^{(1)} (t) & = & 1 / X_0 (t) \end{array} \] $$ It follows that $latex \[ \begin{array}{rcl} X_0 (t) & = & \exp ( \alpha t^2 ) \\ X_1 (t) & = & \int_0^t \exp( - \alpha s^2 ) {\bf d} s \\ & = & \frac{1}{ \sqrt{\alpha} \int_0^{\sqrt{\alpha} t} \exp( - r^2 ) {\bf d} r \\ & = & \frac{\sqrt{\pi}}{ 2 \sqrt{\alpha} {\rm erf} ( \sqrt{\alpha} t ) \end{array} \] $$ If $latex X_0 (t) < 0$$, we return $code nan$$ in order to inform $code OdeErrControl$$ that its is taking to large a step. */ # include // CppAD::Rosen34 # include namespace { // -------------------------------------------------------------- class Fun_three { private: const double alpha_; bool was_negative_; public: // constructor Fun_three(double alpha) : alpha_(alpha), was_negative_(false) { } // set f = x'(t) void Ode( const double &t, const CppAD::vector &x, CppAD::vector &f) { f[0] = 2. * alpha_ * t * x[0]; f[1] = 1. / x[0]; // case where ODE does not make sense if( x[0] < 0. || x[1] < 0. ) { was_negative_ = true; f[0] = std::numeric_limits::quiet_NaN(); } } // set f_t = df / dt void Ode_ind( const double &t, const CppAD::vector &x, CppAD::vector &f_t) { f_t[0] = 2. * alpha_ * x[0]; f_t[1] = 0.; if( x[0] < 0. || x[1] < 0. ) { was_negative_ = true; f_t[0] = std::numeric_limits::quiet_NaN(); } } // set f_x = df / dx void Ode_dep( const double &t, const CppAD::vector &x, CppAD::vector &f_x) { double x0_sq = x[0] * x[0]; f_x[0 * 2 + 0] = 2. * alpha_ * t; // f0 w.r.t. x0 f_x[0 * 2 + 1] = 0.; // f0 w.r.t. x1 f_x[1 * 2 + 0] = -1./x0_sq; // f1 w.r.t. x0 f_x[1 * 2 + 1] = 0.; // f1 w.r.t. x1 if( x[0] < 0. || x[1] < 0. ) { was_negative_ = true; f_x[0] = std::numeric_limits::quiet_NaN(); } } bool was_negative(void) { return was_negative_; } }; // -------------------------------------------------------------- class Method_three { public: Fun_three F; // constructor Method_three(double alpha) : F(alpha) { } void step( double ta, double tb, CppAD::vector &xa , CppAD::vector &xb , CppAD::vector &eb ) { xb = CppAD::Rosen34(F, 1, ta, tb, xa, eb); } size_t order(void) { return 3; } }; } bool OdeErrControl_three(void) { bool ok = true; // initial return value using CppAD::NearEqual; double alpha = 10.; Method_three method(alpha); CppAD::vector xi(2); xi[0] = 1.; xi[1] = 0.; CppAD::vector eabs(2); eabs[0] = 1e-4; eabs[1] = 1e-4; // inputs double ti = 0.; double tf = 1.; double smin = 1e-4; double smax = 1.; double scur = 1.; double erel = 0.; // outputs CppAD::vector ef(2); CppAD::vector xf(2); CppAD::vector maxabs(2); size_t nstep; xf = OdeErrControl(method, ti, tf, xi, smin, smax, scur, eabs, erel, ef, maxabs, nstep); double x0 = exp( alpha * tf * tf ); ok &= NearEqual(x0, xf[0], 1e-4, 1e-4); ok &= NearEqual(0., ef[0], 1e-4, 1e-4); double root_pi = sqrt( 4. * atan(1.)); double root_alpha = sqrt( alpha ); double x1 = CppAD::erf(alpha * tf) * root_pi / (2 * root_alpha); ok &= NearEqual(x1, xf[1], 1e-4, 1e-4); ok &= NearEqual(0., ef[1], 1e-4, 1e-4); ok &= method.F.was_negative(); return ok; } namespace { // -------------------------------------------------------------- class Fun_four { private: size_t n; // dimension of the state space public: // constructor Fun_four(size_t n_) : n(n_) { } // given x(0) = 0 // solution is x_i (t) = t^(i+1) void Ode( const double &t, const CppAD::vector &x, CppAD::vector &f) { size_t i; f[0] = std::numeric_limits::quiet_NaN(); for(i = 1; i < n; i++) f[i] = double(i+1) * x[i-1]; } }; // -------------------------------------------------------------- class Method_four { private: Fun_four F; public: // constructor Method_four(size_t n_) : F(n_) { } void step( double ta, double tb, CppAD::vector &xa , CppAD::vector &xb , CppAD::vector &eb ) { xb = CppAD::Runge45(F, 1, ta, tb, xa, eb); } size_t order(void) { return 4; } }; } bool OdeErrControl_four(void) { bool ok = true; // initial return value // construct method for n component solution size_t n = 6; Method_four method(n); // inputs to OdeErrControl // special case where scur is converted to ti - tf // (so it is not equal to smin) double ti = 0.; double tf = .9; double smin = .8; double smax = 1.; double scur = smin; double erel = 1e-7; CppAD::vector xi(n); CppAD::vector eabs(n); size_t i; for(i = 0; i < n; i++) { xi[i] = 0.; eabs[i] = 0.; } // outputs from OdeErrControl CppAD::vector ef(n); CppAD::vector xf(n); xf = OdeErrControl(method, ti, tf, xi, smin, smax, scur, eabs, erel, ef); // check that Fun_four always returning nan results in nan for(i = 0; i < n; i++) { ok &= CppAD::isnan(xf[i]); ok &= CppAD::isnan(ef[i]); } return ok; } // ========================================================================== bool ode_err_control(void) { bool ok = true; ok &= OdeErrControl_one(); ok &= OdeErrControl_two(); ok &= OdeErrControl_three(); ok &= OdeErrControl_four(); return ok; } ================================================ FILE: test_more/general/optimize.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- // 2DO: Test that optimize.hpp use of atomic_base::rev_sparse_jac works. # include # include # include namespace { // // use val_optimize and ignore conditional_skip_ bool use_val_optimize_; // // include conditional skip optimization bool conditional_skip_; // // optimize_with_options void optimize_with_options(CppAD::ADFun& f) { if( use_val_optimize_ ) { f.optimize("val_graph no_conditional_skip"); } else if( conditional_skip_ ) f.optimize(); else f.optimize("no_conditional_skip"); } // accuracy for almost equal checks using CppAD::NearEqual; // note this enum type is not part of the API (but its values are) CppAD::atomic_base::option_enum atomic_sparsity_option_; // ======================================================================= // Test conditional expressions where left and right are dynamic parameters bool cond_exp_ppvv(void) { bool ok = true; using CppAD::AD; // independent variable vector CPPAD_TESTVECTOR(AD) ax(2), ap(2); ap[0] = 0.; ap[1] = 1.; ax[0] = 2.; ax[1] = 3.; size_t abort_op_index = 0; bool record_compare = false; CppAD::Independent(ax, abort_op_index, record_compare, ap); // dependent variable vector CPPAD_TESTVECTOR(AD) ay(5); // CondExp(parameter, variable, variable, variable) ay[0] = CondExpLt(ap[0], ap[1], ax[0], ax[1]); ay[1] = CondExpLt(ap[0], ap[1], ax[0], ax[1]); ay[2] = CondExpEq(ap[0], ap[1], ax[0], ax[1]); ay[3] = CondExpGe(ap[0], ap[1], ax[0], ax[1]); ay[4] = CondExpGt(ap[0], ap[1], ax[0], ax[1]); // create f: X -> Y CppAD::ADFun f(ax, ay); optimize_with_options(f); // vectors for function values CPPAD_TESTVECTOR(double) x( f.Domain() ), p( f.size_dyn_ind() ); CPPAD_TESTVECTOR(double) y( f.Range() ); // vectors for derivative values CPPAD_TESTVECTOR(double) dx( f.Domain() ); CPPAD_TESTVECTOR(double) dy( f.Range() ); // check original function values // ap[0] < ap[1] ok &= ay[0] == ax[0]; ok &= ay[1] == ax[0]; ok &= ay[2] == ax[1]; ok &= ay[3] == ax[1]; ok &= ay[4] == ax[1]; // function values p[0] = 0.; p[1] = 1.; x[0] = 2.; x[1] = 3.; f.new_dynamic(p); y = f.Forward(0, x); ok &= ( y[0] == x[0] ); ok &= ( y[1] == x[0] ); ok &= ( y[2] == x[1] ); ok &= ( y[3] == x[1] ); ok &= ( y[4] == x[1] ); // forward mode derivative values dx[0] = 1.; dx[1] = 2.; dy = f.Forward(1, dx); ok &= (dy[0] == dx[0] ); ok &= (dy[1] == dx[0] ); ok &= (dy[2] == dx[1] ); ok &= (dy[3] == dx[1] ); ok &= (dy[4] == dx[1] ); // reverse mode derivative values dy[0] = 1.; dy[1] = 2.; dy[2] = 3.; dy[3] = 4.; dy[4] = 5.; dx = f.Reverse(1, dy); ok &= dx[0] == dy[0] + dy[1]; ok &= dx[1] == dy[2] + dy[3] + dy[4]; // now change the dynamic parameter so the results are reversed p[0] = 1.0; p[1] = 0.0; f.new_dynamic(p); // function values y = f.Forward(0, x); ok &= ( y[0] == x[1] ); ok &= ( y[1] == x[1] ); ok &= ( y[2] == x[1] ); ok &= ( y[3] == x[0] ); ok &= ( y[4] == x[0] ); // forward mode derivative values dx[0] = 1.; dx[1] = 2.; dy = f.Forward(1, dx); ok &= (dy[0] == dx[1] ); ok &= (dy[1] == dx[1] ); ok &= (dy[2] == dx[1] ); ok &= (dy[3] == dx[0] ); ok &= (dy[4] == dx[0] ); // reverse mode derivative values dy[0] = 1.; dy[1] = 2.; dy[2] = 3.; dy[3] = 4.; dy[4] = 5.; dx = f.Reverse(1, dy); ok &= dx[0] == dy[3] + dy[4]; ok &= dx[1] == dy[0] + dy[1] + dy[2]; return ok; } // ==================================================================== // test collision_limit bool exceed_collision_limit(void) { bool ok = true; using CppAD::vector; using CppAD::AD; // number of rows in the matrix size_t nr = 5; // number of element in the matrix size_t n = nr * nr; // independent variable vector< AD > ax(n); for(size_t j = 0; j < n; ++j) ax[j] = double(j); CppAD::Independent(ax); // determinant by minors function CppAD::det_by_minor< AD > adet(nr); // dependent variable vector< AD > ay(1); ay[0] = adet(ax); // ADFun CppAD::ADFun f(ax, ay); // optimize the function std::string options = "collision_limit=1"; f.optimize(options); // check that the limit was exceeded ok &= f.exceed_collision_limit(); return ok; } // ==================================================================== // check no_cumulative_sum_op option bool no_cumulative_sum(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 2; CppAD::vector< AD > ax(n); for(size_t j = 0; j < n; j++) ax[j] = double(j + 2); size_t n_original = 1 + n; size_t n_optimize = 1 + n; // range space vector size_t m = 1; CppAD::vector< AD > ay(m); // declare independent variables and start tape recording CppAD::Independent(ax); // A cumulative summation and a multiply AD sum = 1.0; for(size_t j = 0; j < n; ++j) sum += ax[j]; ay[0] = ax[0] * sum; n_original += n + 1; n_optimize += n + 1; // not gathered as a cumulative sum // same cumulative summation different multiply // all of the terms in the sum have been calculated before sum = 1.0; for(size_t j = 0; j < n; ++j) sum += ax[j]; ay[0] += ax[1] * sum; n_original += n + 2; // n sums, one multiply, and final sum n_optimize += 2; // one multiply, and final sum CppAD::ADFun f; f.Dependent(ax, ay); // check number of variables in original function ok &= f.size_var() == n_original; CppAD::vector x(n), y(m); for(size_t j = 0; j < n; j++) x[j] = double(j + 2); y = f.Forward(0, x); for(size_t i = 0; i < m; i++) ok &= NearEqual(y[i], Value(ay[i]), eps10, eps10); if( conditional_skip_ ) f.optimize("no_cumulative_sum_op"); else f.optimize("no_conditional_skip no_cumulative_sum_op"); // check number of variables in optimized version ok &= (f.size_var() == n_optimize ); y = f.Forward(0, x); for(size_t i = 0; i < m; i++) ok &= NearEqual(y[i], Value(ay[i]), eps10, eps10); return ok; } // --------------------------------------------------------------------- // optimize_csum bool optimize_csum(void) { bool ok = true; using CppAD::AD; using CppAD::vector; size_t n = 4; vector< AD > ax(n), ap(n); for(size_t j = 0; j < n; ++j) { ax[j] = 0.0; ap[j] = 0.0; } size_t abort_op_index = 0; bool record_compare = false; Independent(ax, abort_op_index, record_compare, ap); // AD aplus = ax[0] + ax[1]; AD aminus = ax[0] - ax[1]; AD asum = CondExpLe(ax[0], ax[1], aplus, aminus); size_t n2 = n / 2; for(size_t j = 0; j < n2; ++j) asum += ax[j] + ap[j]; for(size_t j = n2; j < n; ++j) asum -= ax[j] + ap[j]; // vector< AD > ay(1); ay[0] = asum * asum; CppAD::ADFun f(ax, ay); // optimize_with_options(f); // creates a cumulative sum operator optimize_with_options(f); // optimizes such a function // // zero order forward vector x(n), p(n), y(1); double sum = 0.0; for(size_t j = 0; j < n2; ++j) { x[j] = double(j + 1); p[j] = double(j + 1); sum += x[j] + p[j]; } for(size_t j = n2; j < n; ++j) { x[j] = double(j + 1); p[j] = double(j + 1); sum -= x[j] + p[j]; } if( x[0] <= x[1] ) sum += x[0] + x[1]; else sum += x[0] - x[1]; f.new_dynamic(p); y = f.Forward(0, x); double check = sum * sum; ok &= y[0] == check; // vector w(1), dx(n); w[0] = 1.0; dx = f.Reverse(1, w); // ok &= 1 < n2; if( x[0] <= x[1] ) { ok &= dx[0] == 4.0 * sum; ok &= dx[1] == 4.0 * sum; } else { ok &= dx[0] == 4.0 * sum; ok &= dx[1] == 0.0; } for(size_t j = 2; j < n2; ++j) ok &= dx[j] == 2.0 * sum; for(size_t j = n2; j < n; ++j) ok &= dx[j] == - 2.0 * sum; // return ok; } // ---------------------------------------------------------------- class ode_evaluate_fun { public: // Given that y_i (0) = x_i, // the following y_i (t) satisfy the ODE below: // y_0 (t) = x[0] // y_1 (t) = x[1] + x[0] * t // y_2 (t) = x[2] + x[1] * t + x[0] * t^2/2 // y_3 (t) = x[3] + x[2] * t + x[1] * t^2/2 + x[0] * t^3 / 3! // ... void Ode( const CppAD::AD& t, const CppAD::vector< CppAD::AD >& y, CppAD::vector< CppAD::AD >& f) { size_t n = y.size(); f[0] = 0.; for(size_t k = 1; k < n; k++) f[k] = y[k-1]; } }; bool optimize_ode(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); using CppAD::vector; // independent variable vector size_t n = 2; vector< AD > ax(n); for(size_t j = 0; j < n; j++) ax[j] = AD(j+1); Independent(ax); // function that defines the ode ode_evaluate_fun F; // initial and final time AD ati = 0.0; AD atf = 0.5; // initial value for y(x, t); i.e. y(x, 0) // (is a reference to x) size_t m = n; vector< AD > ayi = ax; // final value for y(x, t); i.e., y(x, 1) vector< AD > ayf(m); // Use one fourth order Runge-Kutta step to solve ODE size_t M = 1; ayf = CppAD::Runge45(F, M, ati, atf, ayi); // function f(x) = y(x, tf) CppAD::ADFun f(ax, ayf); // optimize f(x) optimize_with_options(f); // compute f'(x) vector x(n), jac(m * n); for(size_t j = 0; j < n; j++) x[j] = double(j+1); jac = f.Jacobian(x); // check f'(x) double tj = 1.0; for(size_t j = 0; j < n; j++) { for(size_t i = j; i < m; i++) ok &= CppAD::NearEqual( tj , jac[ i * n * j], eps10, eps10); tj *= Value( atf - ati ); } return ok; } // ---------------------------------------------------------------- // Test nested conditional expressions. bool nested_cond_exp(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); using CppAD::vector; // independent variable vector vector< AD > ax(2), ay(1); ax[0] = 1.0; ax[1] = 2.0; Independent(ax); // first conditional expression AD ac1 = CondExpLe(ax[0], ax[1], 2.0 * ax[0], 3.0 * ax[1] ); // second conditional expression AD ac2 = CondExpGe(ax[0], ax[1], 4.0 * ax[0], 5.0 * ax[1] ); // third conditional expression AD ac3 = CondExpLt(ax[0], ax[1], 6.0 * ac1, 7.0 * ac2 ); // create function object f : ax -> ay ay[0] = ac3; CppAD::ADFun f(ax, ay); // now optimize the operation sequence optimize_with_options(f); // now zero order forward vector x(2), y(1); for(size_t i = 0; i < 3; i++) { x[0] = 1.0 - double(i); x[1] = - x[0]; y = f.Forward(0, x); // // first conditional expression double c1; if( x[0] <= x[1] ) c1 = 2.0 * x[0]; else c1 = 3.0 * x[1]; // // second conditional expression double c2; if( x[0] >= x[1] ) c2 = 4.0 * x[0]; else c2 = 5.0 * x[1]; // third conditional expression double c3; if( x[0] < x[1] ) c3 = 6.0 * c1; else c3 = 7.0 * c2; ok &= NearEqual(y[0], c3, eps10, eps10); } return ok; } // ---------------------------------------------------------------- // Test for bug where checkpoint function did not depend on // the operands in the logical comparison because of the CondExp // sparsity pattern. void j_algo( const CppAD::vector< CppAD::AD >& ax , CppAD::vector< CppAD::AD >& ay ) { ay[0] = CondExpGt(ax[0], ax[1], ax[2], ax[3]); } bool atomic_cond_exp_sparsity(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); using CppAD::vector; // Create a checkpoint version of the function g vector< AD > au(4), av(1); for(size_t i = 0; i < 4; i++) au[i] = AD(i); CppAD::checkpoint j_check("j_check", j_algo, au, av); // independent variable vector vector< AD > ax(2), ay(1); ax[0] = 1.; ax[1] = 1.; Independent(ax); // call atomic function that does not get used for(size_t i = 0; i < 4; i++) au[i] = ax[0] + AD(i + 1) * ax[1]; j_check(au, ay); // create function object f : ax -> ay CppAD::ADFun f(ax, ay); // now optimize the operation sequence optimize_with_options(f); // check result where true case is used; i.e., au[0] > au[1] vector x(2), y(1); x[0] = 1.; x[1] = -1; y = f.Forward(0, x); ok &= NearEqual(y[0], x[0] + double(3) * x[1], eps10, eps10); // check result where false case is used; i.e., au[0] <= au[1] x[0] = 1.; x[1] = 1; y = f.Forward(0, x); ok &= NearEqual(y[0], x[0] + double(4) * x[1], eps10, eps10); return ok; } // ------------------------------------------------------------------- // Test conditional optimizing out call to an atomic function call void k_algo( const CppAD::vector< CppAD::AD >& x , CppAD::vector< CppAD::AD >& y ) { y[0] = x[0] + x[1]; } void h_algo( const CppAD::vector< CppAD::AD >& x , CppAD::vector< CppAD::AD >& y ) { y[0] = x[0] - x[1]; } bool atomic_cond_exp(void) { bool ok = true; typedef CppAD::vector< CppAD::AD > ADVector; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); // Create a checkpoint version of the function g ADVector ax(2), ag(1), ah(1), ay(1); ax[0] = 0.; ax[1] = 1.; CppAD::checkpoint k_check("k_check", k_algo, ax, ag); CppAD::checkpoint h_check("h_check", h_algo, ax, ah); // independent variable vector Independent(ax); // atomic function calls that get conditionally used k_check(ax, ag); h_check(ax, ah); // conditional expression ay[0] = CondExpLt(ax[0], ax[1], ag[0], ah[0]); // create function object f : ax -> ay CppAD::ADFun f; f.Dependent(ax, ay); // use zero order to evaluate when condition is true CppAD::vector x(2), dx(2); CppAD::vector y(1), dy(1), w(1); x[0] = 3.; x[1] = 4.; y = f.Forward(0, x); ok &= NearEqual(y[0], x[0] + x[1], eps10, eps10); // before optimize k_check.option( atomic_sparsity_option_ ); h_check.option( atomic_sparsity_option_ ); ok &= f.number_skip() == 0; // now optimize the operation sequence optimize_with_options(f); // optimized zero order forward when condition is false x[0] = 4.; x[1] = 3.; y = f.Forward(0, x); ok &= NearEqual(y[0], x[0] - x[1], eps10, eps10); // after optimize can skip either call to g or call to h ok &= f.number_skip() == 1; // optimized first order forward dx[0] = 2.; dx[1] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], dx[0] - dx[1], eps10, eps10); // optimized first order reverse w[0] = 1.; dx = f.Reverse(1, w); ok &= NearEqual(dx[0], 1., eps10, eps10); ok &= NearEqual(dx[1], -1., eps10, eps10); return ok; } // ------------------------------------------------------------------- // Test of optimizing out arguments to an atomic function void g_algo( const CppAD::vector< CppAD::AD >& ax , CppAD::vector< CppAD::AD >& ay ) { ay = ax; } bool atomic_no_used(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); using CppAD::vector; // Create a checkpoint version of the function g vector< AD > ax(2), ay(2), az(1); ax[0] = 0.; ax[1] = 1.; CppAD::checkpoint g_check("g_check", g_algo, ax, ay); // independent variable vector Independent(ax); // call atomic function that does not get used g_check(ax, ay); // conditional expression az[0] = CondExpLt(ax[0], ax[1], ax[0] + ax[1], ax[0] - ax[1]); // create function object f : ax -> az CppAD::ADFun f(ax, az); // number of variables before optimization // (include ay[0] and ay[1]) size_t n_before = f.size_var(); // now optimize the operation sequence g_check.option( atomic_sparsity_option_ ); optimize_with_options(f); // number of variables after optimization // (does not include ay[0] and ay[1]) size_t n_after = f.size_var(); ok &= n_after + 2 == n_before; // check optimization works ok vector x(2), z(1); x[0] = 4.; x[1] = 3.; z = f.Forward(0, x); ok &= NearEqual(z[0], x[0] - x[1], eps10, eps10); return ok; } bool atomic_arguments(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); using CppAD::vector; vector< AD > au(2), aw(2), ax(2), ay(1); // create atomic function corresponding to g_algo au[0] = 1.0; au[1] = 2.0; CppAD::checkpoint g_check("g_algo", g_algo, au, ax); // start recording a new function CppAD::Independent(ax); // now use g_check during the recording au[0] = ax[0] + ax[1]; // this argument requires a new variable au[1] = ax[0] - ax[1]; // this argument also requires a new variable g_check(au, aw); // now create f(x) = x_0 + x_1 ay[0] = aw[0]; CppAD::ADFun f(ax, ay); // number of variables before optimization size_t n_before = f.size_var(); // ax[0], ax[1], ax[0] + ax[1]. ax[0] - ax[1], g[0], g[1] // and phantom variable at index 0 ok &= n_before == 7; // now optimize f so that the calculation of au[1] is removed g_check.option( atomic_sparsity_option_ ); optimize_with_options(f); // number of variables after optimization size_t n_after = f.size_var(); // ax[0], ax[1], ax[0] + ax[1]. g[0] // and phantom variable at index 0 ok &= n_after == 5; // now compute and check a forward mode calculation vector x(2), y(1); x[0] = 5.0; x[1] = 6.0; y = f.Forward(0, x); ok &= NearEqual(y[0], x[0] + x[1], eps10, eps10); return ok; } // ------------------------------------------------------------------- // Test the reverse dependency analysis optimization template void depend_fun (const Vector& x, Vector& y, size_t& original, size_t& opt) { typedef typename Vector::value_type Scalar; Scalar not_used; Scalar one(1), two(2), three(3); // independent variable and phantom at beginning original = 1 + x.size(); opt = 1 + x.size(); // unary operator where operand is arg[0] // (note that sin corresponds to two tape variables) not_used = fabs(x[0]); y[0] = sin(x[0]); original += 3; opt += 2; // binary operator where left operand is a variable // and right operand is a parameter not_used = not_used + 2.; y[1] = x[1] * 3.; original += 2; opt += 1; // binary operator where left operand is a parameter // and right operation is a variable not_used = 2. - not_used; y[2] = 3. / x[2]; original += 2; opt += 1; // binary operator where both operands are variables not_used = x[3] - not_used; y[3] = x[3] / x[2]; original += 2; opt += 1; // conditional expression that will be optimized out not_used = CppAD::CondExpLt(x[0], x[1], x[2], x[3]) + not_used; y[4] = CppAD::CondExpLt(x[4], one, two, three); original += 3; opt += 1; // y[5] does not depend on the value of not_used. // Make sure a parameter, corresponding to a dependent variable, // is not optimized out of the operation sequence. y[5] = 0.0 * not_used; original += 1; opt += 1; // We do not use the argument x[5], to // make sure it is not optimized out. return; } bool depend_one(void) { // Test all except for VecAD operations bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); size_t original; size_t opt; size_t i, j; // domain space vector size_t n = 6; CppAD::vector< AD > X(n); for(j = 0; j < n; j++) X[j] = 1. / double(j + 1); // declare independent variables and start tape recording CppAD::Independent(X); // range space vector size_t m = n; CppAD::vector< AD > Y(m); depend_fun(X, Y, original, opt); // create f: X -> Y and stop tape recording CppAD::ADFun F; F.Dependent(X, Y); CppAD::vector x(n), y(m), check(m); for(j = 0; j < n; j++) x[j] = Value(X[j]); y = F.Forward(0, x); depend_fun(x, check, original, opt); for(i = 0; i < m; i++) ok &= NearEqual(y[i], check[i], eps10, eps10); // Check size before optimization ok &= F.size_var() == original; // Optimize the operation sequence optimize_with_options(F); // Check size after optimization ok &= F.size_var() == opt; // check result now // (should have already been checked if NDEBUG not defined) y = F.Forward(0, x); for(i = 0; i < m; i++) ok &= NearEqual(y[i], check[i], eps10, eps10); return ok; } bool depend_two(void) { // Test VecAD operations bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); size_t i, j; // domain space vector size_t n = 2; CppAD::vector< AD > X(n); for(j = 0; j < n; j++) X[j] = double(j); // range space vector size_t m = 3; CppAD::vector< AD > Y(m); CppAD::VecAD U(m); CppAD::VecAD V(n); for(i = 0; i < m; i++) U[i] = 0; for(j = 0; j < n; j++) V[j] = 0; // declare independent variables and start tape recording CppAD::Independent(X); // first vecad vector that is a variable U[ X[0] ] = X[1]; // second vecad vector that is a variable V[ X[0] ] = X[1]; // Make dependency for vecad vectors different that for // variables because original code used wrong dependency info. // Y does not depend on the first variable in the tape; i.e. // the one corresponding to the BeginOp. So make it depend // on the first vecad vector in the tape. for(i = 0; i < m; i++) { AD I(i); Y[i] = U[I]; } // create f: X -> Y and stop tape recording // Y[ X[0] ] = X[1] and other components of Y are zero. CppAD::ADFun F; F.Dependent(X, Y); // Check number of VecAD vectors plus number of VecAD elements ok &= (F.size_VecAD() == 2 + n + m); CppAD::vector x(n), y(m); for(j = 0; j < n; j++) x[j] = double(j); y = F.Forward(0, x); for(i = 0; i < m; i++) { if( i != static_cast(x[0]) ) ok &= NearEqual(y[i], 0., eps10, eps10); else ok &= NearEqual(y[i], x[1], eps10, eps10); } if( conditional_skip_ ) F.optimize(); else F.optimize("no_conditional_skip"); // Check number of VecAD vectors plus number of VecAD elements ok &= (F.size_VecAD() == 1 + m); y = F.Forward(0, x); for(i = 0; i < m; i++) { if( i != static_cast(x[0]) ) ok &= NearEqual(y[i], 0., eps10, eps10); else ok &= NearEqual(y[i], x[1], eps10, eps10); } return ok; } bool depend_three(void) { // Power function is a special case for optimize bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); using CppAD::vector; size_t n = 3; size_t j; vector< AD > X(n), Y(n); vector x(n), y(n); for(j = 0; j < n; j++) X[j] = x[j] = double(j+2); CppAD::Independent(X); Y[0] = pow(X[0], 2.0); Y[1] = pow(2.0, X[1]); Y[2] = pow(X[0], X[1]); CppAD::ADFun F(X, Y); optimize_with_options(F); y = F.Forward(0, x); // Use identically equal because the result of the operations // have been stored as double and guard bits have been dropped. // (This may not be true for some compiler in the future). for(j = 0; j < n; j++) ok &= NearEqual(y[j], Value(Y[j]), eps10, eps10); // check reverse mode derivative vector w(n), dw(n); w[0] = 0.; w[1] = 0.; w[2] = 1.; dw = F.Reverse(1, w); double check = x[1] * pow( x[0], x[1] - 1. ); ok &= NearEqual( dw[0], check, eps10, eps10 ); check = log( x[0] ) * pow( x[0], x[1] ); ok &= NearEqual( dw[1], check, eps10, eps10 ); check = 0.; ok &= NearEqual( dw[2], check, eps10, eps10 ); return ok; } bool depend_four(void) { // erf function is a special case for optimize bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); using CppAD::vector; size_t n = 1; size_t m = 2; vector< AD > X(n), Y(m); vector x(n); X[0] = x[0] = double(0.5); CppAD::Independent(X); Y[0] = erf(X[0]) + erf(X[0]); Y[1] = erfc(X[0]) + erfc(X[0]); CppAD::ADFun F(X, Y); vector y_original = F.Forward(0, x); size_t size_original = F.size_var(); optimize_with_options(F); // // each erf (erfc) has 5 result values: // x*x, -x*x, exp(-x*x), exp(-x*x)*2/sqrt(pi), erf(x) ok &= F.size_var() + 10 == size_original; // vector y = F.Forward(0, x); ok &= NearEqual(y[0], y_original[0], eps10, eps10); ok &= NearEqual(y[1], y_original[1], eps10, eps10); return ok; } // =================================================================== // Test duplicate operation analysis template void duplicate_fun (const Vector& x, Vector& y, size_t& original, size_t& opt) { typedef typename Vector::value_type Scalar; original = 0; opt = 0; // unary operator where operand is arg[0] and one result Scalar a1 = CppAD::exp(x[0]); original += 1; opt += 1; // unary operator where operand is arg[0] and two results Scalar b1 = CppAD::sin(x[1]); original += 2; opt += 2; // non-commutative binary operator where left is a variable // and right is a parameter Scalar c1 = x[2] - 3.; original += 1; opt += 1; // non-commutative binary operator where left is a parameter // and right is a variable Scalar d1 = 3. / x[3]; original += 1; opt += 1; // non-commutative binary operator where left is a variable // and right is a variable Scalar e1 = pow(x[3], x[4]); original += 3; opt += 3; // commutative binary operator where left is a variable // and right is a parameter Scalar f1 = x[5] * 5.; original += 1; opt += 1; // commutative binary operator where left is a variable // and right is a variable Scalar g1 = x[5] + x[6]; original += 1; opt += 1; // duplicate variables Scalar a2 = CppAD::exp(x[0]); Scalar b2 = CppAD::sin(x[1]); // counts for 2 variables Scalar c2 = x[2] - 3.; Scalar d2 = 3. / x[3]; Scalar e2 = pow(x[3], x[4]); // counts for 3 variables Scalar f2 = 5. * x[5]; Scalar g2 = x[6] + x[5]; original += 10; // result vector y[0] = a1 * a2; y[1] = b1 * b2; y[2] = c1 * c2; y[3] = d1 * d2; y[4] = e1 * e2; y[5] = f1 * f2; y[6] = g1 * g2; original += 7; opt += 7; return; } bool duplicate_one(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); size_t original; size_t opt; size_t i, j; // domain space vector size_t n = 7; CppAD::vector< AD > X(n); for(j = 0; j < n; j++) X[j] = 1. / double(j + 1); // declare independent variables and start tape recording CppAD::Independent(X); // range space vector size_t m = n; CppAD::vector< AD > Y(m); duplicate_fun(X, Y, original, opt); // create f: X -> Y and stop tape recording CppAD::ADFun F; F.Dependent(X, Y); CppAD::vector x(n), y(m), check(m); for(j = 0; j < n; j++) x[j] = Value(X[j]); y = F.Forward(0, x); duplicate_fun(x, check, original, opt); for(i = 0; i < m; i++) ok &= NearEqual(y[i], check[i], eps10, eps10); // Check size before optimization ok &= F.size_var() == (n + 1 + original); // Optimize the operation sequence optimize_with_options(F); // Check size after optimization ok &= F.size_var() == (n + 1 + opt); // check result now // (should have already been checked if NDEBUG not defined) y = F.Forward(0, x); for(i = 0; i < m; i++) ok &= NearEqual(y[i], check[i], eps10, eps10); return ok; } // ------------------------------------------------------------------- bool duplicate_two(void) { // test that duplicate expression removal is relative to // new and not just old argument indices. bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); size_t i, j; // domain space vector size_t n = 1; CppAD::vector< AD > X(n); for(j = 0; j < n; j++) X[j] = double(j + 2); // range space vector size_t m = 1; CppAD::vector< AD > Y(m); // declare independent variables and start tape recording CppAD::Independent(X); // create a new variable AD A1 = X[0] - 2.; // create a duplicate variable AD A2 = X[0] - 2.; // create a new variable using first version of duplicate AD B1 = A1 / 2.; // create a duplicate that can only be detected using new // argument indices AD B2 = A2 / 2.; // Make a new variable for result // and make it depend on all the variables Y[0] = B1 + B2; // create f: X -> Y and stop tape recording CppAD::ADFun F; F.Dependent(X, Y); // check number of variables in original function ok &= (F.size_var() == 1 + n + m + 4 ); CppAD::vector x(n), y(m); for(j = 0; j < n; j++) x[j] = double(j + 2); y = F.Forward(0, x); for(i = 0; i < m; i++) ok &= NearEqual(y[i], Value(Y[i]), eps10, eps10); // optimize optimize_with_options(F); // check number of variables in optimized version ok &= (F.size_var() == 1 + n + m + 2 ); y = F.Forward(0, x); for(i = 0; i < m; i++) ok &= NearEqual(y[i], Value(Y[i]), eps10, eps10); return ok; } // ------------------------------------------------------------------- bool duplicate_three(void) { // test that duplicate expression removal is relative to // new and not just old argument indices (commutative case). bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); size_t i, j; // domain space vector size_t n = 1; CppAD::vector< AD > X(n); for(j = 0; j < n; j++) X[j] = double(j + 2); // range space vector size_t m = 1; CppAD::vector< AD > Y(m); // declare independent variables and start tape recording CppAD::Independent(X); // create a new variable AD A1 = X[0] + 2.; // create a duplicate variable AD A2 = 2. + X[0]; // create a new variable using first version of duplicate AD B1 = A1 * 2.; // create a duplicate that can only be detected using new // argument indices AD B2 = 2. * A2; // Make a new variable for result // and make it depend on all the variables Y[0] = B1 + B2; // create f: X -> Y and stop tape recording CppAD::ADFun F; F.Dependent(X, Y); // check number of variables in original function ok &= (F.size_var() == 1 + n + m + 4 ); CppAD::vector x(n), y(m); for(j = 0; j < n; j++) x[j] = double(j + 2); y = F.Forward(0, x); for(i = 0; i < m; i++) ok &= NearEqual(y[i], Value(Y[i]), eps10, eps10); // optimize optimize_with_options(F); // check number of variables in optimized version ok &= (F.size_var() == 1 + n + m + 2 ); y = F.Forward(0, x); for(i = 0; i < m; i++) ok &= NearEqual(y[i], Value(Y[i]), eps10, eps10); return ok; } // ------------------------------------------------------------------- bool duplicate_four(void) { // Check that unary expression matching not only checks hash code, // and operator, but also operand (old bug that has been fixed). bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); size_t j; // domain space vector size_t n = 1; CppAD::vector< AD > X(n); X[0] = 1.; // range space vector size_t m = 1; CppAD::vector< AD > Y(m); // declare independent variables and start tape recording CppAD::Independent(X); // check a huge number of same operation with different operands size_t n_operations = std::min( size_t(CPPAD_HASH_TABLE_SIZE) + 5, size_t(std::numeric_limits::max()) - 5 ); Y[0] = X[0]; for(j = 0; j < n_operations; j++) Y[0] = fabs(Y[0]); // create f: X -> Y and stop tape recording CppAD::ADFun F; F.Dependent(X, Y); // check number of variables in original function ok &= (F.size_var() == 1 + n + n_operations ); CppAD::vector x(n), y(m); x[0] = 1.; y = F.Forward(0, x); ok &= NearEqual(y[0], Value(Y[0]), eps10, eps10); // optimize optimize_with_options(F); // check same number of variables in optimized version ok &= (F.size_var() == 1 + n + n_operations ); y = F.Forward(0, x); ok &= NearEqual(y[0], Value(Y[0]), eps10, eps10); return ok; } // ==================================================================== bool cumulative_sum(void) { // test conversion of a sequence of additions and subtraction // to a cumulative summation sequence. bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); size_t i, j; // domain space vector size_t n = 7; CppAD::vector< AD > X(n); for(j = 0; j < n; j++) X[j] = double(j + 2); size_t n_original = 1 + n; size_t n_optimize = 1 + n; // range space vector size_t m = 2; CppAD::vector< AD > Y(m); // declare independent variables and start tape recording CppAD::Independent(X); // Operations inside of optimize_cadd Y[0] = 5. + (X[0] + X[1]) + (X[1] - X[2]) // Addvv, Subvv + (X[2] - 1.) + (2. - X[3]) // Subvp, Subpv + (X[4] + 3.) + (4. + X[5]); // Addpv, Addpv (no Addvp) n_original += 12; n_optimize += 1; // Operations inside of optimize_csub Y[1] = 5. - (X[1] + X[2]) - (X[2] - X[3]) // Addvv, Subvv - (X[3] - 1.) - (2. - X[4]) // Subvp, Subpv - (X[5] + 3.) - (4. + X[6]); // Addpv, Addpv (no Addvp) n_original += 12; n_optimize += 1; CppAD::ADFun F; F.Dependent(X, Y); // check number of variables in original function ok &= (F.size_var() == n_original ); CppAD::vector x(n), y(m); for(j = 0; j < n; j++) x[j] = double(j + 2); y = F.Forward(0, x); for(i = 0; i < m; i++) ok &= NearEqual(y[i], Value(Y[i]), eps10, eps10); // optimize optimize_with_options(F); // check number of variables in optimized version ok &= (F.size_var() == n_optimize ); y = F.Forward(0, x); for(i = 0; i < m; i++) ok &= NearEqual(y[i], Value(Y[i]), eps10, eps10); return ok; } // ------------------------------------------------------------------- bool forward_csum(void) { bool ok = true; using namespace CppAD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); // independent variable vector CppAD::vector< AD > X(2); X[0] = 0.; X[1] = 1.; Independent(X); // compute sum of elements in X CppAD::vector< AD > Y(1); Y[0] = X[0] + X[0] + X[1]; // create function object F : X -> Y ADFun F(X, Y); // optimize optimize_with_options(F); // use zero order to evaluate F[ (3, 4) ] CppAD::vector x0( F.Domain() ); CppAD::vector y0( F.Range() ); x0[0] = 3.; x0[1] = 4.; y0 = F.Forward(0, x0); ok &= NearEqual(y0[0] , x0[0]+x0[0]+x0[1], eps10, eps10); // evaluate derivative of F in X[0] direction CppAD::vector x1( F.Domain() ); CppAD::vector y1( F.Range() ); x1[0] = 1.; x1[1] = 0.; y1 = F.Forward(1, x1); ok &= NearEqual(y1[0] , x1[0]+x1[0]+x1[1], eps10, eps10); // evaluate second derivative of F in X[0] direction CppAD::vector x2( F.Domain() ); CppAD::vector y2( F.Range() ); x2[0] = 0.; x2[1] = 0.; y2 = F.Forward(2, x2); double F_00 = 2. * y2[0]; ok &= NearEqual(F_00, 0., eps10, eps10); return ok; } // ------------------------------------------------------------------- bool reverse_csum(void) { bool ok = true; using namespace CppAD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); // independent variable vector CppAD::vector< AD > X(2); X[0] = 0.; X[1] = 1.; Independent(X); // compute sum of elements in X CppAD::vector< AD > Y(1); Y[0] = X[0] - (X[0] - X[1]); // create function object F : X -> Y ADFun F(X, Y); // optimize optimize_with_options(F); // use zero order to evaluate F[ (3, 4) ] CppAD::vector x0( F.Domain() ); CppAD::vector y0( F.Range() ); x0[0] = 3.; x0[1] = 4.; y0 = F.Forward(0, x0); ok &= NearEqual(y0[0] , x0[0]-x0[0]+x0[1], eps10, eps10); // evaluate derivative of F CppAD::vector dF( F.Domain() ); CppAD::vector w( F.Range() ); w[0] = 1.; dF = F.Reverse(1, w); ok &= NearEqual(dF[0] , 0., eps10, eps10); ok &= NearEqual(dF[1] , 1., eps10, eps10); return ok; } bool forward_sparse_jacobian(void) { bool ok = true; using namespace CppAD; // dimension of the domain space size_t n = 3; // dimension of the range space size_t m = 3; // independent variable vector CppAD::vector< AD > X(n); X[0] = 2.; X[1] = 3.; X[2] = 4.; Independent(X); // dependent variable vector CppAD::vector< AD > Y(m); // check results vector CppAD::vector< bool > Check(m * n); // initialize index into Y size_t index = 0; // Y[0] Y[index] = X[0] + X[1] + 5.; Check[index * n + 0] = true; Check[index * n + 1] = true; Check[index * n + 2] = false; index++; // Y[1] Y[index] = Y[0] - (X[1] + X[2]); Check[index * n + 0] = true; Check[index * n + 1] = true; Check[index * n + 2] = true; index++; // Y[2] // 2DO: There is a subtitle issue that has to do with using reverse // jacobian sparsity patterns during the optimization process. // We need an option to include X[0] in the sparsity pattern // so the optimizer can know it affects the results. Y[index] = CondExpLe(X[0], X[1], X[1]+X[1], X[2]-X[2]); Check[index * n + 0] = false; Check[index * n + 1] = true; Check[index * n + 2] = true; index++; // check final index assert( index == m ); // create function object F : X -> Y ADFun F(X, Y); // optimize optimize_with_options(F); // --------------------------------------------------------- // dependency matrix for the identity function CppAD::vector< std::set > Sx(n); size_t i, j; for(i = 0; i < n; i++) { assert( Sx[i].empty() ); Sx[i].insert(i); } // evaluate the dependency matrix for F(x) CppAD::vector< std::set > Sy(m); Sy = F.ForSparseJac(n, Sx); // check values bool found; for(i = 0; i < m; i++) { for(j = 0; j < n; j++) { found = Sy[i].find(j) != Sy[i].end(); ok &= (found == Check[i * n + j]); } } return ok; } bool reverse_sparse_jacobian(void) { bool ok = true; using namespace CppAD; // dimension of the domain space size_t n = 3; // dimension of the range space size_t m = 3; // independent variable vector CppAD::vector< AD > X(n); X[0] = 2.; X[1] = 3.; X[2] = 4.; Independent(X); // dependent variable vector CppAD::vector< AD > Y(m); // check results vector CppAD::vector< bool > Check(m * n); // initialize index into Y size_t index = 0; // Y[0] Y[index] = X[0] + X[1] + 5.; Check[index * n + 0] = true; Check[index * n + 1] = true; Check[index * n + 2] = false; index++; // Y[1] Y[index] = Y[0] - (X[1] + X[2]); Check[index * n + 0] = true; Check[index * n + 1] = true; Check[index * n + 2] = true; index++; // Y[2] Y[index] = CondExpLe(X[0], X[1], X[1]+X[1], X[2]-X[2]); Check[index * n + 0] = false; Check[index * n + 1] = true; Check[index * n + 2] = true; index++; // check final index assert( index == m ); // create function object F : X -> Y ADFun F(X, Y); // optimize optimize_with_options(F); // ---------------------------------------------------------- // dependency matrix for the identity function CppAD::vector< bool > Py(m * m); size_t i, j; for(i = 0; i < m; i++) { for(j = 0; j < m; j++) Py[ i * m + j ] = i == j; } // evaluate the dependency matrix for F(x) CppAD::vector< bool > Px(m * n); Px = F.RevSparseJac(m, Py); // check values for(i = 0; i < m; i++) { for(j = 0; j < n; j++) ok &= Px[i * n + j] == Check[i * n + j]; } return ok; } bool reverse_sparse_hessian(void) { bool ok = true; using CppAD::AD; size_t i, j; size_t n = 3; CppAD::vector< AD > X(n); X[0] = 1.; X[1] = 2.; X[2] = 3.; CppAD::Independent(X); size_t m = 1; CppAD::vector< AD > Y(m); Y[0] = CondExpGe( X[0], X[1], X[0] + (2. + X[1] + 3.) * X[1], X[0] + (2. + X[2] + 3.) * X[1] ); CppAD::vector check(n * n); check[0 * n + 0] = false; // partial w.r.t. x[0], x[0] check[0 * n + 1] = false; // x[0], x[1] check[0 * n + 2] = false; // x[0], x[2] check[1 * n + 0] = false; // partial w.r.t. x[1], x[0] check[1 * n + 1] = true; // x[1], x[1] check[1 * n + 2] = true; // x[1], x[2] check[2 * n + 0] = false; // partial w.r.t. x[2], x[0] check[2 * n + 1] = true; // x[2], x[1] check[2 * n + 2] = false; // x[2], x[2] // create function object F : X -> Y CppAD::ADFun F(X, Y); // optimize optimize_with_options(F); // sparsity pattern for the identity function U(x) = x CppAD::vector Px(n * n); for(i = 0; i < n; i++) for(j = 0; j < n; j++) Px[ i * n + j ] = i == j; // compute sparsity pattern for Jacobian of F(U(x)) CppAD::vector P_jac(m * n); P_jac = F.ForSparseJac(n, Px); // compute sparsity pattern for Hessian of F_k ( U(x) ) CppAD::vector Py(m); CppAD::vector Pxx(n * n); Py[0] = true; Pxx = F.RevSparseHes(n, Py); // check values for(i = 0; i < n * n; i++) ok &= (Pxx[i] == check[i]); return ok; } // check that CondExp properly detects dependencies bool cond_exp_depend(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); AD zero(0.), one(1.), two(2.), three(3.); size_t n = 4; CppAD::vector< AD > X(n); X[0] = zero; X[1] = one; X[2] = two; X[3] = three; CppAD::Independent(X); size_t m = 4; CppAD::vector< AD > Y(m); Y[0] = CondExpLt(X[0] + .5, one, two, three); Y[1] = CondExpLt(zero, X[1] + .5, two, three); Y[2] = CondExpLt(zero, one, X[2] + .5, three); Y[3] = CondExpLt(zero, one, two, X[3] + .5); CppAD::ADFun f(X, Y); // optimize optimize_with_options(f); CppAD::vector x(n), y(m); size_t i; for(i = 0; i < n; i++) x[i] = double(n - i); y = f.Forward(0, x); if( x[0] + .5 < 1. ) ok &= NearEqual(y[0], 2., eps10, eps10); else ok &= NearEqual(y[0], 3., eps10, eps10); if( 0. < x[1] + .5 ) ok &= NearEqual(y[1], 2., eps10, eps10); else ok &= NearEqual(y[1], 3., eps10, eps10); ok &= NearEqual(y[2], x[2] + .5, eps10, eps10);; ok &= NearEqual(y[3], 2., eps10, eps10); return ok; } // check that CondExp properly handles expressions that get // removed during optimization bool cond_exp_removed(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); AD zero(0.); size_t n = 1; CppAD::vector< AD > X(n); X[0] = 1.0; CppAD::Independent(X); size_t m = 1; CppAD::vector< AD > Y(m); AD true_case = sin(X[0]) + sin(X[0]); AD false_case = cos(X[0]) + cos(X[0]); Y[0] = CondExpLt(X[0], zero, true_case, false_case); CppAD::ADFun f(X, Y); // optimize optimize_with_options(f); CppAD::vector x(n), y(m), w(m), dw(n); x[0] = 1.0; y = f.Forward(0, x); ok &= NearEqual(y[0], false_case, eps10, eps10); w[0] = 1.0; dw = f.Reverse(1, w); // derivative of cos is minus sin ok &= NearEqual(dw[0], - true_case, eps10, eps10); return ok; } // ------------------------------------------------------------------- using CppAD::set_union; bool atomic_one_forward( size_t id , size_t k , size_t n , size_t m , const CppAD::vector& vx , CppAD::vector& vy , const CppAD::vector& tx , CppAD::vector& ty ) { assert(n == 3 && m == 2); if( k > 0 ) return false; // y[0] = x[0] + x[1] ty[0] = tx[0] + tx[1]; // y[1] = x[1] + x[2] ty[1] = tx[1] + tx[2]; if( vy.size() > 0 ) { vy[0] = vx[0] || vx[1]; vy[1] = vx[1] || vx[2]; } return true; } bool atomic_one_reverse( size_t id , size_t k , size_t n , size_t m , const CppAD::vector& tx , const CppAD::vector& ty , CppAD::vector& px , const CppAD::vector& py ) { return false; } bool atomic_one_for_jac_sparse( size_t id , size_t n , size_t m , size_t q , const CppAD::vector< std::set >& r , CppAD::vector< std::set >& s ) { return false; } bool atomic_one_rev_jac_sparse( size_t id , size_t n , size_t m , size_t q , CppAD::vector< std::set >& r , const CppAD::vector< std::set >& s ) { assert(n == 3 && m == 2); r[0].clear(); r[1].clear(); r[2].clear(); // y[0] = x[0] + x[1] r[0] = set_union(r[0], s[0]); r[1] = set_union(r[1], s[0]); // y[1] = x[1] + x[2] r[1] = set_union(r[1], s[1]); r[2] = set_union(r[2], s[1]); return true; } bool atomic_one_rev_hes_sparse( size_t id , size_t n , size_t m , size_t q , const CppAD::vector< std::set >& r , const CppAD::vector& s , CppAD::vector& t , const CppAD::vector< std::set >& u , CppAD::vector< std::set >& v ) { return false; } CPPAD_USER_ATOMIC( my_atomic_one , CppAD::vector , double , atomic_one_forward , atomic_one_reverse , atomic_one_for_jac_sparse , atomic_one_rev_jac_sparse , atomic_one_rev_hes_sparse ) bool atomic_one_test(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); size_t j; size_t n = 3; size_t m = 2; CppAD::vector< AD > ax(n), ay(m), az(m); for(j = 0; j < n; j++) ax[j] = AD(j + 1); CppAD::Independent(ax); size_t id = 0; // first call should stay in the tape my_atomic_one(id++, ax, ay); // second call will not get used my_atomic_one(id++, ax, az); // create function CppAD::ADFun g(ax, ay); // should have 1 + n + m + m variables ok &= g.size_var() == (1 + n + m + m); // optimize optimize_with_options(g); // should have 1 + n + m variables ok &= g.size_var() == (1 + n + m); // now test that the optimized function gives same results CppAD::vector x(n), y(m); for(j = 0; j < n; j++) x[j] = double( (j + 1) * (j + 1) ); y = g.Forward(0, x); // y[0] = x[0] + x[1] ok &= NearEqual(y[0], x[0] + x[1], eps10, eps10); // y[1] = x[1] + x[2] ok &= NearEqual(y[0], x[0] + x[1], eps10, eps10); return ok; } bool not_identically_equal(void) { bool ok = true; using CppAD::AD; // independent variable vector size_t n = 5; CppAD::vector< AD > ax(n); size_t j; for(j = 0; j < n; j++) ax[j] = 1. / 3.; CppAD::Independent(ax); // dependent variable vector size_t m = 1; CppAD::vector< AD > ay(m); ay[0] = 0.; for(j = 0; j < n; j++) { if( j % 2 == 0 ) ay[0] += ax[j]; else ay[0] -= ax[j]; } CppAD::ADFun f(ax, ay); // Used to fail assert in optimize that forward mode results // are identically equal optimize_with_options(f); return ok; } // ----------------------------------------------------------------------- double floor(const double& x) { return std::floor(x); } CPPAD_DISCRETE_FUNCTION(double, floor) bool discrete_function(void) { bool ok = true; using CppAD::vector; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); vector< CppAD::AD > ax(1), ay(1); ax[0] = 0.0; CppAD::Independent(ax); ay[0] = floor(ax[0]) + floor(ax[0]); CppAD::ADFun f(ax, ay); size_t size_before = f.size_var(); // optimize optimize_with_options(f); size_t size_after = f.size_var(); ok &= size_after + 1 == size_before; vector x(1), y(1); x[0] = -2.2; y = f.Forward(0, x); ok &= NearEqual(y[0], -6.0, eps10, eps10); return ok; } // ---------------------------------------------------------------- void i_algo( const CppAD::vector< CppAD::AD >& ax , CppAD::vector< CppAD::AD >& ay ) { ay[0] = 1.0 / ax[0]; } // // Test bug where atomic functions were not properly conditionally skipped. bool cond_exp_skip_atomic(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); using CppAD::vector; // Create a checkpoint version of the function i_algo vector< AD > au(1), av(1), aw(1); au[0] = 1.0; CppAD::checkpoint i_check("i_check", i_algo, au, av); // independent variable vector vector< AD > ax(2), ay(1); ax[0] = 1.0; ax[1] = 2.0; Independent(ax); // call atomic function that does not get used au[0] = ax[0]; i_check(au, av); au[0] = ax[1]; i_check(au, aw); AD zero = 0.0; ay[0] = CondExpGt(av[0], zero, av[0], aw[0]); // create function object f : ax -> ay CppAD::ADFun f(ax, ay); // run case that skips the second call to afun // (can use trace in sweep/forward_0.hpp to see this). vector x(2), y_before(1), y_after(1); x[0] = 1.0; x[1] = 2.0; y_before = f.Forward(0, x); // optimize optimize_with_options(f); y_after = f.Forward(0, x); ok &= NearEqual(y_before[0], y_after[0], eps10, eps10); return ok; } // // Test bug where conditional dependence did not pass through // atomic functions bool cond_exp_atomic_dependence(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); using CppAD::vector; // Create a checkpoint version of the function i_algo vector< AD > au(1), av(1), aw(1); au[0] = 1.0; CppAD::checkpoint i_check("i_check", i_algo, au, av); vector< AD > ax(2), ay(1); AD zero = 0.0; ax[0] = 1.0; ax[1] = 1.0; Independent(ax); av[0] = ax[0] + ax[1]; i_check(av, aw); ay[0] = CondExpGt(aw[0], zero, zero, aw[0]); CppAD::ADFun f; f.Dependent(ax, ay); // run case that skips the second call to afun // (but not for order zero) vector x(2), y_before(1), y_after(1); vector dx(2), dy_before(1), dy_after(1); x[0] = 1.0; x[1] = 1.0; y_before = f.Forward(0, x); dx[0] = 2.0; dx[1] = 2.0; dy_before = f.Forward(1, dx); // optimize optimize_with_options(f); y_after = f.Forward(0, x); dy_after = f.Forward(1, dx); ok &= NearEqual(y_before[0] , y_after[0], eps10, eps10); ok &= NearEqual(dy_before[0], dy_after[0], eps10, eps10); return ok; } // ----------------------------------------------------------------------- // Test reverse mode conditionalay skipping commands. template Type my_max(const CppAD::vector& arg) { Type res = arg[0]; for(size_t j = 0;j < arg.size(); j++) res = CondExpGt(res, arg[j], res, arg[j]); return res; } bool cond_exp_reverse(void) { bool ok = true; size_t n = 3; using CppAD::vector; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); vector< AD > ax(n), ay(1); for(size_t j = 0; j < n; j++) ax[j] = 1.0; Independent(ax); ay[0] = my_max(ax) + my_max(ax); CppAD::ADFun f(ax, ay); // optimize optimize_with_options(f); vector x(n), w(1), dx(n); for(size_t j = 0;j < n; j++) x[j] = double(j); f.Forward(0, x); w[0] = 1.0; dx = f.Reverse(1, w); for(size_t j = 0; j < n; j++) { if( j == n-1 ) ok &= NearEqual(dx[j], 2.0, eps10, eps10); else ok &= NearEqual(dx[j], 0.0, eps10, eps10); } return ok; } // ----------------------------------------------------------------------- // Test case where an expression depends on both the true // and false cases (bug fixed 2014-12-22) bool cond_exp_both_true_and_false(void) { bool ok = true; using CppAD::vector; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); // f(x) = x[0] + x[0] if x[0] >= 3 // = x[0] + x[1] otherwise vector< AD > ax(2), ay(3); ax[0] = 1.0; ax[1] = 2.0; Independent(ax); AD three(3); AD value = ax[0] + ax[1]; // a simple value ay[0] = CppAD::CondExpGe(ax[0], three, value, value); // a binary exprpression ay[1] = CppAD::CondExpGe(ax[0], three, ax[0]-ax[1], ax[0]-ax[1]); // a unary expression ay[2] = CppAD::CondExpGe(ax[0], three, exp(ax[0]), exp(ax[0]) ); CppAD::ADFun f(ax, ay); // optimize optimize_with_options(f); // check case where x[0] >= 3 vector x(2), y(3); x[0] = 4.0; x[1] = 2.0; y = f.Forward(0, x); ok &= NearEqual(y[0], x[0] + x[1], eps10, eps10); ok &= NearEqual(y[1], x[0] - x[1], eps10, eps10); ok &= NearEqual(y[2], exp(x[0]), eps10, eps10); // check case where x[0] < 3 x[0] = 1.0; x[1] = 2.0; y = f.Forward(0, x); ok &= NearEqual(y[0], x[0] + x[1], eps10, eps10); ok &= NearEqual(y[1], x[0] - x[1], eps10, eps10); ok &= NearEqual(y[2], exp(x[0]), eps10, eps10); return ok; } // Test case where a variable is removed during optimization // (bug fixed 2017-03-04) bool cond_exp_skip_remove_var(void) { bool ok = true; using CppAD::vector; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); vector< AD > ax(2), ay(2); ax[0] = 1.0; ax[1] = 2.0; Independent(ax); // AD var_1 = ax[0] + ax[1]; AD var_2 = ax[0] + ax[1]; // gets removed during optimization AD var_3 = ax[0] + ax[1]; // gets removed during optimization AD var_4 = ax[0] - ax[1]; AD par_1 = 1.0; // // first conditional expression depends on var_1 // 6 * x_0 if x_0 + x_1 >= 1.0, 7 * x_1 otherwise ay[0] = CppAD::CondExpGe(var_1, par_1, 6.0 * ax[0], 7.0 * ax[1]); // // second conditional expression depends on var_4 // 8 * x_0 if x_0 - x_1 >= x_0 + x_1, 9 * x_1 otherwise ay[1] = CppAD::CondExpGe(var_4, par_1, 8.0 * ax[0], 9.0 * ax[1]); CppAD::ADFun f(ax, ay); // // optimize optimize_with_options(f); // check case where x[0] = 2, x[1] = 4 vector x(2), y(2); x[0] = 2.0; x[1] = 4.0; y = f.Forward(0, x); ok &= NearEqual(y[0], 6.0 * x[0], eps10, eps10); ok &= NearEqual(y[1], 9.0 * x[1], eps10, eps10); return ok; } // ----------------------------------------------------------------------- // Test case where if_false case is not used by conditional expression // but is use after conditional expression. // (bug fixed 2017-04-02) bool cond_exp_if_false_used_after(void) { bool ok = true; using CppAD::vector; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); vector< AD > ax(2), ay(1); ax[0] = 1.0; ax[1] = 2.0; Independent(ax); // AD left = ax[0]; AD right = ax[1]; AD if_true = ax[0] + ax[0]; AD if_false = ax[1] + ax[1]; // AD cexp = CondExpLt(left, right, if_true, if_false); ay[0] = cexp + if_false; CppAD::ADFun f(ax, ay); // optimize optimize_with_options(f); // // check case where x[0] < x[1] vector x(2), y(1); x[0] = 2.0; x[1] = 4.0; y = f.Forward(0, x); ok &= NearEqual(y[0], x[0] + x[0] + x[1] + x[1], eps10, eps10); ok &= f.number_skip() == 0; // // check case where x[0] >= x[1] (if_true is not used) x[0] = 4.0; x[1] = 2.0; y = f.Forward(0, x); ok &= NearEqual(y[0], x[1] + x[1] + x[1] + x[1], eps10, eps10); if( conditional_skip_ ) ok &= f.number_skip() == 1; else ok &= f.number_skip() == 0; // return ok; } // ----------------------------------------------------------------------- // Test case where only variable arguments were being checked for // a complete match once hash_codes were equal. // (*bug fixed 2017-11-23) bool only_check_variables_when_hash_codes_match(void) { bool ok = true; using CppAD::AD; using CppAD::vector; // double eps99 = 99.0 * std::numeric_limits::epsilon(); // // length of the data vector z size_t nz = 9999; // // factor for last term double factor = 1e+5; // // z starts at -1.0 and ends at 1.0 vector z(nz); for(size_t i = 0; i < nz; i++) z[i] = -1.0 + 2.0 * double(i) / double(nz - 1); // // f(x) = sum from i=0 to nz-1 of (x - z[i])^2 vector< AD > ax(1), ay(1); ax[0] = 0.0; CppAD::Independent(ax); AD asum = 0.0; for(size_t i = 0; i < nz; i++) { AD aterm = z[i] - ax[0]; if( i == nz - 1 ) asum += factor * aterm; else asum += aterm / factor; } ay[0] = asum; CppAD::ADFun f(ax, ay); // // value of x where we are computing derivative vector x(1), y_before(1), y_after(1); x[0] = .1; y_before = f.Forward(0, x); // optimize optimize_with_options(f); y_after = f.Forward(0, x); // ok &= CppAD::NearEqual(y_before[0], y_after[0], eps99, eps99); // return ok; } // ----------------------------------------------------------------------- // Test case with print operator in optimized f bool check_print_for(void) { bool ok = true; using CppAD::AD; using CppAD::vector; // double eps99 = 99.0 * std::numeric_limits::epsilon(); // vector< AD > ax(1), ay(1); ax[0] = 1.0; CppAD::Independent(ax); // // check case where value is a parameter AD pos = ax[0]; const char* before = "long text before: value = "; AD value = 2.0; const char* after = "\n"; CppAD::PrintFor(pos, before, value, after); CppAD::PrintFor(pos, before, value, after); // ay[0] = ax[0]; CppAD::ADFun f; f.Dependent(ax, ay); // // value of x where we are computing derivative std::stringstream s; vector x(1), y_before(1), y_after(1); x[0] = -0.1; y_before = f.Forward(0, x, s); // optimize optimize_with_options(f); y_after = f.Forward(0, x, s); // ok &= CppAD::NearEqual(y_before[0], y_after[0], eps99, eps99); // return ok; } // ---------------------------------------------------------------- // Test case where two non-empty sets need to be intersected to obtain // set of variables that can be skipped bool intersect_cond_exp(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps10 = 10.0 * std::numeric_limits::epsilon(); using CppAD::vector; // independent variable vector vector< AD > ax(2), ay(1); ax[0] = 1.0; ax[1] = 2.0; Independent(ax); // can only be skipped when second conditional expression is true AD askip_second_true = ax[1] + 1.0; // at this point reverse mode analysis yields // skip set for askip_second_true = {skip if 2 true} // value of first conditional expression when it is true / false AD first_true = askip_second_true * 2.0; AD first_false = askip_second_true; // at this point reverse mode analysis yields // skip set for askip_second_true = {skip if 1 true, skip if 2 true} // first conditional expression AD ac1 = CondExpLe(ax[0], ax[1], first_true, first_false); // value of second conditional expression when it is true / false AD second_true = ax[1] + 2.0; AD second_false = ac1; // at this point reverse mode analysis yields // skip set for ac1 = {skip if 2 true} // second conditional expression AD ac2 = CondExpLe(ax[0], ax[1], second_true, second_false); // create function object f : ax -> ay ay[0] = ac2; CppAD::ADFun f(ax, ay); // optimize optimize_with_options(f); // now zero order forward vector x(2), y(1); for(size_t i = 0; i < 3; i++) { x[0] = 1.0 - double(i); x[1] = - x[0]; y = f.Forward(0, x); // double skip_second_true = x[1] + 1.0;; // // first conditional expression double c1; if( x[0] <= x[1] ) c1 = skip_second_true * 2.0; else c1 = skip_second_true; // // second conditional expression double c2; if( x[0] <= x[1] ) c2 = x[1] + 2.0; else c2 = c1; // ok &= NearEqual(y[0], c2, eps10, eps10); } return ok; } } bool optimize(void) { bool ok = true; // // val_optimize cases use_val_optimize_ = true; ok &= cond_exp_ppvv(); // skip: exceed_collision_limit(void) // skip: no_cumulative_sum(void) ok &= optimize_csum(); ok &= optimize_ode(); ok &= nested_cond_exp(); ok &= atomic_cond_exp_sparsity(); ok &= atomic_no_used(); ok &= atomic_arguments(); ok &= depend_one(); ok &= depend_two(); ok &= depend_three(); ok &= depend_four(); ok &= duplicate_one(); ok &= duplicate_two(); ok &= duplicate_three(); ok &= duplicate_four(); ok &= cumulative_sum(); ok &= forward_csum(); ok &= reverse_csum(); ok &= forward_sparse_jacobian(); ok &= reverse_sparse_jacobian(); ok &= reverse_sparse_hessian(); ok &= cond_exp_depend(); ok &= cond_exp_removed(); ok &= atomic_one_test(); ok &= not_identically_equal(); ok &= discrete_function(); ok &= cond_exp_skip_atomic(); ok &= cond_exp_atomic_dependence(); ok &= cond_exp_reverse(); ok &= cond_exp_both_true_and_false(); ok &= cond_exp_skip_remove_var(); ok &= cond_exp_if_false_used_after(); ok &= only_check_variables_when_hash_codes_match(); ok &= check_print_for(); ok &= intersect_cond_exp(); use_val_optimize_ = false; // // conditional_skip_, atomic_sparsity_option_ conditional_skip_ = true; atomic_sparsity_option_ = CppAD::atomic_base::bool_sparsity_enum; // ok &= atomic_cond_exp_sparsity(); // check exceed_collision_limit ok &= exceed_collision_limit(); // check no_cumulative_sum_op ok &= no_cumulative_sum(); // check optimization with cumulative sum operators ok &= optimize_csum(); // check optimization with print_for operations ok &= check_print_for(); // optimize an example ODE ok &= optimize_ode(); // atomic sparsity loop for(size_t i = 0; i < 3; i++) { if( i == 0 ) atomic_sparsity_option_ = CppAD::atomic_base::pack_sparsity_enum; else if( i == 1 ) atomic_sparsity_option_ = CppAD::atomic_base::bool_sparsity_enum; else if( i == 2 ) atomic_sparsity_option_ = CppAD::atomic_base::set_sparsity_enum; else ok &= false; // // check conditional expression sparsity pattern // (used to optimize calls to atomic functions). ok &= atomic_cond_exp_sparsity(); // check optimizing out entire atomic function ok &= atomic_cond_exp(); // check optimizing out atomic arguments ok &= atomic_no_used(); ok &= atomic_arguments(); } // conditional skip loop for(size_t i = 0; i < 2; i++) { conditional_skip_ = i == 0; // dynamic parameter conditional expression ok &= cond_exp_ppvv(); // // check nested conditional expressions ok &= nested_cond_exp(); // check reverse dependency analysis optimization ok &= depend_one(); ok &= depend_two(); ok &= depend_three(); ok &= depend_four(); // check removal of duplicate expressions ok &= duplicate_one(); ok &= duplicate_two(); ok &= duplicate_three(); ok &= duplicate_four(); // convert sequence of additions to cumulative summation ok &= cumulative_sum(); ok &= forward_csum(); ok &= reverse_csum(); // sparsity patterns ok &= forward_sparse_jacobian(); ok &= reverse_sparse_jacobian(); ok &= reverse_sparse_hessian(); // check that CondExp properly detects dependencies ok &= cond_exp_depend(); // check that it properly handles expressions that have been removed ok &= cond_exp_removed(); // check atomic_one functions ok &= atomic_one_test(); // case where results are not identically equal ok &= not_identically_equal(); // case where a discrete function is used ok &= discrete_function(); // check conditional skip of an atomic function ok &= cond_exp_skip_atomic(); // check conditional dependence through atomic function ok &= cond_exp_atomic_dependence(); // check reverse mode conditional skipping ok &= cond_exp_reverse(); // check case where an expression needed by both true and false case ok &= cond_exp_both_true_and_false(); // check case were a variable in left or right expressions // is removed during the optimization ok &= cond_exp_skip_remove_var(); // check case where an if case is used after the conditional expression ok &= cond_exp_if_false_used_after(); // check case that has non-empty binary intersection operation ok &= intersect_cond_exp(); } // not using conditional_skip or atomic functions ok &= only_check_variables_when_hash_codes_match(); // ----------------------------------------------------------------------- // CppAD::user_atomic::clear(); return ok; } ================================================ FILE: test_more/general/parameter.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // Test hash coding of parameter during recording # include namespace { // BEGIN empty namespace template bool test_repeat(void) { bool ok = true; using namespace CppAD; // number of different constant parameters size_t n_constant = 13; // number of normal parameter repeats size_t n_repeat = 17; // number of independent dynamic parameters size_t n_dynamic = 5; // independent variable vector size_t n = n_constant * n_repeat; CPPAD_TESTVECTOR(AD) ax(n), dynamic(n_dynamic); // dynamic parameter all have same value, but that could change for(size_t j = 0; j < n_dynamic; ++j) dynamic[j] = 3.0; for(size_t j = 0; j < n; j++) ax[j] = Float(j); size_t abort_op_index = 0; bool record_compare = true; Independent(ax, abort_op_index, record_compare, dynamic); // dependent variable vector and indices size_t m = n; CPPAD_TESTVECTOR(AD) ay(m); for(size_t i = 0; i < m; i++) { // must avoid Float(k) = 0 because it would get optimized out size_t k = (i % n_constant); k = k * k * 10 + 1; size_t j = i; ay[i] = ax[j] + Float(k); } // create f: ax -> ay ADFun f(ax, ay); // add one for the phantom parameter at index zero ok = f.size_par() == 1 + n_constant + n_dynamic; return ok; } } // END empty namespace bool parameter(void) { bool ok = true; ok &= test_repeat(); ok &= test_repeat(); // return ok; } ================================================ FILE: test_more/general/poly.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* // Old GetStarted example now used just for validation testing */ // BEGIN C++ // directory where cppad/cppad.hpp is stored must be searched by compiler # include bool Poly(void) { bool ok = true; // make CppAD routines visible without CppAD:: infront of names using namespace CppAD; // degree of the polynomial that we will differentiate size_t deg = 4; // vector that will hold polynomial coefficients for p(z) CPPAD_TESTVECTOR(AD) A(deg + 1); // AD elements CPPAD_TESTVECTOR(double) a(deg + 1); // double elements // set the polynomial coefficients A[0] = 1.; size_t k; for(k = 1; k <= deg; k++) A[k] = a[k] = 1.; // independent variables CPPAD_TESTVECTOR(AD) Z(1); // one independent variable Z[0] = 3.; // value of independent variable Independent(Z); // declare independent variable // dependent variables CPPAD_TESTVECTOR(AD) P(1); // one dependent variable P[0] = Poly(0, A, Z[0]); // value of polynomial at Z[0] // define f : Z -> P as a function mapping independent to dependent ADFun f(Z, P); // ADFun corresponding to polynomial // compute derivative of polynomial CPPAD_TESTVECTOR(double) z(1); // vector length f.Domain() CPPAD_TESTVECTOR(double) J(1); // vector length f.Range * f.Domain() z[0] = 3.; // point at which to compute derivative J = f.Jacobian(z); // value of derivative // compare with derivative as computed by Poly ok &= (Poly(1, a, z[0]) == J[0]); return ok; } // END C++ ================================================ FILE: test_more/general/pow.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-25 Bradley M. Bell // SPDX-FileContributor: 2025 Perry de Valpine // ---------------------------------------------------------------------------- /* Old examples now just used for validation testing. */ # include namespace { // BEGIN empty namespace // --------------------------------------------------------------------------- // dynamic_zero // Cases during recording where the base or exponent is a dynamic parameter // with value zero. bool dynamic_zero(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); const size_t nd = 1, nx = 1, ny = 1; size_t abort_op_index = 0; bool record_compare = false; // ----------------------------- // Case A: x = variable, y = dynamic; y == 0 during recording // ----------------------------- { CPPAD_TESTVECTOR(AD) ax(nx), ay(ny), adyn(nd); ax[0] = 2.0; // x during recording adyn[0]= 0.0; // y during recording (dynamic zero) CppAD::Independent(ax, abort_op_index, record_compare, adyn); AD x = ax[0]; AD y = adyn[0]; // dynamic param (zero) ay[0] = pow(x, y); // variable ^ dynamic 0 CppAD::ADFun f(ax, ay); // Evaluate with new dynamic values CPPAD_TESTVECTOR(double) x_in(nx), y_out(ny), dyn(nd); // First eval: y = 3 (non-zero), x = 2 => 2^3 = 8 x_in[0] = 2.0; dyn[0] = 3.0; f.new_dynamic(dyn); y_out = f.Forward(0, x_in); ok &= NearEqual(y_out[0], 8.0, eps99, eps99); // Second eval: y = 2, x = 3 => 3^2 x_in[0] = 3.0; dyn[0] = 2.0; f.new_dynamic(dyn); y_out = f.Forward(0, x_in); ok &= NearEqual(y_out[0], std::pow(3.0, 2.0), eps99, eps99); } // ----------------------------- // Case B: x = dynamic (zero at recording), y = variable // ----------------------------- { CPPAD_TESTVECTOR(AD) ax(nx), ay(ny), adyn(nd); ax[0] = 3.0; // y during recording adyn[0] = 0.0; // x during recording (dynamic zero) CppAD::Independent(ax, abort_op_index, record_compare, adyn); AD x = adyn[0]; // dynamic param (zero) AD y = ax[0]; // variable ay[0] = pow(x, y); // dynamic 0 ^ variable CppAD::ADFun f(ax, ay); // Evaluate with new dynamic values CPPAD_TESTVECTOR(double) x_in(nx), y_out(ny), dyn(nd); // First eval: x = 2 (non-zero), y = 3 => 2^3 = 8 x_in[0] = 3.0; // y dyn[0] = 2.0; // x f.new_dynamic(dyn); y_out = f.Forward(0, x_in); ok &= NearEqual(y_out[0], 8.0, eps99, eps99); // Second eval: x = 3, y = 2 => 3^2 x_in[0] = 2.0; // y dyn[0] = 3.0; // x f.new_dynamic(dyn); y_out = f.Forward(0, x_in); ok &= NearEqual(y_out[0], std::pow(3.0, 2.0), eps99, eps99); } return ok; } // --------------------------------------------------------------------------- bool PowTestOne(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 2; double x = 0.5; double y = 2.; CPPAD_TESTVECTOR(AD) XY(n); XY[0] = x; XY[1] = y; // declare independent variables and start tape recording CppAD::Independent(XY); // range space vector size_t m = 3; CPPAD_TESTVECTOR(AD) Z(m); Z[0] = CppAD::pow(XY[0], XY[1]); // pow(variable, variable) Z[1] = CppAD::pow(XY[0], y); // pow(variable, parameter) Z[2] = CppAD::pow(x, XY[1]); // pow(parameter, variable) // create f: XY -> Z and stop tape recording CppAD::ADFun f(XY, Z); // check value double check = std::pow(x, y); size_t i; for(i = 0; i < m; i++) ok &= NearEqual(Z[i] , check, eps99, eps99); // forward computation of first partial w.r.t. x CPPAD_TESTVECTOR(double) dxy(n); CPPAD_TESTVECTOR(double) dz(m); dxy[0] = 1.; dxy[1] = 0.; dz = f.Forward(1, dxy); check = y * std::pow(x, y-1.); ok &= NearEqual(dz[0], check, eps99, eps99); ok &= NearEqual(dz[1], check, eps99, eps99); ok &= NearEqual(dz[2], 0., eps99, eps99); // forward computation of first partial w.r.t. y dxy[0] = 0.; dxy[1] = 1.; dz = f.Forward(1, dxy); check = std::log(x) * std::pow(x, y); ok &= NearEqual(dz[0], check, eps99, eps99); ok &= NearEqual(dz[1], 0., eps99, eps99); ok &= NearEqual(dz[2], check, eps99, eps99); // reverse computation of derivative of z[0] + z[1] + z[2] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; w[1] = 1.; w[2] = 1.; dw = f.Reverse(1, w); check = y * std::pow(x, y-1.); ok &= NearEqual(dw[0], 2. * check, eps99, eps99); check = std::log(x) * std::pow(x, y); ok &= NearEqual(dw[1], 2. * check, eps99, eps99); // use a VecAD::reference object with pow CppAD::VecAD v(2); AD zero(0); AD one(1); v[zero] = XY[0]; v[one] = XY[1]; AD result = CppAD::pow(v[zero], v[one]); ok &= NearEqual(result, Z[0], eps99, eps99); return ok; } bool PowTestTwo(void) { bool ok = true; using CppAD::pow; using CppAD::exp; using namespace CppAD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector, indices, values, and declaration CPPAD_TESTVECTOR(AD) U(2); size_t s = 0; size_t t = 1; U[s] = 2.; U[t] = 3.; Independent(U); // dependent variable vector and indices CPPAD_TESTVECTOR(AD) Z(2); size_t x = 0; size_t y = 1; // dependent variable values AD u = exp(U[s]); // u = exp(s) Z[x] = pow(u, U[t]); // x = exp(s * t) Z[y] = pow(Z[x], u); // y = exp( s * t * exp(s) ) // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v( f.Domain() ); CPPAD_TESTVECTOR(double) w( f.Range() ); /* u_s (s, t) = u u_t (s, t) = 0 y_s (s, t) = (1 + s) t * u * y y_t (s, t) = s * u * y y_st (s, t) = ( u + s * u ) * y + ( t * u + s * t * u ) * s * u * y */ // check values ok &= NearEqual(Z[x] , exp(2. * 3.), eps99, eps99); ok &= NearEqual(Z[y] , exp( 2. * 3. * exp(2.) ), eps99, eps99); // forward computation of partials w.r.t. s v[s] = 1.; v[t] = 0.; w = f.Forward(1, v); ok &= ( w[x] == U[t] * Z[x] ); // dx/ds ok &= ( w[y] == (1. + U[s]) * U[t] * u * Z[y] ); // dy/ds // forward computation of partials w.r.t. t v[s] = 0.; v[t] = 1.; w = f.Forward(1, v); ok &= ( w[y] == U[s] * u * Z[y] ); // dy/dt // forward computation of second Taylor coefficient w.r.t. t v[t] = 1.; w = f.Forward(1, v); v[t] = 0.; CPPAD_TESTVECTOR(double) f_tt = f.Forward(2, v); // forward computation of second Taylor coefficient w.r.t. s v[s] = 1.; w = f.Forward(1, v); v[s] = 0.; CPPAD_TESTVECTOR(double) f_ss = f.Forward(2, v); // second Taylor coefficient w.r.t. direction r = (s,t) v[s] = 1.; v[t] = 1.; w = f.Forward(1, v); v[s] = 0.; v[t] = 0.; CPPAD_TESTVECTOR(double) f_rr = f.Forward(2, v); // check second order partial of y ok &= NearEqual( f_rr[y] - f_ss[y] - f_tt[y], (1. + U[s]) * u * Z[y] + (1. + U[s]) * U[t] * u * U[s] * u * Z[y], eps99 , eps99 ); return ok; } bool PowTestThree(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) x(n); x[0] = 2.; // declare independent variables and start tape recording CppAD::Independent(x); // range space vector size_t m = 4; CPPAD_TESTVECTOR(AD) y(m); // some special cases y[0] = pow(x[0], 0.); y[1] = pow(0., x[0]); y[2] = pow(x[0], 1.); y[3] = pow(1., x[0]); // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check function values ok &= (Value(y[0]) == 1.); ok &= (Value(y[1]) == 0.); ok &= (Value(y[2]) == Value(x[0])); ok &= (Value(y[3]) == 1.); // forward computation of first partial w.r.t. x CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= (dy[0] == 0.); ok &= (dy[1] == 0.); ok &= NearEqual(dy[2], 1., eps99, eps99); ok &= (dy[3] == 0.); // reverse mode computation of derivative of y[0]+y[1]+y[2]+y[3] CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); w[0] = 1.; w[1] = 1.; w[2] = 1.; w[3] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], 1., eps99, eps99); return ok; } bool PowTestFour(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = -2; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // range space vector size_t m = 5; CPPAD_TESTVECTOR(AD) y(m); // some special cases (skip zero raised to a negative power) y[0] = pow(1., x[0]); size_t i; for(i = 1; i < m; i++) y[i] = CppAD::pow(x[0], int(i-1) ); // pow(AD, int) // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); ok &= (Value(y[0]) == 1.); double check; for(i = 1; i < m; i++) { check = std::pow(x0, double(i-1)); ok &= NearEqual(y[i], check, eps99, eps99); } // forward computation of first partial w.r.t. x CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= (dy[0] == 0.); double sum = 0; for(i = 1; i < m; i++) { if( i == 1 ) check = 0.; else check = double(i-1) * std::pow(x0, double(i-2)); ok &= NearEqual(dy[i], check, eps99, eps99); sum += check; } // reverse mode computation of derivative of y[0] + .. y[m-1]; CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) dw(n); for(i = 0; i < m; i++) w[i] = 1.; dw = f.Reverse(1, w); ok &= NearEqual(dw[0], sum, eps99, eps99); return ok; } bool PowTestFive(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = -1.; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // declare independent variables and start tape recording CppAD::Independent(x); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) y(m); // case of zero raised to a positive integer power double e = 2.; y[0] = pow(x[0], int(e)); // use pow(AD, int) // create f: x -> y and stop tape recording CppAD::ADFun f(x, y); // check function value ok &= (Value(y[0]) == pow(x0, e) ); // forward computation of first partial w.r.t. x[1] double d1 = e * pow(x0, (e-1)); CPPAD_TESTVECTOR(double) dx(n); CPPAD_TESTVECTOR(double) dy(m); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], d1, eps99, eps99); // reverse mode computation of second partials // x.r.t. x[1],x[0] and x[1], x[1] double d2 = e * (e-1) * pow(x0, (e-2)); CPPAD_TESTVECTOR(double) w(m); CPPAD_TESTVECTOR(double) ddw(2*n); w[0] = 1.; ddw = f.Reverse(2, w); ok &= NearEqual(ddw[0], d1, eps99, eps99); ok &= NearEqual(ddw[1], d2, eps99, eps99); return ok; } bool PowTestSix(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; double x0 = 1.5; CPPAD_TESTVECTOR(AD) x(n); x[0] = x0; // domain space vector CPPAD_TESTVECTOR(AD< AD >) X(n); X[0] = x[0]; // declare independent variables and start tape recording CppAD::Independent(X); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD< AD >) Y(m); // case of AD< AD > raised to a double power double e = 2.5; Y[0] = pow(X[0], e); // create F: X -> Y and stop tape recording CppAD::ADFun< AD > F(X, Y); // check function value ok &= NearEqual(Value(Value(Y[0])), pow(x0, e), eps99, eps99); // forward computation of first partial w.r.t. x[1] double d1 = e * pow(x0, (e-1)); CPPAD_TESTVECTOR(AD) dx(n); CPPAD_TESTVECTOR(AD) dy(m); dx[0] = 1.; dy = F.Forward(1, dx); ok &= NearEqual(dy[0], d1, eps99, eps99); // reverse mode computation of second partials // x.r.t. x[1],x[0] and x[1], x[1] double d2 = e * (e-1) * pow(x0, (e-2)); CPPAD_TESTVECTOR(AD) w(m); CPPAD_TESTVECTOR(AD) ddw(2*n); w[0] = 1.; ddw = F.Reverse(2, w); ok &= NearEqual(ddw[0], d1, eps99, eps99); ok &= NearEqual(ddw[1], d2, eps99, eps99); return ok; } // Test x^e where x is negative and e is AD equal to an integer bool PowTestEight(void) { bool ok = true; using std::cout; using CppAD::AD; using CppAD::vector; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // vector x(1), y(2), dx(1), dy(2), w(2), dw(2); vector< AD > ax(1), ay(2); // x[0] = -2.0; ax[0] = x[0]; // CppAD::Independent(ax); ay[0] = pow(ax[0], 2.0); ay[1] = pow(ax[0], -2.0); CppAD::ADFun f(ax, ay); f.check_for_nan(true); // double check; y = f.Forward(0, x); check = x[0] * x[0]; ok &= NearEqual(y[0], check, eps99, eps99); check = 1.0 / (x[0] * x[0]); ok &= NearEqual(y[1], check, eps99, eps99); // dx[0] = 1.0; dy = f.Forward(1, dx); check = 2.0 * x[0]; ok &= NearEqual(dy[0], check, eps99, eps99); check = -2.0 / ( x[0] * x[0] * x[0] ); ok &= NearEqual(dy[1], check, eps99, eps99); // w[0] = 1.0; w[1] = 0.0; dw = f.Reverse(2, w); check = 2.0 * x[0]; ok &= NearEqual(dw[0], check, eps99, eps99); check = 2.0; ok &= NearEqual(dw[1], check, eps99, eps99); // w[0] = 0.0; w[1] = 1.0; dw = f.Reverse(2, w); check = - 2.0 / (x[0] * x[0] * x[0]); ok &= NearEqual(dw[0], check, eps99, eps99); check = 6.0 / (x[0] * x[0] * x[0] * x[0]); ok &= NearEqual(dw[1], check, eps99, eps99); // return ok; } // k-th derivative of x^y .w.r.t. x double dpow_dx(double x, double y, size_t k) { double result = std::pow(x, y - double(k)); for(size_t ell = 0; ell < k; ++ell) result *= (y - double(ell)); return result; } // Testing PowvpOp bool PowTestNine(void) { bool ok = true; // using std::cout; using CppAD::AD; using CppAD::vector; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // vector x(1), dx(1); vector z(1), dz(1); vector w(1), dw(5); vector< AD > ax(1), az(1); // ax[0] = 1.0; double y = 2.5; // CppAD::Independent(ax); az[0] = pow(ax[0], y); CppAD::ADFun f(ax, az); // double check; // // zero order forward for(size_t ix = 0; ix < 3; ++ix) { x[0] = double(ix); z = f.Forward(0, x); check = dpow_dx(x[0], y, 0); ok &= NearEqual(z[0], check, eps99, eps99); // // first order forward dx[0] = 1.0; dz = f.Forward(1, dx); check = dpow_dx(x[0], y, 1); ok &= NearEqual(dz[0], check, eps99, eps99); // // ell-th order forward double factorial = 1.0; for(size_t k = 2; k < 5; ++k) { factorial *= double(k); dx[0] = 0.0; // x^(k) dz = f.Forward(k, dx); check = dpow_dx(x[0], y, k) / factorial; ok &= NearEqual(dz[0], check, eps99, eps99); } // second order reverse w[0] = 1.0; dw = f.Reverse(5, w); factorial = 1.0; for(size_t k = 0; k < 5; ++k) { check = dpow_dx(x[0], y, k+1) / factorial; ok &= NearEqual(dw[k], check, eps99, eps99); factorial *= double(k+1); } } // return ok; } // Testing PowvpOp multiple direction forward bool PowTestTen(void) { bool ok = true; // using std::cout; using CppAD::AD; using CppAD::vector; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // size_t n = 3; vector x(n), xq(n * n); vector z(n), zq(n * n); vector y(n); vector< AD > ax(n), az(n); // for(size_t j = 0; j < n; ++j) { ax[j] = double(j); y[j] = double(j) + 1.5; } // CppAD::Independent(ax); for(size_t j = 0; j < n; ++j) az[j] = pow(ax[j], y[j]); CppAD::ADFun f(ax, az); // // zero order forward for(size_t j = 0; j < n; ++j) x[j] = double(j) + 1.5; z = f.Forward(0, x); double check; for(size_t j = 0; j < n; ++j) { check = dpow_dx(x[j], y[j], 0); ok &= NearEqual(z[j], check, eps99, eps99); } // // first order forward multiple directions size_t r = n; for(size_t j = 0; j < n; ++j) { for(size_t ell = 0; ell < r; ell++) { if( j == ell ) xq[ r * j + ell] = 1.0; else xq[ r * j + ell] = 0.0; } } size_t q = 1; zq = f.Forward(q, r, xq); for(size_t j = 0; j < n; ++j) { for(size_t ell = 0; ell < r; ell++) { if( j == ell ) check = dpow_dx(x[j], y[j], 1); else check = 0.0; ok &= NearEqual(zq[r * j + ell], check, eps99, eps99); } } // // second order forward multiple directions for(size_t j = 0; j < n; ++j) { for(size_t ell = 0; ell < r; ell++) xq[ r * j + ell] = 0.0; } q = 2; zq = f.Forward(q, r, xq); for(size_t j = 0; j < n; ++j) { for(size_t ell = 0; ell < r; ell++) { if( j == ell ) check = dpow_dx(x[j], y[j], 2) / 2.0; else check = 0.0; ok &= NearEqual(zq[r * j + ell], check, eps99, eps99); } } return ok; } } // END empty namespace bool Pow(void) { bool ok = true; ok &= dynamic_zero(); ok &= PowTestOne(); ok &= PowTestTwo(); ok &= PowTestThree(); ok &= PowTestFour(); ok &= PowTestFive(); ok &= PowTestSix(); // PowTestSeven was removed ok &= PowTestEight(); ok &= PowTestNine(); ok &= PowTestTen(); // return ok; } ================================================ FILE: test_more/general/pow_int.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old example now just used for validation testing. */ # include bool PowInt(void) { bool ok = true; using CppAD::pow; using CppAD::exp; using CppAD::log; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector, indices, values, and declaration CPPAD_TESTVECTOR(AD) U(1); U[0] = 2.; Independent(U); // dependent variable vector and indices CPPAD_TESTVECTOR(AD) Z(2); // dependent variable values Z[0] = pow(U[0], 5); // x = u^5 Z[1] = pow(U[0], -5); // y = u^{-5} // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v( f.Domain() ); CPPAD_TESTVECTOR(double) w( f.Range() ); /* x_u = 5 * u^4 y_u = - 5 * u^{-6} */ // check function values values double u = Value(U[0]); ok &= NearEqual(Z[0] , exp( log(u) * 5.), eps99 , eps99); ok &= NearEqual(Z[1] , exp( - log(u) * 5.), eps99 , eps99); // forward computation of partials v[0] = 1.; w = f.Forward(1, v); ok &= NearEqual(w[0] , 5. * exp( log(u) * 4.), eps99 , eps99); ok &= NearEqual(w[1] , - 5. * exp( - log(u) * 6.), eps99 , eps99); return ok; } ================================================ FILE: test_more/general/print_for.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // modified version of test that used to be in ../example/print_for/print_for.cpp # include namespace { using std::endl; using CppAD::AD; // use of PrintFor to check for invalid function arguments AD check_log(const AD& y) { // check during recording CPPAD_ASSERT_UNKNOWN( y > 0. ); // check during zero order forward calculation PrintFor(y, "check_log: y == ", y , " which is <= 0\n"); return log(y); } } bool print_for(void) { bool ok = true; using CppAD::PrintFor; std::stringstream stream_out; // stream that output is written to std::string string_check; // what we expect the output to be // independent variable vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 1.; Independent(ax); // print a VecAD::reference object that is a parameter CppAD::VecAD av(1); AD Zero(0); av[Zero] = 0.; PrintFor("v[0] = ", av[Zero]); string_check += "v[0] = 0"; // v[0] == 0 during Forward(0, x) // Print a newline to separate this from previous output, // then print an AD object that is a variable. PrintFor("\nv[0] + x[0] = ", av[0] + ax[0]); string_check += "\nv[0] + x[0] = 2"; // x[0] == 2 during Forward(0, x) // A conditional print that will not generate output when x[0] = 2. PrintFor(ax[0], "\n 2. + x[0] = ", 2. + ax[0], "\n"); // A conditional print that will generate output when x[0] = 2. PrintFor(ax[0] - 2., "\n 3. + x[0] = ", 3. + ax[0], "\n"); string_check += "\n 3. + x[0] = 5\n"; // A log evaluations that will result in an error message when x[0] = 2. AD var = 2. - ax[0]; AD log_var = check_log(var); string_check += "check_log: y == 0 which is <= 0\n"; // dependent variable vector size_t m = 2; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = av[Zero] + ax[0]; // define f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // zero order forward with x[0] = 2 CPPAD_TESTVECTOR(double) x(n); x[0] = 2.; f.Forward(0, x, stream_out); std::string string_out = stream_out.str(); ok &= string_out == string_check; return ok; } ================================================ FILE: test_more/general/rev_sparse_jac.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # include # define CheckOp(Op) \ Y[index] = X[0] Op 2.; \ Check[index * n + 0] = true; \ Check[index * n + 1] = false; \ Check[index * n + 2] = false; \ index++; \ Y[index] = X[0] Op X[1]; \ Check[index * n + 0] = true; \ Check[index * n + 1] = true; \ Check[index * n + 2] = false; \ index++; \ Y[index] = 3. Op X[1]; \ Check[index * n + 0] = false; \ Check[index * n + 1] = true; \ Check[index * n + 2] = false; \ index++; # define CheckUnaryFun(Fun) \ Y[index] = Fun(X[0]); \ Check[index * n + 0] = true; \ Check[index * n + 1] = false; \ Check[index * n + 2] = false; \ index++; \ Y[index] = Fun(X[0] + X[1]); \ Check[index * n + 0] = true; \ Check[index * n + 1] = true; \ Check[index * n + 2] = false; \ index++; \ Y[index] = Fun(X[1]); \ Check[index * n + 0] = false; \ Check[index * n + 1] = true; \ Check[index * n + 2] = false; \ index++; # define CheckBinaryFun(Fun) \ Y[index] = Fun( X[0] , 2.); \ Check[index * n + 0] = true; \ Check[index * n + 1] = false; \ Check[index * n + 2] = false; \ index++; \ Y[index] = Fun( X[0] , X[1]); \ Check[index * n + 0] = true; \ Check[index * n + 1] = true; \ Check[index * n + 2] = false; \ index++; \ Y[index] = Fun( 3. , X[1]); \ Check[index * n + 0] = false; \ Check[index * n + 1] = true; \ Check[index * n + 2] = false; \ index++; namespace { // BEGIN empty namespace bool case_one() { bool ok = true; using namespace CppAD; // dimension of the domain space size_t n = 3; // dimension of the range space size_t m = (4 + 11 + 1) * 3 + 4; // independent variable vector CPPAD_TESTVECTOR(AD) X(n); X[0] = .1; X[1] = .2; X[2] = .3; Independent(X); // dependent variable vector CPPAD_TESTVECTOR(AD) Y(m); // check results vector CPPAD_TESTVECTOR( bool ) Check(m * n); // initialize index into Y size_t index = 0; // 4 binary operators CheckOp(+); CheckOp(-); CheckOp(*); CheckOp(/); // 11 unary functions CheckUnaryFun(abs); CheckUnaryFun(acos); CheckUnaryFun(asin); CheckUnaryFun(atan); CheckUnaryFun(cos); CheckUnaryFun(cosh); CheckUnaryFun(exp); CheckUnaryFun(log); CheckUnaryFun(sin); CheckUnaryFun(sinh); CheckUnaryFun(sqrt); // 1 binary function CheckBinaryFun(pow); // conditional expression Y[index] = CondExpLt(X[0], X[1], X[0], AD(2.)); Check[index * n + 0] = true; Check[index * n + 1] = false; Check[index * n + 2] = false; index++; Y[index] = CondExpLt(X[0], X[1], X[0], X[1]); Check[index * n + 0] = true; Check[index * n + 1] = true; Check[index * n + 2] = false; index++; Y[index] = CondExpLt(X[0], X[1], AD(3.), X[1]); Check[index * n + 0] = false; Check[index * n + 1] = true; Check[index * n + 2] = false; index++; // non-trivial composition Y[index] = Y[0] + Y[1] + X[2]; Check[index * n + 0] = true; Check[index * n + 1] = true; Check[index * n + 2] = true; index++; // check final index assert( index == m ); // create function object F : X -> Y ADFun F(X, Y); // -------------------------------------------------------- // dependency matrix for the identity function U(y) = y CPPAD_TESTVECTOR( bool ) Py(m * m); size_t i, j; for(i = 0; i < m; i++) { for(j = 0; j < m; j++) Py[ i * m + j ] = false; Py[ i * m + i ] = true; } // evaluate the dependency matrix for F(x) CPPAD_TESTVECTOR( bool ) Px(m * n); Px = F.RevSparseJac(m, Py); // check values for(i = 0; i < m; i++) { for(j = 0; j < n; j++) ok &= (Px[i * n + j] == Check[i * n + j]); } // -------------------------------------------------------- // dependency matrix for the identity function U(y) = y CPPAD_TESTVECTOR(std::set) Sy(m); for(i = 0; i < m; i++) { assert( Sy[i].empty() ); Sy[i].insert(i); } // evaluate the dependency matrix for U(F(x)) CPPAD_TESTVECTOR(std::set) Sx(m); Sx = F.RevSparseJac(m, Sy); // check values std::set::iterator itr; bool found; for(i = 0; i < m; i++) { for(j = 0; j < n; j++) { found = Sx[i].find(j) != Sx[i].end(); ok &= (found == Check[i * n + j]); } } return ok; } bool case_two() { bool ok = true; using namespace CppAD; // dimension of the domain space size_t n = 3; // dimension of the range space size_t m = 3; // initialize the vector as zero CppAD::VecAD Z(n - 1); size_t k; for(k = 0; k < n-1; k++) Z[k] = 0.; // independent variable vector CPPAD_TESTVECTOR(AD) X(n); X[0] = 0.; X[1] = 1.; X[2] = 2.; Independent(X); // VecAD vector is going to depend on X[1] and X[2] Z[ X[0] ] = X[1]; Z[ X[1] ] = X[2]; // dependent variable vector CPPAD_TESTVECTOR(AD) Y(m); // check results vector CPPAD_TESTVECTOR( bool ) Check(m * n); // initialize index into Y size_t index = 0; // First component only depends on X[0]; Y[index] = X[0]; Check[index * n + 0] = true; Check[index * n + 1] = false; Check[index * n + 2] = false; index++; // Second component depends on the vector Z AD zero(0); Y[index] = Z[zero]; // Load by a parameter Check[index * n + 0] = false; Check[index * n + 1] = true; Check[index * n + 2] = true; index++; // Third component depends on the vector Z Y[index] = Z[ X[0] ]; // Load by a variable Check[index * n + 0] = false; Check[index * n + 1] = true; Check[index * n + 2] = true; index++; // check final index assert( index == m ); // create function object F : X -> Y ADFun F(X, Y); // dependency matrix for the identity function S(y) = y CPPAD_TESTVECTOR( bool ) Py(m * m); size_t i, j; for(i = 0; i < m; i++) { for(j = 0; j < m; j++) Py[ i * m + j ] = false; Py[ i * m + i ] = true; } // evaluate the dependency matrix for S [ F(x) ] CPPAD_TESTVECTOR( bool ) Px(m * n); Px = F.RevSparseJac(m, Py); // check values for(i = 0; i < m; i++) { for(j = 0; j < n; j++) ok &= (Px[i * n + j] == Check[i * n + j]); } // -------------------------------------------------------- // dependency matrix for the identity function U(y) = y CPPAD_TESTVECTOR(std::set) Sy(m); for(i = 0; i < m; i++) { assert( Sy[i].empty() ); Sy[i].insert(i); } // evaluate the dependency matrix for U(F(x)) CPPAD_TESTVECTOR(std::set) Sx(m); Sx = F.RevSparseJac(m, Sy); // check values std::set::iterator itr; bool found; for(i = 0; i < m; i++) { for(j = 0; j < n; j++) { found = Sx[i].find(j) != Sx[i].end(); ok &= (found == Check[i * n + j]); } } return ok; } bool case_three() { bool ok = true; using namespace CppAD; // dimension of the domain space size_t n = 2; // dimension of the range space size_t m = 3; // independent variable vector CPPAD_TESTVECTOR(AD) X(n); X[0] = 2.; X[1] = 3.; Independent(X); // dependent variable vector CPPAD_TESTVECTOR(AD) Y(m); // check results vector CPPAD_TESTVECTOR( bool ) Check(m * n); // initialize index into Y size_t index = 0; // Y[0] only depends on X[0]; Y[index] = pow(X[0], 2.); Check[index * n + 0] = true; Check[index * n + 1] = false; index++; // Y[1] depends on X[1] Y[index] = pow(2., X[1]); Check[index * n + 0] = false; Check[index * n + 1] = true; index++; // Y[2] depends on X[0] and X[1] Y[index] = pow(X[0], X[1]); Check[index * n + 0] = true; Check[index * n + 1] = true; index++; // check final index assert( index == m ); // create function object F : X -> Y ADFun F(X, Y); // ----------------------------------------------------------------- // dependency matrix for the identity function CPPAD_TESTVECTOR( bool ) Py(m * m); size_t i, j; for(i = 0; i < m; i++) { for(j = 0; j < m; j++) Py[ i * m + j ] = (i == j); } // evaluate the dependency matrix for F(x) CPPAD_TESTVECTOR( bool ) Px(m * n); Px = F.RevSparseJac(m, Py); // check values for(i = 0; i < m; i++) { for(j = 0; j < n; j++) ok &= (Px[i * n + j] == Check[i * n + j]); } // --------------------------------------------------------- // dependency matrix for the identity function CPPAD_TESTVECTOR(std::set) Sy(m); for(i = 0; i < m; i++) { assert( Sy[i].empty() ); Sy[i].insert(i); } // evaluate the dependency matrix for F(x) CPPAD_TESTVECTOR(std::set) Sx(m); Sx = F.RevSparseJac(m, Sy); // check values bool found; for(i = 0; i < m; i++) { for(j = 0; j < n; j++) { found = Sx[i].find(j) != Sx[i].end(); ok &= (found == Check[i * n + j]); } } return ok; } // case where s is not identity matrix bool case_four() { bool ok = true; using namespace CppAD; // dimension of the domain space size_t n = 2; // dimension of the range space size_t m = n; // independent and variable vectors CPPAD_TESTVECTOR(AD) ax(n), ay(m); ax[0] = 2.; ax[1] = 3.; Independent(ax); ay[0] = ax[1]; ay[1] = ax[0]; // create function object F : x -> y ADFun F(ax, ay); // evaluate the dependency matrix for F(x) size_t q = 1; CPPAD_TESTVECTOR( bool ) s(q * m), r(q * n); s[0] = true; s[1] = false; r = F.RevSparseJac(q, s); ok &= size_t( r.size() ) == q * n; ok &= r[0] == false; ok &= r[1] == true; return ok; } bool case_five() { bool ok = true; using namespace CppAD; // dimension of the domain space size_t n = 2; // dimension of the range space size_t m = 3; // independent variable vector CPPAD_TESTVECTOR(AD) X(n); X[0] = 2.; X[1] = 3.; Independent(X); // dependent variable vector CPPAD_TESTVECTOR(AD) Y(m); // check results vector CPPAD_TESTVECTOR( bool ) Check(m * n); // initialize index into Y size_t index = 0; // Y[0] only depends on X[0]; Y[index] = pow(X[0], 2.); Check[index * n + 0] = true; Check[index * n + 1] = false; index++; // Y[1] depends on X[1] Y[index] = pow(2., X[1]); Check[index * n + 0] = false; Check[index * n + 1] = true; index++; // Y[2] depends on X[0] and X[1] Y[index] = pow(X[0], X[1]); Check[index * n + 0] = true; Check[index * n + 1] = true; index++; // check final index assert( index == m ); // create function object F : X -> Y ADFun F(X, Y); // ----------------------------------------------------------------- // dependency matrix for the identity function CPPAD_TESTVECTOR( bool ) Py(m * m); size_t i, j; for(i = 0; i < m; i++) { for(j = 0; j < m; j++) Py[ i * m + j ] = (i == j); } // evaluate the dependency matrix for F(x) bool transpose = true; CPPAD_TESTVECTOR( bool ) Px(n * m); Px = F.RevSparseJac(m, Py, transpose); // check values for(i = 0; i < m; i++) { for(j = 0; j < n; j++) ok &= (Px[j * m + i] == Check[i * n + j]); } // --------------------------------------------------------- // dependency matrix for the identity function CPPAD_TESTVECTOR(std::set) Sy(m); for(i = 0; i < m; i++) { assert( Sy[i].empty() ); Sy[i].insert(i); } // evaluate the dependency matrix for F(x) CPPAD_TESTVECTOR(std::set) Sx(n); Sx = F.RevSparseJac(m, Sy, transpose); // check values bool found; for(i = 0; i < m; i++) { for(j = 0; j < n; j++) { found = Sx[j].find(i) != Sx[j].end(); ok &= (found == Check[i * n + j]); } } return ok; } } // END empty namespace bool rev_sparse_jac(void) { bool ok = true; ok &= case_one(); ok &= case_two(); ok &= case_three(); ok &= case_four(); ok &= case_five(); return ok; } ================================================ FILE: test_more/general/rev_two.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include bool RevTwo() { bool ok = true; using CppAD::AD; using CppAD::vector; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); size_t n = 2; vector< AD > X(n); X[0] = 1.; X[1] = 1.; Independent(X); size_t m = 1; vector< AD > Y(m); Y[0] = X[0] * X[0] + X[0] * X[1] + 2. * X[1] * X[1]; CppAD::ADFun F(X,Y); vector x(n); x[0] = .5; x[1] = 1.5; size_t L = 1; vector I(L); vector J(L); vector H(n); I[0] = 0; J[0] = 0; H = F.RevTwo(x, I, J); ok &= NearEqual(H[0], 2., eps99, eps99); ok &= NearEqual(H[1], 1., eps99, eps99); J[0] = 1; H = F.RevTwo(x, I, J); ok &= NearEqual(H[0], 1., eps99, eps99); ok &= NearEqual(H[1], 4., eps99, eps99); return ok; } ================================================ FILE: test_more/general/reverse.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old Reverse example now used just for valiadation testing */ # include namespace { // ---------------------------------------------------------- bool reverse_one(void) { bool ok = true; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector CPPAD_TESTVECTOR(AD) U(3); U[0] = 0.; U[1] = 1.; U[2] = 2.; Independent(U); // compute sum and product of elements in U AD Sum = 0.; AD Prod = 1.; size_t i; for(i = 0; i < 3; i++) { Sum += U[i]; Prod *= U[i]; } // dependent variable vector CPPAD_TESTVECTOR(AD) V(2); V[0] = Sum; V[1] = Prod; // V = f(U) ADFun f(U, V); // Evaluate ( v[0] * f_0 + v[1] * f_1 )^(1) [ u0 ] --------------- size_t p = 1; CPPAD_TESTVECTOR(double) v( f.Range() ); CPPAD_TESTVECTOR(double) u0( f.Domain() ); CPPAD_TESTVECTOR(double) r1( f.Domain() * p ); v[0] = 1.; v[1] = -1.; r1 = f.Reverse(p, v); // direct evaluation of gradients of components of f CPPAD_TESTVECTOR(double) g0(3), g1(3); u0[0] = Value(U[0]); u0[1] = Value(U[1]); u0[2] = Value(U[2]); g0[0] = 1.; g0[1] = 1.; g0[2] = 1.; g1[0] = u0[1]*u0[2]; g1[1] = u0[0]*u0[2]; g1[2] = u0[0]*u0[1]; // compare values for(i = 0; i < 3; i++) { ok &= NearEqual(r1[i] , v[0] * g0[i] + v[1] * g1[i], eps99, eps99); } // ------------------------------------------------------------------- // Define the function z(t, u0, u1) = f( u0 + u1 * t ) and evaluate // the first order Taylor coefficient column vector z(*, u0, u1) p = 1; CPPAD_TESTVECTOR(double) u1( f.Domain() ); u1[0] = 2.; u1[1] = -1.; u1[2] = 3.; f.Forward(p, u1); // Evaluate the derivatives with respect to u0 of the functions // order 0: v[0] * z_0 (0, u0, u1) + v[1] * z_1 (0, u0, u1) // order 1: v[0] * d/dt z_0 (0, u0, u1) + v[1] * d/dt z_1 (0, u0, u1) p = 2; CPPAD_TESTVECTOR(double) r2( f.Domain() * p ); v[0] = -.5; v[1] = .5; r2 = f.Reverse(p, v); // check derivative of the zero order term for(i = 0; i < 3; i++) { ok &= NearEqual(r2[p * i + 0] , v[0] * g0[i] + v[1] * g1[i], eps99, eps99); } /* The j-th component of the first order term is d/dt z_j(0, u0, u1) = f_j^{(1)} (u0) * u1 We use ei to denote the vector with its i-th component one and all the other components zero. The partial derivative of the j-th component of the first order term with respect u0[i] is ei * f_j^{(2)} ( u0 ) * u1 */ // direct evaluation of the Hessian f_1^{(2)} (u0) // (the Hessian f_0^{(2)} is identically zero) CPPAD_TESTVECTOR(double) H1(9); H1[0] = 0.; H1[1] = u0[2]; H1[2] = u0[1]; H1[3] = u0[2]; H1[4] = 0.; H1[5] = u0[0]; H1[6] = u0[1]; H1[7] = u0[0]; H1[8] = 0.; size_t j; for(i = 0; i < 3; i++) { double sum = 0.; for(j = 0; j < 3; j++) sum += H1[i * 3 + j] * u1[j]; // note term corresponding to v[0] is zero ok &= NearEqual(r2[p * i + 1], v[1] * sum, eps99, eps99); } return ok; } // define the template function reverse_any_cases in empty namespace template bool reverse_any_cases(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 3; CPPAD_TESTVECTOR(AD) X(n); X[0] = 0.; X[1] = 1.; X[2] = 2.; // declare independent variables and start recording CppAD::Independent(X); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = X[0] * X[1] * X[2]; // create f : X -> Y and stop recording CppAD::ADFun f(X, Y); // define W(t, u) = (u_0 + dx_0*t)*(u_1 + dx_1*t)*(u_2 + dx_2*t) // use zero order forward to evaluate W0(u) = W(0, u) Vector u(n), W0(m); u[0] = 2.; u[1] = 3.; u[2] = 4.; W0 = f.Forward(0, u); double check; check = u[0]*u[1]*u[2]; ok &= NearEqual(W0[0] , check, eps99, eps99); // define W_t(t, u) = partial W(t, u) w.r.t t // W_t(t, u) = (u_0 + dx_0*t)*(u_1 + dx_1*t)*dx_2 // + (u_0 + dx_0*t)*(u_2 + dx_2*t)*dx_1 // + (u_1 + dx_1*t)*(u_2 + dx_2*t)*dx_0 // use first order forward mode to evaluate W1(u) = W_t(0, u) Vector dx(n), W1(m); dx[0] = .2; dx[1] = .3; dx[2] = .4; W1 = f.Forward(1, dx); check = u[0]*u[1]*dx[2] + u[0]*u[2]*dx[1] + u[1]*u[2]*dx[0]; ok &= NearEqual(W1[0], check, eps99, eps99); // define W_tt (t, u) = partial W_t(t, u) w.r.t t // W_tt(t, u) = 2*(u_0 + dx_0*t)*dx_1*dx_2 // + 2*(u_1 + dx_1*t)*dx_0*dx_2 // + 2*(u_3 + dx_3*t)*dx_0*dx_1 // use second order forward to evaluate W2(u) = 1/2 * W_tt(0, u) Vector ddx(n), W2(m); ddx[0] = ddx[1] = ddx[2] = 0.; W2 = f.Forward(2, ddx); check = u[0]*dx[1]*dx[2] + u[1]*dx[0]*dx[2] + u[2]*dx[0]*dx[1]; ok &= NearEqual(W2[0], check, eps99, eps99); // use third order reverse mode to evaluate derivatives size_t p = 3; Vector w(m), dw(n * p); w[0] = 1.; dw = f.Reverse(p, w); // check derivative of W0(u) w.r.t. u ok &= NearEqual(dw[0*p+0], u[1]*u[2], eps99, eps99); ok &= NearEqual(dw[1*p+0], u[0]*u[2], eps99, eps99); ok &= NearEqual(dw[2*p+0], u[0]*u[1], eps99, eps99); // check derivative of W1(u) w.r.t. u ok &= NearEqual(dw[0*p+1], u[1]*dx[2] + u[2]*dx[1], eps99, eps99); ok &= NearEqual(dw[1*p+1], u[0]*dx[2] + u[2]*dx[0], eps99, eps99); ok &= NearEqual(dw[2*p+1], u[0]*dx[1] + u[1]*dx[0], eps99, eps99); // check derivative of W2(u) w.r.t u ok &= NearEqual(dw[0*p+2], dx[1]*dx[2], eps99, eps99); ok &= NearEqual(dw[1*p+2], dx[0]*dx[2], eps99, eps99); ok &= NearEqual(dw[2*p+2], dx[0]*dx[1], eps99, eps99); return ok; } /* $comment rev_checkpoint.cpp$$ $spell Taylor $$ $section Reverse Mode General Case: Example and Test$$ $index general, reverse example$$ $index reverse, general example$$ $index example, general reverse$$ $index test, general reverse$$ $index composition, example$$ $index example, composition$$ $index test, composition$$ $head Purpose$$ Break a derivative computation into pieces and only store values at the interface of the pieces. In actual applications, there may be many functions, but for this example there are only two. The functions $latex F : \B{R}^2 \rightarrow \B{R}^2$$ and $latex G : \B{R}^2 \rightarrow \B{R}^2$$ defined by $latex \[ F(x) = \left( \begin{array}{c} x_0 x_1 \\ x_1 - x_0 \end{array} \right) \; , \; G(y) = \left( \begin{array}{c} y_0 - y_1 \\ y_1 y_0 \end{array} \right) \] $$ Another difference is that in actual applications, the memory corresponding to function objects not currently being used is sometimes returned to the system (see $cref/checkpoint/chkpoint_one/.cpp$$). $head Processing Steps$$ We apply reverse mode to compute the derivative of $latex H : \B{R}^2 \rightarrow \B{R}$$ is defined by $latex \[ \begin{array}{rcl} H(x) & = & G_0 [ F(x) ] + G_1 [ F(x) ] \\ & = & x_0 x_1 - ( x_1 - x_0 ) + x_0 x_1 ( x_1 - x_0 ) \\ & = & x_0 x_1 ( 1 - x_0 + x_1 ) - x_1 + x_0 \end{array} \] $$ Given the zero and first order Taylor coefficients $latex x^{(0)} $$ and $latex x^{(1)}$$, we use $latex X(t)$$, $latex Y(t)$$ and $latex Z(t)$$ for the corresponding functions; i.e., $latex \[ \begin{array}{rcl} X(t) & = & x^{(0)} + x^{(1)} t \\ Y(t) & = & F[X(t)] = y^{(0)} + y^{(1)} t + O(t^2) \\ Z(t) & = & G \{ F [ X(t) ] \} = z^{(0)} + z^{(1)} t + O(t^2) \\ h^{(0)} & = & z^{(0)}_0 + z^{(0)}_1 \\ h^{(1)} & = & z^{(1)}_0 + z^{(1)}_1 \end{array} \] $$ Here are the processing steps: $list number$$ Use forward mode on $latex F(x)$$ to compute $latex y^{(0)}$$ and $latex y^{(1)}$$ $lnext Use forward mode on $latex G(y)$$ to compute $latex z^{(0)}$$ and $latex z^{(1)}$$ $lnext Use reverse mode on $latex G(y)$$ to compute the derivative of $latex h^{(k)}$$ with respect to $latex y^{(0)}$$ and $latex y^{(1)}$$. $lnext Use reverse mode on $latex F(x)$$ to compute the derivative of $latex h^{(k)}$$ with respect to $latex x^{(0)}$$ and $latex x^{(1)}$$. $lend This uses the following relations for $latex k = 0 , 1$$: $latex \[ \begin{array}{rcl} \partial_{x(0)} h^{(k)} [ x^{(0)} , x^{(1)} ] & = & \partial_{y(0)} h^{(k)} [ y^{(0)} , y^{(1)} ] \partial_{x(0)} y^{(0)} [ x^{(0)} , x^{(1)} ] \\ & + & \partial_{y(1)} h^{(k)} [ y^{(0)} , y^{(1)} ] \partial_{x(0)} y^{(1)} [ x^{(0)} , x^{(1)} ] \\ \partial_{x(1)} h^{(k)} [ x^{(0)} , x^{(1)} ] & = & \partial_{y(0)} h^{(k)} [ y^{(0)} , y^{(1)} ] \partial_{x(1)} y^{(0)} [ x^{(0)} , x^{(1)} ] \\ & + & \partial_{y(1)} h^{(k)} [ y^{(0)} , y^{(1)} ] \partial_{x(1)} y^{(1)} [ x^{(0)} , x^{(1)} ] \end{array} \] $$ where $latex \partial_{x(0)}$$ denotes the partial with respect to $latex x^{(0)}$$. $end */ template Vector F_reverse_mul(const Vector& x) { Vector y(2); y[0] = x[0] * x[1]; y[1] = x[1] - x[0]; return y; } template Vector G_reverse_mul(const Vector& y) { Vector z(2); z[0] = y[0] - y[1]; z[1] = y[1] * y[0]; return z; } bool reverse_mul(void) { bool ok = true; double eps = 10. * CppAD::numeric_limits::epsilon(); using CppAD::AD; using CppAD::NearEqual; CppAD::ADFun f, g; // Record the function F(x) size_t n = 2; CPPAD_TESTVECTOR(AD) X(n), Y(n); X[0] = X[1] = 0.; CppAD::Independent(X); Y = F_reverse_mul(X); f.Dependent(X, Y); // Record the function G(x) CPPAD_TESTVECTOR(AD) Z(n); Y[0] = Y[1] = 0.; CppAD::Independent(Y); Z = G_reverse_mul(Y); g.Dependent(Y, Z); // argument and function values CPPAD_TESTVECTOR(double) x0(n), y0(n), z0(n); x0[0] = 1.; x0[1] = 2.; y0 = f.Forward(0, x0); z0 = g.Forward(0, y0); // check function value double check = x0[0] * x0[1] * (1. - x0[0] + x0[1]) - x0[1] + x0[0]; double h0 = z0[0] + z0[1]; ok &= NearEqual(h0, check, eps, eps); // first order Taylor coefficients CPPAD_TESTVECTOR(double) x1(n), y1(n), z1(n); x1[0] = 3.; x1[1] = 4.; y1 = f.Forward(1, x1); z1 = g.Forward(1, y1); // check first order Taylor coefficients check = x0[0] * x0[1] * (- x1[0] + x1[1]) - x1[1] + x1[0]; check += x1[0] * x0[1] * (1. - x0[0] + x0[1]); check += x0[0] * x1[1] * (1. - x0[0] + x0[1]); double h1 = z1[0] + z1[1]; ok &= NearEqual(h1, check, eps, eps); // ---------------------------------------------------------------- // dw^0 (y) = \partial_y^0 h^0 (y) // dw^1 (y) = \partial_y^1 h^0 (y) size_t p = 2; CPPAD_TESTVECTOR(double) w(n*p), dw(n*p); w[0*p+0] = 1.; // coefficient for z^0_0 w[1*p+0] = 1.; // coefficient for z^0_1 w[0*p+1] = 0.; // coefficient for z^1_0 w[1*p+1] = 0.; // coefficient for z^1_1 dw = g.Reverse(p, w); // dv^0 = dw^0 * \partial_x^0 y^0 (x) + dw^1 * \partial_x^0 y^1 (x) // dv^1 = dw^0 * \partial_x^1 y^0 (x) + dw^1 * \partial_x^1 y^1 (x) CPPAD_TESTVECTOR(double) dv(n*p); dv = f.Reverse(p, dw); // check partial of h^0 w.r.t x^0_0 check = x0[1] * (1. - x0[0] + x0[1]) + 1.; check -= x0[0] * x0[1]; ok &= NearEqual(dv[0*p+0], check, eps, eps); // check partial of h^0 w.r.t x^0_1 check = x0[0] * (1. - x0[0] + x0[1]) - 1.; check += x0[0] * x0[1]; ok &= NearEqual(dv[1*p+0], check, eps, eps); // check partial of h^0 w.r.t x^1_0 and x^1_1 check = 0.; ok &= NearEqual(dv[0*p+1], check, eps, eps); ok &= NearEqual(dv[1*p+1], check, eps, eps); // ---------------------------------------------------------------- // dw^0 (y) = \partial_y^0 h^1 (y) // dw^1 (y) = \partial_y^1 h^1 (y) w[0*p+0] = 0.; // coefficient for z^0_0 w[1*p+0] = 0.; // coefficient for z^0_1 w[0*p+1] = 1.; // coefficient for z^1_0 w[1*p+1] = 1.; // coefficient for z^1_1 dw = g.Reverse(p, w); // dv^0 = dw^0 * \partial_x^0 y^0 (x) + dw^1 * \partial_x^0 y^1 (x) // dv^1 = dw^0 * \partial_x^1 y^0 (x) + dw^1 * \partial_x^1 y^1 (x) dv = f.Reverse(p, dw); // check partial of h^1 w.r.t x^0_0 check = x0[1] * (- x1[0] + x1[1]); check -= x1[0] * x0[1]; check += x1[1] * (1. - x0[0] + x0[1]) - x0[0] * x1[1]; ok &= NearEqual(dv[0*p+0], check, eps, eps); // check partial of h^1 w.r.t x^0_1 check = x0[0] * (- x1[0] + x1[1]); check += x1[0] * (1. - x0[0] + x0[1]) + x1[0] * x0[1]; check += x0[0] * x1[1]; ok &= NearEqual(dv[1*p+0], check, eps, eps); // check partial of h^1 w.r.t x^1_0 // (by reverse mode identity is equal to partial h^0 w.r.t. x^0_0) check = 1. - x0[0] * x0[1]; check += x0[1] * (1. - x0[0] + x0[1]); ok &= NearEqual(dv[0*p+1], check, eps, eps); // check partial of h^1 w.r.t x^1_1 // (by reverse mode identity is equal to partial h^0 w.r.t. x^0_1) check = x0[0] * x0[1] - 1.; check += x0[0] * (1. - x0[0] + x0[1]); ok &= NearEqual(dv[1*p+1], check, eps, eps); return ok; } // ---------------------------------------------------------------------------- bool duplicate_dependent_var(void) { bool ok = true; using CppAD::AD; // f(x) = [ y_0, y_1 ] = [ x_0 * x_0 , x_0 * x_0 ] size_t nx = 1; size_t ny = 2; CPPAD_TESTVECTOR( AD ) ax(nx), ay(ny); ax[0] = 1.0; CppAD::Independent(ax); ay[0] = ax[0] * ax[0]; ay[1] = ay[0]; CppAD::ADFun f(ax, ay); // // Forward zero CPPAD_TESTVECTOR(double) x0(nx), x1(nx), yk(ny); x0[0] = 3.0; yk = f.Forward(0, x0); ok &= yk[0] == x0[0] * x0[0]; ok &= yk[1] == yk[0]; // // Forward one x1[0] = 1.0; yk = f.Forward(1, x1); ok &= yk[0] == 2.0 * x0[0]; ok &= yk[1] == yk[0]; // // Reverse two size_t q = 2; // number of orders CPPAD_TESTVECTOR(double) w(ny * q), dw(nx * q); for(size_t i = 0; i < ny; ++i) { w[ i * q + 0 ] = 0.0; // derivative w.r.t. zero order coefficient w[ i * q + 1 ] = 1.0; // derivative w.r.t. first order coefficient } dw = f.Reverse(2, w); // derivative w.r.t zero order coefficients ok &= dw[nx * 0 + 0] == 4.0; // derivative w.r.t first order coefficients ok &= dw[nx * 0 + 1] == 4.0 * x0[0]; // return ok; } } // End empty namespace # include # include bool reverse(void) { bool ok = true; ok &= reverse_one(); ok &= reverse_mul(); ok &= duplicate_dependent_var(); ok &= reverse_any_cases< CppAD::vector >(); ok &= reverse_any_cases< std::vector >(); ok &= reverse_any_cases< std::valarray >(); return ok; } ================================================ FILE: test_more/general/romberg_one.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* old romberg_one.cpp example / test $spell Romberg $$ $section One Dimensional Romberg Integration: Example and Test$$ $index Romberg, example$$ $index example, Romberg$$ $index test, Romberg$$ old verbatim%example/romberg_one.cpp%0%// BEGIN C++%// END C++%1%$$ $end */ // BEGIN C++ # include namespace { class Fun { private: const size_t degree; public: // constructor Fun(size_t degree_) : degree(degree_) { } // function F(x) = x^degree template Float operator () (const Float &x) { size_t i; Float f(1); for(i = 0; i < degree; i++) f *= x; return f; } }; template bool RombergOneCase(void) { bool ok = true; size_t i; size_t degree = 4; Fun F(degree); // arguments to RombergOne Float a(0); Float b(1); Float r; size_t n = 4; Float e; size_t p; // int_a^b F(x) dx = // [ b^(degree+1) - a^(degree+1) ] / (degree+1) Float bpow(1); Float apow(1); for(i = 0; i <= degree; i++) { bpow *= b; apow *= a; } Float check = (bpow - apow) / Float(degree+1); // step size corresponding to r Float step = (b - a) / exp(log(Float(2.))*Float(n-1)); // step size corresponding to error estimate step *= Float(2.); // step size raised to a power Float spow = Float(1); for(p = 0; p < n; p++) { spow = spow * step * step; r = CppAD::RombergOne(F, a, b, n, p, e); ok &= e < double(degree+1) * spow; ok &= CppAD::NearEqual(check, r, Float(0.), e); } return ok; } } bool RombergOne(void) { bool ok = true; using CppAD::AD; ok &= RombergOneCase(); ok &= RombergOneCase< AD >(); ok &= RombergOneCase< AD< AD > >(); return ok; } // END C++ ================================================ FILE: test_more/general/rosen_34.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old OdeImplicit example now used just for valiadation testing of Rosen34 */ // BEGIN C++ # include # include # include /* Case where x[0](0) = 1, x[0]'(t) = - w[0] * x[0](t) x[1](0) = 1, x[1]'(t) = - w[1] * x[1](t) x[2](0) = 0, x[2]'(t) = w[2] * t x[0](t) = exp( - w[0] * t ) x[1](t) = exp( - w[1] * t ) x[2](t) = w[2] * t^2 / 2 */ namespace { // BEGIN Empty namespace class TestFun { public: TestFun(const CPPAD_TESTVECTOR(CppAD::AD) &w_) { w.resize( w_.size() ); w = w_; } void Ode( const CppAD::AD &t, const CPPAD_TESTVECTOR(CppAD::AD) &x, CPPAD_TESTVECTOR(CppAD::AD) &f) { f[0] = - w[0] * x[0]; f[1] = - w[1] * x[1]; f[2] = w[2] * t; } void Ode_ind( const CppAD::AD &t, const CPPAD_TESTVECTOR(CppAD::AD) &x, CPPAD_TESTVECTOR(CppAD::AD) &f_t) { f_t[0] = 0.; f_t[1] = 0.; f_t[2] = w[2]; } void Ode_dep( const CppAD::AD &t, const CPPAD_TESTVECTOR(CppAD::AD) &x, CPPAD_TESTVECTOR(CppAD::AD) &f_x) { f_x[0] = - w[0]; f_x[1] = 0.; f_x[2] = 0.; f_x[3] = 0.; f_x[4] = - w[1]; f_x[5] = 0.; f_x[6] = 0.; f_x[7] = 0.; f_x[8] = 0.; } private: CPPAD_TESTVECTOR(CppAD::AD) w; }; } // END empty namespace bool Rosen34(void) { bool ok = true; using namespace CppAD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); CPPAD_TESTVECTOR(AD) x(3); CPPAD_TESTVECTOR(AD) w(3); size_t n = 3; size_t nstep = 20; AD t0 = 0.; AD t1 = 1.; // set independent variables size_t i; for(i = 0; i < n; i++) w[i] = double(100 * i + 1); Independent(w); // construct the function object using the independent variables TestFun fun(w); // initial value of x CPPAD_TESTVECTOR(AD) xini(3); xini[0] = 1.; xini[1] = 1.; xini[2] = 0.; // integrate the differential equation x = Rosen34(fun, nstep, t0, t1, xini); // create f : w -> x and vectors for evaluating derivatives ADFun f(w, x); CPPAD_TESTVECTOR(double) q( f.Domain() ); CPPAD_TESTVECTOR(double) r( f.Range() ); // check function values AD x0 = exp( - w[0] * t1 ); ok &= NearEqual(x[0], x0, 0., 1. / double(nstep * nstep) ); AD x1 = exp( - w[1] * t1 ); ok &= NearEqual(x[1], x1, 0., 1. / double(nstep * nstep) ); AD x2 = w[2] * t1 * t1 / 2.; ok &= NearEqual(x[2], x2, eps99, eps99); // check dx[0] / dw[0] for(i = 0; i < size_t(w.size()); i++) q[i] = 0.; q[0] = 1.; r = f.Forward(1, q); ok &= NearEqual(r[0], - w[0] * x0, 0., 1. / double(nstep * nstep) ); // check dx[1] / dw[1] q[0] = 0.; q[1] = 1.; r = f.Forward(1, q); ok &= NearEqual(r[1], - w[1] * x1, 0., 1. / double(nstep * nstep) ); // check dx[2] / dw[2] q[1] = 0.; q[2] = 1.; r = f.Forward(1, q); ok &= NearEqual(r[2], x2 / w[2], eps99, eps99); return ok; } // END C++ ================================================ FILE: test_more/general/runge_45.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old OdeRunge example now used just for valiadation testing of Runge45 */ # include # include # include namespace { // BEGIN Empty namespace class TestFun { public: TestFun(const CPPAD_TESTVECTOR(CppAD::AD) &w_) { w.resize( w_.size() ); w = w_; } void Ode( const CppAD::AD &t, const CPPAD_TESTVECTOR(CppAD::AD) &x, CPPAD_TESTVECTOR(CppAD::AD) &f) { using CppAD::exp; size_t n = x.size(); size_t i; f[0] = 0.; for(i = 1; i < n-1; i++) f[i] = w[i] * x[i-1]; f[n-1] = x[0] * x[1]; } private: CPPAD_TESTVECTOR(CppAD::AD) w; }; } // END Empty namespace bool Runge45(void) { bool ok = true; using namespace CppAD; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); size_t i; size_t j; size_t k; size_t n = 6; size_t m = n - 1; CPPAD_TESTVECTOR(AD) x(n); AD t0 = 0.; AD t1 = 2.; size_t nstep = 2; // vector of independent variables CPPAD_TESTVECTOR(AD) w(m); for(i = 0; i < m; i++) w[i] = double(i); Independent(w); // construct function object using independent variables TestFun fun(w); // initial value of x CPPAD_TESTVECTOR(AD) x0(n); for(i = 0; i < n; i++) x0[i] = 0.; x0[0] = exp( w[0] ); // solve the differential equation x = Runge45(fun, nstep, t0, t1, x0); // create f : w -> x and vectors for evaluating derivatives ADFun f(w, x); CPPAD_TESTVECTOR(double) q( f.Domain() ); CPPAD_TESTVECTOR(double) r( f.Range() ); // for i < n-1, // x[i](2) = exp( w[0] ) * (w[1] / 1) * ... * (w[i] / i) * 2^i AD xi2 = exp(w[0]); for(i = 0; i < n-1; i++) { ok &= NearEqual(x[i], xi2, eps99, eps99); if( i < n-2 ) xi2 *= w[i+1] * 2. / double(i+1); } // x[n-1](2) = exp(2 * w[0]) * w[1] * 2^2 / 2 xi2 = exp(2. * w[0]) * w[1] * 2.; ok &= NearEqual(x[n-1], xi2, eps99, eps99); // the partial of x[i](2) with respect to w[j] is // x[i](2) / w[j] if 0 < j <= i < n-1 // x[i](2) if j == 0 and i < n-1 // 2*x[i](2) if j == 0 and i = n-1 // x[i](2) / w[j] if j == 1 and i = n-1 // zero otherwise for(i = 0; i < n-1; i++) { // compute partials of x[i] for(k = 0; k < n; k++) r[k] = 0.; r[i] = 1.; q = f.Reverse(1,r); for(j = 0; j < m; j++) { // check partial of x[i] w.r.t w[j] if (j == 0 ) ok &= NearEqual(q[j], x[i], eps99, eps99); else if( j <= i ) ok &= NearEqual( q[j], x[i]/w[j], 1e-14, 1e-14); else ok &= NearEqual(q[j], 0., eps99, eps99); } } // compute partials of x[n-1] i = n-1; for(k = 0; k < n; k++) r[k] = 0.; r[i] = 1.; q = f.Reverse(1,r); for(j = 0; j < m; j++) { // check partial of x[n-1] w.r.t w[j] if (j == 0 ) ok &= NearEqual(q[j], 2.*x[i], eps99, eps99); else if( j == 1 ) ok &= NearEqual( q[j], x[i]/w[1], 1e-14, 1e-14); else ok &= NearEqual(q[j], 0., eps99, eps99); } return ok; } ================================================ FILE: test_more/general/simple_vector.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old simple vector example now used just for validation testing */ // BEGIN C++ // need this to define size_t # include // # include // # define MySimpleVector std::vector // # include // # define MySimpleVector std::valarray # include # define MySimpleVector CppAD::vector // Assuming Boost (http://www.boost.org) is installed // # include // # define MySimpleVector boost::numeric::ublas::vector // A special type of element that counts assignment operations class MyInt { public: // constructors MyInt(void) : count(0), value(0) { } MyInt(int _value) : count(0), value(_value) { } // assignment operator MyInt& operator=(const MyInt &x) { value = x.value; count++; return *this; } // equality operator bool operator==(const MyInt &x) const { return value == x.value; } int Value(void) const { return value; } size_t Count(void) const { return count; } private: size_t count; // count number of assignments int value; // value of this object }; // Test of a Simple Vector template class bool SimpleVector(void) { bool ok = true; typedef MySimpleVector vector; // class we are testing typedef vector::value_type myInt; // type of elements vector x; // default constructor ok &= (x.size() == 0); x.resize(2); // resize and set element assignment ok &= (x.size() == 2); x[0] = myInt(0); x[1] = myInt(1); vector y(2); // sizing constructor ok &= (y.size() == 2); const vector z(x); // copy constructor and const element access ok &= (z.size() == 2); ok &= ( (z[0] == myInt(0)) && (z[1] == myInt(1)) ); // check that vector assignment x[0] = 2; // modify so that assignment changes x size_t x0count = x[0].Count(); // store initial counts size_t y1count = y[1].Count(); size_t z0count = z[0].Count(); x = y = z; // vector assignment // check resulting values ok &= ( (x[0] == myInt(0)) && (x[1] == myInt(1)) ); ok &= ( (y[0] == myInt(0)) && (y[1] == myInt(1)) ); ok &= ( (z[0] == myInt(0)) && (z[1] == myInt(1)) ); // check that MyInt assignment was called (not raw memory copy) ok &= (x[0].Count() == x0count + 1); ok &= (y[1].Count() == y1count + 1); ok &= (z[0].Count() == z0count); return ok; } // END C++ ================================================ FILE: test_more/general/sin.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old example now just used for validation testing. */ # include bool Sin(void) { bool ok = true; using CppAD::sin; using CppAD::cos; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector CPPAD_TESTVECTOR(AD) U(1); U[0] = 1.; Independent(U); // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); Z[0] = sin(U[0]); // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v(1); CPPAD_TESTVECTOR(double) w(1); // check value double sin_u = sin( Value(U[0]) ); double cos_u = cos( Value(U[0]) ); ok &= NearEqual(sin_u, Value(Z[0]), eps99 , eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; v[0] = 1.; for(j = 1; j < p; j++) { w = f.Forward(j, v); double value; if( j % 4 == 1 ) value = cos_u; else if( j % 4 == 2 ) value = -sin_u; else if( j % 4 == 3 ) value = -cos_u; else value = sin_u; jfac *= double(j); ok &= NearEqual(w[0], value/jfac, eps99, eps99); // d^jz/du^j v[0] = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r(p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; for(j = 0; j < p; j++) { double value; if( j % 4 == 0 ) value = cos_u; else if( j % 4 == 1 ) value = -sin_u; else if( j % 4 == 2 ) value = -cos_u; else value = sin_u; ok &= NearEqual(r[j], value/jfac, eps99, eps99); // d^jz/du^j jfac *= double(j + 1); } return ok; } ================================================ FILE: test_more/general/sin_cos.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* Comprehensive test of Trigonometric and Hyperbolic Sine and Cosine */ # include # include namespace { // Begin empty namespace bool Sin(void) { bool ok = true; using CppAD::sin; using CppAD::cos; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector double x = .5; double y = .8; CPPAD_TESTVECTOR(AD) X(2); X[0] = x; X[1] = y; Independent(X); // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); AD U = X[0] * X[1]; Z[0] = sin( U ); // create f: X -> Z and vectors used for derivative calculations // f(x, y) = sin(x, y) ADFun f(X, Z); CPPAD_TESTVECTOR(double) v( 2 ); CPPAD_TESTVECTOR(double) w( 1 ); // check value double sin_u = sin( Value(U) ); double cos_u = cos( Value(U) ); ok &= NearEqual(sin_u, Value(Z[0]), eps99 , eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; v[0] = 1.; // differential w.r.t. x v[1] = 0; // differential w.r.t. y double yj = 1; // y^j for(j = 1; j < p; j++) { w = f.Forward(j, v); // compute j-th power of y yj *= y ; // compute j-th derivartive of sin function double sinj; if( j % 4 == 1 ) sinj = cos_u; else if( j % 4 == 2 ) sinj = -sin_u; else if( j % 4 == 3 ) sinj = -cos_u; else sinj = sin_u; jfac *= double(j); // check j-th derivative of z w.r.t x ok &= NearEqual(jfac*w[0], sinj * yj, eps99 , eps99); v[0] = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r( 2 * p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; yj = 1.; double sinjp = 0.; for(j = 0; j < p; j++) { double sinj = sinjp; // compute j+1 derivative of sin function if( j % 4 == 0 ) sinjp = cos_u; else if( j % 4 == 1 ) sinjp = -sin_u; else if( j % 4 == 2 ) sinjp = -cos_u; else sinjp = sin_u; // derivative w.r.t x of sin^{(j)} (x * y) * y^j ok &= NearEqual(jfac*r[0+j], sinjp * yj * y, eps99 , eps99); // derivative w.r.t y of sin^{(j)} (x * y) * y^j double value = sinjp * yj * x + double(j) * sinj * yj / y; ok &= NearEqual(r[p+j], value/jfac, eps99, eps99); jfac *= double(j + 1); yj *= y; } return ok; } bool Cos(void) { bool ok = true; using CppAD::sin; using CppAD::cos; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector double x = .5; double y = .8; CPPAD_TESTVECTOR(AD) X(2); X[0] = x; X[1] = y; Independent(X); // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); AD U = X[0] * X[1]; Z[0] = cos( U ); // create f: X -> Z and vectors used for derivative calculations // f(x, y) = cos(x, y) ADFun f(X, Z); CPPAD_TESTVECTOR(double) v( 2 ); CPPAD_TESTVECTOR(double) w( 1 ); // check value double sin_u = sin( Value(U) ); double cos_u = cos( Value(U) ); ok &= NearEqual(cos_u, Value(Z[0]), eps99 , eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; v[0] = 1.; // differential w.r.t. x v[1] = 0; // differential w.r.t. y double yj = 1; // y^j for(j = 1; j < p; j++) { w = f.Forward(j, v); // compute j-th power of y yj *= y ; // compute j-th derivartive of cos function double cosj; if( j % 4 == 1 ) cosj = -sin_u; else if( j % 4 == 2 ) cosj = -cos_u; else if( j % 4 == 3 ) cosj = sin_u; else cosj = cos_u; jfac *= double(j); // check j-th derivative of z w.r.t x ok &= NearEqual(jfac*w[0], cosj * yj, eps99 , eps99); v[0] = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r( 2 * p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; yj = 1.; double cosjp = 0.; for(j = 0; j < p; j++) { double cosj = cosjp; // compute j+1 derivative of cos function if( j % 4 == 0 ) cosjp = -sin_u; else if( j % 4 == 1 ) cosjp = -cos_u; else if( j % 4 == 2 ) cosjp = sin_u; else cosjp = cos_u; // derivative w.r.t x of cos^{(j)} (x * y) * y^j ok &= NearEqual(jfac*r[0+j], cosjp * yj * y, eps99 , eps99); // derivative w.r.t y of cos^{(j)} (x * y) * y^j double value = cosjp * yj * x + double(j) * cosj * yj / y; ok &= NearEqual(r[p+j], value/jfac, eps99, eps99); jfac *= double(j + 1); yj *= y; } return ok; } bool Cosh(void) { bool ok = true; using CppAD::sinh; using CppAD::cosh; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector double x = .5; double y = .8; CPPAD_TESTVECTOR(AD) X(2); X[0] = x; X[1] = y; Independent(X); // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); AD U = X[0] * X[1]; Z[0] = cosh( U ); // create f: X -> Z and vectors used for derivative calculations // f(x, y) = cosh(x, y) ADFun f(X, Z); CPPAD_TESTVECTOR(double) v( 2 ); CPPAD_TESTVECTOR(double) w( 1 ); // check value double sinh_u = sinh( Value(U) ); double cosh_u = cosh( Value(U) ); ok &= NearEqual(cosh_u, Value(Z[0]), eps99 , eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; v[0] = 1.; // differential w.r.t. x v[1] = 0; // differential w.r.t. y double yj = 1; // y^j for(j = 1; j < p; j++) { w = f.Forward(j, v); // compute j-th power of y yj *= y ; // compute j-th derivartive of cosh function double coshj; if( j % 2 == 1 ) coshj = sinh_u; else coshj = cosh_u; jfac *= double(j); // check j-th derivative of z w.r.t x ok &= NearEqual(jfac*w[0], coshj * yj, eps99 , eps99); v[0] = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r( 2 * p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; yj = 1.; double coshjp = 0.; for(j = 0; j < p; j++) { double coshj = coshjp; // compute j+1 derivative of cosh function if( j % 2 == 0 ) coshjp = sinh_u; else coshjp = cosh_u; // derivative w.r.t x of cosh^{(j)} (x * y) * y^j ok &= NearEqual(jfac*r[0+j], coshjp * yj * y, eps99 , eps99); // derivative w.r.t y of cosh^{(j)} (x * y) * y^j double value = coshjp * yj * x + double(j) * coshj * yj / y; ok &= NearEqual(r[p+j], value/jfac, eps99, eps99); jfac *= double(j + 1); yj *= y; } return ok; } bool Sinh(void) { bool ok = true; using CppAD::sinh; using CppAD::cosh; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector double x = .5; double y = .8; CPPAD_TESTVECTOR(AD) X(2); X[0] = x; X[1] = y; Independent(X); // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); AD U = X[0] * X[1]; Z[0] = sinh( U ); // create f: X -> Z and vectors used for derivative calculations // f(x, y) = sinh(x, y) ADFun f(X, Z); CPPAD_TESTVECTOR(double) v( 2 ); CPPAD_TESTVECTOR(double) w( 1 ); // check value double sinh_u = sinh( Value(U) ); double cosh_u = cosh( Value(U) ); ok &= NearEqual(sinh_u, Value(Z[0]), eps99 , eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; v[0] = 1.; // differential w.r.t. x v[1] = 0; // differential w.r.t. y double yj = 1; // y^j for(j = 1; j < p; j++) { w = f.Forward(j, v); // compute j-th power of y yj *= y ; // compute j-th derivartive of sinh function double sinhj; if( j % 2 == 1 ) sinhj = cosh_u; else sinhj = sinh_u; jfac *= double(j); // check j-th derivative of z w.r.t x ok &= NearEqual(jfac*w[0], sinhj * yj, eps99 , eps99); v[0] = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r( 2 * p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; yj = 1.; double sinhjp = 0.; for(j = 0; j < p; j++) { double sinhj = sinhjp; // compute j+1 derivative of sinh function if( j % 2 == 0 ) sinhjp = cosh_u; else sinhjp = sinh_u; // derivative w.r.t x of sinh^{(j)} (x * y) * y^j ok &= NearEqual(jfac*r[0+j], sinhjp * yj * y, eps99 , eps99); // derivative w.r.t y of sinh^{(j)} (x * y) * y^j double value = sinhjp * yj * x + double(j) * sinhj * yj / y; ok &= NearEqual(r[p+j], value/jfac, eps99, eps99); jfac *= double(j + 1); yj *= y; } return ok; } } // End empty namespace bool SinCos(void) { bool ok = Sin() && Cos() && Cosh() && Sinh(); return ok; } ================================================ FILE: test_more/general/sinh.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old example now just used for validation testing. */ # include bool Sinh(void) { bool ok = true; using CppAD::sinh; using CppAD::cosh; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector CPPAD_TESTVECTOR(AD) U(1); U[0] = 1.; Independent(U); // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); Z[0] = sinh(U[0]); // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v(1); CPPAD_TESTVECTOR(double) w(1); // check value double sin_u = sinh( Value(U[0]) ); double cos_u = cosh( Value(U[0]) ); ok &= NearEqual(sin_u, Value(Z[0]), eps99 , eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; v[0] = 1.; for(j = 1; j < p; j++) { w = f.Forward(j, v); double value; if( j % 2 == 1 ) value = cos_u; else value = sin_u; jfac *= double(j); ok &= NearEqual(w[0], value/jfac, eps99, eps99); // d^jz/du^j v[0] = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r(p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; for(j = 0; j < p; j++) { double value; if( j % 2 == 0 ) value = cos_u; else value = sin_u; ok &= NearEqual(r[j], value/jfac, eps99, eps99); // d^jz/du^j jfac *= double(j + 1); } return ok; } ================================================ FILE: test_more/general/sparse_hessian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old SparseHessian example */ # include namespace { // --------------------------------------------------------- bool rc_tridiagonal(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; size_t i, j, k, ell; double eps10 = 10. * CppAD::epsilon(); size_t n = 12; // must be greater than or equal 3; see n_sweep. size_t m = n - 1; CPPAD_TESTVECTOR(AD) a_x(n), a_y(m); CPPAD_TESTVECTOR(double) x(n), check(n * n), w(m); for(j = 0; j < n; j++) a_x[j] = x[j] = double(j+1); // declare independent variables and start taping CppAD::Independent(a_x); for(ell = 0; ell < n * n; ell++) check[ell] = 0.0; for(i = 0; i < m; i++) { AD diff = a_x[i+1] - a_x[i]; a_y[i] = 0.5 * diff * diff / double(i+2); w[i] = double(i+1); ell = i * n + i; check[ell] += w[i] / double(i+2); ell = (i+1) * n + i+1; check[ell] += w[i] / double(i+2); ell = (i+1) * n + i; check[ell] -= w[i] / double(i+2); ell = i * n + i+1; check[ell] -= w[i] / double(i+2); } // create f: x -> y CppAD::ADFun f(a_x, a_y); // determine the sparsity pattern p for Hessian of w^T f typedef CppAD::vector< std::set > SetVector; SetVector p_r(n); for(j = 0; j < n; j++) p_r[j].insert(j); f.ForSparseJac(n, p_r); // SetVector p_s(1); for(i = 0; i < m; i++) if( w[i] != 0 ) p_s[0].insert(i); SetVector p_h = f.RevSparseHes(n, p_s); // requires the upper triangle of the Hessian size_t K = 2 * n - 1; CPPAD_TESTVECTOR(size_t) r(K), c(K); CPPAD_TESTVECTOR(double) hes(K); k = 0; for(i = 0; i < n; i++) { r[k] = i; c[k] = i; k++; if( i < n-1 ) { r[k] = i; c[k] = i+1; k++; } } ok &= k == K; // test computing sparse Hessian CppAD::sparse_hessian_work work; size_t n_sweep = f.SparseHessian(x, w, p_h, r, c, hes, work); ok &= n_sweep == 3; for(k = 0; k < K; k++) { ell = r[k] * n + c[k]; ok &= NearEqual(check[ell], hes[k], eps10, eps10); ell = c[k] * n + r[k]; ok &= NearEqual(check[ell], hes[k], eps10, eps10); } return ok; } template bool bool_case() { bool ok = true; using CppAD::AD; using CppAD::NearEqual; size_t i, j, k; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 3; CPPAD_TESTVECTOR(AD) X(n); for(i = 0; i < n; i++) X[i] = AD (0); // declare independent variables and starting recording CppAD::Independent(X); size_t m = 1; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = X[0] * X[0] + X[0] * X[1] + X[1] * X[1] + X[2] * X[2]; // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // new value for the independent variable vector BaseVector x(n); for(i = 0; i < n; i++) x[i] = double(i); // second derivative of y[1] BaseVector w(m); w[0] = 1.; BaseVector h( n * n ); h = f.SparseHessian(x, w); /* [ 2 1 0 ] h = [ 1 2 0 ] [ 0 0 2 ] */ BaseVector check(n * n); check[0] = 2.; check[1] = 1.; check[2] = 0.; check[3] = 1.; check[4] = 2.; check[5] = 0.; check[6] = 0.; check[7] = 0.; check[8] = 2.; for(k = 0; k < n * n; k++) ok &= NearEqual(check[k], h[k], eps99, eps99 ); // determine the sparsity pattern p for Hessian of w^T F BoolVector r(n * n); for(j = 0; j < n; j++) { for(k = 0; k < n; k++) r[j * n + k] = false; r[j * n + j] = true; } f.ForSparseJac(n, r); // BoolVector s(m); for(i = 0; i < m; i++) s[i] = w[i] != 0; BoolVector p = f.RevSparseHes(n, s); // test passing sparsity pattern h = f.SparseHessian(x, w, p); for(k = 0; k < n * n; k++) ok &= NearEqual(check[k], h[k], eps99, eps99 ); return ok; } template bool set_case() { bool ok = true; using CppAD::AD; using CppAD::NearEqual; size_t i, j, k; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 3; CPPAD_TESTVECTOR(AD) X(n); for(i = 0; i < n; i++) X[i] = AD (0); // declare independent variables and starting recording CppAD::Independent(X); size_t m = 1; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = X[0] * X[0] + X[0] * X[1] + X[1] * X[1] + X[2] * X[2]; // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // new value for the independent variable vector BaseVector x(n); for(i = 0; i < n; i++) x[i] = double(i); // second derivative of y[1] BaseVector w(m); w[0] = 1.; BaseVector h( n * n ); h = f.SparseHessian(x, w); /* [ 2 1 0 ] h = [ 1 2 0 ] [ 0 0 2 ] */ BaseVector check(n * n); check[0] = 2.; check[1] = 1.; check[2] = 0.; check[3] = 1.; check[4] = 2.; check[5] = 0.; check[6] = 0.; check[7] = 0.; check[8] = 2.; for(k = 0; k < n * n; k++) ok &= NearEqual(check[k], h[k], eps99, eps99 ); // determine the sparsity pattern p for Hessian of w^T F SetVector r(n); for(j = 0; j < n; j++) r[j].insert(j); f.ForSparseJac(n, r); // SetVector s(1); for(i = 0; i < m; i++) if( w[i] != 0 ) s[0].insert(i); SetVector p = f.RevSparseHes(n, s); // test passing sparsity pattern h = f.SparseHessian(x, w, p); for(k = 0; k < n * n; k++) ok &= NearEqual(check[k], h[k], eps99, eps99 ); return ok; } } // End empty namespace # include # include bool sparse_hessian(void) { bool ok = true; ok &= rc_tridiagonal(); // --------------------------------------------------------------- // vector of bool cases ok &= bool_case< CppAD::vector , CppAD::vectorBool >(); ok &= bool_case< std::vector , CppAD::vector >(); ok &= bool_case< std::valarray , std::vector >(); // --------------------------------------------------------------- // vector of set cases typedef std::vector< std::set > std_vector_set; typedef CppAD::vector< std::set > cppad_vector_set; // ok &= set_case< CppAD::vector, std_vector_set >(); ok &= set_case< std::valarray, std_vector_set >(); ok &= set_case< std::vector, cppad_vector_set >(); ok &= set_case< CppAD::vector, cppad_vector_set >(); // // According to section 26.3.2.3 of the 1998 C++ standard // a const valarray does not return references to its elements. // so do not include it in the testing for sets. // --------------------------------------------------------------- return ok; } ================================================ FILE: test_more/general/sparse_jac_work.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin sparse_jac_work.cpp@@ $spell Cpp Jacobian $$ $section Using Same Work With Different Levels of AD: Example and Test$$ $srcthisfile%0%// BEGIN C++%// END C++%1%$$ $end */ // BEGIN C++ # include bool sparse_jac_work(void) { bool ok = true; // using CppAD::AD; using CppAD::NearEqual; using CppAD::sparse_rc; using CppAD::sparse_rcv; // typedef AD a1double; typedef AD a2double; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR(a1double) a1vector; typedef CPPAD_TESTVECTOR(a2double) a2vector; typedef CPPAD_TESTVECTOR(size_t) s_vector; // // domain space vector size_t n = 4; a2vector a2x(n); a1vector a1x(n); for(size_t j = 0; j < n; j++) a2x[j] = a1x[j] = 0.0; // // declare independent variables and starting recording CppAD::Independent(a2x); // size_t m = 3; a2vector a2y(m); a2y[0] = a2x[0] + a2x[1]; a2y[1] = a2x[2] + a2x[3]; a2y[2] = a2x[0] + a2x[1] + a2x[2] + a2x[3] * a2x[3] / 2.; // // create f: x -> y for computation with a1double CppAD::ADFun a1f(a2x, a2y); // // create an identical function for computation with double CppAD::Independent(a1x); a1vector a1y = a1f.Forward(0, a1x); CppAD::ADFun f(a1x, a1y); // // new value for the independent variable vector d_vector x(n); for(size_t j = 0; j < n; j++) x[j] = double(j); /* [ 1 1 0 0 ] J(x) = [ 0 0 1 1 ] [ 1 1 1 x_3] */ // // row-major order values of J(x) size_t nnz = 8; s_vector check_row(nnz), check_col(nnz); d_vector check_val(nnz); for(size_t k = 0; k < nnz; k++) { // check_val if( k < 7 ) check_val[k] = 1.0; else check_val[k] = x[3]; // // check_row and check_col check_col[k] = k; if( k < 2 ) check_row[k] = 0; else if( k < 4 ) check_row[k] = 1; else { check_row[k] = 2; check_col[k] = k - 4; } } // // m by m identity matrix sparsity sparse_rc pattern_in(m, m, m); for(size_t k = 0; k < m; k++) pattern_in.set(k, k, k); // // sparsity for J(x) bool transpose = false; bool dependency = false; bool internal_bool = true; sparse_rc pattern_jac; f.rev_jac_sparsity( pattern_in, transpose, dependency, internal_bool, pattern_jac ); // // compute entire reverse mode Jacobian sparse_rcv subset( pattern_jac ); CppAD::sparse_jac_work work; std::string coloring = "cppad"; size_t n_sweep = f.sparse_jac_rev(x, subset, pattern_jac, coloring, work); ok &= n_sweep == 2; // const s_vector row( subset.row() ); const s_vector col( subset.col() ); const d_vector val( subset.val() ); s_vector row_major = subset.row_major(); ok &= subset.nnz() == nnz; for(size_t k = 0; k < nnz; k++) { ok &= row[ row_major[k] ] == check_row[k]; ok &= col[ row_major[k] ] == check_col[k]; ok &= val[ row_major[k] ] == check_val[k]; } // // test using work stored by previous sparse_jac_rev with f sparse_rc pattern_not_used; std::string coloring_not_used; n_sweep = f.sparse_jac_rev(x, subset, pattern_jac, coloring, work); ok &= n_sweep == 2; for(size_t k = 0; k < nnz; k++) { ok &= row[ row_major[k] ] == check_row[k]; ok &= col[ row_major[k] ] == check_col[k]; ok &= val[ row_major[k] ] == check_val[k]; } // // test using work stored by previous sparse_jac_rev with a1f sparse_rcv a1subset( pattern_jac ); for(size_t j = 0; j < n; ++j) a1x[j] = x[j]; n_sweep = a1f.sparse_jac_rev(a1x, a1subset, pattern_jac, coloring, work); ok &= n_sweep == 2; const a1vector a1val( a1subset.val() ); for(size_t k = 0; k < nnz; k++) { ok &= row[ row_major[k] ] == check_row[k]; ok &= col[ row_major[k] ] == check_col[k]; ok &= Value( a1val[ row_major[k] ] ) == check_val[k]; } // return ok; } // END C++ ================================================ FILE: test_more/general/sparse_jacobian.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old sparse Jacobian example */ # include namespace { // --------------------------------------------------------- bool rc_tridiagonal(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; size_t i, j, k, ell; double eps10 = 10. * CppAD::epsilon(); // domain space vector size_t n = 13; // must be greater than or equal 3 (see n_sweep below) CPPAD_TESTVECTOR(AD) X(n); CPPAD_TESTVECTOR(double) x(n); for(j = 0; j < n; j++) X[j] = x[j] = double(j+1); // declare independent variables and starting recording CppAD::Independent(X); size_t m = n; CPPAD_TESTVECTOR(AD) Y(m); CPPAD_TESTVECTOR(double) check(m * n ); for(ell = 0; ell < m * n; ell++) check[ell] = 0.0; size_t K = 0; for(i = 0; i < n; i++) { ell = i * n + i; Y[i] = double(ell+1) * 0.5 * X[i] * X[i]; check[ell] = double(ell+1) * x[i]; K++; if( i < n-1 ) { j = i + 1; ell = i * n + j; Y[i] += double(ell+1) * 0.5 * X[i+1] * X[i+1]; check[ell] = double(ell+1) * x[i+1]; K++; } if(i > 0 ) { j = i - 1; ell = i * n + j; Y[i] += double(ell+1) * 0.5 * X[i-1] * X[i-1]; check[ell] = double(ell+1) * x[i-1]; } } // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // sparsity pattern CppAD::vector< std::set > s(m), p(m); for(i = 0; i < m; i++) s[i].insert(i); p = f.RevSparseJac(m, s); // Request the upper triangle of the array CPPAD_TESTVECTOR(size_t) r(K), c(K); CPPAD_TESTVECTOR(double) jac(K); k = 0; for(i = 0; i < n; i++) { r[k] = i; c[k] = i; k++; if( i < n-1 ) { r[k] = i; c[k] = i+1; k++; } } ok &= K == k; CppAD::sparse_jacobian_work work; size_t n_sweep = f.SparseJacobianForward(x, p, r, c, jac, work); ok &= n_sweep == 3; for(k = 0; k < K; k++) { ell = r[k] * n + c[k]; ok &= NearEqual(check[ell], jac[k], eps10, eps10); } work.clear(); n_sweep = f.SparseJacobianReverse(x, p, r, c, jac, work); ok &= n_sweep == 3; for(k = 0; k < K; k++) { ell = r[k] * n + c[k]; ok &= NearEqual(check[ell], jac[k], eps10, eps10); } return ok; } template bool rc_set(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; size_t i, j, k, ell; double eps10 = 10. * CppAD::epsilon(); // domain space vector size_t n = 4; CPPAD_TESTVECTOR(AD) X(n); for(j = 0; j < n; j++) X[j] = AD (0); // declare independent variables and starting recording CppAD::Independent(X); size_t m = 3; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = 1.0*X[0] + 2.0*X[1]; Y[1] = 3.0*X[2] + 4.0*X[3]; Y[2] = 5.0*X[0] + 6.0*X[1] + 7.0*X[3]*X[3]/2.; // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // new value for the independent variable vector BaseVector x(n); for(j = 0; j < n; j++) x[j] = double(j); // Jacobian of y /* [ 1 2 0 0 ] jac = [ 0 0 3 4 ] [ 5 6 0 7*x_3] */ BaseVector check(m * n); check[0] = 1.; check[1] = 2.; check[2] = 0.; check[3] = 0.; check[4] = 0.; check[5] = 0.; check[6] = 3.; check[7] = 4.; check[8] = 5.; check[9] = 6.; check[10] = 0.; check[11] = 7.*x[3]; // sparsity pattern SetVector s(m), p(m); for(i = 0; i < m; i++) s[i].insert(i); p = f.RevSparseJac(m, s); // Use forward mode to compute columns 0 and 2 // (make sure order of rows and columns does not matter) CPPAD_TESTVECTOR(size_t) r(3), c(3); BaseVector jac(3); r[0] = 2; c[0] = 0; r[1] = 1; c[1] = 2; r[2] = 0; c[2] = 0; CppAD::sparse_jacobian_work work; size_t n_sweep = f.SparseJacobianForward(x, p, r, c, jac, work); for(k = 0; k < 3; k++) { ell = r[k] * n + c[k]; ok &= NearEqual(check[ell], jac[k], eps10, eps10); } ok &= (n_sweep == 1); // Use reverse mode to compute rows 0 and 1 // (make sure order of rows and columns does not matter) r.resize(4), c.resize(4); jac.resize(4); r[0] = 0; c[0] = 0; r[1] = 1; c[1] = 2; r[2] = 0; c[2] = 1; r[3] = 1; c[3] = 3; work.clear(); n_sweep = f.SparseJacobianReverse(x, p, r, c, jac, work); for(k = 0; k < 4; k++) { ell = r[k] * n + c[k]; ok &= NearEqual(check[ell], jac[k], eps10, eps10); } ok &= (n_sweep == 1); return ok; } template bool rc_bool(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; size_t j, k, ell; double eps10 = 10. * CppAD::epsilon(); // domain space vector size_t n = 4; CPPAD_TESTVECTOR(AD) X(n); for(j = 0; j < n; j++) X[j] = AD (0); // declare independent variables and starting recording CppAD::Independent(X); size_t m = 3; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = 1.0*X[0] + 2.0*X[1]; Y[1] = 3.0*X[2] + 4.0*X[3]; Y[2] = 5.0*X[0] + 6.0*X[1] + 7.0*X[3]*X[3]/2.; // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // new value for the independent variable vector BaseVector x(n); for(j = 0; j < n; j++) x[j] = double(j); // Jacobian of y /* [ 1 2 0 0 ] jac = [ 0 0 3 4 ] [ 5 6 0 7*x_3] */ BaseVector check(m * n); check[0] = 1.; check[1] = 2.; check[2] = 0.; check[3] = 0.; check[4] = 0.; check[5] = 0.; check[6] = 3.; check[7] = 4.; check[8] = 5.; check[9] = 6.; check[10] = 0.; check[11] = 7.*x[3]; BoolVector s(m * n); s[0] = true; s[1] = true; s[2] = false; s[3] = false; s[4] = false; s[5] = false; s[6] = true; s[7] = true; s[8] = true; s[9] = true; s[10] = false; s[11] = true; // Use forward mode to compute columns 0 and 2 // (make sure order of rows and columns does not matter) CPPAD_TESTVECTOR(size_t) r(3), c(3); BaseVector jac(3); r[0] = 2; c[0] = 0; r[1] = 1; c[1] = 2; r[2] = 0; c[2] = 0; CppAD::sparse_jacobian_work work; size_t n_sweep = f.SparseJacobianForward(x, s, r, c, jac, work); for(k = 0; k < 3; k++) { ell = r[k] * n + c[k]; ok &= NearEqual(check[ell], jac[k], eps10, eps10); } ok &= (n_sweep == 1); // Use reverse mode to compute rows 0 and 1 // (make sure order of rows and columns does not matter) r.resize(4), c.resize(4); jac.resize(4); r[0] = 0; c[0] = 0; r[1] = 1; c[1] = 2; r[2] = 0; c[2] = 1; r[3] = 1; c[3] = 3; work.clear(); n_sweep = f.SparseJacobianReverse(x, s, r, c, jac, work); for(k = 0; k < 4; k++) { ell = r[k] * n + c[k]; ok &= NearEqual(check[ell], jac[k], eps10, eps10); } ok &= (n_sweep == 1); return ok; } template bool reverse_bool(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; size_t i, j, k; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 4; CPPAD_TESTVECTOR(AD) X(n); for(j = 0; j < n; j++) X[j] = AD (0); // declare independent variables and starting recording CppAD::Independent(X); size_t m = 3; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = 1.0*X[0] + 2.0*X[1]; Y[1] = 3.0*X[2] + 4.0*X[3]; Y[2] = 5.0*X[0] + 6.0*X[1] + 7.0*X[2] + 8.0*X[3]*X[3]/2.; // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // new value for the independent variable vector BaseVector x(n); for(j = 0; j < n; j++) x[j] = double(j); // Jacobian of y without sparsity pattern BaseVector jac(m * n); jac = f.SparseJacobian(x); /* [ 1 2 0 0 ] jac = [ 0 0 3 4 ] [ 5 6 7 8*x_3] */ BaseVector check(m * n); check[0] = 1.; check[1] = 2.; check[2] = 0.; check[3] = 0.; check[4] = 0.; check[5] = 0.; check[6] = 3.; check[7] = 4.; check[8] = 5.; check[9] = 6.; check[10] = 7.; check[11] = 8.*x[3]; for(k = 0; k < 12; k++) ok &= NearEqual(check[k], jac[k], eps99, eps99 ); // test passing sparsity pattern BoolVector s(m * m); BoolVector p(m * n); for(i = 0; i < m; i++) { for(k = 0; k < m; k++) s[i * m + k] = false; s[i * m + i] = true; } p = f.RevSparseJac(m, s); jac = f.SparseJacobian(x, p); for(k = 0; k < 12; k++) ok &= NearEqual(check[k], jac[k], eps99, eps99 ); return ok; } template bool reverse_set(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; size_t i, j, k; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 4; CPPAD_TESTVECTOR(AD) X(n); for(j = 0; j < n; j++) X[j] = AD (0); // declare independent variables and starting recording CppAD::Independent(X); size_t m = 3; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = X[0] + X[1]; Y[1] = X[2] + X[3]; Y[2] = X[0] + X[1] + X[2] + X[3] * X[3] / 2.; // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // new value for the independent variable vector BaseVector x(n); for(j = 0; j < n; j++) x[j] = double(j); // Jacobian of y without sparsity pattern BaseVector jac(m * n); jac = f.SparseJacobian(x); /* [ 1 1 0 0 ] jac = [ 0 0 1 1 ] [ 1 1 1 x_3] */ BaseVector check(m * n); check[0] = 1.; check[1] = 1.; check[2] = 0.; check[3] = 0.; check[4] = 0.; check[5] = 0.; check[6] = 1.; check[7] = 1.; check[8] = 1.; check[9] = 1.; check[10] = 1.; check[11] = x[3]; for(k = 0; k < 12; k++) ok &= NearEqual(check[k], jac[k], eps99, eps99 ); // test passing sparsity pattern SetVector s(m), p(m); for(i = 0; i < m; i++) s[i].insert(i); p = f.RevSparseJac(m, s); jac = f.SparseJacobian(x, p); for(k = 0; k < 12; k++) ok &= NearEqual(check[k], jac[k], eps99, eps99 ); return ok; } template bool forward_bool(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; size_t j, k; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 3; CPPAD_TESTVECTOR(AD) X(n); for(j = 0; j < n; j++) X[j] = AD (0); // declare independent variables and starting recording CppAD::Independent(X); size_t m = 4; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = X[0] + X[2]; Y[1] = X[0] + X[2]; Y[2] = X[1] + X[2]; Y[3] = X[1] + X[2] * X[2] / 2.; // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // new value for the independent variable vector BaseVector x(n); for(j = 0; j < n; j++) x[j] = double(j); // Jacobian of y without sparsity pattern BaseVector jac(m * n); jac = f.SparseJacobian(x); /* [ 1 0 1 ] jac = [ 1 0 1 ] [ 0 1 1 ] [ 0 1 x_2 ] */ BaseVector check(m * n); check[0] = 1.; check[1] = 0.; check[2] = 1.; check[3] = 1.; check[4] = 0.; check[5] = 1.; check[6] = 0.; check[7] = 1.; check[8] = 1.; check[9] = 0.; check[10] = 1.; check[11] = x[2]; for(k = 0; k < 12; k++) ok &= NearEqual(check[k], jac[k], eps99, eps99 ); // test passing sparsity pattern BoolVector r(n * n); BoolVector p(m * n); for(j = 0; j < n; j++) { for(k = 0; k < n; k++) r[j * n + k] = false; r[j * n + j] = true; } p = f.ForSparseJac(n, r); jac = f.SparseJacobian(x, p); for(k = 0; k < 12; k++) ok &= NearEqual(check[k], jac[k], eps99, eps99 ); return ok; } template bool forward_set(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; size_t j, k; double eps99 = 99.0 * std::numeric_limits::epsilon(); // domain space vector size_t n = 3; CPPAD_TESTVECTOR(AD) X(n); for(j = 0; j < n; j++) X[j] = AD (0); // declare independent variables and starting recording CppAD::Independent(X); size_t m = 4; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = X[0] + X[2]; Y[1] = X[0] + X[2]; Y[2] = X[1] + X[2]; Y[3] = X[1] + X[2] * X[2] / 2.; // create f: X -> Y and stop tape recording CppAD::ADFun f(X, Y); // new value for the independent variable vector BaseVector x(n); for(j = 0; j < n; j++) x[j] = double(j); // Jacobian of y without sparsity pattern BaseVector jac(m * n); jac = f.SparseJacobian(x); /* [ 1 0 1 ] jac = [ 1 0 1 ] [ 0 1 1 ] [ 0 1 x_2 ] */ BaseVector check(m * n); check[0] = 1.; check[1] = 0.; check[2] = 1.; check[3] = 1.; check[4] = 0.; check[5] = 1.; check[6] = 0.; check[7] = 1.; check[8] = 1.; check[9] = 0.; check[10] = 1.; check[11] = x[2]; for(k = 0; k < 12; k++) ok &= NearEqual(check[k], jac[k], eps99, eps99 ); // test passing sparsity pattern SetVector r(n), p(m); for(j = 0; j < n; j++) r[j].insert(j); p = f.ForSparseJac(n, r); jac = f.SparseJacobian(x, p); for(k = 0; k < 12; k++) ok &= NearEqual(check[k], jac[k], eps99, eps99 ); return ok; } bool multiple_of_n_bit(void) { bool ok = true; using CppAD::AD; using CppAD::vector; size_t i, j; // should be the same as the corresponding typedef in // cppad/local/sparse/pack.hpp typedef size_t Pack; // number of bits per packed value size_t n_bit = std::numeric_limits::digits; // check case where number of variables is equal to n_bit vector< AD > x(n_bit); vector< AD > y(n_bit); // create an AD function with domain and range dimension equal to n_bit CppAD::Independent(x); for(i = 0; i < n_bit; i++) y[i] = x[n_bit - i - 1]; CppAD::ADFun f(x, y); // Jacobian sparsity patterns vector r(n_bit * n_bit); vector s(n_bit * n_bit); for(i = 0; i < n_bit; i++) { for(j = 0; j < n_bit; j++) r[ i * n_bit + j ] = (i == j); } s = f.ForSparseJac(n_bit, r); // check the result for(i = 0; i < n_bit; i++) { for(j = 0; j < n_bit; j++) { if( i == n_bit - j - 1 ) ok = ok & s[ i * n_bit + j ]; else ok = ok & (! s[i * n_bit + j] ); } } return ok; } // check for a sparse_list bug that was fixed on 2017-04-06 void algo_sparse_list_bug( const CppAD::vector < CppAD::AD >& ax , CppAD::vector < CppAD::AD >& ay ) { ay[2] = 1.0; ay[0] = 1.0; ay[1] = ax[2]; ay[3] = ax[2]; } bool sparse_list_bug(void) { bool ok = true; using CppAD::AD; using CppAD::vector; typedef CppAD::vector < std::set > sparsity; // size_t n = 4; vector< AD > ay(n), ax(n); for(size_t i = 0; i < n; ++i) ax[i] = 1.0; // // sparsity pattern corresponding to identity matrix sparsity eye(n); for (size_t i = 0; i < n; i++) eye[i].insert(i); // CppAD::checkpoint atom_fun( "sparse_list_bug", algo_sparse_list_bug, ax, ay, CppAD::atomic_base::set_sparsity_enum ); // vector < AD > au(n); for (size_t j = 0; j < n; j++) au[j] = 1.0; // // version of function that uses atom_fun CppAD::Independent(au); vector< AD > av(n); atom_fun(au, ay); for (size_t j = 0; j < n; j++) { av[j] = ay[j] + au[j]; } CppAD::ADFun yes_atom_fun(au, av); // // version of function that uses algoright CppAD::Independent(au); algo_sparse_list_bug(au, ay); for (size_t j = 0; j < n; j++) { av[j] = ay[j] + au[j]; } CppAD::ADFun no_atom_fun(au, av); // sparsity pattern_yes = yes_atom_fun.RevSparseJac(n, eye); sparsity pattern_no = no_atom_fun.RevSparseJac(n, eye); // for(size_t i = 0; i < n; i++) ok &= pattern_yes[i] == pattern_no[i]; // return ok; } } // End empty namespace # include # include bool sparse_jacobian(void) { bool ok = true; ok &= rc_tridiagonal(); ok &= multiple_of_n_bit(); ok &= sparse_list_bug(); // --------------------------------------------------------------- // vector of bool cases ok &= rc_bool< CppAD::vector, CppAD::vectorBool >(); ok &= forward_bool< CppAD::vector, CppAD::vector >(); // ok &= reverse_bool< std::vector, std::vector >(); ok &= rc_bool< std::vector, std::valarray >(); // ok &= forward_bool< std::valarray, CppAD::vectorBool >(); ok &= reverse_bool< std::valarray, CppAD::vector >(); // --------------------------------------------------------------- // vector of set cases typedef std::vector< std::set > std_vector_set; typedef CppAD::vector< std::set > cppad_vector_set; // ok &= rc_set< CppAD::vector, std_vector_set >(); ok &= forward_set< std::valarray, std_vector_set >(); // ok &= reverse_set< std::vector, cppad_vector_set >(); ok &= rc_set< CppAD::vector, cppad_vector_set >(); // // According to section 26.3.2.3 of the 1998 C++ standard // a const valarray does not return references to its elements. // typedef std::valarray< std::set > std_valarray_set; // ok &= forward_set< std::valarray, std_valarray_set >(); // ok &= reverse_set< std::valarray, std_valarray_set >(); // --------------------------------------------------------------- // return ok; } ================================================ FILE: test_more/general/sparse_sub_hes.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* @begin sparse_sub_hes.cpp$$ $spell $$ $section Sparse Hessian on Subset of Variables: Example and Test$$ $head Purpose$$ This example uses a $cref/column subset/sparse_hessian/p/Column Subset/$$ of the sparsity pattern to compute the Hessian for a subset of the variables. The values in the rest of the sparsity pattern do not matter. $head See Also$$ $cref sub_sparse_hes.cpp$$ $end */ // BEGIN C++ # include namespace { // BEGIN_EMPTY_NAMESPACE // -------------------------------------------------------------------------- void record_function(CppAD::ADFun& f, size_t n) { // must be greater than or equal 3; see n_sweep below assert( n >= 3 ); // using CppAD::AD; typedef CppAD::vector< AD > a_vector; // // domain space vector a_vector a_x(n); for(size_t j = 0; j < n; j++) a_x[j] = AD (0); // declare independent variables and starting recording CppAD::Independent(a_x); // range space vector size_t m = 1; a_vector a_y(m); a_y[0] = 0.0; for(size_t j = 1; j < n; j++) a_y[0] += a_x[j-1] * a_x[j] * a_x[j]; // create f: x -> y and stop tape recording // (without executing zero order forward calculation) f.Dependent(a_x, a_y); // return; } // -------------------------------------------------------------------------- bool test_set(const char* color_method) { bool ok = true; // typedef CppAD::vector< double > d_vector; typedef CppAD::vector i_vector; typedef CppAD::vector< std::set > s_vector; // size_t n = 12; CppAD::ADFun f; record_function(f, n); // // sparsity patteren for the sub-set of variables we are computing // the hessian w.r.t. size_t n_sub = 4; s_vector r(n); for(size_t j = 0; j < n_sub; j++) { assert( r[j].empty() ); r[j].insert(j); } // store forward sparsity for J(x) = F^{(1)} (x) * R f.ForSparseJac(n_sub, r); // compute sparsity pattern for H(x) = (S * F)^{(2)} ( x ) * R s_vector s(1); assert( s[0].empty() ); s[0].insert(0); bool transpose = true; s_vector h = f.RevSparseHes(n_sub, s, transpose); // set the row and column indices that correspond to lower triangle i_vector row, col; for(size_t i = 0; i < n_sub; i++) { if( i > 0 ) { // diagonal element row.push_back(i); col.push_back(i); // lower diagonal element row.push_back(i); col.push_back(i-1); } } // weighting for the Hessian d_vector w(1); w[0] = 1.0; // compute Hessian CppAD::sparse_hessian_work work; work.color_method = color_method; d_vector x(n), hes( row.size() ); for(size_t j = 0; j < n; j++) x[j] = double(j+1); f.SparseHessian(x, w, h, row, col, hes, work); // check the values in the sparse hessian for(size_t ell = 0; ell < row.size(); ell++) { size_t i = row[ell]; size_t j = col[ell]; if( i == j ) ok &= hes[ell] == 2.0 * x[i-1]; else { ok &= j+1 == i; ok &= hes[ell] == 2.0 * x[i]; } } return ok; } // -------------------------------------------------------------------------- bool test_bool(const char* color_method) { bool ok = true; // typedef CppAD::vector< double > d_vector; typedef CppAD::vector i_vector; typedef CppAD::vector s_vector; // size_t n = 12; CppAD::ADFun f; record_function(f, n); // // sparsity patteren for the sub-set of variables we are computing // the hessian w.r.t. size_t n_sub = 4; s_vector r(n * n_sub); for(size_t i = 0; i < n; i++) { for(size_t j = 0; j < n_sub; j++) r[ i * n_sub + j ] = (i == j); } // store forward sparsity for J(x) = F^{(1)} (x) * R f.ForSparseJac(n_sub, r); // compute sparsity pattern for H(x) = (S * F)^{(2)} ( x ) * R s_vector s(1); s[0] = true; bool transpose = true; s_vector h = f.RevSparseHes(n_sub, s, transpose); // set the row and column indices that correspond to lower triangle i_vector row, col; for(size_t i = 0; i < n_sub; i++) { if( i > 0 ) { // diagonal element row.push_back(i); col.push_back(i); // lower diagonal element row.push_back(i); col.push_back(i-1); } } // weighting for the Hessian d_vector w(1); w[0] = 1.0; // extend sparsity pattern (values in extended columns do not matter) s_vector h_extended(n * n); for(size_t i = 0; i < n; i++) { for(size_t j = 0; j < n_sub; j++) h_extended[ i * n + j ] = h[ i * n_sub + j ]; for(size_t j = n_sub; j < n; j++) h_extended[ i * n + j ] = false; } // compute Hessian CppAD::sparse_hessian_work work; work.color_method = color_method; d_vector x(n), hes( row.size() ); for(size_t j = 0; j < n; j++) x[j] = double(j+1); f.SparseHessian(x, w, h_extended, row, col, hes, work); // check the values in the sparse hessian for(size_t ell = 0; ell < row.size(); ell++) { size_t i = row[ell]; size_t j = col[ell]; if( i == j ) ok &= hes[ell] == 2.0 * x[i-1]; else { ok &= j+1 == i; ok &= hes[ell] == 2.0 * x[i]; } } return ok; } } // END_EMPTY_NAMESPACE bool sparse_sub_hes(void) { bool ok = true; ok &= test_set("cppad.symmetric"); ok &= test_set("cppad.general"); // ok &= test_bool("cppad.symmetric"); ok &= test_bool("cppad.general"); return ok; } // END C++ ================================================ FILE: test_more/general/sparse_vec_ad.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- # include bool sparse_vec_ad(void) { bool ok = true; using namespace CppAD; // dimension of the domain space size_t n = 3; size_t i, j; // independent variable vector CPPAD_TESTVECTOR(AD) X(n); for(j = 0; j < n; j++) X[j] = AD(j); Independent(X); // dependent variable vector size_t m = n; CPPAD_TESTVECTOR(AD) Y(m); // check results vector CPPAD_TESTVECTOR( bool ) Check(m * n); // Create a VecAD so that there are two in the tape and the sparsity // pattern depends on the second one (checks addressing VecAD objects) VecAD W(n); // VecAD equal to X VecAD Z(n); AD J; for(j = 0; j < n; j++) { J = AD(j); W[J] = X[0]; Z[J] = X[j]; // y[i] depends on x[j] for j <= i // (and is non-linear for j <= 1). if( j == 1 ) Y[j] = Z[J] * Z[J]; else Y[j] = Z[J]; } // compute dependent variables values AD P = 1; J = AD(0); for(j = 0; j < n; j++) { for(i = 0; i < m; i++) Check[ i * m + j ] = (j <= i); } // create function object F : X -> Y ADFun F(X, Y); // dependency matrix for the identity function W(x) = x CPPAD_TESTVECTOR( bool ) Identity(n * n); for(i = 0; i < n; i++) { for(j = 0; j < n; j++) Identity[ i * n + j ] = false; Identity[ i * n + i ] = true; } // evaluate the dependency matrix for Identity(F(x)) CPPAD_TESTVECTOR( bool ) Px(m * n); Px = F.RevSparseJac(n, Identity); // check values for(i = 0; i < m; i++) { for(j = 0; j < n; j++) ok &= (Px[i * m + j] == Check[i * m + j]); } // evaluate the dependency matrix for F(Identity(x)) CPPAD_TESTVECTOR( bool ) Py(m * n); Py = F.ForSparseJac(n, Identity); // check values for(i = 0; i < m; i++) { for(j = 0; j < n; j++) ok &= (Py[i * m + j] == Check[i * m + j]); } // test sparsity pattern for Hessian of F_2 ( Identity(x) ) CPPAD_TESTVECTOR(bool) Hy(m); for(i = 0; i < m; i++) Hy[i] = false; Hy[2] = true; CPPAD_TESTVECTOR(bool) Pxx(n * n); Pxx = F.RevSparseHes(n, Hy); for(i = 0; i < n; i++) { for(j = 0; j < n; j++) ok &= (Pxx[i * n + j] == false ); } // test sparsity pattern for Hessian of F_1 ( Identity(x) ) for(i = 0; i < m; i++) Hy[i] = false; Hy[1] = true; Pxx = F.RevSparseHes(n, Hy); for(i = 0; i < n; i++) { for(j = 0; j < n; j++) ok &= (Pxx[i * n + j] == ( (i <= 1) && (j <= 1) ) ); } return ok; } ================================================ FILE: test_more/general/sqrt.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Two old sqrt examples now used just for validation testing. */ # include # include namespace { // BEGIN empty namespace bool SqrtTestOne(void) { bool ok = true; using CppAD::sqrt; using CppAD::pow; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector, indices, values, and declaration CPPAD_TESTVECTOR(AD) U(1); size_t s = 0; U[s] = 4.; Independent(U); // dependent variable vector, indices, and values CPPAD_TESTVECTOR(AD) Z(2); size_t x = 0; size_t y = 1; Z[x] = sqrt(U[s]); Z[y] = sqrt(Z[x]); // define f : U -> Z and vectors for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v( f.Domain() ); CPPAD_TESTVECTOR(double) w( f.Range() ); // check values ok &= NearEqual(Z[x] , 2., eps99 , eps99); ok &= NearEqual(Z[y] , sqrt(2.), eps99 , eps99); // forward computation of partials w.r.t. s v[s] = 1.; w = f.Forward(1, v); ok &= NearEqual(w[x], .5 * pow(4., -.5), eps99 , eps99); // dx/ds ok &= NearEqual(w[y], .25 * pow(4., -.75), eps99 , eps99); // dy/ds // reverse computation of partials of y w[x] = 0.; w[y] = 1.; v = f.Reverse(1,w); ok &= NearEqual(v[s], .25 * pow(4., -.75), eps99 , eps99); // dy/ds // forward computation of second partials w.r.t s v[s] = 1.; w = f.Forward(1, v); v[s] = 0.; w = f.Forward(2, v); ok &= NearEqual( // d^2 y / (ds ds) 2. * w[y] , -.75 * .25 * pow(4., -1.75), eps99 , eps99 ); // reverse computation of second partials of y CPPAD_TESTVECTOR(double) r( f.Domain() * 2 ); w[x] = 0.; w[y] = 1.; r = f.Reverse(2, w); ok &= NearEqual( // d^2 y / (ds ds) r[2 * s + 1] , -.75 * .25 * pow(4., -1.75), eps99 , eps99 ); return ok; } bool SqrtTestTwo(void) { bool ok = true; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector CPPAD_TESTVECTOR(AD) U(1); U[0] = 2.; Independent(U); // a temporary values AD x = U[0] * U[0]; // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); Z[0] = sqrt( x ); // z = sqrt( u * u ) // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v(1); CPPAD_TESTVECTOR(double) w(1); // check value ok &= NearEqual(U[0] , Z[0], eps99 , eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; double value = 1.; v[0] = 1.; for(j = 1; j < p; j++) { jfac *= double(j); w = f.Forward(j, v); ok &= NearEqual(w[0], value/jfac, eps99, eps99); // d^jz/du^j v[0] = 0.; value = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r(p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; value = 1.; for(j = 0; j < p; j++) { ok &= NearEqual(r[j], value/jfac, eps99, eps99); // d^jz/du^j jfac *= double(j + 1); value = 0.; } return ok; } bool SqrtTestThree(void) { bool ok = true; using CppAD::sqrt; using CppAD::exp; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector, indices, values, and declaration double x = 4.; CPPAD_TESTVECTOR(AD) X(1); X[0] = x; Independent(X); // dependent variable vector, indices, and values CPPAD_TESTVECTOR(AD) Y(1); Y[0] = sqrt( exp(X[0]) ); // define f : X -> Y and vectors for derivative calculations ADFun f(X, Y); // forward computation of first Taylor coefficient CPPAD_TESTVECTOR(double) x1( f.Domain() ); CPPAD_TESTVECTOR(double) y1( f.Range() ); x1[0] = 1.; y1 = f.Forward(1, x1); ok &= NearEqual(y1[0], exp(x/2.)/2., eps99 , eps99); // forward computation of second Taylor coefficient CPPAD_TESTVECTOR(double) x2( f.Domain() ); CPPAD_TESTVECTOR(double) y2( f.Range() ); x2[0] = 0.; y2 = f.Forward(2, x2); ok &= NearEqual(2.*y2[0] , exp(x/2.)/4., eps99 , eps99 ); // forward computation of third Taylor coefficient CPPAD_TESTVECTOR(double) x3( f.Domain() ); CPPAD_TESTVECTOR(double) y3( f.Range() ); x3[0] = 0.; y3 = f.Forward(3, x3); ok &= NearEqual(6.*y3[0] , exp(x/2.)/8., eps99 , eps99 ); // reverse computation of deritavitve of Taylor coefficients CPPAD_TESTVECTOR(double) r( f.Domain() * 4 ); CPPAD_TESTVECTOR(double) w(1); w[0] = 1.; r = f.Reverse(4, w); ok &= NearEqual(r[0], exp(x/2.)/2., eps99 , eps99); ok &= NearEqual(r[1], exp(x/2.)/4., eps99 , eps99 ); ok &= NearEqual(2.*r[2], exp(x/2.)/8., eps99 , eps99 ); ok &= NearEqual(6.*r[3], exp(x/2.)/16., eps99 , eps99 ); return ok; } } // END empty namespace bool Sqrt(void) { bool ok = true; ok &= SqrtTestOne(); ok &= SqrtTestTwo(); ok &= SqrtTestThree(); return ok; } ================================================ FILE: test_more/general/std_math.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Test the using standard math functions with AD< AD > */ # include typedef CppAD::AD ADdouble; typedef CppAD::AD< ADdouble > ADDdouble; bool std_math(void) { using CppAD::NearEqual; bool ok = true; ADDdouble half(.5); ADDdouble one(1.); ADDdouble two(2.); ADDdouble ten(10.); ADDdouble eps(1e-6); ADDdouble pi_4(3.141592653 / 4.); ADDdouble root_2( sqrt(two) ); ADDdouble y( acos(one / root_2) ); ok &= NearEqual( pi_4, y, eps, eps ); y = cos(pi_4); ok &= NearEqual( one / root_2, y, eps, eps ); y = asin(one / root_2); ok &= NearEqual( pi_4, y, eps, eps ); y = sin(pi_4); ok &= NearEqual( one / root_2, y, eps, eps ); y = atan(one); ok &= NearEqual( pi_4, y, eps, eps ); y = tan(pi_4); ok &= NearEqual( one, y, eps, eps ); y = two * cosh(one); ok &= NearEqual( exp(one) + exp(-one), y, eps, eps ); y = two * sinh(one); ok &= NearEqual( exp(one) - exp(-one), y, eps, eps ); y = log( exp(one) ); ok &= NearEqual( one, y, eps, eps ); y = log10( exp( log(ten) ) ); ok &= NearEqual( one, y, eps, eps ); return ok; } ================================================ FILE: test_more/general/sub.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Two old Sub examples now used just for valiadation testing */ # include namespace { // BEGIN empty namespace bool One(void) { bool ok = true; using namespace CppAD; // independent variable vector, indices, values, and declaration CPPAD_TESTVECTOR(AD) U(2); size_t s = 0; size_t t = 1; U[s] = 3.; U[t] = 2.; Independent(U); // dependent variable vector and indices CPPAD_TESTVECTOR(AD) Z(3); size_t x = 0; size_t y = 1; size_t z = 2; // dependent variable values Z[x] = U[s] - U[t]; // AD - AD Z[y] = Z[x] - 1.; // AD - double Z[z] = 1. - Z[y]; // double - AD // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v( f.Domain() ); CPPAD_TESTVECTOR(double) w( f.Range() ); // check function values ok &= ( Z[x] == 3. - 2. ); ok &= ( Z[y] == 3. - 2. - 1. ); ok &= ( Z[z] == 1. - 3. + 2. + 1. ); // forward computation of partials w.r.t. s v[s] = 1.; v[t] = 0.; w = f.Forward(1, v); ok &= ( w[x] == 1. ); // dx/ds ok &= ( w[y] == 1. ); // dy/ds ok &= ( w[z] == -1. ); // dz/ds // reverse computation of second partials of z CPPAD_TESTVECTOR(double) r( f.Domain() * 2 ); w[x] = 0.; w[y] = 0.; w[z] = 1.; r = f.Reverse(2, w); ok &= ( r[2 * s + 1] == 0. ); // d^2 z / (ds ds) ok &= ( r[2 * t + 1] == 0. ); // d^2 z / (ds dt) return ok; } bool Two(void) { bool ok = true; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector double u0 = .5; CPPAD_TESTVECTOR(AD) U(1); U[0] = u0; Independent(U); AD a = 2. * U[0] - 1.; // AD - double AD b = a - 2; // AD - int AD c = 3. - b; // double - AD AD d = 4 - c; // int - AD // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); Z[0] = U[0] - d; // AD - AD // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v(1); CPPAD_TESTVECTOR(double) w(1); // check value ok &= NearEqual(Value(Z[0]) , u0-4+3-2*u0+1+2, eps99 , eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; double value = -1.; v[0] = 1.; for(j = 1; j < p; j++) { jfac *= double(j); w = f.Forward(j, v); ok &= NearEqual(w[0], value/jfac, eps99, eps99); // d^jz/du^j v[0] = 0.; value = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r(p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; value = -1.; for(j = 0; j < p; j++) { ok &= NearEqual(r[j], value/jfac, eps99, eps99); // d^jz/du^j jfac *= double(j + 1); value = 0.; } return ok; } bool Three(void) { bool ok = true; using namespace CppAD; // special cases where tests above check OK and SubpvOp // implementation is known to be wrong. // Probably two minuses make a plus. size_t n = 1; CPPAD_TESTVECTOR(AD) X(n); X[0] = 1.; Independent(X); size_t m = 1; CPPAD_TESTVECTOR(AD) Y(m); Y[0] = 1. - X[0]; ADFun f(X, Y); CPPAD_TESTVECTOR(double) w(m), dw(n); w[0] = 1.; dw = f.Reverse(1, w); ok &= (dw[0] == -1.); return ok; } bool Four(void) { bool ok = true; using namespace CppAD; // special cases where parameter number is equal to // variable index in result. size_t n = 1; CPPAD_TESTVECTOR(AD) X(n); X[0] = 1.; Independent(X); size_t m = 1; CPPAD_TESTVECTOR(AD) Y(m); if( 0. < X[0] && X[0] < 10. ) Y[0] = X[0] - 2.; else Y[0] = X[0] - 2.; ADFun f(X, Y); CPPAD_TESTVECTOR(double) y(m), x(n); x[0] = 1.; y = f.Forward(0, x); ok &= (y[0] == -1.); CPPAD_TESTVECTOR(double) dy(m), dx(n); dx[0] = 1.; dy = f.Forward(1, dx); ok &= (dy[0] == 1.); return ok; } } // END empty namespace bool Sub(void) { bool ok = true; ok &= One(); ok &= Two(); ok &= Three(); ok &= Four(); return ok; } ================================================ FILE: test_more/general/sub_eq.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Two old SubEq examples now used just for valiadation testing */ # include namespace { // BEGIN empty namespace bool SubEqTestOne(void) { bool ok = true; using namespace CppAD; // independent variable vector, indices, values, and declaration CPPAD_TESTVECTOR(AD) U(2); size_t s = 0; size_t t = 1; U[s] = 3.; U[t] = 2.; Independent(U); // dependent variable vector and indices CPPAD_TESTVECTOR(AD) Z(2); size_t x = 0; size_t y = 1; // dependent variable values Z[x] = U[s]; Z[y] = U[t]; Z[x] -= U[t]; // AD -= AD Z[y] -= 5.; // AD -= double // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v( f.Domain() ); CPPAD_TESTVECTOR(double) w( f.Range() ); // check function values ok &= ( Z[x] == 3. - 2. ); ok &= ( Z[y] == 2. - 5. ); // forward computation of partials w.r.t. t v[s] = 0.; v[t] = 1.; w = f.Forward(1, v); ok &= ( w[x] == -1. ); // dx/dt ok &= ( w[y] == 1. ); // dy/dt // reverse computation of second partials of x CPPAD_TESTVECTOR(double) r( f.Domain() * 2 ); w[x] = 1.; w[y] = 0.; r = f.Reverse(2, w); ok &= ( r[2 * s + 1] == 0. ); // d^2 x / (ds ds) ok &= ( r[2 * t + 1] == 0. ); // d^2 x / (ds dt) return ok; } bool SubEqTestTwo(void) { bool ok = true; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); // independent variable vector double u0 = .5; CPPAD_TESTVECTOR(AD) U(1); U[0] = u0; Independent(U); // dependent variable vector CPPAD_TESTVECTOR(AD) Z(1); Z[0] = U[0]; // initial value Z[0] -= 2; // AD -= int Z[0] -= 4.; // AD -= double Z[0] -= 2 * U[0]; // AD -= AD // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v(1); CPPAD_TESTVECTOR(double) w(1); // check value ok &= NearEqual(Z[0] , u0-2-4-2*u0, eps99 , eps99); // forward computation of partials w.r.t. u size_t j; size_t p = 5; double jfac = 1.; double value = -1.; v[0] = 1.; for(j = 1; j < p; j++) { jfac *= double(j); w = f.Forward(j, v); ok &= NearEqual(w[0], value/jfac, eps99, eps99); // d^jz/du^j v[0] = 0.; value = 0.; } // reverse computation of partials of Taylor coefficients CPPAD_TESTVECTOR(double) r(p); w[0] = 1.; r = f.Reverse(p, w); jfac = 1.; value = -1.; for(j = 0; j < p; j++) { ok &= NearEqual(r[j], value/jfac, eps99, eps99); // d^jz/du^j jfac *= double(j + 1); value = 0.; } return ok; } } // END empty namespace bool SubEq(void) { bool ok = true; ok &= SubEqTestOne(); ok &= SubEqTestTwo(); return ok; } ================================================ FILE: test_more/general/sub_zero.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Test the use of the special parameters zero and one with the multiply operator */ # include typedef CppAD::AD ADdouble; typedef CppAD::AD< ADdouble > ADDdouble; bool SubZero(void) { using namespace CppAD; bool ok = true; size_t i; for(i = 0; i < 2; i++) { // run through the cases x = 0, 1 size_t j; for(j = 0; j < 2; j++) { // run through the cases y = 0, 1 CPPAD_TESTVECTOR( ADdouble ) x(1); x[0] = double(i); Independent(x); CPPAD_TESTVECTOR( ADDdouble ) y(1); y[0] = ADDdouble(j); Independent(y); CPPAD_TESTVECTOR( ADDdouble ) z(2); z[0] = x[0] - y[0]; z[1] = y[0] - x[0]; z[1] -= x[0]; // f(y) = z = { x - y , y - x - x } ADFun< ADdouble > f(y, z); CPPAD_TESTVECTOR( ADdouble ) u( f.Domain() ); CPPAD_TESTVECTOR( ADdouble ) v( f.Range() ); // v = f(y) u[0] = ADdouble(j); v = f.Forward(0, u); // check value of f ok &= v[0] == x[0] - ADdouble(j); ok &= v[1] == ADdouble(j) - x[0] - x[0]; // g(x) = f(y) = {x - y , y - x - x} ADFun g(x, v); CPPAD_TESTVECTOR( double ) a( g.Domain() ); CPPAD_TESTVECTOR( double ) b( g.Range() ); // b = g'(x) a[0] = 1.; b = g.Forward(1, a); // check derivatives of g ok &= (b[0] == 1.); ok &= (b[1] == -2.); } } return ok; } ================================================ FILE: test_more/general/subgraph_1.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace { typedef CPPAD_TESTVECTOR(size_t) svector; typedef CppAD::sparse_rc sparsity; // using CppAD::AD; typedef CPPAD_TESTVECTOR(AD) avector; // ======================================================================== // algorithm that will be checkpointed void g_algo(const avector& u, avector& v) { for(size_t j = 0; j < size_t( u.size() ); ++j) v[0] += u[j]; } // will be a pointer to atomic version of g_algo CppAD::checkpoint* atom_g = nullptr; // ------------------------------------------------------------------------ // record function void record_function( bool optimize , size_t& n , size_t& m , CppAD::ADFun& fun ) { // declare checkpoint function avector au(3), av(1); for(size_t j = 0; j < 3; j++) au[j] = AD(j); if( atom_g == nullptr ) atom_g = new CppAD::checkpoint("atom_g", g_algo, au, av); // // domain space vector n = 6; CPPAD_TESTVECTOR(AD) ax(n); for(size_t j = 0; j < n; j++) ax[j] = AD(j); // declare independent variables and start recording CppAD::Independent(ax); // range space vector m = 8; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = 0.0; // does not depend on anything ay[1] = ax[1]; // is equal to an independent variable AD sum = ax[1] + ax[1]; // only uses ax[1] ay[2] = sum * ax[2]; // operator(variable, variable) ay[3] = sin(ax[1]); // operator(variable) ay[4] = ax[4] / 2.0; // operator(variable, parameter) ay[5] = 2.0 / ax[3]; // operator(parameter, variable) // // a atomic function call for(size_t j = 0; j < size_t(au.size()); ++j) au[j] = ax[j + 3]; (*atom_g)(au, av); ay[6] = av[0]; // // variable + variable operatros that optimizer will change to // cumulative summation ay[7] = 0.0; for(size_t j = 0; j < n; ++j) ay[7] += ax[j]; // // create f: x -> y and stop tape recording fun.Dependent(ax, ay); // if( optimize ) fun.optimize(); return; } // ------------------------------------------------------------------------ bool compare_subgraph_sparsity( CppAD::sparse_rc subgraph , CppAD::sparse_rc check ) { bool ok = true; // check nnz size_t sub_nnz = subgraph.nnz(); size_t chk_nnz = check.nnz(); ok &= sub_nnz == chk_nnz; size_t nnz = std::min(sub_nnz, chk_nnz); // row major order svector sub_order = subgraph.row_major(); svector chk_order = check.row_major(); // check row indices const svector& sub_row( subgraph.row() ); const svector& chk_row( check.row() ); for(size_t k = 0; k < nnz; k++) ok &= sub_row[ sub_order[k] ] == chk_row[ chk_order[k] ]; // check column indices const svector& sub_col( subgraph.col() ); const svector& chk_col( check.col() ); for(size_t k = 0; k < nnz; k++) ok &= sub_col[ sub_order[k] ] == chk_col[ chk_order[k] ]; /* std::cout << "\nsub_row = " << sub_row << "\n"; std::cout << "chk_row = " << chk_row << "\n"; std::cout << "sub_col = " << sub_col << "\n"; std::cout << "chk_col = " << chk_col << "\n"; */ return ok; } // ------------------------------------------------------------------------ bool test_subgraph_sparsity(bool optimize) { bool ok = true; // create f: x -> y size_t n, m; CppAD::ADFun f; record_function(optimize, n, m, f); // -------------------------------------------------------------------- // Entire sparsity pattern // compute sparsity using subgraph_sparsity CPPAD_TESTVECTOR(bool) select_domain(n), select_range(m); for(size_t j = 0; j < n; j++) select_domain[j] = true; for(size_t i = 0; i < m; i++) select_range[i] = true; bool transpose = false; sparsity subgraph_out; f.subgraph_sparsity( select_domain, select_range, transpose, subgraph_out ); // compute sparsity using for_jac_sparsity sparsity pattern_in(n, n, n); for(size_t k = 0; k < n; k++) pattern_in.set(k, k, k); bool dependency = true; bool internal_bool = true; sparsity check_out; f.for_jac_sparsity( pattern_in, transpose, dependency, internal_bool, check_out ); // compare results ok &= compare_subgraph_sparsity(subgraph_out, check_out); // -------------------------------------------------------------------- // Exclude ax[1] select_domain[1] = false; f.subgraph_sparsity( select_domain, select_range, transpose, subgraph_out ); pattern_in.resize(n, n, n-1); for(size_t k = 0; k < n-1; k++) { if( k < 1 ) pattern_in.set(k, k, k); else pattern_in.set(k, k+1, k+1); } f.for_jac_sparsity( pattern_in, transpose, dependency, internal_bool, check_out ); // compare results ok &= compare_subgraph_sparsity(subgraph_out, check_out); return ok; } // ------------------------------------------------------------------------ bool compare_subgraph_reverse( const CPPAD_TESTVECTOR(size_t)& col , const CPPAD_TESTVECTOR(double)& dw , const CPPAD_TESTVECTOR(double)& check ) { bool ok = true; double eps99 = 99.0 * std::numeric_limits::epsilon(); // size_t n = size_t( check.size() ); // // check order in col for(size_t c = 1; c < size_t( col.size() ); c++) ok &= col[c] > col[c-1]; // size_t c = 0; for(size_t j = 1; j < n; j++) { while( c < size_t( col.size() ) && col[c] < j ) ++c; if( c < size_t( col.size() ) && col[c] == j ) ok &= CppAD::NearEqual(dw[j], check[j], eps99, eps99); else ok &= CppAD::NearEqual(0.0, check[j], eps99, eps99); } return ok; } // ------------------------------------------------------------------------ bool test_subgraph_reverse(bool optimize) { bool ok = true; // create f: x -> y size_t n, m; CppAD::ADFun f; record_function(optimize, n, m, f); // value of x at which to compute derivatives CPPAD_TESTVECTOR(double) x(n); for(size_t j = 0; j < n; ++j) x[j] = double(n) / double(j + 1); f.Forward(0, x); // exclude x[4] from the derivative calculations CPPAD_TESTVECTOR(bool) select_domain(n); for(size_t j = 0; j < n; j++) select_domain[j] = true; select_domain[4] = false; f.subgraph_reverse(select_domain); // vector used to check results CPPAD_TESTVECTOR(double) check(n); for(size_t j = 0; j < n; j++) check[j] = 0.0; // derivative of y[0] CPPAD_TESTVECTOR(size_t) col; CPPAD_TESTVECTOR(double) dw; size_t q = 1; size_t ell = 0; f.subgraph_reverse(q, ell, col, dw); ok &= compare_subgraph_reverse(col, dw, check); // // derivative of y[1] check[1] = 1.0; ell = 1; f.subgraph_reverse(q, ell, col, dw); ok &= compare_subgraph_reverse(col, dw, check); // // derivative of y[2] check[1] = 2.0 * x[2]; check[2] = 2.0 * x[1]; ell = 2; f.subgraph_reverse(q, ell, col, dw); ok &= compare_subgraph_reverse(col, dw, check); // // derivative of y[3] check[1] = cos( x[1] ); check[2] = 0.0; ell = 3; f.subgraph_reverse(q, ell, col, dw); ok &= compare_subgraph_reverse(col, dw, check); // // derivative of y[4] (x[4] is not selected) check[1] = 0.0; ell = 4; f.subgraph_reverse(q, ell, col, dw); ok &= compare_subgraph_reverse(col, dw, check); // // derivative of y[5] check[3] = -2.0 / (x[3] * x[3]); ell = 5; f.subgraph_reverse(q, ell, col, dw); ok &= compare_subgraph_reverse(col, dw, check); // // derivative of y[6] (x[4] is not selected) check[3] = 1.0; check[5] = 1.0; ell = 6; f.subgraph_reverse(q, ell, col, dw); ok &= compare_subgraph_reverse(col, dw, check); // // derivative of y[7] (x[4] is not selected) for(size_t j = 0; j < n; ++j) check[j] = 1.0; check[4] = 0.0; ell = 7; f.subgraph_reverse(q, ell, col, dw); ok &= compare_subgraph_reverse(col, dw, check); // return ok; } } bool subgraph_1(void) { bool ok = true; bool optimize = false; ok &= test_subgraph_sparsity(optimize); ok &= test_subgraph_reverse(optimize); optimize = true; ok &= test_subgraph_sparsity(optimize); ok &= test_subgraph_reverse(optimize); // ok &= atom_g != nullptr; delete atom_g; // return ok; } ================================================ FILE: test_more/general/subgraph_2.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- # include namespace { // BEGIN_EMPTY_NAMESPACE bool test_subgraph_subset(void) { bool ok = true; using std::cout; using CppAD::AD; using CppAD::vector; typedef vector d_vector; typedef vector s_vector; // size_t n = 4; d_vector x(n); vector< AD > ax(n), ay(n); for(size_t j = 0; j < n; ++j) ax[j] = x[j] = double(j); CppAD::Independent(ax); for(size_t i = 0; i < n; ++i) { ay[i] = 0.0; for(size_t j = 0; j < n; ++j) ay[i] += double(i + j + 1) * ax[j]; } CppAD::ADFun f(ax, ay); // size_t nnz = (n * (n + 1)) / 2; CppAD::sparse_rc upper_triangle(n, n, nnz); size_t k = 0; for(size_t i = 0; i < n; ++i) { for(size_t j = i; j < n; ++j) upper_triangle.set(k++, i, j); } ok &= k == nnz; CppAD::sparse_rcv subset( upper_triangle ); // f.subgraph_jac_rev(x, subset); const d_vector& val = subset.val(); k = 0; for(size_t i = 0; i < n; ++i) { for(size_t j = i; j < n; ++j) ok &= val[k++] == double(i + j + 1); } ok &= k == nnz; // return ok; } } // END_EMPTY_NAMESPACE bool subgraph_2(void) { bool ok = true; ok &= test_subgraph_subset(); return ok; } ================================================ FILE: test_more/general/subgraph_hes2jac.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Sparse Hessian Using Subgraphs and Jacobian: Example and Test */ # include bool subgraph_hes2jac(void) { bool ok = true; using CppAD::NearEqual; typedef CppAD::AD a1double; typedef CppAD::AD a2double; typedef CPPAD_TESTVECTOR(double) d_vector; typedef CPPAD_TESTVECTOR(a1double) a1vector; typedef CPPAD_TESTVECTOR(a2double) a2vector; typedef CPPAD_TESTVECTOR(size_t) s_vector; typedef CPPAD_TESTVECTOR(bool) b_vector; typedef CppAD::sparse_rcv sparse_matrix; // double eps = 10. * CppAD::numeric_limits::epsilon(); // // double version of x size_t n = 12; d_vector x(n); for(size_t j = 0; j < n; j++) x[j] = double(j + 2); // // a1double version of x a1vector a1x(n); for(size_t j = 0; j < n; j++) a1x[j] = x[j]; // // a2double version of x a2vector a2x(n); for(size_t j = 0; j < n; j++) a2x[j] = a1x[j]; // // declare independent variables and starting recording CppAD::Independent(a2x); // // a2double version of y = f(x) = 5 * x0 * x1 + sum_j xj^3 size_t m = 1; a2vector a2y(m); a2y[0] = 5.0 * a2x[0] * a2x[1]; for(size_t j = 0; j < n; j++) a2y[0] += a2x[j] * a2x[j] * a2x[j]; // // create a1double version of f: x -> y and stop tape recording // (without executing zero order forward calculation) CppAD::ADFun a1f; a1f.Dependent(a2x, a2y); // // Optimize this function to reduce future computations. // Perhaps only one optimization at the end would be faster. a1f.optimize(); // // declare independent variables and start recording g(x) = f'(x) Independent(a1x); // // Use one reverse mode pass to compute z = f'(x) a1vector a1w(m), a1z(n); a1w[0] = 1.0; a1f.Forward(0, a1x); a1z = a1f.Reverse(1, a1w); // // create double version of g : x -> f'(x) CppAD::ADFun g; g.Dependent(a1x, a1z); ok &= g.size_random() == 0; // // Optimize this function to reduce future computations. // Perhaps no optimization would be faster. g.optimize(); // // compute f''(x) = g'(x) b_vector select_domain(n), select_range(n); for(size_t j = 0; j < n; ++j) { select_domain[j] = true; select_range[j] = true; } sparse_matrix hessian; g.subgraph_jac_rev(select_domain, select_range, x, hessian); // ------------------------------------------------------------------- // check number of non-zeros in the Hessian // (only x0 * x1 generates off diagonal terms) ok &= hessian.nnz() == n + 2; // for(size_t k = 0; k < hessian.nnz(); ++k) { size_t r = hessian.row()[k]; size_t c = hessian.col()[k]; double v = hessian.val()[k]; // if( r == c ) { // a diagonal element double check = 6.0 * x[r]; ok &= NearEqual(v, check, eps, eps); } else { // off diagonal element ok &= (r == 0 && c == 1) || (r == 1 && c == 0); double check = 5.0; ok &= NearEqual(v, check, eps, eps); } } ok &= g.size_random() > 0; g.clear_subgraph(); ok &= g.size_random() == 0; return ok; } ================================================ FILE: test_more/general/tan.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Test higher order derivatives for tan(x) function. */ # include namespace { bool tan_two(void) { bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * std::numeric_limits::epsilon(); // domain space vector size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = 0.5; // declare independent variables and starting recording CppAD::Independent(ax); // range space vector size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); ay[0] = tan( ax[0] ); // create f: x -> y and stop tape recording CppAD::ADFun f(ax, ay); // first order Taylor coefficient CPPAD_TESTVECTOR(double) x1(n), y1; x1[0] = 2.0; y1 = f.Forward(1, x1); ok &= size_t( y1.size() ) == m; // secondorder Taylor coefficients CPPAD_TESTVECTOR(double) x2(n), y2; x2[0] = 0.0; y2 = f.Forward(2, x2); ok &= size_t( y2.size() ) == m; // // Y (t) = F[X_0(t)] // = tan(0.5 + 2t ) // Y' (t) = 2 * cos(0.5 + 2t )^(-2) double sec_sq = 1.0 / ( cos(0.5) * cos(0.5) ); double check = 2.0 * sec_sq; ok &= NearEqual(y1[0] , check, eps, eps); // // Y''(0) = 8*cos(0.5)^(-3)*sin(0.5) check = 8.0 * tan(0.5) * sec_sq / 2.0; ok &= NearEqual(y2[0] , check, eps, eps); // return ok; } bool tan_case(bool tan_first) { bool ok = true; double eps = 100. * std::numeric_limits::epsilon(); using CppAD::AD; using CppAD::NearEqual; // independent variable vector, indices, values, and declaration size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = .7; Independent(ax); // dependent variable vector and indices size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); if( tan_first ) ay[0] = atan( tan( ax[0] ) ); else ay[0] = tan( atan( ax[0] ) ); // check value ok &= NearEqual(ax[0] , ay[0], eps, eps); // create f: x -> y and vectors used for derivative calculations CppAD::ADFun f(ax, ay); CPPAD_TESTVECTOR(double) dx(n), dy(m); // forward computation of partials w.r.t. x dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 1e0, eps, eps); size_t p, order = 5; dx[0] = 0.; for(p = 2; p < order; p++) { dy = f.Forward(p, dx); ok &= NearEqual(dy[0], 0e0, eps, eps); } // reverse computation of order partial CPPAD_TESTVECTOR(double) w(m), dw(n * order); w[0] = 1.; dw = f.Reverse(order, w); ok &= NearEqual(dw[0], 1e0, eps, eps); for(p = 1; p < order; p++) ok &= NearEqual(dw[p], 0e0, eps, eps); return ok; } bool tanh_case(bool tanh_first) { bool ok = true; double eps = 100. * std::numeric_limits::epsilon(); using CppAD::AD; using CppAD::NearEqual; // independent variable vector, indices, values, and declaration size_t n = 1; CPPAD_TESTVECTOR(AD) ax(n); ax[0] = .5; Independent(ax); // dependent variable vector and indices size_t m = 1; CPPAD_TESTVECTOR(AD) ay(m); AD z; if( tanh_first ) { z = tanh( ax[0] ); ay[0] = .5 * log( (1. + z) / (1. - z) ); } else { z = .5 * log( (1. + ax[0]) / (1. - ax[0]) ); ay[0] = tanh(z); } // check value ok &= NearEqual(ax[0] , ay[0], eps, eps); // create f: x -> y and vectors used for derivative calculations CppAD::ADFun f(ax, ay); CPPAD_TESTVECTOR(double) dx(n), dy(m); // forward computation of partials w.r.t. x dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], 1e0, eps, eps); size_t p, order = 5; dx[0] = 0.; for(p = 2; p < order; p++) { dy = f.Forward(p, dx); ok &= NearEqual(dy[0], 0e0, eps, eps); } // reverse computation of order partial CPPAD_TESTVECTOR(double) w(m), dw(n * order); w[0] = 1.; dw = f.Reverse(order, w); ok &= NearEqual(dw[0], 1e0, eps, eps); for(p = 1; p < order; p++) ok &= NearEqual(dw[p], 0e0, eps, eps); return ok; } } bool tan(void) { bool ok = true; // ok &= tan_case(true); ok &= tan_case(false); ok &= tanh_case(true); ok &= tanh_case(false); // ok &= tan_two(); return ok; } ================================================ FILE: test_more/general/to_csrc.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- # include # include # if CPPAD_USE_CPLUSPLUS_2017 # include # endif // DIR_SEP, DLL_EXT # ifndef _WIN32 # define DIR_SEP '/' # define DLL_EXT ".so" # else # define DIR_SEP '\\' # define DLL_EXT ".dll" # endif namespace { // BEGIN_EMPTY_NAMESPACE // // dll_file_name std::string dll_file_name(void) { // # if ! CPPAD_USE_CPLUSPLUS_2017 std::string tmp_dir = "./"; # else // tmp_dir std::string tmp_dir = std::filesystem::temp_directory_path().string(); if( tmp_dir.back() != DIR_SEP ) tmp_dir += DIR_SEP; # endif // // base_name # ifdef _WIN32 std::string base_name = "test_to_csrc.dll"; # else std::string base_name = "test_to_csrc.so"; # endif // // file_name std::string file_name = tmp_dir + base_name; // return file_name; } // // create_csrc_file std::string create_csrc_file(size_t index, const std::string& csrc) { // # if ! CPPAD_USE_CPLUSPLUS_2017 std::string tmp_dir = "./"; # else // tmp_dir std::string tmp_dir = std::filesystem::temp_directory_path().string(); if( tmp_dir.back() != DIR_SEP ) tmp_dir += DIR_SEP; # endif // // base_name std::string base_name = "test_to_csrc_" + CppAD::to_string(index) + ".c"; // // file_name std::string file_name = tmp_dir + base_name; // // write file_name std::ofstream os; os.open(file_name, std::ios::out); os << csrc; os.close(); // return file_name; } // // atomic_fun class atomic_fun : public CppAD::atomic_four { private: const std::string name_; public: atomic_fun(const std::string& name) : CppAD::atomic_four(name), name_(name) {} private: bool for_type( size_t call_id , const CppAD::vector& type_x , CppAD::vector& type_y ) override { type_y[0] = type_x[0]; return true; } // forward double bool forward( size_t call_id , const CppAD::vector& select_y , size_t order_low , size_t order_up , const CppAD::vector& taylor_x , CppAD::vector& taylor_y ) override { if( order_up != 0 ) return false;; taylor_y[0] = 1.0 / taylor_x[0]; return true; } public: // forward_zero std::string forward_zero(void) { std::string csrc = "# include \n" "int cppad_atomic_" + name_ + "(\n"; csrc +=R"_( size_t call_id, size_t nx, const double* x, size_t ny, double* y, size_t* compare_change) { if( nx != 1 ) return 1; if( ny != 1 ) return 2; y[0] = 1.0 / x[0]; return 0; } )_"; return csrc; } }; // -------------------------------------------------------------------------- // integer float integer(const float& x) { if( x >= 0.0 ) return std::floor(x); return std::ceil(x); } CPPAD_DISCRETE_FUNCTION(float, integer) std::string discrete_integer(void) { std::string csrc = R"_( # include float cppad_discrete_integer(const float x) { if( x >= 0.0 ) return floor(x); return ceil(x); } )_"; return csrc; } // -------------------------------------------------------------------------- bool simple_cases(void) { // ok bool ok = true; // // AD, NearEqual using CppAD::AD; using CppAD::NearEqual; // // eps99 double eps99 = 99.0 * std::numeric_limits::epsilon(); // // function_name std::string function_name = "test_to_csrc"; // // library_name std::string library_name = function_name; // // nx, ax size_t nx = 2; CPPAD_TESTVECTOR( AD ) ax(nx); ax[0] = 0.5; ax[1] = 2.0; CppAD::Independent(ax); // // ny, ay size_t ny = 28; CPPAD_TESTVECTOR( AD ) ay(ny); // // binary operators ay[0] = ax[0] + ax[1]; // add ay[1] = ax[0] / ax[1]; // div ay[2] = ax[0] * ax[1]; // mul ay[3] = ax[0] - ax[1]; // sub // // unary functions ay[4] = abs( ax[0] ); ay[5] = acos( ax[0] ); // ax[0] < 1 ay[6] = acosh( ax[1] ); // ax[1] > 1 ay[7] = asin( ax[0] ); // ax[0] < 1 ay[8] = asinh( ax[0] ); ay[9] = atan( ax[0] ); ay[10] = atanh( ax[0] ); ay[11] = cos( ax[0] ); ay[12] = cosh( ax[0] ); ay[13] = erf( ax[0] ); ay[14] = erfc( ax[0] ); ay[15] = exp( ax[0] ); ay[16] = expm1( ax[0] ); ay[17] = log1p( ax[0] ); ay[18] = log( ax[0] ); ay[19] = sign( ax[0] ); ay[20] = sin( ax[0] ); ay[21] = sinh( ax[0] ); ay[22] = sqrt( ax[0] ); ay[23] = tan( ax[0] ); ay[24] = tanh( ax[0] ); // // binary functions ay[25] = azmul( ax[0], ax[1] ); ay[26] = pow( ax[0], ax[1] ); // ax[0] > 0 // // constant function ay[27] = 3.0; // // f CppAD::ADFun f(ax, ay); f.function_name_set(function_name); // // dll_file std::string dll_file = dll_file_name(); // // csrc_files CppAD::vector csrc_files(1); std::string type = "double"; std::stringstream ss; f.to_csrc(ss, type); csrc_files[0] = create_csrc_file(0, ss.str() ); // // create dll_lib std::map< std::string, std::string > options; std::string err_msg = CppAD::create_dll_lib(dll_file, csrc_files, options); if( err_msg != "" ) { std::cout << err_msg << "\n"; ok = false; return ok; } // // dll_linker CppAD::link_dll_lib dll_linker(dll_file, err_msg); // // jit_double using CppAD::jit_double; // // jit_function jit_double jit_function = nullptr; if( err_msg != "" ) { std::cout << "dll_linker ctor error: " << err_msg << "\n"; ok = false; } else { // jit_function std::string complete_name = "cppad_jit_" + function_name; jit_function = reinterpret_cast( dll_linker(complete_name, err_msg) ); if( err_msg != "" ) { std::cout << "dll_linker fun_ptr error: " << err_msg << "\n"; ok = false; } } if( ok ) { // // ok // no change CppAD::vector x(nx), y(ny); x[0] = Value( ax[0] ); x[1] = Value( ax[1] ); for(size_t i = 0; i < ny; ++i) y[i] = std::numeric_limits::quiet_NaN(); size_t compare_change = 0; int flag = jit_function( nx, x.data(), ny, y.data(), &compare_change ); ok &= flag == 0; ok &= compare_change == 0; // // check for(size_t i = 0; i < ny; ++i) { // std::cout << "y = " << y[i] << ", ay = " << ay[i] << "\n"; ok &= CppAD::NearEqual( y[i], Value(ay[i]), eps99, eps99); } } return ok; } // -------------------------------------------------------------------------- bool compare_cases(void) { // ok bool ok = true; // // AD, NearEqual using CppAD::AD; // // function_name std::string function_name = "test_to_csrc"; // // library_name std::string library_name = function_name; // // nx, ax size_t nx = 1; CPPAD_TESTVECTOR( AD ) ax(nx); double x0 = 0.5; ax[0] = x0; CppAD::Independent(ax); // // ny, ay size_t ny = 4; CPPAD_TESTVECTOR( AD ) ay(ny); // // comp_eq_graph_op if( ax[0] == x0 ) ay[0] = 1.0; else ay[0] = 0.0; // // comp_le_graph_op if( ax[0] <= x0 ) ay[1] = 1.0; else ay[1] = 0.0; // // comp_lt_graph_op if( ax[0] <= x0 ) ay[2] = 1.0; else ay[2] = 0.0; // // comp_ne_graph_op if( ax[0] != 2.0 * x0 ) ay[3] = 1.0; else ay[3] = 0.0; // // f CppAD::ADFun f(ax, ay); f.function_name_set(function_name); // // dll_file std::string dll_file = dll_file_name(); // // csrc_files CppAD::vector csrc_files(1); std::string type = "double"; std::stringstream ss; f.to_csrc(ss, type); csrc_files[0] = create_csrc_file(0, ss.str()); // // create_dll_lib std::map< std::string, std::string > options; std::string err_msg = CppAD::create_dll_lib(dll_file, csrc_files, options); if( err_msg != "" ) { std::cout << err_msg << "\n"; ok = false; return ok; } // // dll_linker CppAD::link_dll_lib dll_linker(dll_file, err_msg); // // jit_double using CppAD::jit_double; // // jit_function jit_double jit_function = nullptr; if( err_msg != "" ) { std::cout << "dll_linker ctor error: " << err_msg << "\n"; ok = false; } else { // jit_function std::string complete_name = "cppad_jit_" + function_name; jit_function = reinterpret_cast( dll_linker(complete_name, err_msg) ); if( err_msg != "" ) { std::cout << "dll_linker fun_ptr error: " << err_msg << "\n"; ok = false; } } if( ok ) { // // ok // no change CppAD::vector x(nx), y(ny); x[0] = x0; for(size_t i = 0; i < ny; ++i) y[i] = std::numeric_limits::quiet_NaN(); size_t compare_change = 0; int flag = jit_function( nx, x.data(), ny, y.data(), &compare_change ); ok &= flag == 0; ok &= compare_change == 0; for(size_t i = 0; i < ny; ++i) ok &= y[i] == Value( ay[i] ); // // ok // check all change x[0] = 2.0 * x0; flag = jit_function( nx, x.data(), ny, y.data(), &compare_change ); ok &= flag == 0; ok &= compare_change == 4; for(size_t i = 0; i < ny; ++i) ok &= y[i] == Value( ay[i] ); } return ok; } // --------------------------------------------------------------------------- bool atomic_case(void) { // ok bool ok = true; // // AD using CppAD::AD; // // function_name std::string function_name = "reciprocal"; // // reciprocal atomic_fun reciprocal(function_name); // // nx, ax size_t nx = 2; CPPAD_TESTVECTOR( AD ) ax(nx); double x0 = 0.5, x1 = 4.0; ax[0] = x0; ax[1] = x1; CppAD::Independent(ax); // // ny, ay size_t ny = nx; CPPAD_TESTVECTOR( AD ) ay(ny); CPPAD_TESTVECTOR( AD ) au(1), aw(1); for(size_t j = 0; j < nx; ++j) { au[0] = ax[j]; reciprocal(au, aw); ay[j] = aw[0]; } // // function_name function_name = "use_reciprocal"; // // f CppAD::ADFun f(ax, ay); f.function_name_set(function_name); // // library_name std::string library_name = "test_to_csrc"; // // dll_file std::string dll_file = dll_file_name(); // // csrc_files CppAD::vector csrc_files(2); csrc_files[0] = create_csrc_file(0, reciprocal.forward_zero() ); std::string type = "double"; std::stringstream ss; f.to_csrc(ss, type); csrc_files[1] = create_csrc_file(1, ss.str() ); // // create_dll_lib std::map< std::string, std::string > options; std::string err_msg = CppAD::create_dll_lib(dll_file, csrc_files, options); if( err_msg != "" ) { std::cout << err_msg << "\n"; ok = false; return ok; } // // dll_linker CppAD::link_dll_lib dll_linker(dll_file, err_msg); // // jit_double using CppAD::jit_double; // // jit_function jit_double jit_function = nullptr; if( err_msg != "" ) { std::cout << "dll_linker ctor error: " << err_msg << "\n"; ok = false; } else { // jit_function std::string complete_name = "cppad_jit_" + function_name; jit_function = reinterpret_cast( dll_linker(complete_name, err_msg) ); if( err_msg != "" ) { std::cout << "dll_linker fun_ptr error: " << err_msg << "\n"; ok = false; } } if( ok ) { // // ok // no change CppAD::vector x(nx), y(ny); x[0] = x0; x[1] = x1; for(size_t i = 0; i < ny; ++i) y[i] = std::numeric_limits::quiet_NaN(); size_t compare_change = 0; int flag = jit_function( nx, x.data(), ny, y.data(), &compare_change ); ok &= flag == 0; ok &= compare_change == 0; for(size_t i = 0; i < ny; ++i) ok &= y[i] == Value( ay[i] ); } return ok; } // --------------------------------------------------------------------------- bool discrete_case(void) { // ok bool ok = true; // // AD using CppAD::AD; // // nx, ax size_t nx = 2; CPPAD_TESTVECTOR( AD ) ax(nx); float x0 = -1.5, x1 = 1.5; ax[0] = x0; ax[1] = x1; CppAD::Independent(ax); // // ny, ay size_t ny = nx; CPPAD_TESTVECTOR( AD ) ay(ny); for(size_t j = 0; j < nx; ++j) ay[j] = integer( ax[j] ); // // function_name std::string function_name = "use_integer"; // // f CppAD::ADFun f(ax, ay); f.function_name_set(function_name); // // library_name std::string library_name = "test_to_csrc"; // // dll_file std::string dll_file = dll_file_name(); // // csrc_files CppAD::vector csrc_files(2); csrc_files[0] = create_csrc_file(0, discrete_integer() ); std::string type = "float"; std::stringstream ss; f.to_csrc(ss, type); csrc_files[1] = create_csrc_file(1, ss.str()); // // dll_file_str std::map< std::string, std::string > options; std::string err_msg = create_dll_lib(dll_file, csrc_files, options); if( err_msg != "" ) { std::cout << err_msg << "\n"; ok = false; return ok; } // // dll_linker CppAD::link_dll_lib dll_linker(dll_file, err_msg); // // jit_float using CppAD::jit_float; // // jit_function jit_float jit_function = nullptr; if( err_msg != "" ) { std::cout << "dll_linker ctor error: " << err_msg << "\n"; ok = false; } else { // jit_function std::string complete_name = "cppad_jit_" + function_name; jit_function = reinterpret_cast( dll_linker(complete_name, err_msg) ); if( err_msg != "" ) { std::cout << "dll_linker fun_ptr error: " << err_msg << "\n"; ok = false; } } if( ok ) { // // ok // no change CppAD::vector x(nx), y(ny); x[0] = x0; x[1] = x1; for(size_t i = 0; i < ny; ++i) y[i] = std::numeric_limits::quiet_NaN(); size_t compare_change = 0; int flag = jit_function( nx, x.data(), ny, y.data(), &compare_change ); ok &= flag == 0; ok &= compare_change == 0; for(size_t i = 0; i < ny; ++i) ok &= y[i] == Value( ay[i] ); } return ok; } // --------------------------------------------------------------------------- bool csum_case(void) { // ok bool ok = true; // // AD using CppAD::AD; // // nx, ax size_t nx = 4; CPPAD_TESTVECTOR( AD ) ax(nx); for(size_t j = 0; j < nx; ++j) ax[j] = 0.0; CppAD::Independent(ax); // // ny, ay size_t ny = 2; CPPAD_TESTVECTOR( AD ) ay(ny); ay[0] = 0.0; ay[1] = 0.0; for(size_t j = 0; j < nx; ++j) { ay[0] += ax[j]; ay[1] -= ax[j]; } // // function_name std::string function_name = "csum"; // // f CppAD::ADFun f(ax, ay); f.optimize(); f.function_name_set(function_name); // // library_name std::string library_name = "test_to_csrc"; // // dll_file std::string dll_file = dll_file_name(); // // csrc_files CppAD::vector csrc_files(1); std::string type = "double"; std::stringstream ss; f.to_csrc(ss, type); csrc_files[0] = create_csrc_file(0, ss.str() ); // // dll_file_str std::map< std::string, std::string > options; std::string err_msg = create_dll_lib(dll_file, csrc_files, options); if( err_msg != "" ) { std::cout << err_msg << "\n"; ok = false; return ok; } // // dll_linker CppAD::link_dll_lib dll_linker(dll_file, err_msg); // // jit_double using CppAD::jit_double; // // jit_function jit_double jit_function = nullptr; if( err_msg != "" ) { std::cout << "dll_linker ctor error: " << err_msg << "\n"; ok = false; } else { // jit_function std::string complete_name = "cppad_jit_" + function_name; jit_function = reinterpret_cast( dll_linker(complete_name, err_msg) ); if( err_msg != "" ) { std::cout << "dll_linker fun_ptr error: " << err_msg << "\n"; ok = false; } } if( ok ) { // // ok // no change CppAD::vector x(nx), y(ny); for(size_t j = 0; j < nx; ++j) x[j] = double(j+1); y[0] = std::numeric_limits::quiet_NaN(); y[1] = std::numeric_limits::quiet_NaN(); size_t compare_change = 0; int flag = jit_function( nx, x.data(), ny, y.data(), &compare_change ); ok &= flag == 0; ok &= compare_change == 0; ok &= y[0] == double(( nx * (nx + 1) ) / 2 ); ok &= y[1] == - double(( nx * (nx + 1) ) / 2 ); } return ok; } // --------------------------------------------------------------------------- } // END_EMPTY_NAMESPACE // --------------------------------------------------------------------------- bool to_csrc(void) { bool ok = true; ok &= simple_cases(); ok &= compare_cases(); ok &= atomic_case(); ok &= discrete_case(); ok &= csum_case(); return ok; } ================================================ FILE: test_more/general/to_string.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- // Complex examples should suppress conversion warnings # include // # include namespace { template bool test(void) { bool ok = true; Float eps = std::numeric_limits::epsilon(); Float pi = Float( 4.0 * std::atan(1.0) ); Float e = Float( std::exp(1.0) ); typedef std::complex Base; Base c = Base(pi, e); std::string s = CppAD::to_string( CppAD::AD(c) ); // // get strings corresponding to real and imaginary parts std::string real = ""; std::string imag = ""; size_t index = 1; // skip ( at front while( s[index] != ',' ) real += s[index++]; index++; while( s[index] != ')' ) imag += s[index++]; // Float check = Float( std::atof( real.c_str() ) ); ok &= std::fabs( check / pi - 1.0 ) <= 2.0 * eps; // check = Float( std::atof( imag.c_str() ) ); ok &= std::fabs( check / e - 1.0 ) <= 2.0 * eps; // return ok; } } bool to_string(void) { bool ok = true; ok &= test(); ok &= test(); return ok; } // END C++ ================================================ FILE: test_more/general/value.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old Value example now used just for valiadation testing */ // BEGIN C++ # include bool Value(void) { bool ok = true; using namespace CppAD; // independent variable vector, indices, values, and declaration CPPAD_TESTVECTOR(AD) U(2); size_t s = 0; size_t t = 1; U[s] = 3.; U[t] = 4.; Independent(U); // cannot call Value after Independent (tape is recording) // dependent variable vector and indices CPPAD_TESTVECTOR(AD) Z(1); size_t x = 0; // dependent variable values Z[x] = - U[t]; // create f: U -> Z and vectors used for derivative calculations ADFun f(U, Z); CPPAD_TESTVECTOR(double) v( f.Domain() ); CPPAD_TESTVECTOR(double) w( f.Range() ); // can call Value after ADFun constructor (tape is no longer recording) // check value of s double sValue = Value(U[s]); ok &= ( sValue == 3. ); // check value of x double xValue = Value(Z[x]); ok &= ( xValue == -4. ); return ok; } // END C++ ================================================ FILE: test_more/general/vec_ad.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-24 Bradley M. Bell // ---------------------------------------------------------------------------- /* Old examples now only used for validation testing */ // BEGIN C++ # include # include # include # include namespace { // Begin empty namespace void myhandler( bool known , int line , const char *file , const char *exp , const char *msg ) { // error handler must not return, so throw an exception throw msg; } bool test_comp_assign(void) { bool ok = true; // replace the default CppAD error handler CppAD::ErrorHandler info(myhandler); // create a VecAD vector CppAD::VecAD v(1); // assign its element a value v[0] = 1.0; # ifndef NDEBUG // use try / catch because error haandler throws an exception try { // set ok to false unless catch block is executed ok = false; // attempt to use a compound assignment operator // with a reference to a VecAD object CppAD::AD x = 0.0; v[x] += 1.0; } catch (const char* msg) { std::string check = "Can't use VecAD::reference on left side of +="; ok = msg == check; } # endif return ok; } bool VecADTestOne(void) { bool ok = true; using namespace CppAD; using CppAD::sin; using CppAD::cos; double eps99 = 99.0 * std::numeric_limits::epsilon(); size_t n = 3; AD N(n); AD a; size_t i; // create the array CppAD::VecAD V(n); // check assignment from double (while not taping) for(i = 0; i < n; i++) V[i] = double(n - i); // check assignment from an AD (while not taping) for(i = 0; i < n; i++) V[i] = 2. * V[i]; // check array values (while not taping) for(i = 0; i < n; i++) ok &= ( V[i] == 2. * double(n - i) ); // independent variable CPPAD_TESTVECTOR(AD) X(1); X[0] = double(n - 1); Independent(X); // check assignment from double during taping a = -1.; for(i = 0; i < n; i++) { a += 1.; V[a] = double(n - i); } // check assignment from AD during taping a = -1.; for(i = 0; i < n; i++) { a += 1.; V[a] = sin( X[0] ) * V[a]; } // dependent variable CPPAD_TESTVECTOR(AD) Z(1); Z[0] = V[ X[0] ]; // create f: X -> Z ADFun f(X, Z); CPPAD_TESTVECTOR(double) x( f.Domain() ); CPPAD_TESTVECTOR(double) dx( f.Domain() ); CPPAD_TESTVECTOR(double) z( f.Range() ); CPPAD_TESTVECTOR(double) dz( f.Range() ); double vx; for(i = 0; i < n; i++) { // check that the indexing operation was taped x[0] = double(i); z = f.Forward(0, x); vx = double(n - i); ok &= NearEqual(z[0], sin(x[0]) * vx, eps99, eps99); // note that derivative of v[x] w.r.t. x is zero dx[0] = 1.; dz = f.Forward(1, dx); ok &= NearEqual(dz[0], cos(x[0]) * vx, eps99, eps99); // reverse mode calculation of same value dz[0] = 1.; dx = f.Reverse(1, dz); ok &= NearEqual(dx[0], cos(x[0]) * vx, eps99, eps99); } return ok; } // create the discrete function AD Floor(const AD &X) double Floor(const double &x) { return std::floor(x); } CPPAD_DISCRETE_FUNCTION(double, Floor) bool VecADTestTwo(void) { bool ok = true; using namespace CppAD; double eps99 = 99.0 * std::numeric_limits::epsilon(); double pi = 4. * CppAD::atan(1.); size_t nx = 10; // number of x grid point double xLow = 0; // minimum value for x double xUp = 2 * pi; // maximum value for x double xStep = (xUp - xLow) / double(nx - 1); // step size in x double xCur; // current value for x // fill in the data vector on a uniform grid VecAD Data(nx); size_t i; for(i = 0; i < nx; i++) { xCur = xLow + double(i) * xStep; // use size_t indexing of Data while not taping Data[i] = CppAD::sin(xCur); } // declare independent variable CPPAD_TESTVECTOR(AD) X(1); X[0] = 2.; Independent(X); // compute index corresponding to current value of X[0] AD I = X[0] / xStep; AD Ifloor = Floor(I); // make sure Ifloor >= 0 (during taping) AD Zero(0); Ifloor = CondExpLt(Ifloor, Zero, Zero, Ifloor); // make sure Ifloor <= nx - 2 (during taping) AD Nxminus2(nx - 2); Ifloor = CondExpGt(Ifloor, Nxminus2, Nxminus2, Ifloor); // Iceil is Ifloor + 1 AD Iceil = Ifloor + 1.; // linear interpolate Data CPPAD_TESTVECTOR(AD) Y(1); Y[0] = Data[Ifloor] + (I - Ifloor) * (Data[Iceil] - Data[Ifloor]); // create f: X -> Y that linearly interpolates the data vector ADFun f(X, Y); // evaluate the linear interpolant at the mid point for first interval CPPAD_TESTVECTOR(double) x(1); CPPAD_TESTVECTOR(double) y(1); x[0] = xStep / 2.; y = f.Forward(0, x); ok &= NearEqual(y[0], (Data[0] + Data[1])/2., eps99, eps99); // evaluate the derivative with respect to x CPPAD_TESTVECTOR(double) dx(1); CPPAD_TESTVECTOR(double) dy(1); dx[0] = 1.; dy = f.Forward(1, dx); ok &= NearEqual(dy[0], (Data[1] - Data[0]) / xStep, eps99, eps99); return ok; } bool SecondOrderReverse(void) { // Bradley M. Bell 2009-07-06 // Reverse mode for LdpOp was only modifying the highest order partial // This test demonstrated the bug bool ok = true; using CppAD::AD; using CppAD::NearEqual; double eps = 10. * std::numeric_limits::epsilon(); size_t n = 1; CPPAD_TESTVECTOR(AD) X(n); X[0] = 2.; CppAD::Independent(X); size_t m = 2; CPPAD_TESTVECTOR(AD) Y(m); // The LdpOp instruction corresponds to operations with VecAD vectors. CppAD::VecAD Z(2); AD zero = 0; Z[zero] = X[0] + 1; // The LdvOp instruction corresponds to the index being a variable. AD one = X[0] - 1; // one in a variable here Z[one] = X[0] + 1.; // Compute a function where the second order partial for y // depends on the first order partials for z // This will use the LdpOp instruction because the index // access to z is the parameter zero. Y[0] = Z[zero] * Z[zero]; Y[1] = Z[one] * Z[one]; CppAD::ADFun f(X, Y); // first order forward CPPAD_TESTVECTOR(double) dx(n); size_t p = 1; dx[0] = 1.; f.Forward(p, dx); // Test LdpOp // second order reverse (test exp_if_true case) CPPAD_TESTVECTOR(double) w(m), dw(2 * n); w[0] = 1.; w[1] = 0.; p = 2; dw = f.Reverse(p, w); // check first derivative in dw double check = 2. * (Value( X[0] ) + 1.); ok &= NearEqual(dw[0], check, eps, eps); // check second derivative in dw check = 2.; ok &= NearEqual(dw[1], check, eps, eps); // Test LdvOp // second order reverse (test exp_if_true case) w[0] = 0.; w[1] = 1.; p = 2; dw = f.Reverse(p, w); // check first derivative in dw check = 2. * (Value( X[0] ) + 1.); ok &= NearEqual(dw[0], check, eps, eps); // check second derivative in dw check = 2.; ok &= NearEqual(dw[1], check, eps, eps); return ok; } /* get_test_function Function that uses all the VecAD operators; i.e. StppOp, StpvOp, StvpOp, StvvOp, LdOpOp, LdvOp The funcntion v(x) is defined by v_0(x) = 6.0 v_1(x) = x_2 v_2(x) = if int(x_0) == 2 then x_3 else if int(x_1) == 2 then 5.0 else 2.0 v_3(x) = if int(x_0) == 3 then x_3 else if int(x_1) == 3 then 5.0 else 3.0 The function y(x) is defined by y_i(x) = if i < 4 then v_i(x) * v_i(x) else v_k(x) * v_k(x) where k = int(x_4) */ CppAD::ADFun get_test_function(void) { // // AD, VecAD using CppAD::AD; // // ax CPPAD_TESTVECTOR( AD ) ax(5); for(size_t i = 0; i < 5; ++i) ax[i] = 2.0; CppAD::Independent(ax); // // av (parameter) CppAD::VecAD av(4); for(size_t i = 0; i < 4; ++i) av[i] = double(i); // // av (variable) AD azero(0); AD aone(1); av[ ax[1] ] = 5.0; // StvpOp av[ ax[0] ] = ax[3]; // StvvOp av[azero] = 6.0; // StppOp av[aone] = ax[2]; // StpvOp // // ay CPPAD_TESTVECTOR( AD ) ay(5); for(size_t i = 0; i < 4; ++i) { AD ai(i); ay[i] = av[ai] * av[ai]; // LdpOp } ay[4] = av[ ax[4] ] * av[ ax[4] ]; // LdvOp // // test_function return CppAD::ADFun(ax, ay); } // // get_test_function_x CPPAD_TESTVECTOR(double) get_test_function_x(void) { // // seed using std::chrono::seconds; seconds sec = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch() ); unsigned int seed = static_cast( sec.count() ); // // uniform_01 CppAD::uniform_01(seed); // // x CPPAD_TESTVECTOR(double) x(5); CppAD::uniform_01(x.size(), x); for(size_t i = 0; i < 5; ++i) x[i] = 3.999999 * x[i]; // return x; } // // forward_zero_test_function bool forward_zero_test_function( CppAD::ADFun& fun , CPPAD_TESTVECTOR(double)& x ) { // // ok bool ok = true; // // eps99 double eps99 = std::numeric_limits::epsilon(); // // y CPPAD_TESTVECTOR(double) y(5); y = fun.Forward(0, x); // // v CPPAD_TESTVECTOR(double) v(4); v[0] = 6.0; v[1] = x[2]; // v[2] if( int(x[0]) == 2 ) v[2] = x[3]; else if( int(x[1]) == 2 ) v[2] = 5.0; else v[2] = 2.0; // v[3] if( int(x[0]) == 3 ) v[3] = x[3]; else if( int(x[1]) == 3 ) v[3] = 5.0; else v[3] = 3.0; // // ok for(size_t i = 0; i < 4; ++i) ok &= CppAD::NearEqual(y[i], v[i] * v[i], eps99, eps99); size_t k = size_t(x[4]); ok &= CppAD::NearEqual(y[4], v[k] * v[k], eps99, eps99); // return ok; } // forward_one_test_function bool forward_one_test_function( CppAD::ADFun& fun , CPPAD_TESTVECTOR(double)& x ) { // // ok bool ok = true; // // eps99 double eps99 = std::numeric_limits::epsilon(); // // fun fun.Forward(0, x); // // dx, dy CPPAD_TESTVECTOR(double) dx(5), dy(5); for(size_t j = 0; j < 5; ++j) { // dx for(size_t k = 0; k < 5; ++k) dx[k] = 0.0; dx[j] = 1.0; // // dy dy = fun.Forward(1, dx); // // ok // for dy_i / dx_j for i = 0, ..., 3 for(size_t i = 0; i < 4; ++i) { if( j == 0 || j == 1 ) ok &= dy[i] == 0.0; else if( j == 2 ) { if( i == 1 ) ok &= CppAD::NearEqual(dy[i], 2.0 * x[2], eps99, eps99); else ok &= dy[i] == 0.0; } else if( j == 3 ) { if( size_t(x[0]) == i ) { if( i == 2 || i == 3 ) ok &= CppAD::NearEqual(dy[i], 2.0 * x[3], eps99, eps99); else ok &= dy[i] == 0.0; } } else { assert( j == 4 ); ok &= dy[i] == 0.0; } } // // ok // for dy_4 / dx_j if( j == 0 || j == 1 || j == 4 ) ok &= dy[4] == 0.0; else { size_t k = size_t( x[4] ); if( k == 1 && j == 2 ) ok &= CppAD::NearEqual(dy[4], 2.0 * x[2], eps99, eps99); else if( (k == 2 || k == 3) && j == 3 && size_t(x[0]) == k ) ok &= CppAD::NearEqual(dy[4], 2.0 * x[3], eps99, eps99); else ok &= dy[4] == 0.0; } } // return ok; } bool jac_sparsity_test_function(CppAD::ADFun& fun) { // // ok bool ok = true; // // size_vector typedef CppAD::vector size_vector; // // sparse_rc typedef CppAD::sparse_rc sparse_rc; // // dependency for(bool dependency : {false, true}) { // // pattern_check size_t nr = 5, nc = 5, nnz = 0; sparse_rc pattern_check(nr, nc, nnz); for(size_t i = 0; i < nr; ++i) { // values in v depend on x_2 and x_3 pattern_check.push_back(i, 2); pattern_check.push_back(i, 3); if( dependency ) { // indices in v depend on x_0 and x_1 pattern_check.push_back(i, 0); pattern_check.push_back(i, 1); if( i == 4 ) { // y_4 depends on x_4 as an index in v pattern_check.push_back(i, 4); } } } // // pattern_eye nr = 5, nc = 5, nnz = 5; sparse_rc pattern_eye(nr, nc, nnz); for(size_t k = 0; k < nr; ++k) pattern_eye.set(k, k, k); // // pattern_jac sparse_rc pattern_jac; bool transpose = false; bool internal_bool = false; fun.for_jac_sparsity( pattern_eye, transpose, dependency, internal_bool, pattern_jac ); // // ok ok &= pattern_jac == pattern_check; // // pattern_jac fun.rev_jac_sparsity( pattern_eye, transpose, dependency, internal_bool, pattern_jac ); // // ok ok &= pattern_jac == pattern_check; } // return ok; } bool hes_sparsity_test_function(CppAD::ADFun& fun) { // // ok bool ok = true; // // size_vector typedef CppAD::vector size_vector; // // sparse_rc typedef CppAD::sparse_rc sparse_rc; // // pattern_check size_t nr = 5, nc = 5, nnz = 0; sparse_rc pattern_check(nr, nc, nnz); for(int r : {2, 3} ) { for(int c : {2, 3} ) pattern_check.push_back(size_t(r), size_t(c)); } // // pattern_eye nr = 5, nc = 5, nnz = 5; sparse_rc pattern_eye(nr, nc, nnz); for(size_t k = 0; k < nr; ++k) pattern_eye.set(k, k, k); // // pattern_jac sparse_rc pattern_jac; bool dependency = false; bool transpose = false; bool internal_bool = false; fun.for_jac_sparsity( pattern_eye, transpose, dependency, internal_bool, pattern_jac ); // // select_domain CPPAD_TESTVECTOR(bool) select_domain(5); for(size_t i = 0; i < 5; ++i) select_domain[i] = true; // // select_range CPPAD_TESTVECTOR(bool) select_range(5); for(size_t i = 0; i < 5; ++i) select_range[i] = false; // // pattern_hes sparse_rc pattern_hes; // // i for(size_t i = 0; i < 5; ++i) { // // pattern_hes select_range[i] = true; fun.rev_hes_sparsity( select_range, transpose, internal_bool, pattern_hes ); select_range[i] = false; // // ok ok &= pattern_hes == pattern_check; // // pattern_hes select_range[i] = true; fun.for_hes_sparsity( select_domain, select_range, internal_bool, pattern_hes ); select_range[i] = false; // // ok ok &= pattern_hes == pattern_check; } // return ok; } } // END empty namespace bool VecAD(void) { bool ok = true; ok &= test_comp_assign(); ok &= VecADTestOne(); ok &= VecADTestTwo(); ok &= SecondOrderReverse(); // // fun, x CppAD::ADFun fun = get_test_function(); CPPAD_TESTVECTOR(double) x = get_test_function_x(); // ok &= forward_zero_test_function(fun, x); ok &= forward_one_test_function(fun, x); ok &= jac_sparsity_test_function(fun); ok &= hes_sparsity_test_function(fun); // return ok; } ================================================ FILE: test_more/general/vec_ad_par.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Test the use of the parameters in VecAD element assignments */ # include typedef CppAD::AD ADdouble; typedef CppAD::AD< ADdouble > ADDdouble; bool VecADPar(void) { using namespace CppAD; bool ok = true; CPPAD_TESTVECTOR( ADdouble ) x(2); x[0] = 0; x[1] = 0; Independent(x); CPPAD_TESTVECTOR( ADDdouble ) y(1); y[0] = 1; Independent(y); VecAD< ADdouble > v(2); ADDdouble zero(0); ADDdouble one(1); v[zero] = x[0]; // these two parameter values are equal, v[one] = x[1]; // but they are not identically equal CPPAD_TESTVECTOR( ADDdouble ) z(1); z[0] = v[zero] + v[one]; // f(y) = x[0] + x[1] ADFun< ADdouble > f(y, z); CPPAD_TESTVECTOR( ADdouble ) a( f.Domain() ); CPPAD_TESTVECTOR( ADdouble ) b( f.Range() ); // fy = f(y) = x[0] + x[1] a[0] = 0.; b = f.Forward(0, a); // check value of f ok &= b[0] == (x[0] + x[1]); // g(x) = x[0] + x[1]; ADFun g(x, b); CPPAD_TESTVECTOR( double ) c( g.Domain() ); CPPAD_TESTVECTOR( double ) d( g.Range() ); // d = g(1, 2) c[0] = 1.; // these tow values are not equal and correspond c[1] = 2.; // to replacements for the equal parameter values above d = g.Forward(0, c); // check function value ok &= (d[0] == c[0] + c[1]); return ok; } ================================================ FILE: test_more/general/vec_unary.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-22 Bradley M. Bell // ---------------------------------------------------------------------------- /* Test the use of VecADelem with unary operators */ # include bool VecUnary(void) { using namespace CppAD; using CppAD::sin; using CppAD::atan; using CppAD::cos; using CppAD::exp; using CppAD::log; using CppAD::sqrt; using CppAD::NearEqual; double eps99 = 99.0 * std::numeric_limits::epsilon(); bool ok = true; size_t n = 8; size_t i; CPPAD_TESTVECTOR(AD) X(n); VecAD Y(n); CPPAD_TESTVECTOR(AD) Z(n); for(i = 0; i < n; i++) X[i] = int(i); // some compilers require the int here Independent(X); AD j; j = 0.; Y[j] = X[0]; Z[0] = -Y[j]; j = 1.; Y[j] = X[1]; Z[1] = sin( Y[j] ); j = 2.; Y[j] = X[2]; Z[2] = fabs( Y[j] ); j = 3.; Y[j] = X[3]; Z[3] = atan( Y[j] ); j = 4.; Y[j] = X[4]; Z[4] = cos( Y[j] ); j = 5.; Y[j] = X[5]; Z[5] = exp( Y[j] ); j = 6.; Y[j] = X[6]; Z[6] = log( Y[j] ); j = 7.; Y[j] = X[7]; Z[7] = sqrt( Y[j] ); ADFun f(X, Z); CPPAD_TESTVECTOR(double) x(n); CPPAD_TESTVECTOR(double) z(n); for(i = 0; i < n; i++) x[i] = 2. / double(i + 1); x[7] = fabs( x[7] ); z = f.Forward(0, x); ok &= NearEqual(z[0], - x[0], eps99, eps99); ok &= NearEqual(z[1], sin( x[1] ), eps99, eps99); ok &= NearEqual(z[2], fabs( x[2] ), eps99, eps99); ok &= NearEqual(z[3], atan(x[3] ), eps99, eps99); ok &= NearEqual(z[4], cos( x[4] ), eps99, eps99); ok &= NearEqual(z[5], exp( x[5] ), eps99, eps99); ok &= NearEqual(z[6], log( x[6] ), eps99, eps99); ok &= NearEqual(z[7], sqrt(x[7] ), eps99, eps99); return ok; } ================================================ FILE: typos.toml ================================================ [default.extend-words] Fo = "Fo" iy = "iy" nd = "nd" ot = "ot" mak = "mak" yout = "yout" ================================================ FILE: user_guide.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin user_guide} {xrst_spell adolc autodiff fadbad openmp org posix sacado templated } {xrst_comment BEGIN: Before changing see new_release.sh and check_version.sh} CppAD User's Manual ################### .. image:: {xrst_dir coin.png} License ******* CppAD is distributed by `COIN-OR `_ with the Eclipse Public License `EPL-2.0 `_ or the GNU General Public License `GPL-2.0-or-later `_ Git Repository ************** ``_ Versions ******** .. list-table:: :widths: auto * - This version - cppad-20260408 * - Documentation for latest version - `latest `_ * - Documentation for most recent stable version - `stable-2026 `_ * - Most recent release of this stable version - `release-2026 `_ {xrst_comment END: Before changing see new_release.sh and check_version.sh} Other Links *********** .. list-table:: :widths: auto * - :ref:`install-name` - :ref:`get_started` - :ref:`whats_new-name` - :ref:`addon-name` - `project manager `_ Algorithmic Differentiation *************************** We refer to the automatic creation of an algorithm that computes derivative values from an algorithm that computes function values as *Algorithmic Differentiation* , also referred to as *Automatic Differentiation* or just AD. A brief introduction to AD can be found in `wikipedia `_. The web site `autodiff.org `_ is dedicated to research about, and promoting the use of, AD. Features ******** Operator Overloading ==================== CppAD uses operator overloading of the C++ template class :ref:`AD-name` to compute derivatives of algorithms defined using AD objects; see the :ref:`introduction-name` for a description of how this is accomplished. Base Type ========= The operator overloading uses a templated base type that can be user defined; see :ref:`base_require-name` . The required definitions for ``AD`` and ``AD`` are included as part of CppAD. Recording Operations ==================== A sequence of ``AD`` < *Base* > :ref:`operations` can be recorded and stored in an :ref:`AD function object` . This object can then be used to evaluate function values and arbitrary order derivatives, and sparsity patterns of derivative values using the *Base* type. Multi-Threading =============== CppAD supports an arbitrary :ref:`multi threading` environment. Examples are provided using Boost threads, Posix threads, and Openmp threads. Optimizing Operations Sequences =============================== During the recording of a function, the :ref:`Independent-name` variables are know and a forward dependency analysis is used to determine which operations should be recorded. Once the :ref:`Dependent-name` variables are determined, a reverse dependency analysis can be preformed. This :ref:`optimize-name` routine uses a reverse dependency analysis, and other techniques, to remove unnecessary operations. Dynamic Parameters ================== CppAD enables one to specify a vector of :ref:`glossary@Parameter@Dynamic` parameters. The value of the function and derivatives can depend on these parameters, but no derivatives are taken with respect to these parameters. This enables CppAD to reduce the derivative calculations; e.g., the derivative of variable times a variable has two terms while a variable times a parameter only has one. Derivative Calculations ======================= Arbitrary order :ref:`Forward-name` and :ref:`Reverse-name` mode derivative calculations can be preformed using an ``ADFun`` object. Easy to user drivers that compute the entire :ref:`Jacobian-name` and a specific :ref:`Hessian-name` are included. Sparsity ======== Both forward and reverse mode can be used to calculation the sparsity pattern for Jacobians and Hessians; see :ref:`sparsity_pattern-name` . Where a Jacobian or Hessian is sparse, both forward and reverse mode can be combined with the sparsity pattern to speed up the calculation of :ref:`sparse derivatives` . In addition, a :ref:`subgraph` method, that does not require a sparsity pattern, can be used to speed up these derivative calculations. Recording Derivative Operations =============================== A ``ADFun`` object can be converted into an object that evaluates derivatives using the type ``AD`` < *Base* > ; see :ref:`base2ad-name` . This enables one to record new functions that are expressed using derivatives of other functions. Atomic Functions ================ User defined derivative calculations for arbitrary functions can also be included in a recording using :ref:`atomic functions` . A special :ref:`checkpoint` class is included which allows one to record a function and reuse it as an atomic operation in other functions. There also is a special interface for user defined unary :ref:`discrete functions` ; i.e., functions that depend on the :ref:`independent variables` but which have identically zero derivatives (e.g., a step function). Logical Comparisons =================== Logical comparisons can be included in an operation sequence using AD :ref:`conditional expressions` . Vectors ======= The CppAD API allows one to use any :ref:`SimpleVector-name` class. The preprocessor symbol :ref:`CPPAD_TESTVECTOR` is template vector class which is used for correctness testing. Many of the correctness tests use this template vector class which you can choose during the :ref:`cmake-name` configuration command. Software Engineering ******************** CppAD is developed using the software engineering procedures described on the project manager's `software `_ web page. Testing ******* Correctness =========== There is an extensive set of correctness tests; see :ref:`cmake_check-name` . Speed ===== A set of programs for doing :ref:`speed-name` comparisons between `Adolc `_, CppAD, `Fadbad `_, and `Sacado `_ are included. Utilities ********* CppAD includes a set of C++ :ref:`utilities` that are useful for general operator overloaded numerical methods. Release Notes ************* This :ref:`whats_new-name` for a list of recent extensions and bug fixes. Example ******* The file :ref:`get_started.cpp-name` contains an example and test of using CppAD to compute the derivative of a polynomial. There are many other :ref:`examples` . All of the examples are also correctness tests, which ensures that they work properly. Contents ******** {xrst_toc_table xrst/install/install.xrst xrst/theory/theory.xrst include/cppad/core/user_ad.hpp include/cppad/core/ad_fun.hpp xrst/preprocessor.xrst xrst/multi_thread.xrst include/cppad/utility/xrst/utility.xrst include/cppad/ipopt/solve.hpp xrst/example.xrst speed/speed.xrst } {xrst_end user_guide} ================================================ FILE: uw_copy_040507.html ================================================ Vol 4, Part 5, Chapter 7

Chapter 7

PATENT, INVENTION, AND COPYRIGHT POLICY

Section 1. Patent and Invention Policy

  1. This policy covers both patented and nonpatented innovations, including computer software with commercial value, and is applicable to all faculty, staff, and students. The policy is intended to show the University's positive attitude toward transfer of results of its research to the private sector.
  2. The purpose of university research is to seek new knowledge for the general benefit. Although university research is not directed intentionally toward inventions, commercially valuable inventions do often result, and it is generally in the best interests of the University and the public that patents be obtained and/or licenses granted as described in this policy. Inventions shall be promptly reported to the University's Office of Intellectual Property and Technology Transfer and all concerned shall cooperate to assure prompt initiation of appropriate technology transfer actions. The term "invention" means any invention or discovery which is or may be patentable or otherwise protectable as to ownership. An invention may be a process, machine, manufacture, composition of matter or design, or any new or useful improvement thereof. An invention is deemed to be "made" when it is conceived or first actually reduced to practice.
  3. University employees shall report all inventions and discoveries to the University's Office of Intellectual Property and Technology Transfer. As a condition of employment, and even if a specific patent agreement is not signed, University employees agree to assign all inventions in which the University has an interest to the University, to an invention management agency designated by the University, or to the sponsor if required under agreements governing the research. Employees shall execute documents of assignment and do everything reasonably required to assist the assignee(s) in obtaining, protecting, and maintaining patent or other proprietary rights. Students who are also employees, students working on a sponsored project, and students who have used University resources (other than for lecture-based coursework) shall also report all inventions and discoveries to the University's Office of Intellectual Property and Technology Transfer and shall assign all such inventions and discoveries in the same manner as University employees. Inventions in which the University has an interest but which do not meet University criteria for patenting shall be managed in accordance with policies and procedures determined by the University Office of Intellectual Property and Technology Transfer. If and to the extent permitted by state law and other University policies, those procedures may include: (a) a mechanism by which the inventor(s) may personally pay patenting costs; (b) the formation of a commercial enterprise to pursue commercialization; and, under very rare circumstances, (c) the transfer, for appropriate consideration, of the patent rights to the inventor(s). These procedures shall be implemented at the discretion of the Vice Provost for Intellectual Property and Technology Transfer.

    Although all inventions and discoveries must be reported to the Office of Intellectual Property and Technology Transfer, there are instances when the University may choose not to assert ownership. The University will not require assignment of interests for any invention for which no equipment, supplies, facilities, or trade secret information of the University was used and which was developed entirely on the employee's own time, unless (a) the inventions related (i) directly to the business of the University, or (ii) the University's actual or demonstrably anticipated research or development, or (b) the invention results from any work performed by the employee for the University.

  4. Research funded wholly or in part by an outside sponsor is subject to this policy as modified by the provisions of the agreement covering such work. Employees engaged in sponsored research are bound by the provisions of the agreement between the University and the sponsor. Title to any inventions conceived or first reduced to practice in the course of research supported by Federal agencies, industry, or other sponsors shall generally vest in the University. In rare cases, an industrial sponsor may possess a dominant patent position in a certain technology area so that any patent the University might seek would be of little or no value. For this or other reasons, an exception to the University title policy may be approved by the University's Office of Intellectual Property and Technology Transfer when to do so will honor the general principles of this policy, protect the equities involved, and satisfy the requirements of the parties.
  5. Industry supported research is valued by the University when it embraces a proper balance between the University's educational mission and industry's quest for the development of commercial products, processes, and services. Interaction with industry may take any of several forms, including grants, contracts, consortia agreements, and affiliate programs. Industry sponsors may be assured of at least a non-exclusive license to inventions conceived or developed with their support. Where the sponsor uses the invention entirely within its own operations, the license may be royalty-free. Where the sponsor, or a third party, manufactures and sells products, services, or processes based on the invention, reasonable royalty payments to the University, or its assignee, are required. If necessary for the effective development and marketing of a University invention, an exclusive license may be granted, usually for a limited time period. Where an invention is not identifiable in advance, the University may grant the sponsor an option to an exclusive license if the sponsor agrees to finance the cost of the University's patent application and observe certain diligence requirements that will assure promptly bringing the invention into public use. The patent financing may be treated as an offset against royalties payable when the invention is marketed.
  6. The University retains the right to file patents itself or to use other patent management firms. The University has agreements with the Washington Research Foundation, Research Corporation Technologies of Tucson, Arizona and Battelle Development Corporation in Columbus, Ohio as patent and license agents.
  7. Both the University and the inventor are entitled to a share of income from licensed inventions; the University on the basis of salary and facilities support for the inventor and the cost of patent or license administration; and the inventor on the basis of creative activity, documenting the invention, and assisting as necessary with commercialization. Thus, the University allocates a share of income to the inventor. The remainder is dedicated to further research by allocating shares to the college/department (or other unit) in which the invention was conceived or first reduced to practice and to the Office of the Provost.
  8. The University may take an equity position in a company whether or not license fees or royalties are paid to the University as part of a negotiated agreement. A typical circumstance under which the University might receive equity would be as part of an agreement licensing University-developed technology to a start-up or developing business venture. Another example might occur when an employee of the University utilizes the expertise and/or technology he or she has developed in the course of University employment and assists a business venture in the commercialization of an idea. (A business venture includes corporations, partnerships, or other commercial enterprises.) Such a commercial association with the University and its employees adds both value and credibility to the new business venture. To assure a balance of interests for the business venture as well as for the University, the University will generally require that it receive an equity position in such circumstances.
  9. The University's equity interests are managed and disposed of in accordance with guidelines established by the Treasury Office in consultation with the Office of the Provost and the policies and procedures stated in the University Handbook and Administrative Policy Statements. University employees may be eligible to receive a portion of the University's equity interest in accordance with the policies and procedures described in the University's Administrative Policy Statements and as allowed under state law and University conflict of interest policies. When such equities are liquidated, the net proceeds, after recovery of all University costs and after any distributions to eligible recipients, accrue to appropriate University accounts and are administered by the Provost to promote research and technology transfer across the entire University. If the proceeds from the disposition of a particular equity interest are unusually large, the Provost shall confer with the University Budget Committee, the Research Advisory Board, or other appropriate faculty bodies, on alternative uses for amounts in excess of a base figure (set at $3 million in 2000 dollars).

    There may be situations in which both the University and its employees separately own equity interests in a business venture. In such circumstances, the employee's equity interest is considered to be independent of the University's equity interest and is not held, managed, disposed of, or distributed by the University. An example would be a case in which the University receives an equity interest in a business venture as a result of licensing certain intellectual property developed by one of its employees and in which the same employee also owns a equity interest as a result of being a founder of the business venture receiving the license. In this example, the employee's equity interest is not held or managed by the University, but rather by the employee, and the employee's status as a founder having an ownership stake in the business venture renders the employee ineligible to receive a distribution of a portion of the University-owned equity.

  10. As a public institution, the University should undertake sponsored research only when the results can be published. Publication may be deferred for a reasonable time during which the University and the sponsor review the feasibility of patent coverage or other protection on an invention described in the publication. Likewise, graduate student theses or dissertations containing invention details may be withheld from the Library shelves for a limited period while this evaluation process is conducted. Some research agreements may involve University access to a sponsor's proprietary data subject to a clause defining the conditions under which such data will be identified, accepted, and used. Students should be able to participate in such research in a meaningful way without access to proprietary data. When publication of the research involving proprietary data is contemplated, the University may agree to provide the sponsor with advance copy prior to submission for publication to allow the sponsor an opportunity to identify any inadvertent disclosure of proprietary data.
  11. Employee consulting with commercial enterprises can be of significant benefit to the University, the employee, the commercial entity, and the general public. However, such involvements include the potential for conflicts of interest, for the inhibition of the free exchange of information, and for interference with the employee's primary allegiance to the University. University employees should be guided in these arrangements by the policy stated in Volume Four, Part V, Chapters 2 and 6 of the University Handbook. Invention clauses in consulting agreements must be consistent with the policy of the University and with University commitments under sponsored research agreements. Questions concerning potential conflicts should be referred to the University's Office of the Provost and the Office of Research.
  12. Conflicts of interest are of prime concern when a faculty member is involved in "deeper than consulting" arrangements with business ventures. Although the faculty member may hold an equity interest or a management position in a business venture, he or she must do so consistent with the principles and procedures of Executive Order 57 (University Handbook, Volume Four, Part V, Chapter 6, Section 6, Involvement with Commercial Enterprise, Deeper than Consulting). In situations where the employee is a board member, manager, or receives shares of stock, the option to purchase stock, or other equity interest in return for the use of his or her services and/or inventions in a business venture, approval by the Office of the Provost (after review by the dean and the chair) is required. The primary focus of the review by the Office of the Provost will be to ensure that potential conflicts of interest and exposure to liability are properly managed. For example, the interests of the graduate students involved in such cases must be protected, there must be no direct managerial involvement of the faculty member in the business venture, there must be an arms-length relationship between the faculty member's responsibilities to the business venture and the faculty member's academic responsibilities, and mechanisms must be in place to ensure that the research program of the faculty member is not distorted by his or her interests in the business venture.

Section 2. Copyright Policy

  1. Background.
  2. The University encourages the publication of scholarly works as an inherent part of its educational mission. In this connection, the University acknowledges the right of faculty, staff, and students to prepare and publish, through individual initiative, articles, pamphlets, and books that are copyrighted by the authors or their publishers and that may generate royalty income for the authors.

    The variety and number of copyrightable materials that may be created in the university community have increased significantly in recent years as have the author-university- sponsor relationships under which such materials are produced. Therefore, the following statement of University policy on ownership and use of copyrightable materials is provided to clarify the respective rights of individuals and the University in this increasingly important area. The policy will be administered by the University's Office of Intellectual Property and Technology Transfer.

  3. General Statement of University Policy on Ownership and Use of Copyrightable Materials
  4. University faculty, staff, and students retain all rights in copyrightable materials they create, including scholarly works, subject to the following exceptions and conditions:

    1. Grant and Contract Limitations. Conditions regarding rights in data or restrictions on copyright privileges contained in sponsored grants, contracts, or other awards are binding on the University and on faculty, staff, or student authors. Copyright works, with the exception of routine progress reports, prepared as required elements of such sponsored grants, contracts, or other awards shall be reported to the Office of Intellectual Property and Technology Transfer for review prior to any external dissemination of the work. If necessary to fulfill grant and contract limitations, authors shall execute an appropriate written assignment of copyrights to the University.
    2. University-Owned Materials. Materials shall be "University-owned" within the meaning of this policy statement if the work is a "work for hire" under copyright law or the author was commissioned in writing by the University (or one of its colleges, schools, departments, or other divisions) to develop the materials as a part of the author's regularly compensated duties, as for example, released time arrangements in the case of faculty members. As to a faculty member, "commissioned in writing" specifically does not refer to his or her general obligation to produce scholarly works.
    3. University-Sponsored Materials. Materials shall be "University-sponsored materials" within the meaning of this policy statement if the author developed the materials in the course of performance of his or her normal duties and utilized University staff, resources, or funding to develop the work. As to a faculty member, "normal duties" does not include his or her usual scholarly activity unless it involves extensive uncompensated use of University resources.
    4. Written Agreements. It is desirable to reach agreement in writing as to the rights of the University and of participants before work begins whenever (1) a question exists as to whether the materials will be University-owned or University-sponsored, or (2) copyrightable materials are likely to result from the joint efforts of persons in academic departments and University service departments. As to jointly-developed materials, determination of rights in written form shall be accomplished no later than prior to sale of the materials in question. Questions concerning the interpretation and administration of this policy shall be resolved in accordance with Section 3.
    5. Proportional Ownership. In case of materials developed in substantial part under commission and in substantial part through other means, the materials shall be regarded as "University-owned" in an appropriate proportion. In the case of materials developed in substantial part during the course of normal duties and with use of University staff, resources, or funding the materials shall be regarded as "University-sponsored" in an appropriate proportion.
    6. Royalty-Free Privileges to University. The University retains a right to royalty-free use of any copyrightable materials developed by University employees (other than books and materials available from a publisher through normal distribution channels) when the development of such materials was advanced through the use of University facilities, supplies, equipment, or staff services. This right exists even though the materials do not constitute University-owned or University-sponsored materials as defined above (e.g., where use of facilities by a faculty member was not extensive).
    7. Student Writings. Students employed by the University in any capacity are covered by the terms of this policy. In addition, where a student receives financial aid or remuneration under a sponsored research, training, or fellowship program, his or her rights in copyrightable materials are limited by the terms of the University agreement with the sponsoring agency. The University has no ownership rights in copyrightable materials developed by students who are not employees of the University or in materials unrelated to their employment.

  5. Types of Materials.
  6. The types of materials to which this policy is intended to apply include:

    1. Video and audio recordings, tapes, and cassettes.
    2. Film, film strips and other visual aids.
    3. Books, texts, study guides and similar published materials.
    4. Computer programs and software when copyright rather than patent or trade secret protection is relied upon as the primary source of legal protection. (When the primary commercial value of a computer program lies in its transfer in limited quantities under arrangements of confidentiality, it shall be treated as unpatented technology and be subject to the University Patent and Invention Policy.)
    5. Musical or dramatic compositions.
    6. Internet-based productions and multimedia products.
    7. Other copyrightable materials.

  7. Rights Involved in use of University-Owned or University- Sponsored Materials.
    1. Two categories of use are differentiated for purposes of this policy: internal use and external use. Internal use refers to use by any unit of the University for instruction, research, or other educational purposes. External use refers to use by other educational institutions, government and other nonprofit institutions, and use resulting from lease or other contractual arrangements for commercial distribution of the materials.
    2. Use of University-owned or University-sponsored materials under this policy shall be subject to the following conditions:
      1. Internal use
        1. Each instance of use of such materials within the University requires the approval of the author and the department or college unless advance approval is waived through a prior written understanding or the author's consent is implicit in the terms of the grant or contract supporting the work. Internal uses of such materials will not involve a transfer of funds between departments unless the lending department incurs incremental costs in order to make the materials available.
        2. As long as the author or producer of such materials remains an employee of the University, the author may: (a) request reasonable revisions of the materials prior to any instance of internal use, or (b) ask that the materials be withdrawn from internal use if revisions are not feasible.
        3. In cases where the University has invested significant funds in the production of the materials and the author/producer is unable to agree with the department head on appropriate revision or withdrawal of materials, the question will be referred to the dean of the school or college for mediation.

        4. If the author or producer terminates employment with the University, then the University retains the right to continue internal use of the material unless the author/producer and the University agree in writing on special conditions for subsequent internal use of the materials and the procedures for their revision.

      2. External use
      3. Licensing or sale of University-owned or University- sponsored materials for external use shall be preceded by a written agreement between the University and the author or producer specifying the conditions of use, and including provisions concerning the right of the author or producer to revise the materials periodically, or to withdraw them from use-- subject to existing agreements--in the event revisions are not feasible.

  1. Division of Royalties.

    1. General Policy on Royalties. As to University-owned materials, all royalties and income should inure to the University and its schools, colleges, and departments as such materials are prepared in exchange for agreed compensation. As to University-sponsored materials, a sharing of royalties and income is appropriate because of the author's provision of creative efforts on the one hand and the University's provision of salary, facilities, administrative support, and other resources.
    2. Royalties on Sales to Outside Users. Where University-owned or University-sponsored materials are to be sold or rented to outside users, the following guidelines pertaining to financial arrangements should be observed (subject to any limitations specified by granting agencies):
      1. All incremental expenses related to the distribution of copies will be recovered from each sale or rental. Original costs for production of the materials shall be recovered only if and as agreed to in writing prior to preparation of the materials by the author and the academic departments and/or other University units which incur those costs.
      2. In the case of University-owned materials, royalty and other income from sale or use of the materials (after recovery of costs as specified in 1.) shall be divided one-half to the University and one-half to the school/college/department of the author or authors. The University share shall be used to promote research across the whole University and shall normally be administered by the Office of Research. The school/college/department share shall be allocated to the dean of the college or school, and may be used for research, education, and communications. At least 75% of this share should normally go to the author's department for use there according to departmental and college goals. The dean should have discretion in distributing the remaining 25% to promote activities according to the nature and needs of the college or school in question.
      3. In the case of University-sponsored materials, royalty and other income from the sale or use of materials (after recovery of costs as specified in a.) shall be divided according to the Administrative Policy Statement 59.4, "Technology Transfer." In any given case covered by this subsection, the author may dedicate all or any portion of his/her allocation to the school/college/department, the Office of Research, or other administrative unit, subject to the provisions of Administrative Policy Statement 59.4).
      4. In certain instances it may be advantageous to market University-owned or University-sponsored materials through outside commercial sources or the University Press. Net royalty income from such sources shall be divided as specified in b. and c.
      5. Royalty and other income from updating and revision of University-sponsored materials shall be treated as income and royalty from such University-sponsored materials, unless otherwise agreed to in writing by the author/producer and the University before preparation of the original materials. The net income from such upkeep or revision shall be separately computed on an annual basis for the purpose of applying the distributions referenced in paragraph c.

  2. Protection and Liability.

    1. Protection. The Office of Intellectual Property and Technology Transfer shall investigate allegations of unauthorized use or copyright infringement of University-owned and University-sponsored materials and shall recommend appropriate action. If such action is started by the University, all costs of such action shall be borne by the University. All proceeds in excess of such costs shall be shared as provided in Subection F. (subject to sponsoring agency limitations if a grant or contract is involved).
    2. Liability. When there are allegations of violation of personal or property rights by the University, or by the author or producer of University-owned or University-sponsored materials copyrighted by the University, the University shall assume responsibility for the defense of any action and the satisfaction of any judgment rendered against the University or the author or producer.

Section 3. Interpretation and Administration of Policy.

  1. The President has designated the Vice Provost for Intellectual Property and Technology Transfer as the officer of the University to administer, apply, and interpret the provisions of this policy. The Vice Provost shall have the authority to determine whether the facts of a given case merit special consideration.
  2. Committee. The President of the University will appoint an Intellectual Property Management Advisory Committee to review periodically the policy set forth in this statement and recommend such changes to the President as the Committee deems desirable. The Committee will also advise on broader intellectual property issues that arise in the promotion and protection of research. The Committee will report to the Vice Provost for Intellectual Property and Technology Transfer and consist of no fewer than five members, a majority of whom shall be chosen from the faculty.

  3. Distribution of Statement. When this statement becomes effective as University policy, the President's office shall see that all departments and administrative offices of the University are properly informed. Thereafter, the Vice Provost for Intellectual Property and Technology Transfer shall remind deans and department heads periodically of the existence of the policy, inform them about any significant interpretations of the policy, and invite comments or questions regarding it.
  4. Duties of Vice Provost for Intellectual Property and Technology Transfer. The Vice Provost for Intellectual Property and Technology Transfer shall manage the Office of Intellectual Property and Technology Transfer, which will represent the University in negotiating agreements with inventors, authors or producers, or licensees pursuant to this policy. She or he may consult also with department heads and the heads of production units involved in a specific technology transfer transaction, and she or he shall sign or recommend all agreements for signing consistent with delegated authority. Where copyright coverage should be obtained on University-sponsored or University-owned materials, the Office of Intellectual Property and Technology Transfer will facilitate the copyright application. The faculty or staff member who is the author of University-owned or University-sponsored materials shall execute a written transfer of copyright to the University when necessary or appropriate.
  5. Inquiries on Status of Materials. Any faculty or staff member who has a question as to whether or not particular materials will be considered University-owned or University- sponsored should initiate an inquiry to the Office of Intellectual Property and Technology Transfer as to their status. This inquiry, with all relevant facts, should be forwarded via the author's department head. Thereafter, the Vice Provost for Intellectual Property and Technology Transfer shall advise the author or producer as promptly as possible as to whether or not it appears that the materials should be regarded as University-owned or University-sponsored within the meaning of this policy. The Vice Provost for Intellectual Property and Technology Transfer's decision in such cases will be considered as an advisory opinion subject to final clarification when the work is completed. At that time, the faculty or staff member should either (1) indicate concurrence in the original decision, or (2) request that the question of rights be submitted for decision to the Vice Provost for Intellectual Property and Technology Transfer. In the latter case, the decision of the Vice Provost will be final unless the faculty or staff member requests arbitration of the question.
  6. Arbitration. In the event of any differences between faculty or staff members, on the one hand, and the Vice Provost for Intellectual Property and Technology Transfer, on the other hand, and when the questions cannot be reconciled by direct negotiation, the matter shall be submitted for binding arbitration either to a single arbitrator agreed on by all parties or to a special three-person panel consisting of one person representing the faculty or staff member, one person representing the University, and a third person designated by the first two. Knowledgeable members of the University community will normally be chosen for such panels in order to expedite a decision and minimize cost. In the event costs are incurred, they shall be divided equally between the faculty/staff member and the University. Decisions of the panel will be binding on both parties. The panel shall have full access to any pertinent records over which the faculty/staff member or the University has jurisdiction.

BR, March 1969; Executive Order No. 36 of the President, June 1, 1972; revised October 3, 1977; September 26, 1983; September 21, 1992; May 2, 2000, December 20, 2000; October 27, 2003.

================================================ FILE: val_graph/CMakeLists.txt ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-23 Bradley M. Bell # ---------------------------------------------------------------------------- # Build the val_graph directory tests # # BEGIN_SORT_THIS_LINE_PLUS_2 SET(source_list val_graph.cpp binary_xam.cpp call_xam.cpp cexp_xam.cpp comp_xam.cpp compress_xam.cpp con_xam.cpp csum_xam.cpp cumulative_xam.cpp dead_xam.cpp dis_xam.cpp fold_con_xam.cpp fun2val_xam.cpp pri_xam.cpp renumber_xam.cpp summation_xam.cpp test/ad_double.cpp test/fold.cpp test/fun2val.cpp test/nan.cpp test/opt_call.cpp test/optimize.cpp test/val2fun.cpp test/val_optimize.cpp unary_xam.cpp val2fun_xam.cpp vec_xam.cpp ) # END_SORT_THIS_LINE_MINUS_2 # set_compile_flags( val_graph "${cppad_debug_which}" "${source_list}" ) # ADD_EXECUTABLE(val_graph EXCLUDE_FROM_ALL ${source_list}) # # List of libraries to be linked into the specified target TARGET_LINK_LIBRARIES(val_graph ${cppad_lib} ${colpack_libs} ) # # check_example_print_for add_check_executable(check val_graph) ================================================ FILE: val_graph/atomic_xam.hpp ================================================ # ifndef CPPAD_VAL_GRAPH_ATOMIC_XAM_HPP # define CPPAD_VAL_GRAPH_ATOMIC_XAM_HPP // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell /* {xrst_begin val_atomic_xam.hpp dev} An Example Atomic Function ########################## Function ******** This function maps :math:`\B{R}^4 \rightarrow \B{R}^2` by .. math:: f_0 (x) & = x_0 + x_1 \\ f_1 (x) & = x_1 \cdot x_2 Identically Zero **************** If :math:`x_1` or :math:`x_2` is identically zero, :math:`f_1 (x)` is identically zero. The routine below keeps the constant zero, but the expression it multiplies may get replaced by nan during optimization. Note the for ``double``, identically zero is the same as equals zero. Source ****** This is an :ref:`atomic_four-name` implementation of the function above {xrst_literal // BEGIN_VAL_ATOMIC_XAM // END_VAL_ATOMIC_XAM } {xrst_end val_atomic_xam.hpp} */ // BEGIN_VAL_ATOMIC_XAM # include class val_atomic_xam : public CppAD::atomic_four { public: val_atomic_xam(void) : CppAD::atomic_four("val_atomic_xam") { } private: // for_type bool for_type( size_t call_id , const CppAD::vector& type_x , CppAD::vector& type_y ) override { assert( call_id == 0 ); // default value CPPAD_ASSERT_UNKNOWN( type_x.size() == 4 ); assert( type_y.size() == 2 ); // type_y[0] = std::max(type_x[0], type_x[1]); type_y[1] = std::max(type_x[2], type_x[3]); // return true; } // forward bool forward( size_t call_id , const CppAD::vector& select_y , size_t order_low , size_t order_up , const CppAD::vector& taylor_x , CppAD::vector& taylor_y ) override { // assert( call_id == 0 ); // default value assert( order_low == 0); assert( order_up == 0); assert( taylor_x.size() == 4 ); assert( taylor_y.size() == 2 ); // // x, y const CppAD::vector& x = taylor_x; CppAD::vector& y = taylor_y; // y[0] = x[0] + x[1]; // // y[1] = x[2] * x[3]; if( x[2] == 0.0 ) y[1] = 0.0; else if ( x[3] == 0.0 ) y[1] = 0.0; else y[1] = x[2] * x[3]; // return true; } bool rev_depend( size_t call_id , const CppAD::vector& ident_zero_x , CppAD::vector& depend_x , const CppAD::vector& depend_y ) override { // assert( call_id == 0 ); assert( depend_x.size() == 4 ); assert( depend_y.size() == 2 ); // depend_x[0] = depend_x[1] = depend_y[0]; if( ident_zero_x[2] ) { // Keep the constant zero (so forward (see above) knows about it). depend_x[2] = true; depend_x[3] = false; } else if( ident_zero_x[3] ) { depend_x[2] = false; // Keep the constant zero (not much overhead in doing so). depend_x[3] = true; } else depend_x[2] = depend_x[3] = depend_y[1]; // return true; } }; // END_VAL_ATOMIC_XAM # endif ================================================ FILE: val_graph/binary_xam.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include /* {xrst_begin val_binary_xam.cpp dev} Binary Value Operator Example ############################# {xrst_literal // BEGIN_C++ // END_C++ } {xrst_end val_binary_xam.cpp} */ // BEGIN_C++ bool binary_xam(void) { bool ok = true; // // tape_t, Vector, addr_t, add_op_enum; using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; op_enum_t add_op_enum = CppAD::local::val_graph::add_op_enum; // // tape, ok tape_t tape; addr_t n_ind = 2; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // op_arg Vector op_arg(2); op_arg[0] = 0; // x[0] op_arg[1] = 1; // x[1] // // dep_vec Vector dep_vec(1); // // tape dep_vec[0] = tape.record_op(add_op_enum, op_arg); // x[0] + x[1] // // set_dep tape.set_dep( dep_vec ); // // x Vector x(2); x[0] = 5.0; x[1] = 6.0; // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // ok ok &= tape.n_op() == 2; ok &= tape.arg_vec().size() == 3; ok &= tape.con_vec().size() == 1; ok &= tape.n_val() == n_ind + 2; // // y Vector y(1); y[0] = val_vec[ dep_vec[0] ]; // // ok ok &= y[0] == x[0] + x[1]; // return ok; } // END_C++ ================================================ FILE: val_graph/call_xam.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include # include "atomic_xam.hpp" /* {xrst_begin val_call_xam.cpp dev} Function Value Operator Example ############################### {xrst_toc_hidden val_graph/atomic_xam.hpp } atomic_xam ********** This example uses :ref:`val_atomic_xam.hpp-name` . Source Code *********** {xrst_literal // BEGIN_C++ // END_C++ } {xrst_end val_call_xam.cpp} */ // BEGIN_C++ // call_xam bool call_xam(void) { bool ok = true; // // tape_t, Vector, addr_t using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; // // atomic_xam val_atomic_xam atomic_xam; // // callarg, op_arg Vector callarg(4), op_arg(2); // // f tape_t tape; addr_t n_ind = 4; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // n_res, dep_vec addr_t n_res = 2; Vector dep_vec(n_res); // // atomic_index addr_t atomic_index = addr_t( atomic_xam.atomic_index() ); // // f_0(x) = x[0] + x[1] // f_1(x) = x[2] * x[3] for(addr_t i = 0; i < 4; ++i) callarg[i] = i; addr_t call_id = 0; addr_t n_call_res = 2; addr_t res_index = tape.record_call_op( atomic_index, call_id, n_call_res, callarg ); // // dep_vec dep_vec[0] = res_index + 0; dep_vec[1] = res_index + 1; // // set_dep tape.set_dep( dep_vec ); // // x Vector x(n_ind); for(addr_t i = 0; i < n_ind; ++i) x[i] = 2.0 + double(n_ind - i); // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // ok ok &= tape.arg_vec().size() == 1 + 4 + 5; ok &= tape.con_vec().size() == 1; ok &= tape.n_op() == 2; ok &= tape.n_val() == n_ind + 1 + 2; // // y Vector y(n_res); dep_vec = tape.dep_vec(); for(addr_t i = 0; i < n_res; ++i) y[i] = val_vec[ dep_vec[i] ]; // // ok ok &= y[0] == x[0] + x[1]; ok &= y[1] == x[2] * x[3]; // return ok; } // END_C++ ================================================ FILE: val_graph/cexp_xam.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include # include /* {xrst_begin val_cexp_xam.cpp dev} Conditional Expression Value Operator Example ############################################# {xrst_literal // BEGIN_C++ // END_C++ } {xrst_end val_cexp_xam.cpp} */ // BEGIN_C++ bool cexp_xam(void) { bool ok = true; // // tape_t, Vector, addr_t, compare_lt_enum using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::compare_enum_t; compare_enum_t compare_lt_enum = CppAD::local::val_graph::compare_lt_enum; // // tape, ok tape_t tape; addr_t n_ind = 4; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // dep_vec, tape Vector dep_vec(1); addr_t left = 3; // x[3] addr_t right = 2; // x[2] addr_t if_true = 1; // x[1] addr_t if_false = 0; // x[0] dep_vec[0] = tape.record_cexp_op( compare_lt_enum, left, right, if_true, if_false ); // // set_dep tape.set_dep( dep_vec ); // // x Vector x(n_ind); for(addr_t i = 0; i < n_ind; ++i) x[0] = double(i + 1); // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // ok ok &= tape.n_op() == 2; ok &= tape.con_vec().size() == 1; ok &= tape.arg_vec().size() == 1 + 5; ok &= tape.n_val() == n_ind + 2; // // y Vector y(1); y[0] = val_vec[ dep_vec[0] ]; // // ok if( x[left] < x[right] ) ok &= y[0] == x[if_true]; else ok &= y[0] == x[if_false]; // return ok; } // END_C++ ================================================ FILE: val_graph/comp_xam.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include # include /* {xrst_begin val_comp_xam.cpp dev} Binary Value Operator Example ############################# {xrst_literal // BEGIN_C++ // END_C++ } {xrst_end val_comp_xam.cpp} */ // BEGIN_C++ bool comp_xam(void) { bool ok = true; // // tape_t, Vector, addr_t, add_op_enum, compare_lt_enum; using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; using CppAD::local::val_graph::compare_enum_t; op_enum_t add_op_enum = CppAD::local::val_graph::add_op_enum; compare_enum_t compare_lt_enum = CppAD::local::val_graph::compare_lt_enum; // // tape, ok tape_t tape; addr_t n_ind = 2; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // dep_vec Vector dep_vec(1); // // op_arg Vector op_arg(2); op_arg[0] = 0; // x[0] op_arg[1] = 1; // x[1] // // tape // put two identical compare operators in the tape addr_t left_index = 0; // x[0] addr_t right_index = 1; // x[1] for(size_t i = 0; i < 2; ++i) { addr_t res_index = tape.record_comp_op( compare_lt_enum, left_index, right_index // x[0] < x[1] ); ok &= res_index == 0; // no result for this operator } // // tape, dep_vec tape.record_op(add_op_enum, op_arg); // not used dep_vec[0] = tape.record_op(add_op_enum, op_arg); // x[0] + x[1] // // set_dep tape.set_dep( dep_vec ); // // trace bool trace = false; // // x Vector x(2); x[0] = 5.0; x[1] = 6.0; // // val_vec, compare_false Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; size_t compare_false = 0; tape.eval(trace, val_vec, compare_false); ok &= compare_false == 0; // x[0] < x[1] is true // // x x[0] = 6.0; x[1] = 5.0; // // val_vec, compare_false for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; compare_false = 0; tape.eval(trace, val_vec, compare_false); ok &= compare_false == 2; // there are two x[0] < x[1] comparisons ok &= tape.n_op() == 5; // 1 con_op, 2 comp_op, 2 add_op // // tape tape.set_option("keep_compare", "true"); tape.renumber(); tape.dead_code(); ok &= tape.n_op() == 3; // 1 con_op, 1 comp_op, 1 add_op // // ok, val_vec compare_false = 0; val_vec.resize( tape.n_val() ); tape.eval(trace, val_vec, compare_false); ok &= compare_false == 1; // only one x[0] < x[1] left // // tape tape.set_option("keep_compare", "false"); tape.dead_code(); ok &= tape.n_op() == 2; // 1 con_op, 0 comp_op, 1 add_op // // ok, val_vec val_vec.resize( tape.n_val() ); compare_false = 0; tape.eval(trace, val_vec, compare_false); ok &= compare_false == 0; // none of the x[0] < x[1] left // // ok Vector y(1); dep_vec = tape.dep_vec(); y[0] = val_vec[ dep_vec[0] ]; ok &= y[0] == x[0] + x[1]; // return ok; } // END_C++ ================================================ FILE: val_graph/compress_xam.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include /* {xrst_begin val_compress_xam.cpp dev} Compress Tape Example ##################### {xrst_literal // BEGIN_C++ // END_C++ } {xrst_end val_compress_xam.cpp} */ // BEGIN_C++ bool compress_xam(void) { bool ok = true; // // tape_t, Vector, addr_t, add_op_enum, sub_op_enum; using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; op_enum_t add_op_enum = CppAD::local::val_graph::add_op_enum; op_enum_t sub_op_enum = CppAD::local::val_graph::sub_op_enum; // // tape tape_t tape; addr_t n_ind = 2; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // op_arg Vector op_arg(2); op_arg[0] = 0; // x[0] op_arg[1] = 1; // x[1] // // operator that is not used tape.record_op(sub_op_enum, op_arg); // x[0] - x[1] // // operator that is computer twich addr_t add_zero_one_1 = tape.record_op(add_op_enum, op_arg); // x[0] + x[1] addr_t add_zero_one_2 = tape.record_op(add_op_enum, op_arg); // x[0] + x[1] // // dep_vec // y[0] = x[0] + x[1] + x[0] + x[1] Vector dep_vec(1); op_arg[0] = add_zero_one_1; op_arg[1] = add_zero_one_2; dep_vec[0] = tape.record_op(add_op_enum, op_arg); // // set_dep tape.set_dep( dep_vec ); // // x Vector x(2); x[0] = 5.0; x[1] = 6.0; // // trace bool trace = false; // // val_vec Vector val_vec; // // val_vec, ok // before optimizing val_vec.resize( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); ok &= tape.n_op() == 1 + 4; ok &= tape.arg_vec().size() == 1 + 4 * 2; ok &= tape.con_vec().size() == 1; // // compress, val_vec tape.compress(); val_vec.resize( tape.n_val() ); tape.eval(trace, val_vec); // // ok ok &= tape.n_op() == 1 + 2; ok &= tape.arg_vec().size() == 1 + 2 * 2; ok &= tape.con_vec().size() == 1; // // dep_vec dep_vec = tape.dep_vec(); // // y Vector y(1); y[0] = val_vec[ dep_vec[0] ]; // // ok ok &= y[0] == x[0] + x[1] + x[0] + x[1]; // return ok; } // END_C++ ================================================ FILE: val_graph/con_xam.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include /* {xrst_begin val_con_xam.cpp dev} Constant Operator Example ######################### {xrst_literal // BEGIN_C++ // END_C++ } {xrst_end val_con_xam.cpp} */ // BEGIN_C++ bool con_xam(void) { bool ok = true; // // tape_t, Vector, addr_t, con_op_enum, add_op_enum; using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; op_enum_t add_op_enum = CppAD::local::val_graph::add_op_enum; // // tape tape_t tape; addr_t n_ind = 1; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // op_arg Vector op_arg(2); // // dep_vec Vector dep_vec(1); // // tape addr_t five = tape.record_con_op(5.0); // 5.0 op_arg[0] = 0; // x[0] op_arg[1] = five; dep_vec[0] = tape.record_op(add_op_enum, op_arg); // x[0] + 5.0 // // set_dep tape.set_dep( dep_vec ); // // x Vector x(1); x[0] = 6.0; // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // ok ok &= tape.n_op() == 3; ok &= tape.arg_vec().size() == 4; ok &= tape.con_vec().size() == 2; ok &= tape.n_val() == n_ind + 3; // // y Vector y(1); y[0] = val_vec[ dep_vec[0] ]; // // ok ok &= y[0] == x[0] + 5.0; // return ok; } // END_C++ ================================================ FILE: val_graph/csum_xam.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include /* {xrst_begin val_csum_xam.cpp dev} Cumulative Summation Value Operator Example ########################################### {xrst_literal // BEGIN_C++ // END_C++ } {xrst_end val_csum_xam.cpp} */ // BEGIN_C++ bool csum_xam(void) { bool ok = true; // // tape_t, Vector, addr_t, csum_op_enum; using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; // // tape, ok tape_t tape; addr_t n_ind = 2; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // p0, p1 addr_t p0 = tape.record_con_op(5.0); addr_t p1 = tape.record_con_op(6.0); // // add Vector add(2), sub(2); add[0] = 0; // x[0] add[1] = 1; // x[1] sub[0] = p0; // 5.0 sub[1] = p1; // 6.0 // // dep_vec, tape Vector dep_vec(1); dep_vec[0] = tape.record_csum_op(add, sub); // x[0] + x[1] - 5.0 - 6.0 // // set_dep tape.set_dep( dep_vec ); // // x Vector x(2); x[0] = 3.0; x[1] = 4.0; // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // ok ok &= tape.n_op() == 4; ok &= tape.con_vec().size() == 3; ok &= tape.arg_vec().size() == 3 + 7; ok &= tape.n_val() == n_ind + 4; // // y Vector y(1); y[0] = val_vec[ dep_vec[0] ]; // // ok ok &= y[0] == x[0] + x[1] - 11.0; // return ok; } // END_C++ ================================================ FILE: val_graph/cumulative_xam.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include /* {xrst_begin val_cumulative_xam dev} Example Converting Add, Subtract and Negative to a Cumulative Summation ####################################################################### {xrst_literal // BEGIN_C++ // END_C++ } {xrst_end val_cumulative_xam} */ // BEGIN_C++ bool cumulative_xam(void) { bool ok = true; // // tape_t, Vector, addr_t, add_op_enum, sub_op_enum, neg_op_enum using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; op_enum_t con_op_enum = CppAD::local::val_graph::con_op_enum; op_enum_t add_op_enum = CppAD::local::val_graph::add_op_enum; op_enum_t sub_op_enum = CppAD::local::val_graph::sub_op_enum; op_enum_t neg_op_enum = CppAD::local::val_graph::neg_op_enum; op_enum_t csum_op_enum = CppAD::local::val_graph::csum_op_enum; // // tape, ok tape_t tape; addr_t n_ind = 5; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; Vector op_arg(2); // // op_arg, add = x[0] + x[1] op_arg[0] = 0; // x[0] op_arg[1] = 1; // x[1] addr_t add = tape.record_op(add_op_enum, op_arg); // // op_arg, sub = x[2] - x[3] op_arg[0] = 2; // x[2] op_arg[1] = 3; // x[3] addr_t sub = tape.record_op(sub_op_enum, op_arg); // // op_arg, neg = - x[4] op_arg.resize(1); op_arg[0] = 4; addr_t neg = tape.record_op(neg_op_enum, op_arg); // // dep_vec, tape Vector dep_vec(3); dep_vec[0] = add; // x[0] + x[1] dep_vec[1] = sub; // x[2] - x[3] dep_vec[2] = neg; // - x[4] // // set_dep tape.set_dep( dep_vec ); // // x Vector x(n_ind); for(addr_t i = 0; i < n_ind; ++i) x[i] = double(2 + i); // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // ok addr_t i_op = 0; ok &= tape.base_op_ptr(i_op++)->op_enum() == con_op_enum; ok &= tape.base_op_ptr(i_op++)->op_enum() == add_op_enum; ok &= tape.base_op_ptr(i_op++)->op_enum() == sub_op_enum; ok &= tape.base_op_ptr(i_op++)->op_enum() == neg_op_enum; // // ok ok &= tape.n_op() == 4; ok &= tape.con_vec().size() == 1; ok &= tape.arg_vec().size() == 6; ok &= tape.n_val() == n_ind + 4; // // y Vector y(3); dep_vec = tape.dep_vec(); y[0] = val_vec[ dep_vec[0] ]; y[1] = val_vec[ dep_vec[1] ]; y[2] = val_vec[ dep_vec[2] ]; // // ok ok &= y[0] == x[0] + x[1]; ok &= y[1] == x[2] - x[3]; ok &= y[2] == - x[4]; // // tape tape.set_op2arg_index(); for(addr_t op_index = 1; op_index < 4; ++op_index) tape.op2csum(op_index); // // val_vec for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // ok i_op = 0; ok &= tape.base_op_ptr(i_op++)->op_enum() == con_op_enum; ok &= tape.base_op_ptr(i_op++)->op_enum() == csum_op_enum; ok &= tape.base_op_ptr(i_op++)->op_enum() == csum_op_enum; ok &= tape.base_op_ptr(i_op++)->op_enum() == csum_op_enum; // // ok ok &= tape.n_op() == 4; ok &= tape.con_vec().size() == 1; ok &= tape.arg_vec().size() == 6 + 3 * 5 - 1; ok &= tape.n_val() == n_ind + 4; // // y dep_vec = tape.dep_vec(); y[0] = val_vec[ dep_vec[0] ]; y[1] = val_vec[ dep_vec[1] ]; y[2] = val_vec[ dep_vec[2] ]; // // ok ok &= y[0] == x[0] + x[1]; ok &= y[1] == x[2] - x[3]; ok &= y[2] == - x[4]; // return ok; } // END_C++ ================================================ FILE: val_graph/dead_xam.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include /* {xrst_begin val_dead_xam.cpp dev} Dead Code Elimination Example ############################# {xrst_literal // BEGIN_C++ // END_C++ } {xrst_end val_dead_xam.cpp} */ // BEGIN_C++ bool dead_xam(void) { bool ok = true; // // tape_t, Vector, addr_t, add_op_enum, sub_op_enum; using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; op_enum_t add_op_enum = CppAD::local::val_graph::add_op_enum; op_enum_t sub_op_enum = CppAD::local::val_graph::sub_op_enum; // // tape tape_t tape; addr_t n_ind = 2; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // op_arg Vector op_arg(2); op_arg[0] = 0; // x[0] op_arg[1] = 1; // x[1] // // dep_vec Vector dep_vec(2); // // operators that are not used tape.record_op(add_op_enum, op_arg); // x[0] + x[1] tape.record_con_op(5.0); // 5.0 // // operators that are used dep_vec[0] = tape.record_op(add_op_enum, op_arg); // x[0] + x[1] op_arg[0] = dep_vec[0]; // x[0] + x[1] op_arg[1] = tape.record_con_op(4.0); // 4.0 dep_vec[1] = tape.record_op(sub_op_enum, op_arg); // x[0] + x[1] - 4.0 // // set_dep tape.set_dep( dep_vec ); // // x Vector x(2); x[0] = 5.0; x[1] = 6.0; // // trace bool trace = false; // // val_vec Vector val_vec; // // val_vec, ok // before optimizing val_vec.resize( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); ok &= tape.n_op() == 6; ok &= tape.arg_vec().size() == 9; ok &= tape.con_vec().size() == 3; // // dead_code, val_vec tape.dead_code(); val_vec.resize( tape.n_val() ); tape.eval(trace, val_vec); // // ok ok &= tape.n_op() == 4; ok &= tape.arg_vec().size() == 6; ok &= tape.con_vec().size() == 2; // // dep_vec dep_vec = tape.dep_vec(); // // y Vector y(2); for(size_t i = 0; i < 2; ++i) y[i] = val_vec[ dep_vec[i] ]; // // ok ok &= y[0] == x[0] + x[1]; ok &= y[1] == x[0] + x[1] - 4.0; // return ok; } // END_C++ ================================================ FILE: val_graph/dis_xam.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include # include /* {xrst_begin val_dis_xam.cpp dev} Unary Value Operator Example ############################ {xrst_literal // BEGIN_C++ // END_C++ } {xrst_end val_dis_xam.cpp} */ // BEGIN_C++ namespace { double floor(const double& arg) { return std::floor(arg); } // CPPAD_DISCRETE_FUNCTION(double, floor); } bool dis_xam(void) { bool ok = true; // // tape_t, Vector, addr_t using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; // // // tape, ok tape_t tape; addr_t n_ind = 1; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // dep_vec Vector dep_vec(1); // // tape, dep_vec addr_t discrete_index = addr_t( CppAD::discrete::index("floor") ); addr_t val_index = 0; // x[0]; dep_vec[0] = tape.record_dis_op(discrete_index, val_index); // floor( x[0] ) // // tape tape.set_dep( dep_vec ); // // x Vector x(1); x[0] = 6.5; // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); val_vec[0] = x[0]; tape.eval(trace, val_vec); // // ok ok &= tape.n_op() == 2; ok &= tape.arg_vec().size() == 3; ok &= tape.con_vec().size() == 1; ok &= tape.n_val() == n_ind + 2; // // y Vector y(1); // // ok y[0] = val_vec[ dep_vec[0] ]; ok &= y[0] == std::floor( x[0] ); // return ok; } // END_C++ ================================================ FILE: val_graph/fold_con_xam.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include /* {xrst_begin val_fold_con_xam.cpp dev} Value Tape Re-Numbering Example ############################### {xrst_literal // BEGIN_C++ // END_C++ } {xrst_end val_fold_con_xam.cpp} */ // BEGIN_C++ bool fold_con_xam(void) { bool ok = true; // // tape_t, Vector, addr_t, add_op_enum; using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; op_enum_t add_op_enum = CppAD::local::val_graph::add_op_enum; // // tape tape_t tape; addr_t n_ind = 1; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // c_0, c_1 Vector c(2); c[0] = 1.0; c[1] = 2.0; addr_t c_0 = tape.record_con_op( c[0] ); addr_t c_1 = tape.record_con_op( c[1] ); // // tape, c_2 Vector op_arg(2); op_arg[0] = c_0; op_arg[1] = c_1; addr_t c_2 = tape.record_op(add_op_enum, op_arg); // c[2] = c[0] + c[1] // // dep_vec Vector dep_vec(1); // // tape op_arg[0] = 0; op_arg[1] = c_2; dep_vec[0] = tape.record_op(add_op_enum, op_arg); // y[0] = x[0] + c[2] // // set_dep tape.set_dep( dep_vec ); // // trace bool trace = false; // // x Vector x(1); x[0] = 5.0; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // y, ok Vector y(1); dep_vec = tape.dep_vec(); y[0] = val_vec[ dep_vec[0] ]; ok &= y[0] == x[0] + c[0] + c[1]; ok &= tape.con_vec().size() == 3; ok &= tape.n_op() == 5; // // fold_con tape.fold_con(); ok &= tape.con_vec().size() == 4; // // val_vec val_vec.resize( tape.n_val() ); tape.eval(trace, val_vec); // // dead_code tape.dead_code(); ok &= tape.con_vec().size() == 2; // // val_vec val_vec.resize( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // y, ok dep_vec = tape.dep_vec(); y[0] = val_vec[ dep_vec[0] ]; ok &= y[0] == x[0] + c[0] + c[1]; // return ok; } // END_C++ ================================================ FILE: val_graph/fun2val_xam.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include # include # include /* {xrst_begin val_fun2val_xam.cpp dev} Example Vale Graph From ADFun ############################# {xrst_literal // BEGIN_C++ // END_C++ } {xrst_end val_fun2val_xam.cpp} */ // BEGIN_C++ bool fun2val_xam(void) { bool ok = true; // // AD, addr_t using CppAD::AD; using CppAD::addr_t; // // tape_t, Vector using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; // // ax, ay, f Vector< AD > ap(1), ax(2), ay(2); ap[0] = 1.0; ax[0] = 2.0; ax[1] = 3.0; CppAD::Independent(ax, ap); ay[0] = ax[0] + 7.0; ay[1] = ax[1] - ap[0]; CppAD::ADFun f(ax, ay); // // tape tape_t tape; f.fun2val(tape); // // dep_vec Vector dep_vec = tape.dep_vec(); // // x Vector p(1), x(2); p[0] = 4.0; x[0] = 5.0; x[1] = 6.0; // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); val_vec[0] = p[0]; val_vec[1] = x[0]; val_vec[2] = x[1]; tape.eval(trace, val_vec); // // y Vector y(2); y[0] = val_vec[ dep_vec[0] ]; y[1] = val_vec[ dep_vec[1] ]; // // ok ok &= y[0] == x[0] + 7.0; ok &= y[1] == x[1] - p[0]; // return ok; } // END_C++ ================================================ FILE: val_graph/pri_xam.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include # include /* {xrst_begin val_pri_xam.cpp dev} Print Operator Example ###################### {xrst_literal // BEGIN_C++ // END_C++ } {xrst_end val_pri_xam.cpp} */ // BEGIN_C++ bool pri_xam(void) { bool ok = true; // // tape_t, Vector, addr_t, add_op_enum, compare_lt_enum; using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; op_enum_t sub_op_enum = CppAD::local::val_graph::sub_op_enum; // // tape, ok tape_t tape; addr_t n_ind = 2; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // op_arg Vector op_arg(2); op_arg[0] = 0; // x[0] op_arg[1] = 1; // x[1] // // tape, dep_vec Vector dep_vec(1); dep_vec[0] = tape.record_op(sub_op_enum, op_arg); // x[0] - x[1] // // tape std::string before = "0 >= x[0] - x[1] = "; std::string after = "\n"; addr_t flag_index = dep_vec[0]; addr_t value_index = dep_vec[0]; tape.record_pri_op(before, after, flag_index, value_index); tape.record_pri_op(before, after, flag_index, value_index); // // set_dep tape.set_dep( dep_vec ); // // trace bool trace = false; // // x Vector x(2); x[0] = 6.0; x[1] = 5.0; // // val_vec, compare_false Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // ok ok &= tape.n_val() == 4; ok &= tape.n_op() == 4; ok &= tape.str_vec().size() == 3; ok &= tape.str_vec()[0] == ""; // // tape tape.set_option("keep_print", "true"); tape.renumber(); tape.dead_code(); // // val_vec, compare_false val_vec.resize( tape.n_val() ); tape.eval(trace, val_vec); // // ok ok &= tape.n_val() == 4; ok &= tape.n_op() == 3; ok &= tape.str_vec().size() == 3; ok &= tape.str_vec()[0] == ""; // // ok Vector y(1); dep_vec = tape.dep_vec(); y[0] = val_vec[ dep_vec[0] ]; ok &= y[0] == x[0] - x[1]; // return ok; } // END_C++ ================================================ FILE: val_graph/renumber_xam.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include /* {xrst_begin val_renumber_xam.cpp dev} Value Tape Re-Numbering Example ############################### {xrst_literal // BEGIN_C++ // END_C++ } {xrst_end val_renumber_xam.cpp} */ // BEGIN_C++ bool renumber_xam(void) { bool ok = true; // // tape_t, Vector, addr_t, add_op_enum; using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; op_enum_t add_op_enum = CppAD::local::val_graph::add_op_enum; // // tape tape_t tape; addr_t n_ind = 2; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // op_arg Vector op_arg(2); op_arg[0] = 0; // x[0] op_arg[1] = 1; // x[1] // // dep_vec Vector dep_vec(1); // // tape tape.record_op(add_op_enum, op_arg); // x[0] + x[1] dep_vec[0] = tape.record_op(add_op_enum, op_arg); // x[0] + x[1] // // set_dep tape.set_dep( dep_vec ); // // trace bool trace = false; // // x Vector x(2); x[0] = 5.0; x[1] = 6.0; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // y, ok Vector y(1); dep_vec = tape.dep_vec(); y[0] = val_vec[ dep_vec[0] ]; ok &= dep_vec[0] == n_ind + 2; ok &= y[0] == x[0] + x[1]; // // renumber ok &= dep_vec[0] == n_ind + 2; tape.renumber(); dep_vec = tape.dep_vec(); ok &= dep_vec[0] == n_ind + 1; // // val_vec tape.eval(trace, val_vec); // // y, ok dep_vec = tape.dep_vec(); y[0] = val_vec[ dep_vec[0] ]; ok &= dep_vec[0] == n_ind + 1; ok &= y[0] == x[0] + x[1]; // return ok; } // END_C++ ================================================ FILE: val_graph/summation_xam.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include /* {xrst_begin val_summation_xam.cpp dev} Combining Value Operators Into a Cumulative Summation Example ############################################################# {xrst_literal // BEGIN_C++ // END_C++ } {xrst_end val_summation_xam.cpp} */ // BEGIN_C++ bool summation_xam(void) { bool ok = true; // // tape_t, Vector, addr_t, // add_op_enum, sub_op_enum, mul_op_enum, neg_op_enum using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; op_enum_t add_op_enum = CppAD::local::val_graph::add_op_enum; op_enum_t sub_op_enum = CppAD::local::val_graph::sub_op_enum; op_enum_t mul_op_enum = CppAD::local::val_graph::mul_op_enum; op_enum_t neg_op_enum = CppAD::local::val_graph::neg_op_enum; // // tape, ok tape_t tape; addr_t n_ind = 4; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; Vector op_arg(2); // // op_arg, add = x[0] + x[1] op_arg[0] = 0; // x[0] op_arg[1] = 1; // x[1] addr_t add = tape.record_op(add_op_enum, op_arg); // // op_arg, mul = x[2] * x[3] op_arg[0] = 2; // x[2] op_arg[1] = 3; // x[3] addr_t mul = tape.record_op(mul_op_enum, op_arg); // // op_arg, sub = x[0] + x[1] - x[2] * x[3] op_arg[0] = add; op_arg[1] = mul; addr_t sub = tape.record_op(sub_op_enum, op_arg); // // op_arg, neg = x[2] * x[3] - x[0] - x[1] op_arg.resize(1); op_arg[0] = sub; addr_t neg = tape.record_op(neg_op_enum, op_arg); // // dep_vec, tape Vector dep_vec(1); dep_vec[0] = neg; // x[2] * x[3] - x[0] - x[1] // // set_dep tape.set_dep( dep_vec ); // // x Vector x(n_ind); for(addr_t i = 0; i < n_ind; ++i) x[i] = double(2 + i); // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // ok ok &= tape.n_op() == 5; ok &= tape.con_vec().size() == 1; ok &= tape.arg_vec().size() == 2 + 3 * 2; ok &= tape.n_val() == n_ind + 1 + 4; // // summation tape.summation(); tape.dead_code(); // // ok ok &= tape.n_op() == 3; ok &= tape.con_vec().size() == 1; ok &= tape.arg_vec().size() == 1 + 2 + 6; ok &= tape.n_val() == n_ind + 3; // // eval val_vec.resize( tape.n_val() ); tape.eval(trace, val_vec); // // y Vector y(1); dep_vec = tape.dep_vec(); y[0] = val_vec[ dep_vec[0] ]; // // ok ok &= y[0] == x[2] * x[3] - x[0] - x[1]; // return ok; } // END_C++ ================================================ FILE: val_graph/test/ad_double.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include namespace { // BEGIN_EMPTY_NAMESPACE // ---------------------------------------------------------------------------- bool con_op(void) { bool ok = true; // // AD, tape_t, Vector, addr_t, con_op_enum, add_op_enum; using CppAD::AD; using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; op_enum_t add_op_enum = CppAD::local::val_graph::add_op_enum; // // tape tape_t< AD > tape; addr_t n_ind = 1; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // op_arg Vector op_arg(2); // // dep_vec Vector dep_vec(1); // // tape addr_t five = tape.record_con_op(5.0); // 5.0 tape.record_con_op(5.0); // not used op_arg[0] = 0; // x[0] op_arg[1] = five; dep_vec[0] = tape.record_op(add_op_enum, op_arg); // x[0] + 5.0 // // set_dep tape.set_dep( dep_vec ); // // x Vector< AD > ax(1); ax[0] = 6.0; // // trace bool trace = false; // // val_vec Vector< AD > val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = ax[i]; tape.eval(trace, val_vec); // // ok ok &= tape.n_op() == 4; ok &= tape.arg_vec().size() == 5; ok &= tape.con_vec().size() == 3; ok &= tape.n_val() == n_ind + 4; // // renumber tape.renumber(); // // ok ok &= tape.n_op() == 4; ok &= tape.arg_vec().size() == 5; ok &= tape.con_vec().size() == 3; ok &= tape.n_val() == n_ind + 4; // // dead_code tape.dead_code(); // // val_vec val_vec.resize( tape.n_val() ); tape.eval(trace, val_vec); dep_vec = tape.dep_vec(); // // ok ok &= tape.n_op() == 3; ok &= tape.arg_vec().size() == 4; ok &= tape.con_vec().size() == 2; ok &= tape.n_val() == n_ind + 3; // // y Vector< AD > ay(1); ay[0] = val_vec[ dep_vec[0] ]; // // ok ok &= ay[0] == ax[0] + 5.0; // return ok; } // ---------------------------------------------------------------------------- } // END_EMPTY_NAMESPACE bool test_ad_double(void) { bool ok = true; ok &= con_op(); return ok; } ================================================ FILE: val_graph/test/fold.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include # include "../atomic_xam.hpp" // namespace { // BEGIN_EMPTY_NAMESPACE // ---------------------------------------------------------------------------- // atom bool atom(void) { bool ok = true; // // tape_t, Vector, addr_t, add_op_enum; using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; op_enum_t add_op_enum = CppAD::local::val_graph::add_op_enum; // // atomic_xam val_atomic_xam atomic_xam; // // callarg, op_arg Vector callarg(4), op_arg(2); // // tape, ok tape_t tape; addr_t n_ind = 1; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // atomic_index addr_t atomic_index = addr_t( atomic_xam.atomic_index() ); // // g_0(x) = c[0] + c[1] // g_1(x) = c[2] * c[3] Vector c(4); for(addr_t i = 0; i < 4; ++i) { c[i] = double(i + 2); callarg[i] = tape.record_con_op(c[i]); } addr_t n_call_res = 2; addr_t call_id = 0; addr_t res_index = tape.record_call_op( atomic_index, call_id, n_call_res, callarg ); // // dep_vec Vector dep_vec(2); // // y[0] = x[0] + c[0] + c[1] op_arg[0] = 0; op_arg[1] = res_index; dep_vec[0] = tape.record_op(add_op_enum, op_arg); // // y[0] = x[0] + c[2] * c[3] op_arg[0] = 0; op_arg[1] = res_index + 1; dep_vec[1] = tape.record_op(add_op_enum, op_arg); // // set_dep tape.set_dep( dep_vec ); // // trace bool trace = false; // // x Vector x(1); x[0] = 5.0; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // y, ok Vector y(2); y[0] = val_vec[ dep_vec[0] ]; y[1] = val_vec[ dep_vec[1] ]; ok &= y[0] == x[0] + c[0] + c[1]; ok &= y[1] == x[0] + c[2] * c[3]; ok &= tape.con_vec().size() == 5; ok &= tape.n_op() == 8; ok &= tape.arg_vec().size() == 1 + 4 + 4 + 5 + 2 + 2; // // fold_con tape.fold_con(); // // val_vec val_vec.resize( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // dead_code tape.dead_code(); // // val_vec val_vec.resize( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // y, ok dep_vec = tape.dep_vec(); y[0] = val_vec[ dep_vec[0] ]; y[1] = val_vec[ dep_vec[1] ]; ok &= y[0] == x[0] + c[0] + c[1]; ok &= y[1] == x[0] + c[2] * c[3]; ok &= tape.con_vec().size() == 3; ok &= tape.n_op() == 5; ok &= tape.arg_vec().size() == 7; // return ok; } // ---------------------------------------------------------------------------- // dis_op // double floor(const double& arg) { return std::floor(arg); } // CPPAD_DISCRETE_FUNCTION(double, floor); bool dis_op(void) { bool ok = true; // // tape_t, Vector, addr_t, add_op_enum; using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; op_enum_t add_op_enum = CppAD::local::val_graph::add_op_enum; // // tape, ok tape_t tape; addr_t n_ind = 1; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // dep_vec Vector dep_vec(1); // // c Vector c(2); c[0] = 5.5; c[1] = floor( c[0] ); // // tape addr_t c0_addr = tape.record_con_op( c[0] ); addr_t discrete_index = addr_t( CppAD::discrete::index("floor") ); addr_t c1_addr = tape.record_dis_op(discrete_index, c0_addr); // // tape, dep_vec // y[0] = x[0] + c[1] Vector op_arg(2); op_arg[0] = 0; // x[0] op_arg[1] = c1_addr; // c[1] dep_vec[0] = tape.record_op(add_op_enum, op_arg); // x[0] + c[1] // // tape tape.set_dep( dep_vec ); // // trace bool trace = false; // // x Vector x(1); x[0] = 3.0; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // y, ok Vector y(1); y[0] = val_vec[ dep_vec[0] ]; ok &= y[0] == x[0] + c[1]; ok &= tape.con_vec().size() == 2; ok &= tape.n_op() == 4; ok &= tape.arg_vec().size() == 6; // // fold_con tape.fold_con(); // // dead_code tape.dead_code(); // // val_vec val_vec.resize( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // y, ok dep_vec = tape.dep_vec(); y[0] = val_vec[ dep_vec[0] ]; ok &= y[0] == x[0] + c[1]; ok &= tape.con_vec().size() == 2; ok &= tape.n_op() == 3; ok &= tape.arg_vec().size() == 4; // return ok; } // ---------------------------------------------------------------------------- // cexp_op bool cexp_op(void) { bool ok = true; // // tape_t, Vector, addr_t, add_op_enum; using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; CppAD::local::val_graph::compare_enum_t compare_lt_enum = CppAD::local::val_graph::compare_lt_enum; // // tape, ok tape_t tape; addr_t n_ind = 1; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // addr_t left = tape.record_con_op( 5.0 ); addr_t right = tape.record_con_op( 4.0 ); addr_t if_true = tape.record_con_op( 3.0 ); addr_t if_false = tape.record_con_op( 2.0 ); // y[0] = x[0] + c[1] Vector op_arg(2); // // dep_vec Vector dep_vec(1); dep_vec[0] = tape.record_cexp_op( compare_lt_enum, left, right, if_true, if_false ); // // tape tape.set_dep( dep_vec ); // // trace bool trace = false; // // x Vector x(1); x[0] = 3.0; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // y, ok Vector y(1); y[0] = val_vec[ dep_vec[0] ]; ok &= y[0] == 2.0; ok &= tape.con_vec().size() == 5; ok &= tape.n_op() == 6; ok &= tape.arg_vec().size() == 5 + 5; // // fold_con tape.fold_con(); // // dead_code tape.dead_code(); // // val_vec val_vec.resize( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // y, ok dep_vec = tape.dep_vec(); y[0] = val_vec[ dep_vec[0] ]; ok &= y[0] == 2.0; ok &= tape.con_vec().size() == 2; ok &= tape.n_op() == 2; ok &= tape.arg_vec().size() == 2; // return ok; } // ---------------------------------------------------------------------------- } // END_EMPTY_NAMESPACE // // test_fold bool test_fold(void) { bool ok = true; // ok &= cexp_op(); ok &= atom(); ok &= dis_op(); // return ok; } ================================================ FILE: val_graph/test/fun2val.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-24 Bradley M. Bell # include # include # include "../atomic_xam.hpp" namespace { // BEGIN_EMPTY_NAMESPACE // ---------------------------------------------------------------------------- // dynamic_atom bool dynamic_atom(void) { bool ok = true; // // AD, addr_t using CppAD::AD; using CppAD::addr_t; // // tape_t, Vector using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; // // atomic_xam val_atomic_xam atomic_xam; // // ap, ax Vector< AD > ap(4), ax(1); for(size_t i = 0; i < 4; ++i) ap[i] = double(i + 1); ax[0] = 5.0; CppAD::Independent(ax, ap); // // f Vector< AD > ad(2), ay(1); atomic_xam(ap, ad); // d[0] = p[0] + p[1], d[1] = p[2] * p[3] ay[0] = ad[0] + ad[1] + ax[0]; // y[0] = p[0] + p[1] + p[2] * p[3] + x[0] CppAD::ADFun f(ax, ay); // // tape tape_t tape; f.fun2val(tape); // // dep_vec Vector dep_vec = tape.dep_vec(); ok &= dep_vec.size() == 1; // // x ok &= tape.n_ind() == 5; Vector p(4), x(1); for(size_t i = 0; i < 4; ++i) p[i] = double(i + 2); x[0] = 6.0; // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); for(size_t i = 0; i < 4; ++i) val_vec[i] = p[i]; val_vec[4] = x[0]; size_t compare_false = 0; tape.eval(trace, val_vec, compare_false); ok &= compare_false == 0; // // y Vector y(1); y[0] = val_vec[ dep_vec[0] ]; // // ok ok &= y[0] == p[0] + p[1] + p[2] * p[3] + x[0]; // return ok; } // ---------------------------------------------------------------------------- // variable_atom bool variable_atom(void) { bool ok = true; // // AD, addr_t using CppAD::AD; using CppAD::addr_t; // // tape_t, Vector using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; // // atomic_xam val_atomic_xam atomic_xam; // // ax, ap Vector< AD > ax(4), ap(1); for(size_t i = 0; i < 4; ++i) ax[i] = double(i + 1); ap[0] = 5.0; CppAD::Independent(ax, ap); // // f Vector< AD > ay(2); atomic_xam(ax, ay); ay[0] = ay[0] + ap[0]; // y[0] = x[0] + x[1] + p[0] ay[1] = ay[1] - ap[0]; // y[1] = x[2] * x[3] - p[0] CppAD::ADFun f(ax, ay); // // tape tape_t tape; f.fun2val(tape); // // dep_vec Vector dep_vec = tape.dep_vec(); ok &= dep_vec.size() == 2; // // x ok &= tape.n_ind() == 5; Vector x(4), p(1); for(size_t i = 0; i < 4; ++i) x[i] = double(i + 2); p[0] = 6.0; // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); val_vec[0] = p[0]; for(size_t i = 0; i < 4; ++i) val_vec[i + 1] = x[i]; size_t compare_false = 0; tape.eval(trace, val_vec, compare_false); ok &= compare_false == 0; // // y Vector y(2); y[0] = val_vec[ dep_vec[0] ]; y[1] = val_vec[ dep_vec[1] ]; // // ok ok &= y[0] == x[0] + x[1] + p[0]; ok &= y[1] == x[2] * x[3] - p[0]; // return ok; } // ---------------------------------------------------------------------------- // unary bool unary_op(void) { bool ok = true; // // AD, addr_t using CppAD::AD; using CppAD::addr_t; // // tape_t, Vector using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; // // ap, ax Vector< AD > ap(1), ax(1); ap[0] = 2.0; ax[0] = 3.0; CppAD::Independent(ax, ap); // // f Vector< AD > ay(2); ay[0] = - ap[0]; ay[1] = - ax[0]; CppAD::ADFun f(ax, ay); // // tape tape_t tape; f.fun2val(tape); // // dep_vec Vector dep_vec = tape.dep_vec(); ok &= dep_vec.size() == 2; // // x ok &= tape.n_ind() == 2; Vector p(1), x(1); p[0] = 5.0; x[0] = 6.0; // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); val_vec[0] = p[0]; val_vec[1] = x[0]; size_t compare_false = 0; tape.eval(trace, val_vec, compare_false); ok &= compare_false == 0; // // y Vector y(2); y[0] = val_vec[ dep_vec[0] ]; y[1] = val_vec[ dep_vec[1] ]; // // ok ok &= y[0] == - p[0]; ok &= y[1] == - x[0]; // return ok; } // ------------------------------------------------------------------------- // comp_op bool comp_op(void) { bool ok = true; // // AD, addr_t using CppAD::AD; using CppAD::addr_t; // // tape_t, Vector using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; // // ap, ax Vector< AD > ap(1), ax(1); ap[0] = 2.0; ax[0] = 3.0; CppAD::Independent(ax, ap); // // f Vector< AD > ay(1); if( ap[0] < ax[0] ) // 2024-03-01: Test for bug in val_graph/fun2val.hpp: // changed from ay[0] = ax[0] to ay[0] = ax[0] * ax[0] ay[0] = ax[0] * ax[0]; else ay[0] = ap[0]; CppAD::ADFun f(ax, ay); // // tape tape_t tape; f.fun2val(tape); // // dep_vec Vector dep_vec = tape.dep_vec(); ok &= dep_vec.size() == 1; ok &= tape.n_ind() == 2; // // trace bool trace = false; // // p, x Vector p(1), x(1); p[0] = 5.0; // p[0] < x[0] is true x[0] = 6.0; // // val_vec Vector val_vec( tape.n_val() ); val_vec[0] = p[0]; val_vec[1] = x[0]; size_t compare_false = 0; tape.eval(trace, val_vec, compare_false); ok &= compare_false == 0; // // y, ok Vector y(2); y[0] = val_vec[ dep_vec[0] ]; ok &= y[0] == x[0] * x[0]; // // p, x p[0] = 6.0; // p[0] < x[0] is false x[0] = 5.0; // // val_vec val_vec[0] = p[0]; val_vec[1] = x[0]; tape.eval(trace, val_vec, compare_false); ok &= compare_false == 1; // // y, ok y[0] = val_vec[ dep_vec[0] ]; ok &= y[0] == x[0] * x[0]; // return ok; } // ---------------------------------------------------------------------------- // dis_op double floor(const double& arg) { return std::floor(arg); } // CPPAD_DISCRETE_FUNCTION(double, floor); bool dis_op(void) { bool ok = true; // // AD, addr_t using CppAD::AD; using CppAD::addr_t; // // tape_t, Vector using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; // // ap, ax Vector< AD > ap(1), ax(1); ap[0] = 2.0; ax[0] = 3.0; CppAD::Independent(ax, ap); // // f Vector< AD > ay(2); ay[0] = floor( ap[0] ); ay[1] = floor( ax[0] ); CppAD::ADFun f(ax, ay); // // tape tape_t tape; f.fun2val(tape); // // dep_vec Vector dep_vec = tape.dep_vec(); ok &= dep_vec.size() == 2; // // x ok &= tape.n_ind() == 2; Vector p(1), x(1); p[0] = 5.5; x[0] = - 5.5; // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); val_vec[0] = p[0]; val_vec[1] = x[0]; size_t compare_false = 0; tape.eval(trace, val_vec, compare_false); ok &= compare_false == 0; // // y Vector y(2); y[0] = val_vec[ dep_vec[0] ]; y[1] = val_vec[ dep_vec[1] ]; // // ok ok &= y[0] == std::floor( p[0] ); ok &= y[1] == std::floor( x[0] ); // return ok; } // ---------------------------------------------------------------------------- // cexp_op bool cexp_op(void) { bool ok = true; // // AD, addr_t using CppAD::AD; using CppAD::addr_t; // // tape_t, Vector using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; // // ap, ax Vector< AD > ap(2), ax(2); ap[0] = 2.0; ap[1] = 3.0; ax[0] = 4.0; ax[1] = 5.0; CppAD::Independent(ax, ap); // // ay Vector< AD > ay(2); ay[0] = CondExpGt(ap[0], ap[1], AD(6.0), AD(7.0) ); ay[1] = CondExpLt(ax[0], ax[1], ap[0], ap[1]); // // f CppAD::ADFun f(ax, ay); // // tape tape_t tape; f.fun2val(tape); // // dep_vec Vector dep_vec = tape.dep_vec(); ok &= dep_vec.size() == 2; // // x ok &= tape.n_ind() == 4; Vector p(2), x(2); p[0] = 5.0; p[1] = 4.0; x[0] = 3.0; x[1] = 2.0; // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); val_vec[0] = p[0]; val_vec[1] = p[1]; val_vec[2] = x[0]; val_vec[3] = x[1]; size_t compare_false = 0; tape.eval(trace, val_vec, compare_false); ok &= compare_false == 0; // // y Vector y(2); y[0] = val_vec[ dep_vec[0] ]; y[1] = val_vec[ dep_vec[1] ]; // // ok ok &= y[0] == 6.0; ok &= y[1] == p[1]; // return ok; } // ---------------------------------------------------------------------------- // csum_op bool csum_op(void) { bool ok = true; // // tape_t, Vector, addr_t using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; // // tape, ok tape_t tape; addr_t n_ind = 4; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // c[0], c[1] Vector c(2); c[0] = -2.0; c[1] = -3.0; addr_t c0 = tape.record_con_op( c[0] ); addr_t c1 = tape.record_con_op( c[1] ); // // add, sub Vector add(3), sub(3); add[0] = 0; // x[0] add[1] = 2; // p[0] add[2] = c0; // c[0] // sub[0] = 1; // x[1] sub[1] = 3; // p[1] sub[2] = c1; // c[1] // // tape, dep_vec Vector dep_vec(1); dep_vec[0] = tape.record_csum_op(add, sub); tape.set_dep( dep_vec ); // // trace bool trace = false; // // val_vec, ok Vector x(2), p(2), val_vec( tape.n_val() ); val_vec[0] = x[0] = 2.0; val_vec[1] = x[1] = 3.0; val_vec[2] = p[0] = 5.0; val_vec[3] = p[1] = 8.0; size_t compare_false = 0; tape.eval(trace, val_vec, compare_false); ok &= compare_false == 0; // // y, ok Vector y(1); y[0] = val_vec[ dep_vec[0] ]; ok &= y[0] == x[0] - x[1] + p[0] - p[1] + c[0] - c[1]; // // f: val2fun Vector var_ind(2), dyn_ind(2); var_ind[0] = 0; // x[0] var_ind[1] = 1; // x[1] dyn_ind[0] = 2; // p[0] dyn_ind[1] = 3; // p[1] CppAD::ADFun f; f.val2fun(tape, dyn_ind, var_ind); // // tape: fun2val, dep_vec f.fun2val(tape); dep_vec = tape.dep_vec(); // // y, ok f.new_dynamic(p); y = f.Forward(0, x); y[0] = val_vec[ dep_vec[0] ]; ok &= y[0] == x[0] - x[1] + p[0] - p[1] + c[0] - c[1]; // // val_vec, ok val_vec.resize( tape.n_val() ); tape.eval(trace, val_vec, compare_false); ok &= compare_false == 0; // // y, ok y[0] = val_vec[ dep_vec[0] ]; ok &= y[0] == x[0] - x[1] + p[0] - p[1] + c[0] - c[1]; // // return ok; } // ------------------------------------------------------------------------- // pri_op bool pri_op(void) { bool ok = true; // // AD, addr_t using CppAD::AD; using CppAD::addr_t; // // tape_t, Vector using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; // // ap, ax Vector< AD > ap(1), ax(1); ap[0] = 3.0; ax[0] = 2.0; CppAD::Independent(ax, ap); // // f Vector< AD > ay(1); ay[0] = ax[0] - ap[0]; PrintFor(ay[0], "0 >= ax[0] - ap[0] = ", ay[0], "\n"); CppAD::ADFun f(ax, ay); // // tape tape_t tape; f.fun2val(tape); // // dep_vec Vector dep_vec = tape.dep_vec(); ok &= dep_vec.size() == 1; ok &= tape.n_ind() == 2; // // trace bool trace = false; // // p, x Vector p(1), x(1); p[0] = 2.0; // 0 >= x[0] - p[0] x[0] = 3.0; // // val_vec Vector val_vec( tape.n_val() ); val_vec[0] = p[0]; val_vec[1] = x[0]; size_t compare_false = 0; tape.eval(trace, val_vec, compare_false); ok &= compare_false == 0; // // y, ok Vector y(2); y[0] = val_vec[ dep_vec[0] ]; ok &= y[0] == x[0] - p[0]; // return ok; } // ------------------------------------------------------------------------- // vector_op bool vector_op(void) { bool ok = true; // // AD, addr_t using CppAD::AD; using CppAD::addr_t; // // tape_t, Vector using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; // // ax Vector< AD > ax(3); ax[0] = 0.0; // zero or one ax[1] = 2.0; ax[2] = 3.0; CppAD::Independent(ax); // // av CppAD::VecAD au(2); au[ ax[0] ] = ax[1]; au[ 1.0 - ax[0] ] = ax[2]; // // f Vector< AD > ay(2); ay[0] = au[ AD(0) ]; ay[1] = au[ AD(1) ]; CppAD::ADFun f(ax, ay); // // tape tape_t tape; f.fun2val(tape); // // dep_vec Vector dep_vec = tape.dep_vec(); ok &= dep_vec.size() == 2; ok &= tape.n_ind() == 3; // // trace bool trace = false; // // x Vector x(3); x[0] = 1.0; // zero or one x[1] = 4.0; x[2] = 5.0; // // val_vec Vector val_vec( tape.n_val() ); for(size_t i = 0; i < 3; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // y, ok Vector y(2); y[0] = val_vec[ dep_vec[0] ]; y[1] = val_vec[ dep_vec[1] ]; // ok &= y[ size_t( x[0] ) ] == x[1]; ok &= y[ size_t( 1.0 - x[0] ) ] == x[2]; // return ok; } // ---------------------------------------------------------------------------- } // END_EMPTY_NAMESPACE bool test_fun2val(void) { bool ok = true; ok &= vector_op(); ok &= dynamic_atom(); ok &= variable_atom(); ok &= unary_op(); ok &= comp_op(); ok &= dis_op(); ok &= cexp_op(); ok &= csum_op(); ok &= pri_op(); return ok; } ================================================ FILE: val_graph/test/nan.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include # include // bool test_nan(void) { bool ok = true; // // tape_t, Vector, addr_t, con_op_enum using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; // // nan double nan = std::numeric_limits::quiet_NaN(); // // tape tape_t tape; addr_t n_ind = 1; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // op_arg Vector op_arg(2); // // dep_vec Vector dep_vec(1); // // tape tape.record_con_op(5.0); // not used dep_vec[0] = tape.record_con_op(nan); // nan // // set_dep tape.set_dep( dep_vec ); // ok &= tape.n_op() == 2; ok &= tape.con_vec().size() == 2; ok &= tape.arg_vec().size() == 2; // bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = 0.0; tape.eval(trace, val_vec); // // renumber tape.renumber(); // // dead_code tape.dead_code(); // ok &= tape.n_op() == 1; ok &= tape.con_vec().size() == 1; ok &= tape.arg_vec().size() == 1; // // val_vec val_vec.resize( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = 0.0; tape.eval(trace, val_vec); // // y Vector y(1); dep_vec = tape.dep_vec(); y[0] = val_vec[ dep_vec[0] ]; // // ok ok &= std::isnan( y[0] ); // return ok; } // END_C++ ================================================ FILE: val_graph/test/opt_call.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include # include "../atomic_xam.hpp" namespace { // BEGIN_EMPTY_NAMESPACE // --------------------------------------------------------------------------- // // result_not_used bool result_not_used(void) { bool ok = true; // // tape_t, Vector, addr_t, op_enum_t using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; // // add_op_enum; op_enum_t add_op_enum = CppAD::local::val_graph::add_op_enum; op_enum_t sub_op_enum = CppAD::local::val_graph::sub_op_enum; // // atomic_xam val_atomic_xam atomic_xam; // // f tape_t tape; addr_t n_ind = 2; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // n_dep size_t n_dep = 1; // // fun_arg, op_arg Vector fun_arg(4), op_arg(2); // // dep_vec Vector dep_vec(n_dep); // // atomic_index addr_t atomic_index = addr_t( atomic_xam.atomic_index() ); // // add = x[0] + x[1] op_arg[0] = 0; op_arg[1] = 1; addr_t add = tape.record_op(add_op_enum, op_arg); // // sub = x[0] - x[1] op_arg[0] = 0; op_arg[1] = 1; addr_t sub = tape.record_op(sub_op_enum, op_arg); // // g_0(x) = 3.0 + 3.0 // g_1(x) = (x[0] + x[1]) * (x[0] - x[1]) fun_arg[0] = tape.record_con_op(3.0); fun_arg[1] = tape.record_con_op(3.0); fun_arg[2] = add; fun_arg[3] = sub; addr_t call_id = 0; addr_t n_fun_res = 2; addr_t res_index = tape.record_call_op( atomic_index, call_id, n_fun_res, fun_arg ); // // dep_vec // f(x) = g_1(x) = (x[0] + x[1]) * (x[0] - x[1]) dep_vec[0] = res_index + 1; // // set_dep tape.set_dep( dep_vec ); // // x Vector x(n_ind); for(addr_t i = 0; i < n_ind; ++i) x[i] = 2.0 + double(n_ind - i); // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // ok // before optimizing ok &= tape.arg_vec().size() == 1 + 2 + 2 + 2 + 4 + 5; ok &= tape.con_vec().size() == 3; ok &= tape.n_op() == 6; // // renumber tape.renumber(); val_vec.resize( tape.n_val() ); tape.eval(trace, val_vec); // // dead_code tape.dead_code(); val_vec.resize( tape.n_val() ); tape.eval(trace, val_vec); // // ok // after optimizing ok &= tape.arg_vec().size() == 1 + 2 + 2 + 4 + 5; ok &= tape.con_vec().size() == 1; ok &= tape.n_op() == 4; // // y Vector y(n_dep); dep_vec = tape.dep_vec(); for(size_t i = 0; i < n_dep; ++i) y[i] = val_vec[ dep_vec[i] ]; // // ok ok &= y[0] == (x[0] + x[1]) * (x[0] - x[1]); // return ok; } // --------------------------------------------------------------------------- // // ident_zero bool ident_zero(void) { bool ok = true; // // tape_t, Vector, addr_t, op_enum_t using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; // // add_op_enum; op_enum_t add_op_enum = CppAD::local::val_graph::add_op_enum; op_enum_t sub_op_enum = CppAD::local::val_graph::sub_op_enum; // // atomic_xam val_atomic_xam atomic_xam; // // f tape_t tape; addr_t n_ind = 2; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // n_dep size_t n_dep = 2; // // fun_arg, op_arg Vector fun_arg(4), op_arg(2); // // dep_vec Vector dep_vec(n_dep); // // atomic_index addr_t atomic_index = addr_t( atomic_xam.atomic_index() ); // // add = x[0] + x[1] op_arg[0] = 0; op_arg[1] = 1; addr_t add = tape.record_op(add_op_enum, op_arg); // // sub = x[0] - x[1] op_arg[0] = 0; op_arg[1] = 1; addr_t sub = tape.record_op(sub_op_enum, op_arg); // // f_0(x) = (x[0] + x[1]) + 3.0 // f_1(x) = (x[0] - x[1]) * 0.0 fun_arg[0] = add; fun_arg[1] = tape.record_con_op(3.0); fun_arg[2] = sub; fun_arg[3] = tape.record_con_op(0.0); addr_t call_id = 0; addr_t n_fun_res = 2; addr_t res_index = tape.record_call_op( atomic_index, call_id, n_fun_res, fun_arg ); // // dep_vec dep_vec[0] = res_index + 0; dep_vec[1] = res_index + 1; // // set_dep tape.set_dep( dep_vec ); // // x Vector x(n_ind); for(addr_t i = 0; i < n_ind; ++i) x[i] = 2.0 + double(n_ind - i); // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // ok // before optimizing ok &= tape.arg_vec().size() == 1 + 2 + 2 + 2 + 4 + 5; ok &= tape.con_vec().size() == 3; ok &= tape.n_op() == 6; // // dead_code tape.dead_code(); val_vec.resize( tape.n_val() ); tape.eval(trace, val_vec); // // ok // after optimizing the subtract operator has been removd ok &= tape.arg_vec().size() == 1 + 2 + 2 + 4 + 5; ok &= tape.con_vec().size() == 3; ok &= tape.n_op() == 5; // // y Vector y(n_dep); dep_vec = tape.dep_vec(); for(size_t i = 0; i < n_dep; ++i) y[i] = val_vec[ dep_vec[i] ]; // // ok ok &= y[0] == (x[0] + x[1]) + 3.0; ok &= y[1] == 0.0; // return ok; } // --------------------------------------------------------------------------- } // END_EMPTY_NAMESPACE // // test_opt_call bool test_opt_call(void) { bool ok = true; ok &= result_not_used(); ok &= ident_zero(); return ok; } ================================================ FILE: val_graph/test/optimize.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include namespace { // BEGIN_EMPTY_NAMESPACE // --------------------------------------------------------------------------- // communative bool communative(void) { bool ok = true; // // tape_t, Vector, addr_t, op_enum_t using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; // // add_op_enum; op_enum_t add_op_enum = CppAD::local::val_graph::add_op_enum; op_enum_t mul_op_enum = CppAD::local::val_graph::mul_op_enum; // // f tape_t tape; addr_t n_ind = 2; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // dep_vec size_t n_dep = 4; Vector dep_vec(n_dep); // // op_arg Vector op_arg(2); // // y[0] = x[0] + x[1] op_arg[0] = 0; op_arg[1] = 1; dep_vec[0] = tape.record_op(add_op_enum, op_arg); // // y[1] = x[1] + x[0] op_arg[0] = 1; op_arg[1] = 0; dep_vec[1] = tape.record_op(add_op_enum, op_arg); // // y[2] = x[0] * x[1] op_arg[0] = 0; op_arg[1] = 1; dep_vec[2] = tape.record_op(mul_op_enum, op_arg); // // y[3] = x[1] * x[0] op_arg[0] = 1; op_arg[1] = 0; dep_vec[3] = tape.record_op(mul_op_enum, op_arg); // // set_dep tape.set_dep( dep_vec ); // // x Vector x(n_ind); for(addr_t i = 0; i < n_ind; ++i) x[i] = 2.0 + double(n_ind - i); // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // ok // before optimizing ok &= tape.arg_vec().size() == 1 + 4 * 2; ok &= tape.con_vec().size() == 1; ok &= tape.n_op() == 1 + 4; // // renumber tape.renumber(); val_vec.resize( tape.n_val() ); tape.eval(trace, val_vec); // // dead_code tape.dead_code(); val_vec.resize( tape.n_val() ); tape.eval(trace, val_vec); // // ok // after optimizing ok &= tape.arg_vec().size() == 1 + 2 * 2; ok &= tape.con_vec().size() == 1; ok &= tape.n_op() == 1 + 2; // // y Vector y(n_dep); dep_vec = tape.dep_vec(); for(size_t i = 0; i < n_dep; ++i) y[i] = val_vec[ dep_vec[i] ]; // // ok ok &= y[0] == x[0] + x[1]; ok &= y[1] == y[0]; ok &= y[2] == x[0] * x[1]; ok &= y[3] == y[2]; // return ok; } // --------------------------------------------------------------------------- // propagate_match bool propagate_match(void) { bool ok = true; // // tape_t, Vector, addr_t, op_enum_t using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; // // add_op_enum; op_enum_t add_op_enum = CppAD::local::val_graph::add_op_enum; op_enum_t mul_op_enum = CppAD::local::val_graph::mul_op_enum; // // f tape_t tape; addr_t n_ind = 2; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // dep_vec size_t n_dep = 2; Vector dep_vec(n_dep); // // op_arg Vector op_arg(2); // // five addr_t five = tape.record_con_op(5.0); // // add_1: x[0] + x[1] op_arg[0] = 0; op_arg[1] = 1; addr_t add_1 = tape.record_op(add_op_enum, op_arg); // // add_2: x[1] + x[0] op_arg[0] = 1; op_arg[1] = 0; addr_t add_2 = tape.record_op(add_op_enum, op_arg); // // y[0] = 5 * (x[0] * x[1]) op_arg[0] = five; op_arg[1] = add_1; dep_vec[0] = tape.record_op(mul_op_enum, op_arg); // // y[1] = 5 * (x[1] * x[0]) op_arg[0] = five; op_arg[1] = add_2; dep_vec[1] = tape.record_op(mul_op_enum, op_arg); // // set_dep tape.set_dep( dep_vec ); // // x Vector x(n_ind); for(addr_t i = 0; i < n_ind; ++i) x[i] = 2.0 + double(n_ind - i); // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // ok // before optimizing ok &= tape.con_vec().size() == 2; ok &= tape.n_op() == 2 + 4; ok &= tape.arg_vec().size() == 2 + 4 * 2; // // renumber tape.renumber(); val_vec.resize( tape.n_val() ); tape.eval(trace, val_vec); // // dead_code tape.dead_code(); val_vec.resize( tape.n_val() ); tape.eval(trace, val_vec); // // ok // after optimizing ok &= tape.con_vec().size() == 2; ok &= tape.n_op() == 2 + 2; ok &= tape.arg_vec().size() == 2 + 2 * 2; // // y Vector y(n_dep); dep_vec = tape.dep_vec(); for(size_t i = 0; i < n_dep; ++i) y[i] = val_vec[ dep_vec[i] ]; // // ok ok &= y[0] == 5.0 * (x[0] + x[1]); ok &= y[1] == y[0]; // return ok; } // --------------------------------------------------------------------------- bool not_used(void) { bool ok = true; // // tape_t, Vector, addr_t, add_op_enum, sub_op_enum using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; op_enum_t add_op_enum = CppAD::local::val_graph::add_op_enum; op_enum_t sub_op_enum = CppAD::local::val_graph::sub_op_enum; // // tape tape_t tape; addr_t n_ind = 2; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // op_arg Vector op_arg(2); op_arg[0] = 0; // x[0] op_arg[1] = 1; // x[1] // // dep_vec Vector dep_vec(2); // // tape tape.record_op(add_op_enum, op_arg); // x[0] + x[1] (no used) tape.record_con_op(5.0); // 5.0 (not used) // dep_vec[0] = tape.record_op(add_op_enum, op_arg); // x[0] + x[1] op_arg[0] = dep_vec[0]; // x[0] + x[1] op_arg[1] = tape.record_con_op(4.0); // 4.0 addr_t temp_1 = tape.record_op(sub_op_enum, op_arg); // x[0] + x[1] - 4.0 op_arg[0] = temp_1; op_arg[1] = tape.record_con_op(4.0); // duplicate dep_vec[1] = tape.record_op(sub_op_enum, op_arg); // x[0] + x[1] - 8.0 // // set_dep tape.set_dep( dep_vec ); // // x Vector x(2); x[0] = 5.0; x[1] = 6.0; // // trace bool trace = false; // // val_vec Vector val_vec; // // val_vec, ok // before optimizing val_vec.resize( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); ok &= tape.arg_vec().size() == 12; ok &= tape.con_vec().size() == 4; ok &= tape.n_op() == 8; // // renumber, val_vec tape.renumber(); val_vec.resize( tape.n_val() ); tape.eval(trace, val_vec); // // dead_code, val_vec tape.dead_code(); val_vec.resize( tape.n_val() ); tape.eval(trace, val_vec); // // ok // after optimizing ok &= tape.arg_vec().size() == 8; ok &= tape.con_vec().size() == 2; ok &= tape.n_op() == 5; // // dep_vec dep_vec = tape.dep_vec(); // // y Vector y(2); for(size_t i = 0; i < 2; ++i) y[i] = val_vec[ dep_vec[i] ]; // // ok ok &= y[0] == x[0] + x[1]; ok &= y[1] == x[0] + x[1] - 8.0; // return ok; } // --------------------------------------------------------------------------- // summation bool summation(void) { bool ok = true; // // tape_t, Vector, addr_t, csum_op_enum; using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; op_enum_t add_op_enum = CppAD::local::val_graph::add_op_enum; op_enum_t sub_op_enum = CppAD::local::val_graph::sub_op_enum; op_enum_t mul_op_enum = CppAD::local::val_graph::mul_op_enum; // // tape, ok tape_t tape; addr_t n_ind = 4; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; Vector op_arg(2); // // op_arg, add = x[0] + x[0] // This operator will become part of a cumulative summation op_arg[0] = 0; // x[0] op_arg[1] = 0; // x[0] addr_t add = tape.record_op(add_op_enum, op_arg); // // op_arg, mul = x[2] * x[3] // This operator cannot become part of a cumulative summation op_arg[0] = 2; // x[2] op_arg[1] = 3; // x[3] addr_t mul = tape.record_op(mul_op_enum, op_arg); // // op_arg, sub = x[2] * x[3] - x[0] - x[0] // Test case where second arg gets replaced op_arg[0] = mul; op_arg[1] = add; addr_t sub = tape.record_op(sub_op_enum, op_arg); // // op_arg, add = 2 * ( x[2] * x[3] - x[0] - x[0] ) op_arg[0] = sub; op_arg[1] = sub; add = tape.record_op(add_op_enum, op_arg); // // dep_vec, tape Vector dep_vec(1); dep_vec[0] = add; // 2 * ( x[2] * x[3] - x[0] - x[0] ) // // set_dep tape.set_dep( dep_vec ); // // x Vector x(n_ind); for(addr_t i = 0; i < n_ind; ++i) x[i] = double(2 + i); // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // ok ok &= tape.n_op() == 5; ok &= tape.con_vec().size() == 1; ok &= tape.arg_vec().size() == 1 + 4 * 2; ok &= tape.n_val() == n_ind + 1 + 4; // // summation, dead_code tape.summation(); tape.dead_code(); // // ok ok &= tape.n_op() == 3; ok &= tape.con_vec().size() == 1; ok &= tape.arg_vec().size() == 1 + 2 + (6 + 3); ok &= tape.n_val() == n_ind + 3; // // eval val_vec.resize( tape.n_val() ); tape.eval(trace, val_vec); // // y Vector y(1); dep_vec = tape.dep_vec(); y[0] = val_vec[ dep_vec[0] ]; // // ok ok &= y[0] == 2.0 * ( x[2] * x[3] - x[0] - x[0] ); // return ok; } bool dynamic_vector(void) { bool ok = true; // // tape_t, Vector, addr_t, compare_lt_enum using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; // // tape, ok tape_t tape; addr_t n_ind = 2; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // x0, x1, x2 addr_t x0 = 0; // x[0] addr_t x1 = 1; // x[1] // // zero, one, two addr_t zero = tape.record_con_op(0.0); addr_t one = tape.record_con_op(1.0); // // tape // first_vector[0] = x[0], first_vector[1] = 0 Vector first_initial = {x0, zero}; tape.record_vec_op(first_initial); // // tape, second_vector // second_vector[0] = 1, second_vector[1] = x[1] Vector second_initial = {one, x1}; addr_t second_vector = tape.record_vec_op(second_initial); // // dep_vec, tape Vector dep_vec(2); dep_vec[0] = tape.record_load_op(second_vector, zero); // 1 dep_vec[1] = tape.record_load_op(second_vector, one); // x[1] // // set_dep tape.set_dep( dep_vec ); // // x Vector x(n_ind); x[0] = 3.0; x[1] = 4.0; // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // ok ok &= tape.n_op() == 7; ok &= tape.con_vec().size() == 3; ok &= tape.arg_vec().size() == 3 + 2 + 2 * 2; ok &= tape.n_val() == n_ind + 3 + 2; ok &= tape.vec_initial().size() == 2; // // y Vector y(2); for(size_t i = 0; i < 2; ++i) y[i] = val_vec[ dep_vec[i] ]; // // ok ok &= y[0] == 1.0; ok &= y[1] == x[1]; // // dead_code tape.dead_code(); // // val_vec val_vec.resize( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // ok ok &= tape.n_op() == 6; ok &= tape.con_vec().size() == 3; ok &= tape.arg_vec().size() == 3 + 1 + 2 * 2; ok &= tape.n_val() == n_ind + 3 + 2; ok &= tape.vec_initial().size() == 1; // // y dep_vec = tape.dep_vec(); for(size_t i = 0; i < 2; ++i) y[i] = val_vec[ dep_vec[i] ]; // // ok ok &= y[0] == 1.0; ok &= y[1] == x[1]; // return ok; } // END_C++ // --------------------------------------------------------------------------- } // END_EMPTY_NAMESPACE // // test_opt_call bool test_optimize(void) { bool ok = true; ok &= communative(); ok &= propagate_match(); ok &= not_used(); ok &= summation(); ok &= dynamic_vector(); return ok; } ================================================ FILE: val_graph/test/val2fun.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include # include # include # include "../atomic_xam.hpp" namespace { // BEGIN_EMPTY_NAMESPACE // ---------------------------------------------------------------------------- bool dynamic_atom(void) { bool ok = true; // // tape_t, Vector, addr_t, add_op_enum; using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; op_enum_t add_op_enum = CppAD::local::val_graph::add_op_enum; op_enum_t sub_op_enum = CppAD::local::val_graph::sub_op_enum; // // atomic_xam val_atomic_xam atomic_xam; // // tape, ok tape_t tape; addr_t n_ind = 5; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // op_arg Vector call_arg(4), op_arg(2); // // dep_vec Vector dep_vec(2); // // atomic_index addr_t atomic_index = addr_t( atomic_xam.atomic_index() ); // // g_0(x) = p[0] + p[1] // g_1(x) = p[2] * p[3] for(addr_t i = 0; i < 4; ++i) call_arg[i] = i; addr_t call_id = 0; addr_t n_call_res = 2; addr_t res_index = tape.record_call_op( atomic_index, call_id, n_call_res, call_arg ); // // dep_vec[0] = x[0] + p[0] + p[1] op_arg[0] = 4; // x[0] op_arg[1] = res_index; // p[0] - p[1] dep_vec[0] = tape.record_op(add_op_enum, op_arg); // // dep_vec[1] = x[0] - p[2] * p[3] op_arg[0] = 4; // x[1] op_arg[1] = res_index + 1; // p[2] * p[3] dep_vec[1] = tape.record_op(sub_op_enum, op_arg); // // set_dep tape.set_dep( dep_vec ); /* // trace bool trace = true; Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = double(5 + i); tape.eval(trace, val_vec); */ // // ADFun Vector dyn_ind(4), var_ind(1); for(size_t i = 0; i < 4; i++) dyn_ind[i] = i; var_ind[0] = 4; CppAD::ADFun f; f.val2fun( tape, dyn_ind, var_ind ); // // p, x Vector p(4), x(1), y(2); for(addr_t i = 0; i < 4; i++) p[i] = double(i + 5); x[0] = 9.0; // // f, y f.new_dynamic(p); y = f.Forward(0, x); // ok &= y[0] == x[0] + (p[0] + p[1]); ok &= y[1] == x[0] - (p[2] * p[3]); // return ok; } // ---------------------------------------------------------------------------- bool variable_atom(void) { bool ok = true; // // tape_t, Vector, addr_t, add_op_enum; using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; op_enum_t add_op_enum = CppAD::local::val_graph::add_op_enum; op_enum_t sub_op_enum = CppAD::local::val_graph::sub_op_enum; // // atomic_xam val_atomic_xam atomic_xam; // // tape, ok tape_t tape; addr_t n_ind = 5; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // op_arg Vector call_arg(4), op_arg(2); // // dep_vec Vector dep_vec(2); // // atomic_index addr_t atomic_index = addr_t (atomic_xam.atomic_index() ); // // g_0(x) = x[0] + x[1] // g_1(x) = x[2] * x[3] for(addr_t i = 0; i < 4; ++i) call_arg[i] = i; addr_t call_id = 0; addr_t n_call_res = 2; addr_t res_index = tape.record_call_op( atomic_index, call_id, n_call_res, call_arg ); // // dep_vec[0] = p[0] + x[0] + x[1] op_arg[0] = 4; // p[0] op_arg[1] = res_index; // x[0] - x[1] dep_vec[0] = tape.record_op(add_op_enum, op_arg); // // dep_vec[1] = p[0] - x[2] * x[3] op_arg[0] = 4; // p[1] op_arg[1] = res_index + 1; // x[2] * x[3] dep_vec[1] = tape.record_op(sub_op_enum, op_arg); // // set_dep tape.set_dep( dep_vec ); // // trace bool trace = false; Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = double(5 + i); tape.eval(trace, val_vec); // // ADFun Vector var_ind(4), dyn_ind(1); for(size_t i = 0; i < 4; i++) var_ind[i] = i; dyn_ind[0] = 4; CppAD::ADFun f; f.val2fun( tape, dyn_ind , var_ind); // // p, x Vector x(4), p(1), y(2); for(addr_t i = 0; i < 4; i++) x[i] = double(i + 5); p[0] = 9.0; // // f, y f.new_dynamic(p); y = f.Forward(0, x); // ok &= y[0] == p[0] + (x[0] + x[1]); ok &= y[1] == p[0] - (x[2] * x[3]); // return ok; } // ---------------------------------------------------------------------------- bool comp_op(void) { bool ok = true; // // tape_t, Vector, addr_t, sub_op_enum; using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; using CppAD::local::val_graph::compare_enum_t; op_enum_t sub_op_enum = CppAD::local::val_graph::sub_op_enum; compare_enum_t compare_lt_enum = CppAD::local::val_graph::compare_lt_enum; // // tape, ok tape_t tape; addr_t n_ind = 2; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // dep_vec Vector dep_vec(1); // // op_arg Vector op_arg(2); op_arg[0] = 0; // x[0] op_arg[1] = 1; // x[1] // // tape // put two identical compare operators in the tape addr_t left_index = 0; // x[0] addr_t right_index = 1; // x[1] addr_t res_index = tape.record_comp_op( compare_lt_enum, left_index, right_index // x[0] < x[1] ); ok &= res_index == 0; // no result for this operator // // tape, dep_vec dep_vec[0] = tape.record_op(sub_op_enum, op_arg); // x[0] - x[1] // // set_dep tape.set_dep( dep_vec ); // // trace bool trace = false; Vector val_vec( tape.n_val() ); val_vec[0] = 5.0; // x[0] val_vec[1] = 6.0; // x[1] tape.eval(trace, val_vec); // // f // switch x[0] and x[1] so y[0] = x[1] - x[0] and compare is x[1] < x[0] Vector var_ind(2), dyn_ind(0); var_ind[0] = 1; var_ind[1] = 0; CppAD::ADFun f; f.val2fun(tape, dyn_ind, var_ind); // // f, x, y Vector x(2), y; x[0] = 5.0; x[1] = 6.0; y = f.Forward(0, x); // // ok ok &= f.compare_change_number() == 1; ok &= y[0] == x[1] - x[0]; // return ok; } // ---------------------------------------------------------------------------- // dis_op double floor(const double& arg) { return std::floor(arg); } // CPPAD_DISCRETE_FUNCTION(double, floor); bool dis_op(void) { bool ok = true; // // AD, addr_t using CppAD::AD; using CppAD::addr_t; // // tape_t, Vector using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; // // tape tape_t tape; // // ap, ax Vector p(1), x(1); p[0] = 2.3; x[0] = 3.4; // // tape, ok addr_t n_ind = 2; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // dep_vec Vector dep_vec(2); // // tape, dep_vec addr_t discrete_index, val_index; discrete_index = addr_t( CppAD::discrete::index("floor") ); val_index = 0; // p[0] dep_vec[0] = tape.record_dis_op(discrete_index, val_index); // floor( p[0] ) val_index = 1; // x[0] dep_vec[1] = tape.record_dis_op(discrete_index, val_index); // floor( x[0] ) // // tape tape.set_dep(dep_vec); // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); val_vec[0] = p[0]; val_vec[1] = x[0]; tape.eval(trace, val_vec); // // f Vector var_ind(1), dyn_ind(1); dyn_ind[0] = 0; var_ind[0] = 1; CppAD::ADFun f; f.val2fun( tape, dyn_ind , var_ind); // // ok p[0] = 5.5; x[0] = - 5.5; f.new_dynamic(p); Vector y = f.Forward(0, x); ok &= y[0] == std::floor( p[0] ); ok &= y[1] == std::floor( x[0] ); // return ok; } // ---------------------------------------------------------------------------- // csum_op bool csum_op(void) { bool ok = true; // // tape_t, Vector, addr_t using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; // // tape, ok tape_t tape; addr_t n_ind = 4; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // c[0], c[1] Vector c(2); c[0] = -2.0; c[1] = -3.0; addr_t c0 = tape.record_con_op( c[0] ); addr_t c1 = tape.record_con_op( c[1] ); // // add, sub Vector add(3), sub(3); add[0] = 0; // x[0] add[1] = 2; // p[0] add[2] = c0; // c[0] // sub[0] = 1; // x[1] sub[1] = 3; // p[1] sub[2] = c1; // c[1] // // tape, dep_vec Vector dep_vec(1); dep_vec[0] = tape.record_csum_op(add, sub); tape.set_dep( dep_vec ); // // trace bool trace = false; // // val_vec, ok Vector x(2), p(2), val_vec( tape.n_val() ); val_vec[0] = x[0] = 2.0; val_vec[1] = x[1] = 3.0; val_vec[2] = p[0] = 5.0; val_vec[3] = p[1] = 8.0; tape.eval(trace, val_vec); // // y, ok Vector y(1); y[0] = val_vec[ dep_vec[0] ]; ok &= y[0] == x[0] - x[1] + p[0] - p[1] + c[0] - c[1]; // // f Vector var_ind(2), dyn_ind(2); var_ind[0] = 0; // x[0] var_ind[1] = 1; // x[1] dyn_ind[0] = 2; // p[0] dyn_ind[1] = 3; // p[1] CppAD::ADFun f; f.val2fun(tape, dyn_ind, var_ind); // // f, y f.new_dynamic(p); y = f.Forward(0, x); // // ok ok &= f.compare_change_number() == 0; ok &= y[0] == x[0] - x[1] + p[0] - p[1] + c[0] - c[1]; // return ok; } // ---------------------------------------------------------------------------- // cexp_op bool cexp_op(void) { bool ok = true; // // tape_t, Vector, addr_t using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; // // compare_lt_enum CppAD::local::val_graph::compare_enum_t compare_lt_enum = CppAD::local::val_graph::compare_lt_enum; // // tape, ok tape_t tape; addr_t n_ind = 4; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // dep_vec Vector dep_vec(2); // // c[0], c[1] Vector c(2); c[0] = -2.0; c[1] = -3.0; addr_t c0 = tape.record_con_op( c[0] ); addr_t c1 = tape.record_con_op( c[1] ); // // left, right, if_true, if_false addr_t left = 0; // p[0] addr_t right = 1; // p[1] addr_t if_true = 2; // x[0] addr_t if_false = 3; // x[1] // // tape, dep_vec dep_vec[0] = tape.record_cexp_op( compare_lt_enum, left, right, if_true, if_false ); // // left, right, if_true, if_false left = c0; // c[0] right = c1; // c[1] if_true = 0; // p[0] if_false = 1; // p[1] // // tape, dep_vec dep_vec[1] = tape.record_cexp_op( compare_lt_enum, left, right, if_true, if_false ); tape.set_dep( dep_vec ); // // trace bool trace = false; // // val_vec, ok Vector x(2), p(2), val_vec( tape.n_val() ); val_vec[0] = p[0] = 2.0; val_vec[1] = p[1] = 3.0; val_vec[2] = x[0] = 5.0; val_vec[3] = x[1] = 8.0; tape.eval(trace, val_vec); // // y Vector y(2); y[0] = val_vec[ dep_vec[0] ]; y[1] = val_vec[ dep_vec[1] ]; // // ok if( p[0] < p[1] ) ok &= y[0] == x[0]; else ok &= y[0] == x[1]; if( c[0] < c[1] ) ok &= y[1] == p[0]; else ok &= y[1] == p[1]; // // f Vector var_ind(2), dyn_ind(2); dyn_ind[0] = 0; // p[0] dyn_ind[1] = 1; // p[1] var_ind[0] = 2; // x[0] var_ind[1] = 3; // x[1] CppAD::ADFun f; f.val2fun(tape, dyn_ind, var_ind); // // f, y, ok f.new_dynamic(p); y = f.Forward(0, x); ok &= f.compare_change_number() == 0; // // ok if( p[0] < p[1] ) ok &= y[0] == x[0]; else ok &= y[0] == x[1]; if( c[0] < c[1] ) ok &= y[1] == p[0]; else ok &= y[1] == p[1]; // return ok; } // ---------------------------------------------------------------------------- bool pri_op(void) { bool ok = true; // // tape_t, Vector, addr_t, sub_op_enum; using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; using CppAD::local::val_graph::compare_enum_t; op_enum_t sub_op_enum = CppAD::local::val_graph::sub_op_enum; // // tape, ok tape_t tape; addr_t n_ind = 2; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // op_arg Vector op_arg(2); op_arg[0] = 0; // x[0] op_arg[1] = 1; // x[1] // // tape, diff_addr addr_t diff_addr = tape.record_op(sub_op_enum, op_arg); // x[0] - x[1] // // tape std::string before = "0 >= x[0] - x[1] = "; std::string after = "\n"; tape.record_pri_op(before, after, diff_addr, diff_addr); // // dep_vec Vector dep_vec(1); dep_vec[0] = diff_addr; // // set_dep tape.set_dep( dep_vec ); // // trace bool trace = false; Vector val_vec( tape.n_val() ); val_vec[0] = 6.0; // x[0] val_vec[1] = 5.0; // x[1] tape.eval(trace, val_vec); // // f Vector var_ind(2), dyn_ind(0); var_ind[0] = 0; var_ind[1] = 1; CppAD::ADFun f; f.val2fun(tape, dyn_ind, var_ind); // // f, x, y Vector x(2), y; x[0] = 5.0; x[1] = 6.0; // std::cout << "\nNext line should be '" + before << x[0] - x[1]; std::cout << "', line after should be 'OK'\n"; y = f.Forward(0, x); // // ok ok &= y[0] == x[0] - x[1]; // return ok; } // --------------------------------------------------------------------------- bool vector_op(void) { bool ok = true; // // tape_t, Vector, addr_t, compare_lt_enum using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; op_enum_t sub_op_enum = CppAD::local::val_graph::sub_op_enum; // // tape, ok tape_t tape; addr_t n_ind = 3; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // x0, x1, x2 addr_t x0 = 0; // x[0] must be zero or one addr_t x1 = 1; // x[1] addr_t x2 = 2; // x[2] // // zero, one, four addr_t zero = tape.record_con_op(0.0); addr_t one = tape.record_con_op(1.0); addr_t four = tape.record_con_op(4.0); // // which_vector // vec[0] = 4, vec[1] = 1 Vector initial = {four, one}; addr_t which_vector = tape.record_vec_op(initial); // // dep_vec, tape Vector dep_vec(3); dep_vec[0] = tape.record_load_op(which_vector, x0); // y[0] = x[initial[x[0]] // // one_minus_x0 Vector op_arg = {one, x0}; addr_t one_minus_x0 = tape.record_op(sub_op_enum, op_arg); // // dep_vec, tape // vec[x[0]] = x[1] tape.record_store_op(which_vector, x0, x1); // vec[x[0]] = x[1] tape.record_store_op(which_vector, one_minus_x0, x2); // vec[1-x[0]] = x[2] // // set_dep dep_vec[1] = tape.record_load_op(which_vector, zero); // y[1] = vec[0] dep_vec[2] = tape.record_load_op(which_vector, one); // y[2] = vec[1] tape.set_dep( dep_vec ); // // x Vector x(n_ind); x[0] = 1.0; x[1] = 5.0; x[2] = 6.0; // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // y Vector y(3); for(size_t i = 0; i < 3; ++i) y[i] = val_vec[ dep_vec[i] ]; // // ok if( x[0] == 0.0 ) { ok &= y[0] == 4.0; ok &= y[1] == x[1]; ok &= y[2] == x[2]; } else { assert( x[0] == 1.0); ok &= y[0] == 1.0; ok &= y[1] == x[2]; ok &= y[2] == x[1]; } // // f Vector var_ind(3), dyn_ind(0); var_ind[0] = 0; var_ind[1] = 1; var_ind[2] = 2; CppAD::ADFun f; f.val2fun(tape, dyn_ind, var_ind); // // y y = f.Forward(0, x); // // ok if( x[0] == 0.0 ) { ok &= y[0] == 4.0; ok &= y[1] == x[1]; ok &= y[2] == x[2]; } else { assert( x[0] == 1.0); ok &= y[0] == 1.0; ok &= y[1] == x[2]; ok &= y[2] == x[1]; } // return ok; } // END_C++ // --------------------------------------------------------------------------- } // END_EMPTY_NAMESPACE bool test_val2fun(void) { bool ok = true; ok &= dynamic_atom(); ok &= variable_atom(); ok &= comp_op(); ok &= dis_op(); ok &= csum_op(); ok &= cexp_op(); ok &= pri_op(); ok &= vector_op(); return ok; } ================================================ FILE: val_graph/test/val_optimize.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include namespace { // BEGIN_EMPTY_NAMESPACE // ---------------------------------------------------------------------------- // optimize_csum bool csum_op(void) { bool ok = true; using CppAD::AD; using CppAD::vector; // // ax, ap size_t n = 2; vector< AD > ax(n), ap(n); for(size_t j = 0; j < n; ++j) { ax[j] = 0.0; ap[j] = 0.0; } Independent(ax, ap); // // asum AD asum = 0.0; for(size_t j = 0; j < n; ++j) { asum += ax[j]; asum += ap[j]; } AD aplus = ax[0] + ap[0]; AD aminus = ax[0] - ap[0]; asum += CondExpLe(ax[0], ap[0], aplus, aminus); // // f vector< AD > ay(1); ay[0] = asum * asum; CppAD::ADFun f(ax, ay); // // val_optimize f.val_optimize("val_graph no_conditional_skip"); // // x, p, y, check, ok // zero order forward vector x(n), p(n), y(1); double sum = 0.0; for(size_t j = 0; j < n; ++j) { p[j] = double(j + 1); x[j] = double(n + j + 1); sum += x[j]; sum += p[j]; } if( x[0] <= p[0] ) sum += x[0] + p[0]; else sum += x[0] - p[0]; // double check = sum * sum; f.new_dynamic(p); y = f.Forward(0, x); ok &= y[0] == check; // // val_optimize, y, ok f.val_optimize("val_graph no_conditional_skip"); f.new_dynamic(p); y = f.Forward(0, x); ok &= y[0] == check; // return ok; } // ---------------------------------------------------------------------------- } // END_EMPTY_NAMESPACE bool test_val_optimize(void) { bool ok = true; ok &= csum_op(); return ok; } ================================================ FILE: val_graph/unary_xam.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include /* {xrst_begin val_unary_xam.cpp dev} Unary Value Operator Example ############################ {xrst_literal // BEGIN_C++ // END_C++ } {xrst_end val_unary_xam.cpp} */ // BEGIN_C++ bool unary_xam(void) { bool ok = true; // // tape_t, Vector, addr_t, neg_op_enum; using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; op_enum_t neg_op_enum = CppAD::local::val_graph::neg_op_enum; // // tape, ok tape_t tape; addr_t n_ind = 1; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // op_arg Vector op_arg(1); op_arg[0] = 0; // x[0] // // dep_vec Vector dep_vec(1); // // tape dep_vec[0] = tape.record_op(neg_op_enum, op_arg); // - x[0] // // set_dep tape.set_dep( dep_vec ); // // x Vector x(1); x[0] = 5.0; // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // ok ok &= tape.n_op() == 2; ok &= tape.arg_vec().size() == 2; ok &= tape.con_vec().size() == 1; ok &= tape.n_val() == n_ind + 2; // // y Vector y(1); y[0] = val_vec[ dep_vec[0] ]; // // ok ok &= y[0] == - x[0]; // return ok; } // END_C++ ================================================ FILE: val_graph/val2fun_xam.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include # include # include /* {xrst_begin val_val2fun_xam.cpp dev} Example ADFun From Value Graph ############################## {xrst_literal // BEGIN_C++ // END_C++ } {xrst_end val_val2fun_xam.cpp} */ // BEGIN_C++ bool val2fun_xam(void) { bool ok = true; // // tape_t, Vector, addr_t, add_op_enum; using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; using CppAD::local::val_graph::op_enum_t; op_enum_t add_op_enum = CppAD::local::val_graph::add_op_enum; op_enum_t sub_op_enum = CppAD::local::val_graph::sub_op_enum; // // tape, ok tape_t tape; addr_t n_ind = 4; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // op_arg Vector op_arg(2); // // dep_vec Vector dep_vec(2); // // addr_tmp addr_t addr_tmp; // // tape // // addr_tmp = p[0] - p[1] op_arg[0] = 0; // p[0] op_arg[1] = 1; // p[1] addr_tmp = tape.record_op(sub_op_enum, op_arg); // // dep_vec[0] = x[0] + p[0] - p[1] op_arg[0] = 2; // x[0] op_arg[1] = addr_tmp; // p[0] - p[1] dep_vec[0] = tape.record_op(add_op_enum, op_arg); // // addr_tmp = p[1] - p[0] op_arg[0] = 1; // p[1] op_arg[1] = 0; // p[0] addr_tmp = tape.record_op(sub_op_enum, op_arg); // // dep_vec[1] = x[1] + p[1] - p[0] op_arg[0] = 3; // x[1] op_arg[1] = addr_tmp; // p[1] - p[0] dep_vec[1] = tape.record_op(add_op_enum, op_arg); // // set_dep tape.set_dep( dep_vec ); /* // trace bool trace = true; Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = double(5 + i); tape.eval(trace, val_vec); */ // ADFun Vector dyn_ind(2), var_ind(2); dyn_ind[0] = 0; dyn_ind[1] = 1; var_ind[0] = 2; var_ind[1] = 3; CppAD::ADFun f; f.val2fun( tape, dyn_ind, var_ind ); // // p, x Vector p(2), x(2), y(2); p[0] = 5.0; p[1] = 6.0; x[0] = 7.0; x[1] = 8.0; // // f, y f.new_dynamic(p); y = f.Forward(0, x); // ok &= y[0] == x[0] + p[0] - p[1]; ok &= y[1] == x[1] + p[1] - p[0]; // return ok; } // END_C++ ================================================ FILE: val_graph/val_graph.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2003-23 Bradley M. Bell // ---------------------------------------------------------------------------- // CPPAD_HAS_* defines # include // system include files used for I/O # include // C style asserts # include // for thread_alloc # include // test runner # include // BEGIN_SORT_THIS_LINE_PLUS_1 extern bool binary_xam(void); extern bool call_xam(void); extern bool cexp_xam(void); extern bool comp_xam(void); extern bool compress_xam(void); extern bool con_xam(void); extern bool csum_xam(void); extern bool cumulative_xam(void); extern bool dead_xam(void); extern bool dis_xam(void); extern bool fold_con_xam(void); extern bool fun2val_xam(void); extern bool pri_xam(void); extern bool renumber_xam(void); extern bool summation_xam(void); extern bool test_ad_double(void); extern bool test_fold(void); extern bool test_fun2val(void); extern bool test_nan(void); extern bool test_opt_call(void); extern bool test_optimize(void); extern bool test_val2fun(void); extern bool test_val_optimize(void); extern bool unary_xam(void); extern bool val2fun_xam(void); extern bool vec_xam(void); // END_SORT_THIS_LINE_MINUS_1 // main program that runs all the tests int main(void) { std::string group = "val_graph"; size_t width = 20; CppAD::test_boolofvoid Run(group, width); // This line is used by test_one.sh // BEGIN_SORT_THIS_LINE_PLUS_1 Run( binary_xam, "binary_xam" ); Run( call_xam, "call_xam" ); Run( cexp_xam, "cexp_xam" ); Run( comp_xam, "comp_xam" ); Run( compress_xam, "compress_xam" ); Run( con_xam, "con_xam" ); Run( csum_xam, "csum_xam" ); Run( cumulative_xam, "cumulative_xam" ); Run( dead_xam, "dead_xam" ); Run( dis_xam, "dis_xam" ); Run( fold_con_xam, "fold_con_xam" ); Run( fun2val_xam, "fun2val_xam" ); Run( pri_xam, "pri_xam" ); Run( renumber_xam, "renumber_xam" ); Run( summation_xam, "summation_xam" ); Run( test_ad_double, "test_ad_double" ); Run( test_fold, "test_fold" ); Run( test_fun2val, "test_fun2val" ); Run( test_nan, "test_nan" ); Run( test_opt_call, "test_opt_call" ); Run( test_optimize, "test_optimize" ); Run( test_val2fun, "test_val2fun" ); Run( unary_xam, "unary_xam" ); Run( val2fun_xam, "val2fun_xam" ); Run( vec_xam, "vec_xam" ); // END_SORT_THIS_LINE_MINUS_1 // check for memory leak bool memory_ok = CppAD::thread_alloc::free_all(); // print summary at end bool ok = Run.summary(memory_ok); // return static_cast( ! ok ); } ================================================ FILE: val_graph/vec_xam.cpp ================================================ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later // SPDX-FileCopyrightText: Bradley M. Bell // SPDX-FileContributor: 2023-23 Bradley M. Bell # include # include /* {xrst_begin val_vec_xam.cpp dev} Conditional Expression Value Operator Example ############################################# {xrst_literal // BEGIN_C++ // END_C++ } {xrst_end val_vec_xam.cpp} */ // BEGIN_C++ bool vec_xam(void) { bool ok = true; // // tape_t, Vector, addr_t, compare_lt_enum using CppAD::local::val_graph::tape_t; using CppAD::local::val_graph::Vector; using CppAD::local::val_graph::addr_t; // // tape, ok tape_t tape; addr_t n_ind = 3; addr_t index_of_nan = tape.set_ind(n_ind); ok &= index_of_nan == n_ind; // // x0, x1, x2 addr_t x0 = 0; // x[0] addr_t x1 = 1; // x[1] addr_t x2 = 2; // x[2] // // one, four addr_t one = tape.record_con_op(1.0); addr_t four = tape.record_con_op(4.0); // // which_vector // vector[0] = 4, vector[1] = 1 Vector initial = {four, one}; addr_t which_vector = tape.record_vec_op(initial); // // dep_vec, tape Vector dep_vec(3); dep_vec[0] = tape.record_load_op(which_vector, x0); // vector[ x[0] ] // // dep_vec, tape // vector[ x[1] ] = x[2] tape.record_store_op(which_vector, x1, x2); // // set_dep tape.set_dep( dep_vec ); dep_vec[1] = tape.record_load_op(which_vector, x0); // vector[ x[0] ] dep_vec[2] = tape.record_load_op(which_vector, x1); // vector[ x[1] ] // // x Vector x(n_ind); x[0] = 0.0; x[1] = 1.0; x[2] = 2.0; // // trace bool trace = false; // // val_vec Vector val_vec( tape.n_val() ); for(addr_t i = 0; i < n_ind; ++i) val_vec[i] = x[i]; tape.eval(trace, val_vec); // // ok ok &= tape.n_op() == 8; ok &= tape.con_vec().size() == 3; ok &= tape.arg_vec().size() == 3 + 1 + 3 * 2 + 3; ok &= tape.n_val() == n_ind + 3 + 3; // // y Vector y(3); for(size_t i = 0; i < 3; ++i) y[i] = val_vec[ dep_vec[i] ]; // // ok ok &= y[0] == 4.0; ok &= y[1] == 4.0; ok &= y[2] == x[2]; // return ok; } // END_C++ ================================================ FILE: xrst/base_require/base_example.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin base_example} Example AD Base Types That are not AD ################################################ Contents ******** {xrst_toc_list example/general/base_alloc.hpp example/general/base_require.cpp include/cppad/example/base_adolc.hpp include/cppad/example/valvector/class.hpp include/cppad/core/base_float.hpp include/cppad/core/base_double.hpp include/cppad/core/base_complex.hpp } {xrst_end base_example} ================================================ FILE: xrst/base_require/base_identical.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- /* {xrst_begin base_identical} {xrst_spell adolc } Base Type Requirements for Identically Equal Comparisons ######################################################## EqualOpSeq ********** If function :ref:`EqualOpSeq-name` is used with arguments of type ``AD`` < *Base* > , the type *Base* must support the syntax *b* = ``CppAD::EqualOpSeq`` ( *u* , *v* ) This should return true if and only if *u* is identically equal to *v* and it makes no different which one is used. The arguments *u* and *v* have prototype | |tab| ``const`` *Base* & *u* | |tab| ``const`` *Base* & *v* The return value *b* has prototype ``bool`` *b* The Simple Case =============== If *Base* is a relatively simple type, the ``EqualOpSeq`` function can be defined by | ``namespace CppAD`` { | |tab| ``inline`` *Base* ``EqualOpSeq`` ( ``const`` *Base* & ``u`` , ``const`` *Base* & ``v`` ) | |tab| { ``return u`` == ``v`` ; } | } For example, see :ref:`base_alloc` . More Complicated Case ===================== The variables *u* and *v* are not identically equal in the following case: #. The type *Base* is ``AD`` . #. The following assignment made using type *Base* : *x* [0] = *x* [1] = 1. , #. The :ref:`independent-name` operations is used to make *x* the independent variable vector, #. During the corresponding recording, *u* = *x* [0] , *v* = *x* [1] . Note that during a future :ref:`Forward-name` calculation, *u* and *v* could correspond to different values. For example, see :ref:`adolc EqualOpSeq` . Identical ********* IdenticalCon ============ A *Base* object is a :ref:`glossary@Parameter@Constant` parameter when used in an ``AD`` < *Base* > operation sequence. It is however still possible for a parameter to change its value; e.g., see the more complicated case above. Prototypes ========== The argument *u* has prototype ``const`` *Base* *u* If it is present, the argument *v* has prototype ``const`` *Base* *v* The result *b* has prototype ``bool`` *b* Identical Functions =================== The type *Base* must support the following functions (in the CppAD namespace): .. list-table:: :widths: auto * - **Syntax** - **Result** * - *b* = ``IdenticalCon`` ( *u* ) - the *Base* value will always be the same * - *b* = ``IdenticalZero`` ( *u* ) - *u* equals zero and ``IdenticalCon`` ( *u* ) * - *b* = ``IdenticalOne`` ( *u* ) - *u* equals one and ``IdenticalCon`` ( *u* ) * - *b* = ``IdenticalEqualCon`` ( *u* , *v* ) - *u* equals *v* , ``IdenticalCon`` ( *u* ) and ``IdenticalCon`` ( *v* ) Examples ======== See :ref:`base_alloc` . {xrst_end base_identical} */ ================================================ FILE: xrst/base_require/base_member.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-23 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin base_member} Required Base Class Member Functions #################################### Notation ******** .. csv-table:: :widths: auto **Symbol**,**Meaning** *Base*,The base type corresponding to ``AD`` < *Base* > *b*,An object of type ``bool`` *i*,An object of type ``int`` *s*,An object of type ``size_t`` *d*,An object of type ``double`` *x*,An object of type ``const`` *Base* & *y*,An object of type ``const`` *Base* & *z*,An object of type *Base* Constructors ************ Default ======= *Base z* int === *Base z* ( *i* ) size_t ====== *Base z* ( *s* ) double ====== *Base z* ( *d* ) Copy ==== *Base z* ( *x* ) Unary Operators *************** For ``op`` equal to ``+`` , ``-`` the following operation must be supported: *z* = *op* *x* Assignment Operators ******************** For *op* equal to = , ``+=`` , ``-=`` , ``*=`` , and ``/=`` the following operation must be supported: *z* *op* *x* Binary Operators **************** For *op* equal to ``+`` , ``-`` , ``*`` , and ``/`` the following operation must be supported: *z* = *x* *op* *y* Bool Operators ************** For *op* equal to ``==`` , ``!=`` , the following operation must be supported: *b* = *x* *op* *y* Example ******* See the heading Class Definition in :ref:`base_alloc` . {xrst_end base_member} ================================================ FILE: xrst/base_require/base_ordered.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- /* {xrst_begin base_ordered} {xrst_spell geq } Base Type Requirements for Ordered Comparisons ############################################## Purpose ******* The following operations (in the CppAD namespace) are required to use the type ``AD`` < *Base* > : .. list-table:: :widths: auto * - **Syntax** - **Result** * - *b* = ``GreaterThanZero`` ( *x* ) - :math:`x > 0` * - *b* = ``GreaterThanOrZero`` ( *x* ) - :math:`x \geq 0` * - *b* = ``LessThanZero`` ( *x* ) - :math:`x < 0` * - *b* = ``LessThanOrZero`` ( *x* ) - :math:`x \leq 0` * - *b* = ``abs_geq`` ( *x* , *y* ) - :math:`|x| \geq |y|`. where the arguments and return value have the prototypes | |tab| ``const`` *Base* & *x* | |tab| ``const`` *Base* & *y* | |tab| ``bool`` *b* Ordered Type ************ If the type *Base* supports ordered operations, these functions should have their corresponding definitions. For example, | ``namespace CppAD`` { | |tab| ``inline bool GreaterThanZero`` ( ``const`` *Base* & ``x`` ) | |tab| { ``return`` ( ``x > 0`` ); | |tab| } | } The other functions would replace ``>`` by the corresponding operator. For example, see :ref:`base_alloc` . Not Ordered *********** If the type *Base* does not support ordering, one might (but need not) define ``GreaterThanZero`` as follows: | ``namespace CppAD`` { | |tab| ``inline bool GreaterThanZero`` ( ``const`` *Base* & ``x`` ) | |tab| { // ``attempt to use GreaterThanZero with a`` *Base* ``argument`` | |tab| |tab| ``assert`` (0); | |tab| |tab| ``return x`` ; | |tab| } | } The other functions would have the corresponding definition. For example, see :ref:`complex Ordered` . Special Requirements ******************** The following are special requirements when there is no ordered comparison for the base type: Independent =========== :ref:`Independent@record_compare` must be false in the call to ``Independent`` . Optimize ======== The :ref:`optimize@options@no_conditional_skip` option must be present when ``optimize`` is used. PrintFor ======== The :ref:`PrintFor-name` operator cannot be used. {xrst_end base_ordered} */ ================================================ FILE: xrst/det_33_hpp.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin det_33.hpp} Source: det_33 ############## | # ``ifndef CPPAD_DET_33_HPP`` | # ``define CPPAD_DET_33_HPP`` {xrst_literal include/cppad/speed/det_33.hpp // BEGIN C++ // END C++ } ``# endif`` {xrst_end det_33.hpp} ================================================ FILE: xrst/det_by_lu_hpp.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin det_by_lu.hpp} Source: det_by_lu ################# | # ``ifndef CPPAD_DET_BY_LU_HPP`` | # ``define CPPAD_DET_BY_LU_HPP`` {xrst_literal include/cppad/speed/det_by_lu.hpp // BEGIN C++ // END C++ } ``# endif`` {xrst_end det_by_lu.hpp} ================================================ FILE: xrst/det_by_minor_hpp.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin det_by_minor.hpp} Source: det_by_minor #################### | # ``ifndef CPPAD_DET_BY_MINOR_HPP`` | # ``define CPPAD_DET_BY_MINOR_HPP`` {xrst_literal include/cppad/speed/det_by_minor.hpp // BEGIN C++ // END C++ } ``# endif`` {xrst_end det_by_minor.hpp} ================================================ FILE: xrst/det_grad_33_hpp.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin det_grad_33.hpp} Source: det_grad_33 ################### | # ``ifndef CPPAD_DET_GRAD_33_HPP`` | # ``define CPPAD_DET_GRAD_33_HPP`` {xrst_literal include/cppad/speed/det_grad_33.hpp // BEGIN C++ // END C++ } ``# endif`` {xrst_end det_grad_33.hpp} ================================================ FILE: xrst/det_of_minor_hpp.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin det_of_minor.hpp} Source: det_of_minor #################### | # ``ifndef CPPAD_DET_OF_MINOR_HPP`` | # ``define CPPAD_DET_OF_MINOR_HPP`` {xrst_literal include/cppad/speed/det_of_minor.hpp // BEGIN C++ // END C++ } ``# endif`` {xrst_end det_of_minor.hpp} ================================================ FILE: xrst/devel/devel.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin_parent devel dev} Developer Documentation ####################### {xrst_comment BEGIN_SORT_THIS_LINE_PLUS_4} Contents ******** {xrst_toc_table cppad_ipopt/example/ode1.xrst cppad_lib/temp_file.cpp include/cppad/configure.hpp.in include/cppad/core/ad_type.hpp include/cppad/core/atomic/four/devel/devel.xrst include/cppad/core/discrete/devel.xrst include/cppad/core/forward/devel.xrst include/cppad/core/independent/devel.xrst include/cppad/core/vec_ad/vec_ad.hpp include/cppad/local/atomic_index.hpp include/cppad/local/is_pod.hpp include/cppad/local/op_code_var.hpp include/cppad/local/optimize/optimize_run.hpp include/cppad/local/play/dyn_player.hpp include/cppad/local/pod_vector.hpp include/cppad/local/record/recorder.hpp include/cppad/local/set_get_in_parallel.hpp include/cppad/local/sparse/dev_sparse.xrst include/cppad/local/sweep/dev_sweep.xrst include/cppad/utility/xrst/dev_utility.xrst speed/dev_speed.xrst xrst/devel/dynamic.xrst xrst/devel/whats_new/2024.xrst } {xrst_comment END_SORT_THIS_LINE_MINUS_2} {xrst_end devel} # ---------------------------------------------------------------------------- {xrst_begin dev_graph dev} Developer AD Graph Documentation ################################ Contents ******** {xrst_toc_table include/cppad/local/graph/cpp_graph_itr.xrst include/cppad/local/graph/cpp_graph_op.hpp include/cppad/local/graph/json_lexer.xrst include/cppad/local/graph/json_parser.hpp include/cppad/local/graph/json_writer.hpp include/cppad/local/graph/csrc_writer.hpp cppad_lib/csrc_writer.cpp include/cppad/local/val_graph/val_graph.xrst } {xrst_end dev_graph} ================================================ FILE: xrst/devel/dynamic.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin dev_dynamic dev} Developer Documentation for Dynamic Parameters ############################################## Contents ******** {xrst_toc_table include/cppad/local/op_code_dyn.hpp } {xrst_end dev_dynamic} ================================================ FILE: xrst/devel/whats_new/2024.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin dev_2024 dev} Development Release Notes for 2024 ################################## 12-11 ***** The :ref:`pod_vector-name` template class was documented and simplified. For example, the ``pod_vector_ptr`` function was removed and the ``byte_length_`` and ``byte_capacity_`` member variables where changed to ``size_`` and ``capacity_`` which use elements (instead of bytes) for their units. 12-09 ***** The developer documentation for the variable ref:`op codes ` and :ref:`sweeps ` were improved. In addition, the corresponding source code was simplified. {xrst_end dev_2024} ================================================ FILE: xrst/example.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin Example} Examples ######## Introduction ************ This section organizes the information related to the CppAD examples. Each CppAD operation has its own specific example, for example :ref:`add.cpp-name` is an example for :ref:`addition` . Some of the examples are of a more general nature (not connected of a specific feature of CppAD). In addition, there are some utility functions that are used by the examples. get_started *********** The :ref:`get_started.cpp-name` example is a good place to start using CppAD. Running Examples **************** The :ref:`installation instructions` show how the examples can be run on your system. The CppAD Test Vector Template Class ************************************ Many of the examples use the ``CPPAD_TESTVECTOR`` preprocessor symbol ( see :ref:`testvector-name` ) to determine which :ref:`SimpleVector-name` template class is used with the examples. Contents ******** {xrst_toc_table example/get_started/get_started.cpp xrst/example_list.xrst include/cppad/core/testvector.hpp include/cppad/wno_conversion.hpp } {xrst_end Example} ================================================ FILE: xrst/example_list.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin list_all_examples} {xrst_spell acosh adolc asinh azmul bthread cexp chkpoint cholesky colpack comp conj cout ctor div dll dyn erfc expm ext interp inv ipopt jit json lp maxabs neg nso onetape openmp param pthread qp rc rcv retape rosen runge subgraph } List All (Except Deprecated) CppAD Examples ########################################### {xrst_comment omit: TestOne.cpp OneCase.cpp lu_vec_ad.cpp } .. csv-table:: :widths: auto {xrst_comment BEGIN_SORT_THIS_LINE_PLUS_1} a11c_bthread.cpp,:ref:`a11c_bthread.cpp-title` a11c_openmp.cpp,:ref:`a11c_openmp.cpp-title` a11c_pthread.cpp,:ref:`a11c_pthread.cpp-title` abort_recording.cpp,:ref:`abort_recording.cpp-title` abs_eval.cpp,:ref:`abs_eval.cpp-title` abs_eval.hpp,:ref:`abs_eval.hpp-title` abs_get_started.cpp,:ref:`abs_get_started.cpp-title` abs_min_linear.cpp,:ref:`abs_min_linear.cpp-title` abs_min_linear.hpp,:ref:`abs_min_linear.hpp-title` abs_min_quad.cpp,:ref:`abs_min_quad.cpp-title` abs_min_quad.hpp,:ref:`abs_min_quad.hpp-title` acos.cpp,:ref:`acos.cpp-title` acosh.cpp,:ref:`acosh.cpp-title` ad_assign.cpp,:ref:`ad_assign.cpp-title` ad_ctor.cpp,:ref:`ad_ctor.cpp-title` ad_fun.cpp,:ref:`ad_fun.cpp-title` ad_in_c.cpp,:ref:`ad_in_c.cpp-title` ad_input.cpp,:ref:`ad_input.cpp-title` ad_output.cpp,:ref:`ad_output.cpp-title` add.cpp,:ref:`add.cpp-title` add_eq.cpp,:ref:`add_eq.cpp-title` asin.cpp,:ref:`asin.cpp-title` asinh.cpp,:ref:`asinh.cpp-title` atan.cpp,:ref:`atan.cpp-title` atan2.cpp,:ref:`atan2.cpp-title` atanh.cpp,:ref:`atanh.cpp-title` atomic_four_dynamic.cpp,:ref:`atomic_four_dynamic.cpp-title` atomic_four_forward.cpp,:ref:`atomic_four_forward.cpp-title` atomic_four_get_started.cpp,:ref:`atomic_four_get_started.cpp-title` atomic_four_lin_ode_forward.cpp,:ref:`atomic_four_lin_ode_forward.cpp-title` atomic_four_lin_ode_rev_depend.cpp,:ref:`atomic_four_lin_ode_rev_depend.cpp-title` atomic_four_lin_ode_reverse.cpp,:ref:`atomic_four_lin_ode_reverse.cpp-title` atomic_four_lin_ode_sparsity.cpp,:ref:`atomic_four_lin_ode_sparsity.cpp-title` atomic_four_mat_mul_forward.cpp,:ref:`atomic_four_mat_mul_forward.cpp-title` atomic_four_mat_mul_identical_zero.cpp,:ref:`atomic_four_mat_mul_identical_zero.cpp-title` atomic_four_mat_mul_rev_depend.cpp,:ref:`atomic_four_mat_mul_rev_depend.cpp-title` atomic_four_mat_mul_reverse.cpp,:ref:`atomic_four_mat_mul_reverse.cpp-title` atomic_four_mat_mul_sparsity.cpp,:ref:`atomic_four_mat_mul_sparsity.cpp-title` atomic_four_norm_sq.cpp,:ref:`atomic_four_norm_sq.cpp-title` atomic_four_vector.hpp,:ref:`atomic_four_vector.hpp-title` atomic_four_vector_add.cpp,:ref:`atomic_four_vector_add.cpp-title` atomic_four_vector_add_op.hpp,:ref:`atomic_four_vector_add_op.hpp-title` atomic_four_vector_div.cpp,:ref:`atomic_four_vector_div.cpp-title` atomic_four_vector_div_op.hpp,:ref:`atomic_four_vector_div_op.hpp-title` atomic_four_vector_for_type.hpp,:ref:`atomic_four_vector_for_type.hpp-title` atomic_four_vector_forward_op.hpp,:ref:`atomic_four_vector_forward_op.hpp-title` atomic_four_vector_hes_sparsity.cpp,:ref:`atomic_four_vector_hes_sparsity.cpp-title` atomic_four_vector_hes_sparsity.hpp,:ref:`atomic_four_vector_hes_sparsity.hpp-title` atomic_four_vector_jac_sparsity.cpp,:ref:`atomic_four_vector_jac_sparsity.cpp-title` atomic_four_vector_jac_sparsity.hpp,:ref:`atomic_four_vector_jac_sparsity.hpp-title` atomic_four_vector_mul.cpp,:ref:`atomic_four_vector_mul.cpp-title` atomic_four_vector_mul_op.hpp,:ref:`atomic_four_vector_mul_op.hpp-title` atomic_four_vector_neg.cpp,:ref:`atomic_four_vector_neg.cpp-title` atomic_four_vector_neg_op.hpp,:ref:`atomic_four_vector_neg_op.hpp-title` atomic_four_vector_rev_depend.cpp,:ref:`atomic_four_vector_rev_depend.cpp-title` atomic_four_vector_rev_depend.hpp,:ref:`atomic_four_vector_rev_depend.hpp-title` atomic_four_vector_reverse_op.hpp,:ref:`atomic_four_vector_reverse_op.hpp-title` atomic_four_vector_sub.cpp,:ref:`atomic_four_vector_sub.cpp-title` atomic_four_vector_sub_op.hpp,:ref:`atomic_four_vector_sub_op.hpp-title` atomic_three_base2ad.cpp,:ref:`atomic_three_base2ad.cpp-title` atomic_three_dynamic.cpp,:ref:`atomic_three_dynamic.cpp-title` atomic_three_forward.cpp,:ref:`atomic_three_forward.cpp-title` atomic_three_get_started.cpp,:ref:`atomic_three_get_started.cpp-title` atomic_three_hes_sparsity.cpp,:ref:`atomic_three_hes_sparsity.cpp-title` atomic_three_jac_sparsity.cpp,:ref:`atomic_three_jac_sparsity.cpp-title` atomic_three_mat_mul.cpp,:ref:`atomic_three_mat_mul.cpp-title` atomic_three_mat_mul.hpp,:ref:`atomic_three_mat_mul.hpp-title` atomic_three_norm_sq.cpp,:ref:`atomic_three_norm_sq.cpp-title` atomic_three_reciprocal.cpp,:ref:`atomic_three_reciprocal.cpp-title` atomic_three_rev_depend.cpp,:ref:`atomic_three_rev_depend.cpp-title` atomic_three_reverse.cpp,:ref:`atomic_three_reverse.cpp-title` atomic_three_tangent.cpp,:ref:`atomic_three_tangent.cpp-title` atomic_two_eigen_cholesky.cpp,:ref:`atomic_two_eigen_cholesky.cpp-title` atomic_two_eigen_cholesky.hpp,:ref:`atomic_two_eigen_cholesky.hpp-title` atomic_two_eigen_mat_inv.cpp,:ref:`atomic_two_eigen_mat_inv.cpp-title` atomic_two_eigen_mat_inv.hpp,:ref:`atomic_two_eigen_mat_inv.hpp-title` atomic_two_eigen_mat_mul.cpp,:ref:`atomic_two_eigen_mat_mul.cpp-title` atomic_two_eigen_mat_mul.hpp,:ref:`atomic_two_eigen_mat_mul.hpp-title` azmul.cpp,:ref:`azmul.cpp-title` base2ad.cpp,:ref:`base2ad.cpp-title` base2vec_ad.cpp,:ref:`base2vec_ad.cpp-title` base_adolc.hpp,:ref:`base_adolc.hpp-title` base_alloc.hpp,:ref:`base_alloc.hpp-title` base_complex.hpp,:ref:`base_complex.hpp-title` base_require.cpp,:ref:`base_require.cpp-title` bender_quad.cpp,:ref:`bender_quad.cpp-title` bool_fun.cpp,:ref:`bool_fun.cpp-title` bthread_get_started.cpp,:ref:`bthread_get_started.cpp-title` capacity_order.cpp,:ref:`capacity_order.cpp-title` change_param.cpp,:ref:`change_param.cpp-title` check_for_nan.cpp,:ref:`check_for_nan.cpp-title` check_numeric_type.cpp,:ref:`check_numeric_type.cpp-title` check_simple_vector.cpp,:ref:`check_simple_vector.cpp-title` chkpoint_two_base2ad.cpp,:ref:`chkpoint_two_base2ad.cpp-title` chkpoint_two_compare.cpp,:ref:`chkpoint_two_compare.cpp-title` chkpoint_two_dynamic.cpp,:ref:`chkpoint_two_dynamic.cpp-title` chkpoint_two_get_started.cpp,:ref:`chkpoint_two_get_started.cpp-title` chkpoint_two_ode.cpp,:ref:`chkpoint_two_ode.cpp-title` code_gen_fun_file.cpp,:ref:`code_gen_fun_file.cpp-title` code_gen_fun_function.cpp,:ref:`code_gen_fun_function.cpp-title` code_gen_fun_jac_as_fun.cpp,:ref:`code_gen_fun_jac_as_fun.cpp-title` code_gen_fun_jacobian.cpp,:ref:`code_gen_fun_jacobian.cpp-title` code_gen_fun_sparse_jac_as_fun.cpp,:ref:`code_gen_fun_sparse_jac_as_fun.cpp-title` code_gen_fun_sparse_jacobian.cpp,:ref:`code_gen_fun_sparse_jacobian.cpp-title` colpack_hes.cpp,:ref:`colpack_hes.cpp-title` colpack_hessian.cpp,:ref:`colpack_hessian.cpp-title` colpack_jac.cpp,:ref:`colpack_jac.cpp-title` colpack_jacobian.cpp,:ref:`colpack_jacobian.cpp-title` compare.cpp,:ref:`compare.cpp-title` compare_change.cpp,:ref:`compare_change.cpp-title` complex_poly.cpp,:ref:`complex_poly.cpp-title` con_dyn_var.cpp,:ref:`con_dyn_var.cpp-title` cond_exp.cpp,:ref:`cond_exp.cpp-title` conj_grad.cpp,:ref:`conj_grad.cpp-title` cos.cpp,:ref:`cos.cpp-title` cosh.cpp,:ref:`cosh.cpp-title` cppad_eigen.hpp,:ref:`cppad_eigen.hpp-title` cppad_vector.cpp,:ref:`cppad_vector.cpp-title` dependency.cpp,:ref:`dependency.cpp-title` det_by_lu.cpp,:ref:`det_by_lu.cpp-title` det_by_minor.cpp,:ref:`det_by_minor.cpp-title` det_of_minor.cpp,:ref:`det_of_minor.cpp-title` div.cpp,:ref:`div.cpp-title` div_eq.cpp,:ref:`div_eq.cpp-title` dll_lib.cpp,:ref:`dll_lib.cpp-title` eigen_array.cpp,:ref:`eigen_array.cpp-title` eigen_det.cpp,:ref:`eigen_det.cpp-title` elapsed_seconds.cpp,:ref:`elapsed_seconds.cpp-title` equal_op_seq.cpp,:ref:`equal_op_seq.cpp-title` erf.cpp,:ref:`erf.cpp-title` erfc.cpp,:ref:`erfc.cpp-title` error_handler.cpp,:ref:`error_handler.cpp-title` exp.cpp,:ref:`exp.cpp-title` expm1.cpp,:ref:`expm1.cpp-title` expm1.cpp,:ref:`expm1.cpp-title` fabs.cpp,:ref:`fabs.cpp-title` for_hes_sparsity.cpp,:ref:`for_hes_sparsity.cpp-title` for_jac_sparsity.cpp,:ref:`for_jac_sparsity.cpp-title` for_one.cpp,:ref:`for_one.cpp-title` for_sparse_hes.cpp,:ref:`for_sparse_hes.cpp-title` for_sparse_jac.cpp,:ref:`for_sparse_jac.cpp-title` for_two.cpp,:ref:`for_two.cpp-title` forward.cpp,:ref:`forward.cpp-title` forward_dir.cpp,:ref:`forward_dir.cpp-title` forward_order.cpp,:ref:`forward_order.cpp-title` from_json.cpp,:ref:`from_json.cpp-title` fun_assign.cpp,:ref:`fun_assign.cpp-title` fun_check.cpp,:ref:`fun_check.cpp-title` fun_property.cpp,:ref:`fun_property.cpp-title` function_name.cpp,:ref:`function_name.cpp-title` general.cpp,:ref:`general.cpp-title` get_started.cpp,:ref:`get_started.cpp-title` graph_add_op.cpp,:ref:`graph_add_op.cpp-title` graph_atom4_op.cpp,:ref:`graph_atom4_op.cpp-title` graph_atom_op.cpp,:ref:`graph_atom_op.cpp-title` graph_azmul_op.cpp,:ref:`graph_azmul_op.cpp-title` graph_cexp_op.cpp,:ref:`graph_cexp_op.cpp-title` graph_comp_op.cpp,:ref:`graph_comp_op.cpp-title` graph_discrete_op.cpp,:ref:`graph_discrete_op.cpp-title` graph_div_op.cpp,:ref:`graph_div_op.cpp-title` graph_mul_op.cpp,:ref:`graph_mul_op.cpp-title` graph_pow_op.cpp,:ref:`graph_pow_op.cpp-title` graph_print_op.cpp,:ref:`graph_print_op.cpp-title` graph_sub_op.cpp,:ref:`graph_sub_op.cpp-title` graph_sum_op.cpp,:ref:`graph_sum_op.cpp-title` graph_unary_op.cpp,:ref:`graph_unary_op.cpp-title` harmonic.cpp,:ref:`harmonic.cpp-title` hes_lagrangian.cpp,:ref:`hes_lagrangian.cpp-title` hes_lu_det.cpp,:ref:`hes_lu_det.cpp-title` hes_minor_det.cpp,:ref:`hes_minor_det.cpp-title` hes_times_dir.cpp,:ref:`hes_times_dir.cpp-title` hessian.cpp,:ref:`hessian.cpp-title` independent.cpp,:ref:`independent.cpp-title` index_sort.cpp,:ref:`index_sort.cpp-title` integer.cpp,:ref:`integer.cpp-title` interface2c.cpp,:ref:`interface2c.cpp-title` interp_onetape.cpp,:ref:`interp_onetape.cpp-title` interp_retape.cpp,:ref:`interp_retape.cpp-title` ipopt_solve_get_started.cpp,:ref:`ipopt_solve_get_started.cpp-title` ipopt_solve_ode_inverse.cpp,:ref:`ipopt_solve_ode_inverse.cpp-title` ipopt_solve_retape.cpp,:ref:`ipopt_solve_retape.cpp-title` jac_lu_det.cpp,:ref:`jac_lu_det.cpp-title` jac_minor_det.cpp,:ref:`jac_minor_det.cpp-title` jacobian.cpp,:ref:`jacobian.cpp-title` jit_atomic.cpp,:ref:`jit_atomic.cpp-title` jit_compare_change.cpp,:ref:`jit_compare_change.cpp-title` jit_compile.cpp,:ref:`jit_compile.cpp-title` jit_dynamic.cpp,:ref:`jit_dynamic.cpp-title` jit_get_started.cpp,:ref:`jit_get_started.cpp-title` json_add_op.cpp,:ref:`json_add_op.cpp-title` json_atom4_op.cpp,:ref:`json_atom4_op.cpp-title` json_atom_op.cpp,:ref:`json_atom_op.cpp-title` json_azmul_op.cpp,:ref:`json_azmul_op.cpp-title` json_cexp_op.cpp,:ref:`json_cexp_op.cpp-title` json_comp_op.cpp,:ref:`json_comp_op.cpp-title` json_discrete_op.cpp,:ref:`json_discrete_op.cpp-title` json_div_op.cpp,:ref:`json_div_op.cpp-title` json_get_started.cpp,:ref:`json_get_started.cpp-title` json_mul_op.cpp,:ref:`json_mul_op.cpp-title` json_pow_op.cpp,:ref:`json_pow_op.cpp-title` json_print_op.cpp,:ref:`json_print_op.cpp-title` json_sparse.cpp,:ref:`json_sparse.cpp-title` json_sub_op.cpp,:ref:`json_sub_op.cpp-title` json_sum_op.cpp,:ref:`json_sum_op.cpp-title` json_unary_op.cpp,:ref:`json_unary_op.cpp-title` log.cpp,:ref:`log.cpp-title` log10.cpp,:ref:`log10.cpp-title` log1p.cpp,:ref:`log1p.cpp-title` lp_box.cpp,:ref:`lp_box.cpp-title` lp_box.hpp,:ref:`lp_box.hpp-title` lu_factor.cpp,:ref:`lu_factor.cpp-title` lu_invert.cpp,:ref:`lu_invert.cpp-title` lu_ratio.cpp,:ref:`lu_ratio.cpp-title` lu_solve.cpp,:ref:`lu_solve.cpp-title` lu_vec_ad_ok.cpp,:ref:`lu_vec_ad_ok.cpp-title` mat_sum_sq.cpp,:ref:`mat_sum_sq.cpp-title` min_nso_linear.cpp,:ref:`min_nso_linear.cpp-title` min_nso_linear.hpp,:ref:`min_nso_linear.hpp-title` min_nso_quad.cpp,:ref:`min_nso_quad.cpp-title` min_nso_quad.hpp,:ref:`min_nso_quad.hpp-title` mul.cpp,:ref:`mul.cpp-title` mul_eq.cpp,:ref:`mul_eq.cpp-title` mul_level.cpp,:ref:`mul_level.cpp-title` mul_level_adolc.cpp,:ref:`mul_level_adolc.cpp-title` mul_level_adolc_ode.cpp,:ref:`mul_level_adolc_ode.cpp-title` mul_level_ode.cpp,:ref:`mul_level_ode.cpp-title` multi_atomic_three.cpp,:ref:`multi_atomic_three.cpp-title` multi_atomic_two.cpp,:ref:`multi_atomic_two.cpp-title` multi_chkpoint_one.cpp,:ref:`multi_chkpoint_one.cpp-title` multi_chkpoint_two.cpp,:ref:`multi_chkpoint_two.cpp-title` multi_newton.cpp,:ref:`multi_newton.cpp-title` nan.cpp,:ref:`nan.cpp-title` near_equal.cpp,:ref:`near_equal.cpp-title` near_equal_ext.cpp,:ref:`near_equal_ext.cpp-title` new_dynamic.cpp,:ref:`new_dynamic.cpp-title` num_limits.cpp,:ref:`num_limits.cpp-title` number_skip.cpp,:ref:`number_skip.cpp-title` numeric_type.cpp,:ref:`numeric_type.cpp-title` ode_err_control.cpp,:ref:`ode_err_control.cpp-title` ode_err_maxabs.cpp,:ref:`ode_err_maxabs.cpp-title` ode_evaluate.cpp,:ref:`ode_evaluate.cpp-title` ode_gear.cpp,:ref:`ode_gear.cpp-title` ode_gear_control.cpp,:ref:`ode_gear_control.cpp-title` ode_stiff.cpp,:ref:`ode_stiff.cpp-title` openmp_get_started.cpp,:ref:`openmp_get_started.cpp-title` opt_val_hes.cpp,:ref:`opt_val_hes.cpp-title` optimize_compare_op.cpp,:ref:`optimize_compare_op.cpp-title` optimize_conditional_skip.cpp,:ref:`optimize_conditional_skip.cpp-title` optimize_cumulative_sum.cpp,:ref:`optimize_cumulative_sum.cpp-title` optimize_forward_active.cpp,:ref:`optimize_forward_active.cpp-title` optimize_nest_conditional.cpp,:ref:`optimize_nest_conditional.cpp-title` optimize_print_for.cpp,:ref:`optimize_print_for.cpp-title` optimize_reverse_active.cpp,:ref:`optimize_reverse_active.cpp-title` optimize_twice.cpp,:ref:`optimize_twice.cpp-title` poly.cpp,:ref:`poly.cpp-title` pow.cpp,:ref:`pow.cpp-title` pow_int.cpp,:ref:`pow_int.cpp-title` pow_nan.cpp,:ref:`pow_nan.cpp-title` print_for_cout.cpp,:ref:`print_for_cout.cpp-title` print_for_string.cpp,:ref:`print_for_string.cpp-title` print_graph.cpp,:ref:`print_graph.cpp-title` pthread_get_started.cpp,:ref:`pthread_get_started.cpp-title` qp_box.cpp,:ref:`qp_box.cpp-title` qp_box.hpp,:ref:`qp_box.hpp-title` qp_interior.cpp,:ref:`qp_interior.cpp-title` qp_interior.hpp,:ref:`qp_interior.hpp-title` rc_sparsity.cpp,:ref:`rc_sparsity.cpp-title` rev_checkpoint.cpp,:ref:`rev_checkpoint.cpp-title` rev_hes_sparsity.cpp,:ref:`rev_hes_sparsity.cpp-title` rev_jac_sparsity.cpp,:ref:`rev_jac_sparsity.cpp-title` rev_one.cpp,:ref:`rev_one.cpp-title` rev_sparse_hes.cpp,:ref:`rev_sparse_hes.cpp-title` rev_sparse_jac.cpp,:ref:`rev_sparse_jac.cpp-title` rev_two.cpp,:ref:`rev_two.cpp-title` reverse_one.cpp,:ref:`reverse_one.cpp-title` reverse_three.cpp,:ref:`reverse_three.cpp-title` reverse_two.cpp,:ref:`reverse_two.cpp-title` romberg_mul.cpp,:ref:`romberg_mul.cpp-title` romberg_one.cpp,:ref:`romberg_one.cpp-title` rosen_34.cpp,:ref:`rosen_34.cpp-title` runge45_1.cpp,:ref:`runge45_1.cpp-title` runge_45.cpp,:ref:`runge_45.cpp-title` set_union.cpp,:ref:`set_union.cpp-title` simple_vector.cpp,:ref:`simple_vector.cpp-title` simplex_method.cpp,:ref:`simplex_method.cpp-title` simplex_method.hpp,:ref:`simplex_method.hpp-title` sin.cpp,:ref:`sin.cpp-title` sinh.cpp,:ref:`sinh.cpp-title` sparse2eigen.cpp,:ref:`sparse2eigen.cpp-title` sparse_hes.cpp,:ref:`sparse_hes.cpp-title` sparse_hes_fun.cpp,:ref:`sparse_hes_fun.cpp-title` sparse_hessian.cpp,:ref:`sparse_hessian.cpp-title` sparse_jac_for.cpp,:ref:`sparse_jac_for.cpp-title` sparse_jac_fun.cpp,:ref:`sparse_jac_fun.cpp-title` sparse_jac_rev.cpp,:ref:`sparse_jac_rev.cpp-title` sparse_jacobian.cpp,:ref:`sparse_jacobian.cpp-title` sparse_rc.cpp,:ref:`sparse_rc.cpp-title` sparse_rcv.cpp,:ref:`sparse_rcv.cpp-title` sparse_sub_hes.cpp,:ref:`sparse_sub_hes.cpp-title` sparsity_sub.cpp,:ref:`sparsity_sub.cpp-title` speed_example.cpp,:ref:`speed_example.cpp-title` speed_program.cpp,:ref:`speed_program.cpp-title` speed_test.cpp,:ref:`speed_test.cpp-title` sqrt.cpp,:ref:`sqrt.cpp-title` stack_machine.cpp,:ref:`stack_machine.cpp-title` sub.cpp,:ref:`sub.cpp-title` sub_eq.cpp,:ref:`sub_eq.cpp-title` sub_sparse_hes.cpp,:ref:`sub_sparse_hes.cpp-title` subgraph_hes2jac.cpp,:ref:`subgraph_hes2jac.cpp-title` subgraph_jac_rev.cpp,:ref:`subgraph_jac_rev.cpp-title` subgraph_reverse.cpp,:ref:`subgraph_reverse.cpp-title` subgraph_sparsity.cpp,:ref:`subgraph_sparsity.cpp-title` switch_var_dyn.cpp,:ref:`switch_var_dyn.cpp-title` tan.cpp,:ref:`tan.cpp-title` tanh.cpp,:ref:`tanh.cpp-title` tape_index.cpp,:ref:`tape_index.cpp-title` taylor_ode.cpp,:ref:`taylor_ode.cpp-title` team_bthread.cpp,:ref:`team_bthread.cpp-title` team_example.cpp,:ref:`team_example.cpp-title` team_openmp.cpp,:ref:`team_openmp.cpp-title` team_pthread.cpp,:ref:`team_pthread.cpp-title` team_thread.hpp,:ref:`team_thread.hpp-title` thread_alloc.cpp,:ref:`thread_alloc.cpp-title` thread_test.cpp,:ref:`thread_test.cpp-title` time_test.cpp,:ref:`time_test.cpp-title` to_json.cpp,:ref:`to_json.cpp-title` to_string.cpp,:ref:`to_string.cpp-title` unary_minus.cpp,:ref:`unary_minus.cpp-title` unary_plus.cpp,:ref:`unary_plus.cpp-title` value.cpp,:ref:`value.cpp-title` var2par.cpp,:ref:`var2par.cpp-title` vec_ad.cpp,:ref:`vec_ad.cpp-title` vector_bool.cpp,:ref:`vector_bool.cpp-title` {xrst_comment END_SORT_THIS_LINE_MINUS_1} {xrst_end list_all_examples} ----------------------------------------------------------------------------- {xrst_begin General} General Examples ################ Description *********** Most of the examples in CppAD are part of the documentation for a specific feature; for example, :ref:`add.cpp-name` is an example using the :ref:`addition operator` . The examples list in this section are of a more general nature. Contents ******** {xrst_toc_table example/general/ad_fun.cpp example/general/ad_in_c.cpp example/sparse/conj_grad.cpp include/cppad/example/cppad_eigen.hpp example/general/hes_minor_det.cpp example/general/hes_lu_det.cpp example/general/interface2c.cpp example/general/jac_minor_det.cpp example/general/jac_lu_det.cpp xrst/mul_level.xrst example/general/ode_stiff.cpp example/general/mul_level_ode.cpp example/general/mul_level_adolc_ode.cpp example/general/stack_machine.cpp cppad_lib/code_gen_fun.cpp } {xrst_end General} ----------------------------------------------------------------------------- {xrst_begin example_driver} Utility Routines used by CppAD Examples ####################################### Contents ******** {xrst_comment BEGIN_SORT_THIS_LINE_PLUS_2} {xrst_toc_table example/abs_normal/abs_normal.cpp example/atomic_four/atomic_four.cpp example/atomic_three/atomic_three.cpp example/atomic_two/atomic_two.cpp example/chkpoint_two/chkpoint_two.cpp example/cppad_code_gen/cppad_code_gen.cpp example/general/general.cpp example/general/lu_vec_ad.cpp example/graph/graph.cpp example/ipopt_solve/ipopt_solve.cpp example/jit/jit.cpp example/json/json.cpp example/optimize/optimize.cpp example/sparse/sparse.cpp example/utility/utility.cpp example/valvector/valvector.cpp speed/example/example.cpp } {xrst_comment END_SORT_THIS_LINE_MINUS_2} {xrst_end example_driver} ----------------------------------------------------------------------------- ================================================ FILE: xrst/install/adolc.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin adolc} {xrst_spell bashrc cygdrive } Including Adolc Examples and Tests ################################## Adolc Home Page *************** https://github.com/coin-or/ADOL-C Purpose ******* CppAD includes examples and tests that can use the AD package Adolc. The includes speed comparison with other AD packages; see :ref:`speed_adolc-name` . It also includes examples that combine Adolc with CppAD; see .. csv-table:: :widths: auto base_adolc.hpp,:ref:`base_adolc.hpp-title` mul_level_adolc.cpp,:ref:`mul_level_adolc.cpp-title` mul_level_adolc_ode.cpp,:ref:`mul_level_adolc_ode.cpp-title` include_adolc ************* If Adolc is installed on your system, you can specify ``include_adolc=true`` on the :ref:`cmake-name` command line. The value of ``PKG_CONFIG_PATH`` must be such that the command ``pkg-config adolc --path --print-errors`` finds the location of the file ``adolc.pc`` . Note that CppAD assumes adolc has been configured with its sparse matrix computations enabled; i.e, using ``--with-colpack`` = *adolc_prefix* In other words ColPack is installed and with the same prefix as ACOL-C; see :ref:`get_colpack.sh-name` . Examples ******** If you specify :ref:`cmake@include_adolc` on the cmake command line, you will be able to run the Adolc examples listed above by executing the following commands starting in the :ref:`download@Distribution Directory` : | |tab| ``cd build/example`` | |tab| ``make check_example`` If you do this, you will see an indication that the examples ``mul_level_adolc`` and ``mul_level_adolc_ode`` have passed their correctness check. Speed Tests *********** If you include *adolc_prefix* on the :ref:`cmake-name` command line, you will be able to run the Adolc speed correctness tests by executing the following commands starting in the :ref:`download@Distribution Directory` : | |tab| ``cd build/speed/adolc`` | |tab| ``make check_speed_adolc`` After executing ``make check_speed_adolc`` , you can run a specific Adolc speed tests by executing the command ``./speed_adolc`` ; see :ref:`speed_main-name` for the meaning of the command line options to this program. Unix **** If you are using Unix, you may have to add *adolc_prefix* to ``LD_LIBRARY_PATH`` . For example, if you use the ``bash`` shell to run your programs, you could include | |tab| ``LD_LIBRARY_PATH`` = *adolc_prefix* / ``lib:$`` { ``LD_LIBRARY_PATH`` } | |tab| ``export LD_LIBRARY_PATH`` in your ``$HOME/.bashrc`` file. Cygwin ****** If you are using Cygwin, you may have to add to following lines to the file ``.bashrc`` in your home directory: | |tab| ``PATH`` = *adolc_prefix* / ``bin:$`` { ``PATH`` } | |tab| ``export PATH`` in order for Adolc to run properly. If *adolc_prefix* begins with a disk specification, you must use the Cygwin format for the disk specification. For example, if ``d:/adolc_base`` is the proper directory, ``/cygdrive/d/adolc_base`` should be used for *adolc_prefix* . get_adolc ********* If you are using Unix, you can download and install a copy of Adolc using :ref:`get_adolc.sh-name` . The corresponding install prefix is ``build/prefix`` . {xrst_end adolc} ================================================ FILE: xrst/install/cmake.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin cmake} {xrst_spell ansi autotools colpack cstdint cxx datadir dev dlls docdir dylib exe ext fadbad gprof grep hsc includedirs libcppad libdirs makefile makefiles mingw mkdir msys pacman profiler sacado sed testvector txt uint usr vcvarsall wshadow yyyy yyyymmdd } Using CMake to Configure CppAD ############################## The CMake Program ***************** The cmake program enables one to create a single set of scripts, called ``CMakeLists.txt`` , that can be used to test and install a program on Unix, Microsoft, or Apple operating systems; see `cmake resources `_ . Build Directory *************** Create ``build`` , a subdirectory of the :ref:`download@Distribution Directory` , change into the ``build`` . Eigen ***** The cmake command will automatically search for the eigen package. If it is found, the corresponding examples will be tested and the :ref:`sparse2eigen-name` utility will be installed; see :ref:`eigen-name` . The results of cppad's search for Eigen are reported in the output of the cmake command. CMake Command ************* Simple ====== The simplest version of the ``cmake`` command is ``cmake ..`` This assumes that you are starting in the ``build`` directory (so that ``..`` refers to the distribution directory). The command also assumes that ``cmake`` is in your execution path with version greater than or equal 2.8. If not, you can put the path to the version of ``cmake`` in font the command. It is suggested that you should start with the simple version of the cmake command above and then add options one at a time. Warning ======= Version 3.10.2 of cmake will generates the following warning if boost is not present your system: | ``CMake Warning at /usr/share/cmake-3.10/Modules/FindBoost.cmake:567`` ( ``message`` ): | |tab| ``Imported targets and dependency information not available for Boost version`` | |tab| ( ``all versions older than 1.33`` ) | |tab| ... This is not a problem because CppAD will automatically exclude the examples and tests that use boost. Newer version of cmake; e.g., 3.13.4, do not generate this warning (when boost is not present on your system). true_or_false ============= The syntax ``-D`` *name* = *true_or_false* , mean that the variable *name* can be assigned the value ``true`` or ``false`` . Options ======= The full version of the command, with all its optional arguments is: | ``cmake`` \\ | |tab| ``-D CMAKE_VERBOSE_MAKEFILE`` = *cmake_verbose_makefile* \\ | |tab| ``-D CMAKE_BUILD_TYPE`` = *cmake_build_type* \\ | |tab| ``-G`` *generator* \\ | |tab| \\ | |tab| ``-D cppad_prefix`` = *cppad_prefix* \\ | |tab| ``-D cppad_postfix`` = *cppad_postfix* \\ | |tab| \\ | |tab| ``-D cmake_install_includedirs`` = *cmake_install_includedirs* \\ | |tab| ``-D cmake_install_libdirs`` = *cmake_install_libdirs* \\ | |tab| \\ | |tab| ``-D cmake_install_datadir`` = *cmake_install_datadir* \\ | |tab| ``-D cmake_install_docdir`` = *cmake_install_docdir* \\ | |tab| \\ | |tab| ``-D include_doc`` = *true_or_false* \\ | |tab| ``-D cmake_defined_ok`` = *true_or_false* \\ | |tab| ``-D cppad_static_lib`` = *true_or_false* \\ | |tab| ``-D cppad_debug_and_release`` = *true_or_false* \\ | |tab| \\ | |tab| ``-D include_adolc`` = *true_or_false* \\ | |tab| ``-D include_ipopt`` = *true_or_false* \\ | |tab| ``-D include_cppadcg`` = *true_or_false* \\ | |tab| \\ | |tab| ``-D colpack_prefix`` = *colpack_prefix* \\ | |tab| ``-D fadbad_prefix`` = *fadbad_prefix* \\ | |tab| ``-D sacado_prefix`` = *sacado_prefix* \\ | |tab| \\ | |tab| ``-D cppad_cxx_flags`` = *cppad_cxx_flags* \\ | |tab| ``-D cppad_link_flags`` = *cppad_link_flags* \\ | |tab| ``-D cppad_profile_flag`` = *cppad_profile_flag* \\ | |tab| ``-D cppad_testvector`` = *cppad_testvector* \\ | |tab| ``-D cppad_max_num_threads`` = *cppad_max_num_threads* \\ | |tab| ``-D cppad_tape_id_type`` = *cppad_tape_id_type* \\ | |tab| ``-D cppad_tape_addr_type`` = *cppad_tape_addr_type* \\ | |tab| ``-D cppad_debug_which`` = *cppad_debug_which* \\ | |tab| \\ | |tab| .. msys2 ===== The `msys2 `_ system, with ``mingw-64`` and ``g++`` , the following seems to work: Use ``pacman`` to install ``make`` , ``gcc`` , and ``mingw-w64-x86_64-cmake`` . Visual Studio ============= #. Make sure you have a version of `git for windows `_ . #. Launch the Visual Studio `developer command window `_ #. At the command prompt enter :: where cmake.exe to see if ``cmake.exe`` comes with your version of Visual Studio. If not, download and install the win64-x64 version of the `cmake program `_. Make sure you select the box that adds ``cmake`` to your execution path. #. Download the CppAD repository, and make it the current directory, using the following commands {xrst_code bat} clone https://github.com/coin-or/CppAD.git cppad.git cd cppad.git {xrst_code} #. Execute the following commands: {xrst_literal bin/build.bat } Note that when using windows DLL's, CppAD builds a static version of ``cppad_lib`` . There are problems using a DLL for ``cppad_lib`` because Windows makes separate copies of static class member functions, one for library and one for the rest of the program. autotools ========= The autotools build with the Visual Studio compiler should work with the following configure and test commands :: mkdir build cd build ../configure CC=cl CXX=cl CXX_FLAGS="-DEBUG" make test An optimized versions was tested using :: ../configure CXX=cl CC=cl CXX_FLAGS="-DNDEBUG -O2 -EHsc" make test The commands above need to be run a Dos window, that has the ``vcvarsall.bat`` settings, extend path that includes the ``msys2`` bin directory, and was running the bash shell inside the Doc window. It is highly recommended that you use the ``cmake`` commands above, an not ``../configure`` , when building with Visual Studio. make check ********** Important information about the CppAD configuration is output by this command. If you have the ``grep`` program, and store the output in ``cmake.log`` , you can get a list of all the test options with the command: :: grep 'make check' cmake.log cmake_verbose_makefile ********************** This value should be either ``true`` or ``false`` . The default value, when it is not present, is ``false`` . If it is ``true`` , then the output of the ``make`` commands will include all of the files and flags used to run the compiler and linker. This can be useful for seeing how to compile and link your own applications. cmake_build_type **************** This value should be one of the valid CMake build types; e.g., ``Debug`` , ``Release`` , ``RelWithDebInfo`` , ``MinSizeRel`` . The value of this option is not case sensitive; e.g., ``Debug`` and ``debug`` yield the same result. If *cmake_build_type* is specified, :ref:`cmake@cppad_debug_which` must be the empty string. generator ********* The CMake program is capable of generating different kinds of files. Below is a table with a few of the possible files .. csv-table:: :widths: auto *generator*,Description ``"Unix Makefiles"``,make files for unix operating system ``"NMake Makefiles"``,make files for Visual Studio ``"MSYS Makefiles"``,make files for msys2 ``Ninja``, `Ninja`_ build system .. _Ninja: https://en.wikipedia.org/wiki/Ninja_(build_system) Other generator choices are available; see the cmake `generators `_ documentation. cppad_prefix ************ This is the top level absolute path below which all of the CppAD files are installed by the command ``make install`` For example, if *cppad_prefix* is ``/usr`` , *cmake_install_includedirs* is ``include`` , and *cppad_postfix* is not specified, the file ``cppad.hpp`` is installed in the location / ``usr/include/cppad/cppad.hpp`` The default value for *cppad_prefix* is the value of *CMAKE_INSTALL_PREFIX* ; see the cmake documentation. (Before 2019-10-02 the default value was ``/usr`` ; see the heading 10-02 on :ref:`2019<2019@mm-dd@10-02>` .) cppad_postfix ************* This is the bottom level relative path below which all of the CppAD files are installed. For example, if *cppad_prefix* is ``/usr`` , *cmake_install_includedirs* is ``include`` , and *cppad_postfix* is ``coin-or`` , the file ``cppad.hpp`` is installed in the location / ``usr/include/coin-or/cppad/cppad.hpp`` The default value for *cppad_postfix* is empty; i.e, there is no bottom level relative directory for the installed files. cmake_install_includedirs ************************* This is one directory, or a list of directories separated by spaces or by semi-colons. This first entry in the list is the middle level relative path below which the CppAD include files are installed. The entire list is used for searching for include files. For example, if *cppad_prefix* is ``/usr`` , *cmake_install_includedirs* is ``include`` , and *cppad_postfix* is not specified, the file ``cppad.hpp`` is installed in the location / ``usr/include/cppad/cppad.hpp`` The default value for this directory list is ``include`` . cmake_install_libdirs ********************* This is one directory, or a list of directories separated by spaces or by semi-colons. This first entry in the list is the middle level relative path below which the CppAD library files are installed. The entire list is used for searching for library files. The default value for this directory list is ``lib;lib64`` . cppad_lib ========= As an example of where the CppAD library is installed, if *cppad_prefix* is ``/usr`` , *cmake_install_libdirs* is ``lib`` , *cppad_postfix* is not specified, the CppAD library is installed in / ``usr/lib/libcppad_lib.`` *ext* . *major* . *release* Here *major* is the major library version number, *release* is the release number for this version, and *ext* is the extension for shared libraries on this system. If *yyyymmdd* is the CppAD :ref:`download@Version` number, the major CppAD library version number is *major* = *dd* ``- 1`` + 31 * ( *mm* ``- 1`` + 12 * ( *yyyy* ``- 2019`` ) ) If there is no :ref:`download@Release` for this version of CppAD, the CppAD library is installed in / ``usr/lib/libcppad_lib.`` *ext* . *major* The Mac is special, *ext* is ``dylib`` and comes at the end of the file name for that system. cmake_install_datadir ********************* This is the middle level relative path below which the CppAD data files are installed. For example, if *cppad_prefix* is ``/usr`` , *cmake_install_datadir* is ``share`` , and *cppad_postfix* is not specified, the :ref:`pkgconfig-name` file ``cppad.pc`` is installed in the location / ``usr/share/pkgconfig/cppad.pc`` The default value for *cmake_install_datadir* is ``share`` . cmake_install_docdir ******************** This is the middle level relative path below which the CppAD user documentation files are installed. For example, if *cppad_prefix* is ``/usr`` , *cmake_install_docdir* is ``share/doc`` , and *cppad_postfix* is not specified, the file ``user_guide.html`` is installed in the location / ``usr/share/doc/cppad/user_guide.html`` If *cmake_install_docdir* is not specified, or :ref:`cmake@include_doc` is false, the documentation files are not installed. cmake_defined_ok **************** The default value for this option is true. It must be true when running cmake with a non-empty ``CMakeCache.txt`` file. If it is false, some checks will be made to make sure certain variables are not defined twice. include_doc *********** The default value for this option is false. If it is true, is in the command line, the `xrst `_ program must be installed. In this case, the user and developer documentation targets ``doc_user`` and ``doc_dev`` will be available. For example, if the :ref:`cmake@generator` is ``Ninja`` the commands ``ninja doc_user`` and ``ninja doc_dev`` will build the corresponding documentation in the ``build/html`` sub-directory of the :ref:`download@Distribution Directory` . cppad_static_lib **************** The default value for this option is true on ``msys``, ``cygwin`` and ``Windows`` and false otherwise. the cppad library will be a static (shared) library. If the target system uses windows dlls, this setting should be true and if it is false, a warning will be issued and it will be ignored. cppad_debug_and_release *********************** The default value for this option is true. If it is true, the debug and release versions of CppAD can be mixed in the same program. This must be true if :ref:`cmake@cppad_debug_which` is ``debug_even`` or ``debug_odd`` . If ``NDEBUG`` is defined and *cppad_debug_and_release* is false, :ref:`thread_alloc-name` does more error checking. For programs that do a lot of memory allocation, this can take a significant amount of time. This is meant for testing CppAD and as a last resort when debugging. include_adolc ************* The default value for this option is false. If it is true, the :ref:`adolc-name` examples will be compiled and tested. In this case, ``adolc.pc`` must be in the ``PKG_CONFIG_PATH`` . include_ipopt ************* The default value for this option is false. If it is true, the :ref:`ipopt-name` examples will be compiled and tested. In this case, ``ipopt.pc`` must be in the ``PKG_CONFIG_PATH`` . In addition, :ref:`ipopt_solve-name` and :ref:`cppad_ipopt_nlp-name` will be installed. include_cppadcg *************** The default value for this option is false. If it is true, the :ref:`cppadcg-name` examples will be compiled and tested. **Warning** : Do not use this option when installing cppad because the cppadcg package depends on cppad and using this option makes cppad depend on cppadcg. This option, and the script :ref:`get_cppadcg.sh-name` are only intended for testing purposes. package_prefix ************** Each of these packages do not have ``pkg-config`` files corresponding to optional CppAD examples. If a prefix listed below is on the command line, the corresponding examples will be compiled and run: .. csv-table:: :widths: auto colpack_prefix,:ref:`colpack_prefix-title` fadbad_prefix,:ref:`fadbad_prefix-title` sacado_prefix,:ref:`sacado_prefix-title` cppad_cxx_flags *************** This specifies the compiler flags that are used when compiling the CppAD examples, tests, and library. This flags are in addition to the flags automatically generated by cmake for debug and release build; i.e., *CMAKE_CXX_FLAGS_DEBUG* and *CMAKE_CXX_FLAGS_RELEASE* . The default value for these flags is the empty string ``""`` . These flags must be valid for the C++ compiler on your system. For example, if you are using ``g++`` you could specify :: -D cppad_cxx_flags="-Wall -ansi -pedantic-errors -std=c++17 -Wshadow" C++17 ===== In order for the compiler to take advantage of features that are in C++17 ,but not in C++11, the *cppad_cxx_flags* must enable these features. cppad_link_flags **************** This specifies additional flags to use during linking. The default value for these flags is the empty string ``""`` . -m32 ==== If you are on a 64 bit machine and using g++ to compile for 32 bit code, you need to add ``-m32`` to both the additional compiler and linker flags. If ``-m32`` is in *cppad_link_flags* , the OpenMP, Boost, :ref:`jit ` , and :ref:`speed_cppad_jit-name` examples will not be tested. cppad_profile_flag ****************** This specifies an addition compiler and link flag that is used for :ref:`profiling` the speed tests. A profile version of the speed test is only build when this argument is present. If this flag is specified, you can run the gnu profiler with the following command: | |tab| ``cd build/speed/profile`` | |tab| ``make check_speed_profile`` | |tab| ./ ``speed_profile`` *test seed option_list* | |tab| ``gprof -b speed_profile`` | ``sed -f gprof.sed`` cppad_testvector **************** See :ref:`cppad_testvector-title`. cppad_max_num_threads ********************* The value *cppad_max_num_threads* must be greater than or equal to four; i.e., *max_num_threads* >= 4 . The current default value for *cppad_max_num_threads* is 48, but it may change in future versions of CppAD. The value *cppad_max_num_threads* in turn specifies the default value for the preprocessor symbol :ref:`multi_thread@CPPAD_MAX_NUM_THREADS` . cppad_tape_id_type ****************** The type *cppad_tape_id_type* is used for identifying different tapes. The valid values for this type are ``unsigned char`` , ``unsigned short int`` , ``unsigned int`` , and ``size_t`` . The smaller the value of ``sizeof`` ( *cppad_tape_id_type* ) , the less memory is used. On the other hand, the value ``std::numeric_limits<`` *cppad_tape_id_type* >:: ``max`` () must be larger than the maximum number of tapes used by one thread times :ref:`multi_thread@CPPAD_MAX_NUM_THREADS` . cstdint ======= If all of the following ``cstdint`` types are defined, they can also be used as the value of *cppad_tape_addr_type* : ``uint8_t`` , ``uint16_t`` , ``uint32_t`` , ``uint64_t`` . cppad_tape_addr_type ******************** The type *cppad_tape_addr_type* is used for address in the AD recordings (tapes). The valid values for this argument are ``unsigned char`` , ``unsigned short int`` , ``unsigned int`` , ``size_t`` . The smaller the value of ``sizeof`` ( *cppad_tape_addr_type* ) , the less memory is used. On the other hand, the value ``std::numeric_limits<`` *cppad_tape_addr_type* >:: ``max`` () must be larger than any of the following: :ref:`fun_property@size_op` , :ref:`fun_property@size_op_arg` , :ref:`fun_property@size_par` , :ref:`fun_property@size_text` , :ref:`fun_property@size_VecAD` . cstdint ======= If all of the following ``cstdint`` types are defined, they can also be used as the value of *cppad_tape_addr_type* : ``uint8_t`` , ``uint16_t`` , ``uint32_t`` , ``uint64_t`` . cppad_debug_which ***************** All of the CppAD examples and test can optionally be tested in debug or release mode (see exception below). This option controls which mode is chosen for the corresponding files. The value *cppad_debug_which* be one of the following: .. csv-table:: :widths: auto *cppad_debug_which*,*CMAKE_BUILD_TYPE* ``debug_all``,``Debug`` ``debug_none``,``Release`` ``debug_even``,not specified ``debug_odd``,not specified empty string,not changed If ``CMAKE_BUILD_TYPE`` is specified on the command line, then *cppad_debug_which* must be the empty string (its default value). {xrst_toc_hidden bin/get_optional.sh xrst/install/adolc.xrst xrst/install/eigen.xrst xrst/install/cppadcg.xrst xrst/install/ipopt.xrst xrst/install/fadbad_prefix.xrst xrst/install/sacado_prefix.xrst xrst/install/colpack_prefix.xrst xrst/install/testvector.xrst } {xrst_end cmake} ================================================ FILE: xrst/install/cmake_check.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin cmake_check} {xrst_spell grep } Checking the CppAD Examples and Tests ##################################### Purpose ******* After you configure your system with the :ref:`cmake@CMake Command` you can run the CppAD example and tests to make sure that CppAD functions properly on your system. Check All ********* In the ``build`` subdirectory of the :ref:`download@Distribution Directory` execute the command :: make check This will build and run all of the tests that are support by your system and the :ref:`cmake-name` command options. Subsets of make check ********************* In unix, you can determine which subsets of ``make check`` are available by putting the output of the :ref:`cmake@CMake Command` in a file (called *cmake.out* below) and executing: ``grep`` ' ``make check.`` * ``available`` ' *cmake.out* First Level *********** The first level of subsets of ``make check`` are described below: .. list-table:: :widths: auto * - Command - Description * - ``make check_introduction`` - the :ref:`Introduction-name` functions * - ``make check_example`` - the normal :ref:`example` functions plus some deprecated examples. * - ``make check_test_more`` - correctness tests that are not examples * - ``make check_speed`` - correctness for single thread :ref:`speed-name` tests * - ``make check_cppad_ipopt`` - the deprecated :ref:`cppad_ipopt_nlp-name` speed and correctness tests Note that ``make check_example_multi_thread`` is used for the :ref:`multi-threading` speed tests. {xrst_end cmake_check} ================================================ FILE: xrst/install/colpack_prefix.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin colpack_prefix} Enabling Colpack Sparsity Calculations ###################################### Colpack Home Page ***************** https://github.com/CSCsw/ColPack Purpose ******* If you specify a *colpack_prefix* on the :ref:`cmake@CMake Command` line, the CppAD :ref:`sparse_jacobian-name` and :ref:`sparse_hessian-name` calculations can use this package. colpack_prefix ************** If Colpack is installed on your system, you can specify a value for its install *colpack_prefix* on the :ref:`cmake-name` command line. The value of *colpack_prefix* must be such that, for one of the directories *dir* in :ref:`cmake@cmake_install_includedirs` , *colpack_prefix* / *dir* / ``ColPack/ColPackHeaders.h`` is a valid way to reference to the include file ``ColPackHeaders.h`` . cppad_lib ********* The Colpack header files has a ``using namespace std`` at the global level. For this reason, CppAD does not include these files. It is therefore necessary to link the object library ``cppad_lib`` when using Colpack. {xrst_toc_hidden example/sparse/colpack_jac.cpp example/sparse/colpack_jacobian.cpp example/sparse/colpack_hes.cpp example/sparse/colpack_hessian.cpp } Example ******* The file :ref:`colpack_jac.cpp-name` (:ref:`colpack_hes.cpp-name` ) contains an example and test of using Colpack to compute the coloring for sparse Jacobians (Hessians). get_colpack *********** If you are using Unix, you can download and install a copy of Colpack using :ref:`get_colpack.sh-name` . The corresponding *colpack_prefix* would be ``build/prefix`` . {xrst_end colpack_prefix} ================================================ FILE: xrst/install/cppadcg.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin cppadcg} Including CppADCodeGen Examples and Tests ######################################### CppADCodeGen Home Page ********************** https://github.com/joaoleal/CppADCodeGen Purpose ******* CppAD includes examples and test that use the CppADCodeGen package, abbreviated as ``cppadcg`` , see: :ref:`code_gen_fun-name` . include_cppadcg *************** If ``include_cppadcg=true`` is on the cmake command line, then the pkg-config will find the necessary information to include cppadcg. The value of *PKG_CONFIG_PATH* must be such that the command ``pkg-config cppadcg --path --print-errors`` finds the location of the file ``cppadcg.pc`` . Speed Tests *********** If ``include_cppadcg=true`` is on the cmake command line, you will be able to run the cppadcg speed correctness tests by executing the following commands starting in the :ref:`download@Distribution Directory` : | |tab| ``cd build/speed/cppadcg`` | |tab| ``make check_speed_cppadcg`` After executing ``make check_speed_cppadcg`` , you can run a specific cppadcg speed test by executing the command ``./speed_cppadcg`` ; see :ref:`speed_main-name` for the meaning of the command line options to this program. get_cppadcg *********** If you are using Unix, you can download and install a copy of cppadcg using :ref:`get_cppadcg.sh-name` . The corresponding install prefix is ``build/prefix`` . {xrst_end cppadcg} ================================================ FILE: xrst/install/download.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin download} {xrst_spell dev github grep gz txt yyyy } Download The CppAD Source Code ############################## Purpose ******* CppAD is an include file library and you therefore need the source code to use it. This section discusses how to download the different versions of CppAD. Distribution Directory ********************** We refer to the CppAD source directory created by the download instructions below as the distribution directory. As a check, the distribution directory contains the file ``include/cppad/cppad.hpp`` . Version ******* A CppAD *version* number has the following fields: *yyyy* is four decimal digits denoting a year, *mm* is two decimal digits denoting a month, and *dd* is two decimal digits denoting a day. For example *version* = 20160101 corresponds to January 1, 2016. Release ******* Special versions corresponding to the beginning of each year have *dd* equal to zero. These version numbers are combined with release numbers denoted by *rel* . Higher release numbers correspond to more bug fixes. For example *version* . *rel* = 20160000.0 corresponds to the first release of the version for 2016, ``20160000.1`` corresponds to the first bug fix for 2016. The compressed archive names on the github `releases `_ page are named *version* . *rel* . ``tar.gz`` . Before 2019, these archives correspond to the Eclipse Public License Version 1 license and do not include the documentation. Starting in 2019, these archives correspond to the EPL 2.0 or (GPL 2.0 or later) license and a separate compressed archive is available on the releases page with name *version* . ``doc.tar.gz`` . Git *** CppAD source code development is current done using ``git`` You can a git clone of the current version using the command ``git clone https://github.com/coin-or/CppAD.git cppad.git`` This procedure requires that the `git `_ is installed on your system. Version ======= After downloading the source code, and changing into the distribution directory, use the following command to determine the corresponding :ref:`download@Version` : :: grep '^SET(cppad_version' CMakeLists.txt Use the following command to determine the git *hash* code corresponding to this version: :: git show-ref | grep 'refs/heads/master' You can get an old version using the command ``git checkout -q`` *hash* You can check the corresponding version number using the command :: grep '^SET(cppad_version' CMakeLists.txt Compressed Archives =================== You can build a compressed archive, from a clone of the git repository, using the script ``bin/package.sh`` . These archives have the documentation in the ``build/html`` directory and do not have the ``git`` information. They are intended for re-distribution. Building Documentation ====================== If you install `xrst `_ , you can build the documentation for CppAD, and include any changes you make. In the distribution directory execute the following command | |tab| ``bin/run_xrst.sh`` *dev* where *dev* is ``-dev`` or ``+dev`` . If you use ``+dev`` the developer documentation will be included. You can then view documentation that you built by opening the following file in a web browser:: build/html/index.html {xrst_end download} ================================================ FILE: xrst/install/eigen.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-25 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin eigen} {xrst_spell cholesky inv } Including Eigen Examples, Tests, and sparse2eigen ################################################# Eigen Home Page *************** https://libeigen.gitlab.io/ Finding Eigen ************* These example and tests will be included and :ref:`sparse2eigen-name` will be installed if: #. Eigen is installed on your system #. c++14 or higher is supported by your compiler #. *PKG_CONFIG_PATH* is such that the following command finds the location of the file eigen3.pc : ``pkg-config eigen3 --path --print-errors`` The output of the :ref:`cmake-name` command will report if these conditions are met. Examples and Tests ****************** CppAD can include the following examples and tests that use the linear algebra package Eigen: {xrst_comment check list with: grep 'eigen.*\.cpp' omh/example_list.omh'} .. csv-table:: :widths: auto cppad_eigen.hpp,:ref:`cppad_eigen.hpp-title` eigen_array.cpp,:ref:`eigen_array.cpp-title` eigen_det.cpp,:ref:`eigen_det.cpp-title` sparse2eigen.cpp,:ref:`sparse2eigen.cpp-title` atomic_two_eigen_cholesky.hpp,:ref:`atomic_two_eigen_cholesky.hpp-title` atomic_two_eigen_mat_inv.hpp,:ref:`atomic_two_eigen_mat_inv.hpp-title` atomic_two_eigen_mat_mul.hpp,:ref:`atomic_two_eigen_mat_mul.hpp-title` Examples ******** If :ref:`eigen is found ` , you will be able to run the Eigen examples list above by executing the following commands starting in the :ref:`download@Distribution Directory` : | |tab| ``cd build/example`` | |tab| ``make check_example`` If you do this, you will see an indication that the examples ``eigen_array`` and ``eigen_det`` have passed their correctness check. Test Vector *********** If you eigen is found you can choose ``-D cppad_testvector`` = ``eigen`` on the :ref:`cmake@CMake Command` line. This we set the CppAD :ref:`testvector-name` to use Eigen vectors. {xrst_end eigen} ================================================ FILE: xrst/install/fadbad_prefix.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin fadbad_prefix} {xrst_spell badiff } Including Fadbad Speed Tests ############################ Fadbad Home Page **************** http://uning.dk/ Purpose ******* CppAD includes speed comparisons for the Fadbad AD package; see :ref:`speed_fadbad-name` . fadbad_prefix ************* If Fadbad is installed on your system, you can specify a value for its install *fadbad_prefix* on the :ref:`cmake-name` command line. The value of *fadbad_prefix* must be such that, for one of the directories *dir* in :ref:`cmake@cmake_install_includedirs` , *fadbad_prefix* / *dir* / ``FADBAD`` ++/ ``badiff.h`` is a valid way to reference to the include file ``badiff.h`` ; Speed Tests *********** If you include *fadbad_prefix* on the :ref:`cmake-name` command line, you will be able to run the Fadbad speed correctness tests by executing the following commands starting in the :ref:`download@Distribution Directory` : | |tab| ``cd build/speed/fadbad`` | |tab| ``make check_speed_fadbad`` After executing ``make check_speed_fadbad`` , you can run a specific Fadbad speed test by executing the command ``./speed_fadbad`` ; see :ref:`speed_main-name` for the meaning of the command line options to this program. get_fadbad ********** If you are using Unix, you can download and install a copy of Fadbad using :ref:`get_fadbad.sh-name` . The corresponding *fadbad_prefix* would be ``build/prefix`` . {xrst_end fadbad_prefix} ================================================ FILE: xrst/install/install.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin Install} {xrst_spell autotools nmake } CppAD Download, Test, and Install Instructions ############################################## Instructions ************ Step 1: Download ================ Use the :ref:`download-name` instructions to obtain a copy or CppAD. Step 2: Cmake ============= Use the :ref:`cmake-name` instructions to configure CppAD. Step 3: Check ============= Use the :ref:`cmake_check-name` instructions to check the CppAD examples and tests. Step 4: Installation ==================== Use the command :: make install to install CppAD. Generator ********* If you want to use ``nmake`` or ``ninja`` instead of ``make``, in Step 4; see the :ref:`cmake@generator` option for the ``cmake`` command. cppad.spec ********** If you are thinking of creating a system specific install of CppAD, the fedora spec file may be of help; see `cppad.spec `_ . xrst **** The `xrst `_ package is used to build the CppAD documentation. Contents ******** {xrst_toc_table xrst/install/download.xrst xrst/install/cmake.xrst xrst/install/cmake_check.xrst pkgconfig/CMakeLists.txt bin/dos_build.bat } configure ********* The :ref:`configure-name` script provides configuration interface that is similar to the old CppAD autotools install. {xrst_end Install} ================================================ FILE: xrst/install/ipopt.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin ipopt} {xrst_spell cflags } Including Ipopt Library Examples, Tests, and pkg-config ####################################################### Examples and Tests ****************** If :ref:`cmake@include_ipopt` is on the cmake command line, you will be able to run the Ipopt examples and tests. include_ipopt ************* If ``include_ipopt=true`` is on the cmake command line, then the pkg-config will find the necessary information to link in ipopt. The value of *PKG_CONFIG_PATH* must be such that the command ``pkg-config ipopt --path --print-errors`` finds the location of the file ``ipopt.pc`` . Deprecated Library ****************** If ``include_ipopt=true`` is on the cmake command line, the deprecated :ref:`cppad_ipopt_nlp-name` example and tests as well as installing the ``cppad_ipopt`` library during the ``make install`` step. get_ipopt ********* If you are using Unix, you can download and install a copy of Ipopt using :ref:`get_ipopt.sh-name` . The corresponding install prefix is ``build/prefix`` . Include Directories ******************* It may be necessary to remove ``/coin-or`` from the end of the include directories reported by ``pkg-config ipopt --cflags`` {xrst_end ipopt} ================================================ FILE: xrst/install/sacado_prefix.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin sacado_prefix} {xrst_spell kokkos trilinos } Including Sacado Speed Tests ############################ Sacado Home Page **************** https://github.com/trilinos/Trilinos/tree/master/packages/sacado Requirement *********** The c++14 standard (or higher) is necessary to build Sacado because it uses Kokkos; see `Trilinos issue 6260 `_ . Purpose ******* CppAD includes speed comparisons for the Sacado AD package; see :ref:`speed_sacado-name` . sacado_prefix ************* If Sacado is installed on your system, you can specify a value for its install *sacado_prefix* on the :ref:`cmake-name` command line. The value of *sacado_prefix* must be such that, for one of the directories *dir* in :ref:`cmake@cmake_install_includedirs` , *sacado_prefix* / *dir* / ``Sacado.hpp`` is a valid way to reference to the include file ``Sacado.hpp`` ; Speed Tests *********** If you include *sacado_prefix* on the :ref:`cmake-name` command line, you will be able to run the Sacado speed correctness tests by executing the following commands starting in the :ref:`download@Distribution Directory` : | |tab| ``cd build/speed/sacado`` | |tab| ``make check_speed_sacado`` After executing ``make check_speed_sacado`` , you can run a specific Sacado speed test by executing the command ``./speed_sacado`` ; see :ref:`speed_main-name` for the meaning of the command line options to this program. get_sacado ********** If you are using Unix, you can download and install a copy of Sacado using :ref:`get_sacado.sh-name` . The corresponding *sacado_prefix* would be ``build/prefix`` . {xrst_end sacado_prefix} ================================================ FILE: xrst/install/testvector.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin cppad_testvector} {xrst_spell ublas } Choosing the CppAD Test Vector Template Class ############################################# Syntax ****** | ``CPPAD_TESTVECTOR`` ( *Scalar* ) *vec* Example Simple Vector ********************* The value *cppad_testvector* in the :ref:`cmake@CMake Command` must be one of the following: ``boost`` , ``cppad`` , ``eigen`` , or ``std`` . It specifies which :ref:`simplevector-name` corresponds to the template class :ref:`CPPAD_TESTVECTOR` which is used for many of the CppAD examples and tests. std *** If *cppad_testvector* is *std* , the ``std::vector`` template class is used to define ``CPPAD_TESTVECTOR`` . cppad ***** If *cppad_testvector* is *cppad* , the :ref:`cppad_vector-name` template class is used to define ``CPPAD_TESTVECTOR`` . boost ***** If *cppad_testvector* is *boost* , `boost ublas vector `_ template class is used to define ``CPPAD_TESTVECTOR`` . In this case, the `cmake FindBoost `_ module must be able to automatically figure out where Boost is installed. eigen ***** If *cppad_testvector* is *eigen* , one of the eigen template classes is used to define ``CPPAD_TESTVECTOR`` . In this case, :ref:`cmake@Eigen` must be found and c++14 must be supported. {xrst_end cppad_testvector} ================================================ FILE: xrst/introduction.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin Introduction} {xrst_spell andreas griewank } An Introduction by Example to Algorithmic Differentiation ######################################################### Purpose ******* This is an introduction by example to Algorithmic Differentiation. Its purpose is to aid in understand what AD calculates, how the calculations are preformed, and the amount of computation and memory required for a forward or reverse sweep. Preface ******* Algorithmic Differentiation =========================== Algorithmic Differentiation (often referred to as Automatic Differentiation or just AD) uses the software representation of a function to obtain an efficient method for calculating its derivatives. These derivatives can be of arbitrary order and are analytic in nature (do not have any truncation error). Forward Mode ============ A forward mode sweep computes the partial derivative of all the dependent variables with respect to one independent variable (or independent variable direction). Reverse Mode ============ A reverse mode sweep computes the derivative of one dependent variable (or one dependent variable direction) with respect to all the independent variables. Operation Count =============== The number of floating point operations for either a forward or reverse mode sweep is a small multiple of the number required to evaluate the original function. Thus, using reverse mode, you can evaluate the derivative of a scalar valued function with respect to thousands of variables in a small multiple of the work to evaluate the original function. Efficiency ========== AD automatically takes advantage of the speed of your algorithmic representation of a function. For example, if you calculate a determinant using LU factorization, AD will use the LU representation for the derivative of the determinant (which is faster than using the definition of the determinant). Outline ******* A. get_started ============== See :ref:`get_started.cpp-name` for an example that uses CppAD to calculate derivatives of a polynomial. B. Example Algorithms ===================== Present two algorithms that approximate the exponential function. The first algorithm :ref:`exp_2.hpp-name` is simpler and does not include any logical variables or loops. The second algorithm :ref:`exp_eps.hpp-name` includes logical operations and a ``while`` loop. C. Example Steps ================ For each of the algorithms, exp_2 and exp_eps, do the following: #. Define the mathematical function corresponding to the algorithm (:ref:`exp_2-name` and :ref:`exp_eps-name` ). #. Write out the floating point operation sequence, and corresponding values, that correspond to executing the algorithm for a specific input (:ref:`exp_2_for0-name` and :ref:`exp_eps_for0-name` ). #. Compute a forward sweep derivative of the operation sequence (:ref:`exp_2_for1-name` and :ref:`exp_eps_for1-name` ). #. Compute a reverse sweep derivative of the operation sequence (:ref:`exp_2_rev1-name` and :ref:`exp_eps_rev1-name` ). #. Use CppAD to compute both a forward and reverse sweep of the operation sequence (:ref:`exp_2_cppad-name` and :ref:`exp_eps_cppad-name` ). D. Testing Examples =================== The program :ref:`exp_apx.cpp-name` runs all of the test routines that validate the calculations in the :ref:`exp_2-name` and :ref:`exp_eps-name` presentation. Reference ********* An in-depth review of AD theory and methods can be found in the book *Evaluating Derivatives:* *Principles and Techniques of Algorithmic Differentiation* , Andreas Griewank, SIAM Frontiers in Applied Mathematics, 2000. Contents ******** {xrst_toc_table introduction/exp_2.hpp introduction/exp_eps.hpp introduction/introduction.cpp } {xrst_end Introduction} ================================================ FILE: xrst/lu_det_and_solve.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin lu_det_and_solve} Compute Determinants and Solve Equations by LU Factorization ############################################################ Contents ******** {xrst_toc_table include/cppad/utility/lu_solve.hpp include/cppad/utility/lu_factor.hpp include/cppad/utility/lu_invert.hpp } {xrst_end lu_det_and_solve} ================================================ FILE: xrst/lu_factor_hpp.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin lu_factor.hpp} Source: LuFactor ################ | # ``ifndef CPPAD_LU_FACTOR_HPP`` | # ``define CPPAD_LU_FACTOR_HPP`` {xrst_literal include/cppad/utility/lu_factor.hpp // BEGIN C++ // END C++ } ``# endif`` {xrst_end lu_factor.hpp} ================================================ FILE: xrst/lu_invert_hpp.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin lu_invert.hpp} Source: LuInvert ################ | # ``ifndef CPPAD_LU_INVERT_HPP`` | # ``define CPPAD_LU_INVERT_HPP`` {xrst_literal include/cppad/utility/lu_invert.hpp // BEGIN C++ // END C++ } ``# endif`` {xrst_end lu_invert.hpp} ================================================ FILE: xrst/lu_solve_hpp.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin lu_solve.hpp} Source: LuSolve ############### | # ``ifndef CPPAD_LU_SOLVE_HPP`` | # ``define CPPAD_LU_SOLVE_HPP`` {xrst_literal include/cppad/utility/lu_solve.hpp // BEGIN C++ // END C++ } ``# endif`` {xrst_end lu_solve.hpp} ================================================ FILE: xrst/mat_sum_sq_hpp.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin mat_sum_sq.hpp} Source: mat_sum_sq ################## | # ``ifndef CPPAD_MAT_SUM_SQ_HPP`` | # ``define CPPAD_MAT_SUM_SQ_HPP`` {xrst_literal include/cppad/speed/mat_sum_sq.hpp // BEGIN C++ // END C++ } ``# endif`` {xrst_end mat_sum_sq.hpp} ================================================ FILE: xrst/mul_level.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-23 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin mul_level} Using Multiple Levels of AD ########################### Alternative *********** Often it is easier to use :ref:`base2ad-name` and :ref:`dynamic` parameters to accomplish the objective below. Background ********** If *f* is an ``ADFun`` < *Base* > object, the vectors returned by :ref:`f.Forward` , and :ref:`f.Reverse` , have values of type *Base* and not ``AD`` < *Base* > . This reflects the fact that operations used to calculate these function values are not recorded by the tape corresponding to ``AD`` < *Base* > operations. Motivation ********** Suppose that you use derivatives of one or more inner functions as part of the operations needed to compute an outer function. For example, the derivatives returned by *f* . ``Forward`` might be used as part of Taylor's method for solving ordinary differential equations. In addition, we might want to differentiate the solution of a differential equation with respect to parameters in the equation. This can be accomplished in the following way: #. The function defining the differential equation could be calculated using the class ``AD< AD >`` . #. The operations during the calculation of Taylor's method could be done using the ``AD`` class. #. Derivatives of the solution of the differential equation could then be calculated using the ``double`` class. Procedure ********* First Start AD ====================== If some of the :ref:`parameters` in the ``AD< AD >`` recording depend on the :ref:`variables` in the ``AD`` recording, we must first declaring these variables; i.e., ``Independent`` ( *a1x* ) where *a1x* is a :ref:`SimpleVector-name` with elements of type ``AD`` . This will start recording a new tape of operations performed using ``AD`` class objects. Start AD< AD > Recording ================================ The next step is to declare the independent variables using ``Independent`` ( *a2x* ) where *a2x* is a :ref:`SimpleVector-name` with elements of type ``AD< AD >`` . This will start recording a new tape of operations performed using ``AD< AD >`` class objects. Inner Function ============== The next step is to calculate the inner function using ``AD< AD >`` class objects. We then stop the recording using *a1f* . ``Dependent`` ( *a2x* , *a2y* ) where *a2y* is a :ref:`SimpleVector-name` with elements of type ``AD< AD >`` and *a1f* is an ``ADFun< AD >`` object. Second Start AD< AD > ============================= If none of the :ref:`parameters` in the ``AD< AD >`` recording depend on the :ref:`variables` in the ``AD`` recording, it is preferred to delay declaring these variables to this point; i.e., ``Independent`` ( *a1x* ) where *a1x* is a :ref:`SimpleVector-name` with elements of type ``AD`` . This will start recording a new tape of operations performed using ``AD`` class objects. Outer Function ============== The next step is to calculate the outer function using ``AD`` class objects. Note that derivatives of the inner function can be included in the calculation of the outer function using *a1f* . We then stop the recording of ``AD`` operations using *g* . ``Dependent`` ( *a1x* , *a1y* ) where *a1y* is a :ref:`SimpleVector-name` with elements of type ``AD`` and *g* is an ``ADFun`` object. Derivatives of Outer Function ============================= The AD function object *g* can then be used to calculate the derivatives of the outer function. {xrst_toc_hidden example/general/mul_level.cpp example/general/change_param.cpp } Example ******* The files :ref:`mul_level.cpp-name` and :ref:`change_param.cpp-name` contain an examples and tests of this procedure. They return true if they succeed and false otherwise. The file :ref:`mul_level_ode.cpp-name` is a more complex example use of multiple tapes. {xrst_end mul_level} ================================================ FILE: xrst/multi_thread.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin multi_thread} Using CppAD in a Multi-Threading Environment ############################################ Purpose ******* Extra steps and care must be taken to use CppAD in :ref:`parallel` execution mode. This section collects this information in one place. CPPAD_MAX_NUM_THREADS ********************* The value ``CPPAD_MAX_NUM_THREADS`` is an absolute maximum for the number of threads that CppAD should support. If this preprocessor symbol is defined before including any CppAD header files, it must be an integer greater than or equal to one. Otherwise, :ref:`cmake@cppad_max_num_threads` is used to define this preprocessor symbol. Note that the minimum allowable value for *cppad_max_num_threads* is 4; i.e., you can only get smaller values for ``CPPAD_MAX_NUM_THREADS`` by defining it before including the CppAD header files. parallel_setup ************** Using any of the following routines in a multi-threading environment requires that :ref:`thread_alloc::parallel_setup` has been completed: :ref:`CppAD::vector` , :ref:`CheckSimpleVector` , :ref:`CheckNumericType` , :ref:`parallel_ad-name` . hold_memory *********** Memory allocation should be much faster after calling ``hold_memory`` with :ref:`ta_hold_memory@value` equal to true. This may even be true if there is only one thread. Parallel AD *********** One must first call :ref:`thread_alloc::parallel_setup` and then call :ref:`parallel_ad-name` before using ``AD`` types in :ref:`parallel` execution mode. In addition, see :ref:`parallel_ad@Other Initialization` . Same Thread *********** Some operations must be preformed by the same thread: :ref:`ADFun` , :ref:`Independent-name` , :ref:`Dependent-name` . Parallel Prohibited ******************* The following routine cannot be called in parallel mode: :ref:`ErrorHandler constructor` . Contents ******** {xrst_toc_table include/cppad/core/parallel_ad.hpp example/multi_thread/thread_test.cpp } {xrst_end multi_thread} ================================================ FILE: xrst/numeric_type.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- /* {xrst_begin NumericType} {xrst_spell valarray } Definition of a Numeric Type ############################ Type Requirements ***************** A *NumericType* is any type that satisfies the requirements below. The following is a list of some numeric types: ``int`` , ``float`` , ``double`` , ``AD`` , ``AD< AD >`` . The routine :ref:`CheckNumericType-name` can be used to check that a type satisfies these conditions. Default Constructor ******************* The syntax *NumericType* *x* ; creates a *NumericType* object with an unspecified value. Constructor From Integer ************************ If *i* is an ``int`` , the syntax *NumericType* *x* ( *i* ); creates a *NumericType* object with a value equal to *i* where *i* can be ``const`` . Copy Constructor **************** If *x* is a *NumericType* object the syntax *NumericType* *y* ( *x* ); creates a *NumericType* object *y* with the same value as *x* where *x* can be ``const`` . Assignment ********** If *x* and *y* are *NumericType* objects, the syntax *x* = *y* sets the value of *x* equal to the value of *y* where *y* can be ``const`` . The expression corresponding to this operation is unspecified; i.e., it could be ``void`` and hence *x* = *y* = *z* may not be legal. Operators ********* Suppose *x* , *y* and *z* are *NumericType* objects where *x* and *y* may be ``const`` . In the result type column, *NumericType* can be replaced by any type that can be used just like a *NumericType* object. .. csv-table:: :widths: auto :header: Operation, Description, Result Type \+ *x* , unary plus, *NumericType* \- *x* , unary minus, *NumericType* *x* + *y* , binary addition, *NumericType* *x* - *y* , binary subtraction, *NumericType* *x* \* *y* , binary multiplication, *NumericType* *x* / *y* , compound assignment division, *NumericType* *z* += *y* , compound assignment addition, *NumericType* *z* -= *y* , compound assignment subtraction, *NumericType* *z* \*= *y* , compound assignment multiplication, *NumericType* *z* /= *y* , compound assignment division, *NumericType* Example ******* {xrst_toc_hidden example/general/numeric_type.cpp } The file :ref:`numeric_type.cpp-name` contains an example and test of using numeric types. (It is easy to modify to test additional numeric types.) Exercise ******** #. List three operators that are not supported by every numeric type but that are supported by the numeric types ``int`` , ``float`` , ``double`` . #. Which of the following are numeric types: ``std::complex`` , ``std::valarray`` , ``std::vector`` ? {xrst_end NumericType} ================================================ FILE: xrst/ode_evaluate.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin ode_evaluate.hpp} Source: ode_evaluate #################### | # ``ifndef CPPAD_ODE_EVALUATE_HPP`` | # ``define CPPAD_ODE_EVALUATE_HPP`` {xrst_literal include/cppad/speed/ode_evaluate.hpp // BEGIN C++ // END C++ } ``# endif`` {xrst_end ode_evaluate.hpp} ================================================ FILE: xrst/poly_hpp.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin poly.hpp} Source: Poly ############ | # ``ifndef CPPAD_POLY_HPP`` | # ``define CPPAD_POLY_HPP`` {xrst_literal include/cppad/utility/poly.hpp // BEGIN C++ // END C++ } ``# endif`` {xrst_end poly.hpp} ================================================ FILE: xrst/preprocessor.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin preprocessor} {xrst_spell nullptr } CppAD API Preprocessor Symbols ############################## Rule **** The CppAD include files defines preprocessor symbols all of which begin with ``CPPAD_`` . Note that there are some old, deprecated preprocessor symbols that begin with ``CppAD`` . In this section we list all of the CppAD preprocessor symbols that are part of the CppAD Application Interface (API). NOMINMAX, windows.h ******************* There is one exception to the rule above, when using the Microsoft Visual Studio compiler and including ``windows.h`` , it is done as follows: :: # ifndef NOMINMAX # define NOMINMAX # endif # include Documented Here *************** CPPAD_NULL ********** Deprecated 2020-12-03: This preprocessor symbol was used for a null pointer before c++11. Replace it by ``nullptr`` . {xrst_comment # undef CPPAD_NULL } CPPAD_PACKAGE_STRING ==================== Is a ``const char*`` representation of this version of CppAD. {xrst_comment # undef CPPAD_PACKAGE_STRING } CPPAD_USE_CPLUSPLUS_2011 ************************ Deprecated 2020-12-03: Is it OK for CppAD to use C++11 features. This is always 1 (for true). {xrst_comment # undef CPPAD_USE_CPLUSPLUS_2011 } CPPAD_USE_CPLUSPLUS_2017 ************************ Is it OK for CppAD to use C++17 features. {xrst_comment # undef CPPAD_USE_CPLUSPLUS_2017 } Documented Elsewhere ******************** .. list-table:: :widths: auto * - :ref:`CPPAD_BOOL_BINARY` * - :ref:`CPPAD_BOOL_UNARY` * - :ref:`CPPAD_DISCRETE_FUNCTION` * - :ref:`multi_thread@CPPAD_MAX_NUM_THREADS` * - :ref:`base_limits@CPPAD_NUMERIC_LIMITS` * - :ref:`base_std_math@CPPAD_STANDARD_MATH_UNARY` * - :ref:`cmake@cppad_tape_addr_type` * - :ref:`cmake@cppad_tape_id_type` * - :ref:`CPPAD_TESTVECTOR` * - :ref:`base_to_string@CPPAD_TO_STRING` {xrst_comment # undef CPPAD_BOOL_BINARY # undef CPPAD_BOOL_UNARY # undef CPPAD_DISCRETE_FUNCTION # undef CPPAD_MAX_NUM_THREADS # undef CPPAD_NUMERIC_LIMITS # undef CPPAD_STANDARD_MATH_UNARY # undef CPPAD_TAPE_ADDR_TYPE # undef CPPAD_TAPE_ID_TYPE # undef CPPAD_TESTVECTOR # undef CPPAD_TO_STRING } Deprecated ========== .. list-table:: :widths: auto * - :ref:`CppADCreateDiscrete` * - :ref:`CPPAD_TRACK_NEW_VEC` * - :ref:`CPPAD_TRACK_DEL_VEC` * - :ref:`CPPAD_TRACK_EXTEND` * - :ref:`CPPAD_TRACK_COUNT` * - :ref:`CppADTrackNewVec` * - :ref:`CppADTrackDelVec` * - :ref:`CppADTrackExtend` * - :ref:`CppADTrackCount` * - :ref:`CppADCreateBinaryBool` * - :ref:`CppADCreateUnaryBool` * - :ref:`CPPAD_USER_ATOMIC` * - :ref:`CPPAD_CPPADVECTOR` * - :ref:`CPPAD_STDVECTOR` * - :ref:`CPPAD_EIGENVECTOR` * - :ref:`CPPAD_BOOSTVECTOR` {xrst_comment # undef CppADCreateDiscrete # undef CPPAD_TRACK_NEW_VEC # undef CPPAD_TRACK_DEL_VEC # undef CPPAD_TRACK_EXTEND # undef CPPAD_TRACK_COUNT # undef CPPAD_USER_ATOMIC # undef CppADTrackNewVec # undef CppADTrackDelVec # undef CppADTrackExtend # undef CppADTrackCount # undef CppADCreateBinaryBool # undef CppADCreateUnaryBool # undef CPPAD_USER_ATOMIC # undef CPPAD_CPPADVECTOR # undef CPPAD_STDVECTOR # undef CPPAD_EIGENVECTOR # undef CPPAD_BOOSTVECTOR } {xrst_end preprocessor} ================================================ FILE: xrst/reverse/reverse_any.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin reverse_any} {xrst_spell dw } Any Order Reverse Mode ###################### Syntax ****** | *dw* = *f* . ``Reverse`` ( *q* , *w* ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* . Reverse mode computes the derivative of the :ref:`Forward-name` mode :ref:`Taylor coefficients` with respect to the domain variable :math:`x`. To be specific, it computes the derivative :math:`W^{(1)} (u)` at :math:`u = x` which is specified by the following notation: Notation ******** u^(k) ===== For :math:`k = 0, \ldots , q-1`, the vector :math:`u^{(k)} \in \B{R}^n` is defined as the value of *x_k* in the previous calls of the form *f* . ``Forward`` ( *k* , *x_k* ) If there is no previous call with :math:`k = 0`, :math:`u^{(0)}` is the value of the independent variables when the corresponding AD of *Base* :ref:`operation sequence` was recorded. X(t, u) ======= The function :math:`X : \B{R} \times \B{R}^{n \times q} \rightarrow \B{R}^n` is defined by .. math:: X ( t , u ) = u^{(0)} + u^{(1)} * t + \cdots + u^{(q-1)} * t^{q-1} Note that for :math:`k = 0 , \ldots , q-1`, :math:`u^{(k)}` is related to the *k*-th partial of :math:`X(t, u)` with respect to :math:`t` by .. math:: u^{(k)} = \frac{1}{k !} \Dpow{k}{t} X(0, u) Y(t, u) ======= The function :math:`Y : \B{R} \times \B{R}^{n \times q} \rightarrow \B{R}^m` is defined by .. math:: Y(t, u) = F [ X(t,u) ] w^(k) ===== If the argument *w* has size *m* * *q* , for :math:`k = 0 , \ldots , q-1` and :math:`i = 0, \ldots , m-1`, .. math:: w_i^{(k)} = w [ i * q + k ] If the argument *w* has size *m* , for :math:`k = 0 , \ldots , q-1` and :math:`i = 0, \ldots , m-1`, .. math:: w_i^{(k)} = \left\{ \begin{array}{ll} w [ i ] & {\rm if} \; k = q-1 \\ 0 & {\rm otherwise} \end{array} \right. W(u) ==== The function :math:`W : \B{R}^{n \times q} \rightarrow \B{R}` is defined by .. math:: W(u) = \sum_{k=0}^{q-1} ( w^{(k)} )^\R{T} \frac{1}{k !} \Dpow{k}{t} Y(0, u) f * The object *f* has prototype ``const ADFun`` < *Base* > *f* Before this call to ``Reverse`` , the value returned by *f* . ``size_order`` () must be greater than or equal *q* (see :ref:`size_order-name` ). q * The argument *q* has prototype ``size_t`` *q* and specifies the number of Taylor coefficient orders to be differentiated (for each variable). w * The argument *w* has prototype ``const`` *Vector* & *w* (see :ref:`reverse_any@Vector` below) and its size must be equal to *m* or *m* * *q* , It specifies the weighting vector *w* in the definition of :ref:`reverse_any@Notation@W(u)` . dw ** The return value *dw* has prototype *Vector* *dw* (see :ref:`reverse_any@Vector` below). It is a vector with size :math:`n \times q`. For :math:`j = 0, \ldots, n-1` and :math:`k = 0 , \ldots , q-1` If the argument *w* has size *m* * *q* , .. math:: dw[ j * q + k ] = W^{(1)} ( x )_{j,k} where :math:`u = x` is value of the Taylor coefficients where the derivative is evaluated. If the argument *w* has size *m* , .. math:: dw[ j * q + q - k - 1 ] = W^{(1)} ( x )_{j,k} where :math:`u = x` is value of the Taylor coefficients where the derivative is evaluated. Note the reverse order in which the order indices are stored. This is an unfortunate consequence of keeping ``Reverse`` backward compatible. First Order *********** We consider the case where *q* = 1 and *w* . ``size`` () == *m* . In this case .. math:: :nowrap: \begin{eqnarray} W(u) & = & w_0 Y_0 (0, u) + \cdots + w_m Y_m (0, u) \\ W(u) & = & w_0 F_0 [ X(0, u) ] + \cdots + w_m F_m [ X(0, u) ] \\ W^{(1)} (x) & = & w_0 F_0^{(1)} ( x^{(0)} ) + \cdots + w_m F_m^{(1)} ( x^{(0)} ) \end{eqnarray} This is the same as the result documented in :ref:`reverse_one-name` . Second Order ************ We consider the case where *q* = 2 and *w* . ``size`` () == *m* . In this case .. math:: :nowrap: \begin{eqnarray} W(u) & = & w_0 \partial_t Y_0 (0, u) + \cdots + w_m \partial_t Y_m (0, u) \\ W(u) & = & w_0 \partial_t \{ F_0 [ X(t, u) ] \}_{t = 0} + \cdots + w_m \partial_t \{ F_m [ X(t, u) ] \}_{t = 0} \\ W(u) & = & w_0 F_0^{(1)} ( u^{(0)} ) u^{(1)} + \cdots + w_0 F_m^{(1)} ( u^{(0)} ) u^{(1)} \\ \partial_{u(0)} W(x) & = & w_0 ( x^{(1)} )^\R{T} F_0^{(2)} ( x^{(0)} ) + \cdots + w_m ( x^{(1)} )^\R{T} F_m^{(2)} ( x^{(0)} ) \\ \partial_{u(1)} W(x) & = & w_0 F_0^{(1)} ( x^{(0)} ) + \cdots + w_m F_m^{(1)} ( x^{(0)} ) \end{eqnarray} where :math:`\partial{u(0)}` denotes partial with respect to :math:`u^{(0)}`. These are the same as the result documented in :ref:`reverse_two-name` . Vector ****** The type *Vector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` *Base* . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Example ******* {xrst_toc_hidden example/general/reverse_three.cpp example/general/rev_checkpoint.cpp } #. The file :ref:`reverse_three.cpp-name` contains an example and test of using reverse mode to compute third order derivatives. #. The file :ref:`rev_checkpoint.cpp-name` contains an example and test of the general reverse mode case. {xrst_end reverse_any} ================================================ FILE: xrst/reverse/reverse_one.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin reverse_one} {xrst_spell dw } First Order Reverse Mode ######################## Syntax ****** | *dw* = *f* . ``Reverse`` (1, *w* ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* . The function :math:`W : \B{R}^n \rightarrow \B{R}` is defined by .. math:: W(x) = w_0 * F_0 ( x ) + \cdots + w_{m-1} * F_{m-1} (x) The result of this operation is the derivative :math:`dw = W^{(1)} (x)`; i.e., .. math:: dw = w_0 * F_0^{(1)} ( x ) + \cdots + w_{m-1} * F_{m-1}^{(1)} (x) Note that if :math:`w` is the *i*-th :ref:`glossary@Elementary Vector` , :math:`dw = F_i^{(1)} (x)`. f * The object *f* has prototype ``const ADFun`` < *Base* > *f* Before this call to ``Reverse`` , the value returned by *f* . ``size_order`` () must be greater than or equal one (see :ref:`size_order-name` ). x * The vector *x* in expression for *dw* above corresponds to the previous call to :ref:`forward_zero-name` using this ADFun object *f* ; i.e., *f* . ``Forward`` (0, *x* ) If there is no previous call with the first argument zero, the value of the :ref:`Independent-name` variables during the recording of the AD sequence of operations is used for *x* . w * The argument *w* has prototype ``const`` *Vector* & *w* (see :ref:`reverse_one@Vector` below) and its size must be equal to *m* , the dimension of the :ref:`fun_property@Range` space for *f* . dw ** The result *dw* has prototype *Vector* *dw* (see :ref:`reverse_one@Vector` below) and its value is the derivative :math:`W^{(1)} (x)`. The size of *dw* is equal to *n* , the dimension of the :ref:`fun_property@Domain` space for *f* . Vector ****** The type *Vector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` *Base* . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Example ******* {xrst_toc_hidden example/general/reverse_one.cpp } The file :ref:`reverse_one.cpp-name` contains an example and test of this operation. {xrst_end reverse_one} ================================================ FILE: xrst/reverse/reverse_two.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin reverse_two} {xrst_spell dw } Second Order Reverse Mode ######################### Syntax ****** | *dw* = *f* . ``Reverse`` (2, *w* ) Purpose ******* We use :math:`F : \B{R}^n \rightarrow \B{R}^m` to denote the :ref:`glossary@AD Function` corresponding to *f* . Reverse mode computes the derivative of the :ref:`Forward-name` mode :ref:`Taylor coefficients` with respect to the domain variable :math:`x`. x^(k) ***** For :math:`k = 0, 1`, the vector :math:`x^{(k)} \in \B{R}^n` is defined as the value of *x_k* in the previous call (counting this call) of the form *f* . ``Forward`` ( *k* , *x_k* ) If there is no previous call with :math:`k = 0`, :math:`x^{(0)}` is the value of the independent variables when the corresponding AD of *Base* :ref:`operation sequence` was recorded. Capital W ********* The functions :math:`W_0 : \B{R}^n \rightarrow \B{R}` and :math:`W_1 : \B{R}^n \rightarrow \B{R}` are defined by .. math:: :nowrap: \begin{eqnarray} W_0 ( u ) & = & w_0 * F_0 ( u ) + \cdots + w_{m-1} * F_{m-1} (u) \\ W_1 ( u ) & = & w_0 * F_0^{(1)} ( u ) * x^{(1)} + \cdots + w_{m-1} * F_{m-1}^{(1)} (u) * x^{(1)} \end{eqnarray} This operation computes the derivatives .. math:: :nowrap: \begin{eqnarray} W_0^{(1)} (u) & = & w_0 * F_0^{(1)} ( u ) + \cdots + w_{m-1} * F_{m-1}^{(1)} (u) \\ W_1^{(1)} (u) & = & w_0 * \left( x^{(1)} \right)^\R{T} * F_0^{(2)} ( u ) + \cdots + w_{m-1} * \left( x^{(1)} \right)^\R{T} F_{m-1}^{(2)} (u) \end{eqnarray} at :math:`u = x^{(0)}`. f * The object *f* has prototype ``const ADFun`` < *Base* > *f* Before this call to ``Reverse`` , the value returned by *f* . ``size_order`` () must be greater than or equal two (see :ref:`size_order-name` ). Lower w ******* The argument *w* has prototype ``const`` *Vector* & *w* (see :ref:`reverse_two@Vector` below) and its size must be equal to *m* , the dimension of the :ref:`fun_property@Range` space for *f* . dw ** The result *dw* has prototype *Vector* *dw* (see :ref:`reverse_two@Vector` below). It contains both the derivative :math:`W^{(1)} (x)` and the derivative :math:`U^{(1)} (x)`. The size of *dw* is equal to :math:`n \times 2`, where :math:`n` is the dimension of the :ref:`fun_property@Domain` space for *f* . First Order Partials ==================== For :math:`j = 0 , \ldots , n - 1`, .. math:: dw [ j * 2 + 0 ] = \D{ W_0 }{ u_j } \left( x^{(0)} \right) = w_0 * \D{ F_0 }{ u_j } \left( x^{(0)} \right) + \cdots + w_{m-1} * \D{ F_{m-1} }{ u_j } \left( x^{(0)} \right) This part of *dw* contains the same values as are returned by :ref:`reverse_one-name` . Second Order Partials ===================== For :math:`j = 0 , \ldots , n - 1`, .. math:: dw [ j * 2 + 1 ] = \D{ W_1 }{ u_j } \left( x^{(0)} \right) = \sum_{\ell=0}^{n-1} x_\ell^{(1)} \left[ w_0 * \DD{ F_0 }{ u_\ell }{ u_j } \left( x^{(0)} \right) + \cdots + w_{m-1} * \DD{ F_{m-1} }{ u_\ell }{ u_j } \left( x^{(0)} \right) \right] Vector ****** The type *Vector* must be a :ref:`SimpleVector-name` class with :ref:`elements of type` *Base* . The routine :ref:`CheckSimpleVector-name` will generate an error message if this is not the case. Hessian Times Direction *********************** Suppose that :math:`w` is the *i*-th elementary vector. It follows that for :math:`j = 0, \ldots, n-1` .. math:: :nowrap: \begin{eqnarray} dw[ j * 2 + 1 ] & = & w_i \sum_{\ell=0}^{n-1} \DD{F_i}{ u_j }{ u_\ell } \left( x^{(0)} \right) x_\ell^{(1)} \\ & = & \left[ F_i^{(2)} \left( x^{(0)} \right) * x^{(1)} \right]_j \end{eqnarray} Thus the vector :math:`( dw[1], dw[3], \ldots , dw[ n * q - 1 ] )` is equal to the Hessian of :math:`F_i (x)` times the direction :math:`x^{(1)}`. In the special case where :math:`x^{(1)}` is the *l*-th :ref:`glossary@Elementary Vector` , .. math:: dw[ j * 2 + 1 ] = \DD{ F_i }{ x_j }{ x_\ell } \left( x^{(0)} \right) Example ******* {xrst_toc_hidden example/general/reverse_two.cpp example/general/hes_times_dir.cpp } The files :ref:`reverse_two.cpp-name` and :ref:`hes_times_dir.cpp-name` contain a examples and tests of reverse mode calculations. They return true if they succeed and false otherwise. {xrst_end reverse_two} ================================================ FILE: xrst/simple_vector.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- /* {xrst_begin SimpleVector} {xrst_spell valarray } C++ Concept: A Simple Vector ############################ Template Class Requirements *************************** A simple vector template class *SimpleVector* , is any template class that satisfies the requirements below. The following is a list of some simple vector template classes: .. list-table:: :widths: auto * - **Name** - **Documentation** * - ``std::vector`` - Section 16.3 of :ref:`Bib@The C++ Programming Language` * - ``std::valarray`` - Section 22.4 of :ref:`Bib@The C++ Programming Language` * - ``CppAD::vector`` - :ref:`CppAD_vector-title` Elements of Specified Type ************************** A simple vector class with elements of type *Scalar* , is any class that satisfies the requirements for a class of the form *SimpleVector* < *Scalar* > The routine :ref:`CheckSimpleVector-name` can be used to check that a class is a simple vector class with a specified element type. Default Constructor ******************* The syntax *SimpleVector* < *Scalar* > *x* ; creates an empty vector *x* ( *x* . ``size`` () is zero) that can later contain elements of the specified type (see :ref:`SimpleVector@resize` below). Sizing Constructor ****************** If *n* has type ``size_t`` , *SimpleVector* < *Scalar* > *x* ( *n* ) creates a vector *x* with *n* elements each of the specified type. Copy Constructor **************** If *x* is a *SimpleVector* < *Scalar* > object, *SimpleVector* < *Scalar* > *y* ( *x* ) creates a vector with the same type and number of elements as *x* . The *Scalar* assignment operator ( ``=`` ) is used to set each element of *y* equal to the corresponding element of *x* . This is a `deep copy' in that the values of the elements of *x* and *y* can be set independently after the copy. The argument *x* is passed by reference and may be ``const`` . Element Constructor and Destructor ********************************** The default constructor for type *Scalar* is called for every element in a vector when the vector element is created. The *Scalar* destructor is called when it is removed from the vector (this includes when the vector is destroyed). Assignment ********** If *x* and *y* are *SimpleVector* < *Scalar* > objects, *y* = *x* uses the *Scalar* assignment operator ( ``=`` ) to set each element of *y* equal to the corresponding element of *x* . This is a `deep assignment' in that the values of the elements of *x* and *y* can be set independently after the assignment. The vectors *x* and *y* must have the same number of elements. The argument *x* is passed by reference and may be ``const`` . The type returned by this assignment is unspecified; for example, it might be void in which case the syntax *z* = *y* = *x* would not be valid. size **** If *x* is a *SimpleVector* < *Scalar* > object and ``n`` has type ``size_t`` , *n* = ``size_t`` ( *x* . ``size`` () ) sets *n* to the number of elements in the vector *x* . The object *x* may be ``const`` . resize ****** If *x* is a *SimpleVector* < *Scalar* > object and ``n`` has type ``size_t`` , *x* . ``resize`` ( *n* ) changes the number of elements contained in the vector *x* to be *n* . The value of the elements of *x* are not specified after this operation; i.e., any values previously stored in *x* are lost. (The object *x* can not be ``const`` .) value_type ********** If *Vector* is any simple vector class, the syntax *Vector* :: ``value_type`` is the type of the elements corresponding to the vector class; i.e., *SimpleVector* < *Scalar* >:: ``value_type`` is equal to *Scalar* . Element Access ************** If *x* is a *SimpleVector* < *Scalar* > object and *i* has type ``size_t`` , *x* [ *i* ] returns an object of an unspecified type, referred to here as *elementType* . Using Value =========== If *elementType* is not the same as *Scalar* , the conversion operator ``static_cast`` < *Scalar* >( *x* [ *i* ]) is used implicitly when *x* [ *i* ] is used in an expression with values of type *Scalar* . For this type of usage, the object *x* may be ``const`` . Assignment ========== If *y* is an object of type *Scalar* , *x* [ *i* ] = *y* assigns the *i*-th element of *x* to have value *y* . For this type of usage, the object *x* can not be ``const`` . The type returned by this assignment is unspecified; for example, it might be void in which case the syntax *z* = *x* [ *i* ] = *y* would not be valid. Example ******* {xrst_toc_hidden example/utility/simple_vector.cpp } The file :ref:`simple_vector.cpp-name` contains an example and test of a Simple template class. (It is easy to modify to test additional simple vector template classes.) Exercise ******** #. If *Vector* is a simple vector template class, the following code may not be valid: | |tab| *Vector* < ``double> x`` (2); | |tab| ``x`` [2] = 1.; Create and run a program that executes the code segment above where *Vector* is each of the following cases: ``std::vector`` , ``CppAD::vector`` . Do this both where the compiler option ``-DNDEBUG`` is and is not present on the compilation command line. #. If *Vector* is a simple vector template class, the following code may not be valid: | |tab| *Vector* < ``int> x`` (2); | |tab| *Vector* < ``int> y`` (1); | |tab| ``x`` [0] = 0; | |tab| ``x`` [1] = 1; | |tab| ``y`` = ``x`` ; Create and run a program that executes the code segment above where *Vector* is each of the following cases: ``std::valarray`` , ``CppAD::vector`` . Do this both where the compiler option ``-DNDEBUG`` is and is not present on the compilation command line. {xrst_end SimpleVector} ================================================ FILE: xrst/sparse_hes_fun.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin sparse_hes_fun.hpp} Source: sparse_hes_fun ###################### | # ``ifndef CPPAD_SPARSE_HES_FUN_HPP`` | # ``define CPPAD_SPARSE_HES_FUN_HPP`` {xrst_literal include/cppad/speed/sparse_hes_fun.hpp // BEGIN C++ // END C++ } ``# endif`` {xrst_end sparse_hes_fun.hpp} ================================================ FILE: xrst/sparse_jac_fun.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin sparse_jac_fun.hpp} Source: sparse_jac_fun ###################### | # ``ifndef CPPAD_SPARSE_JAC_FUN_HPP`` | # ``define CPPAD_SPARSE_JAC_FUN_HPP`` {xrst_literal include/cppad/speed/sparse_jac_fun.hpp // BEGIN C++ // END C++ } ``# endif`` {xrst_end sparse_jac_fun.hpp} ================================================ FILE: xrst/theory/acos_forward.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin acos_forward} {xrst_spell acosh } Inverse Cosine and Hyperbolic Cosine Forward Mode Theory ######################################################## Derivatives *********** .. math:: :nowrap: \begin{eqnarray} \R{acos}^{(1)} (x) & = & - 1 / \sqrt{ 1 - x * x } \\ \R{acosh}^{(1)} (x) & = & + 1 / \sqrt{ x * x - 1} \end{eqnarray} If :math:`F(x)` is :math:`\R{acos} (x)` or :math:`\R{acosh} (x)` the corresponding derivative satisfies the equation .. math:: \sqrt{ \mp ( x * x - 1 ) } * F^{(1)} (x) - 0 * F (u) = \mp 1 and in the :ref:`standard math function differential equation` , :math:`A(x) = 0`, :math:`B(x) = \sqrt{ \mp( x * x - 1 ) }`, and :math:`D(x) = \mp 1`. We use :math:`a`, :math:`b`, :math:`d` and :math:`z` to denote the Taylor coefficients for :math:`A [ X (t) ]`, :math:`B [ X (t) ]`, :math:`D [ X (t) ]`, and :math:`F [ X(t) ]` respectively. Taylor Coefficients Recursion ***************************** We define :math:`Q(x) = \mp ( x * x - 1 )` and let :math:`q` be the corresponding Taylor coefficients for :math:`Q[ X(t) ]`. It follows that .. math:: q^{(j)} = \left\{ \begin{array}{ll} \mp ( x^{(0)} * x^{(0)} - 1 ) & {\rm if} \; j = 0 \\ \mp \sum_{k=0}^j x^{(k)} x^{(j-k)} & {\rm otherwise} \end{array} \right. It follows that :math:`B[ X(t) ] = \sqrt{ Q[ X(t) ] }` and from the equations for the :ref:`square root` that for :math:`j = 0 , 1, \ldots`, .. math:: :nowrap: \begin{eqnarray} b^{(0)} & = & \sqrt{ q^{(0)} } \\ b^{(j+1)} & = & \frac{1}{j+1} \frac{1}{ b^{(0)} } \left( \frac{j+1}{2} q^{(j+1) } - \sum_{k=1}^j k b^{(k)} b^{(j+1-k)} \right) \end{eqnarray} It now follows from the general :ref:`forward_theory@Standard Math Functions@Taylor Coefficients Recursion Formula` that for :math:`j = 0 , 1, \ldots`, .. math:: :nowrap: \begin{eqnarray} z^{(0)} & = & F ( x^{(0)} ) \\ e^{(j)} & = & d^{(j)} + \sum_{k=0}^{j} a^{(j-k)} * z^{(k)} \\ & = & \left\{ \begin{array}{ll} \mp 1 & {\rm if} \; j = 0 \\ 0 & {\rm otherwise} \end{array} \right. \\ z^{(j+1)} & = & \frac{1}{j+1} \frac{1}{ b^{(0)} } \left( \sum_{k=0}^j e^{(k)} (j+1-k) x^{(j+1-k)} - \sum_{k=1}^j b^{(k)} (j+1-k) z^{(j+1-k)} \right) \\ z^{(j+1)} & = & \frac{1}{j+1} \frac{1}{ b^{(0)} } \left( \mp (j+1) x^{(j+1)} - \sum_{k=1}^j k z^{(k)} b^{(j+1-k)} \right) \end{eqnarray} {xrst_end acos_forward} ================================================ FILE: xrst/theory/acos_reverse.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin acos_reverse} {xrst_spell acosh } Inverse Cosine and Hyperbolic Cosine Reverse Mode Theory ######################################################## We use the reverse theory :ref:`standard math function` definition for the functions :math:`H` and :math:`G`. In addition, we use the forward mode notation in :ref:`acos_forward-name` for .. math:: :nowrap: \begin{eqnarray} Q(t) & = & \mp ( X(t) * X(t) - 1 ) \\ B(t) & = & \sqrt{ Q(t) } \end{eqnarray} We use :math:`q` and :math:`b` for the *p*-th order Taylor coefficient row vectors corresponding to these functions and replace :math:`z^{(j)}` by .. math:: ( z^{(j)} , b^{(j)} ) in the definition for :math:`G` and :math:`H`. The zero order forward mode formulas for the :ref:`acos` function are .. math:: :nowrap: \begin{eqnarray} q^{(0)} & = & \mp ( x^{(0)} x^{(0)} - 1) \\ b^{(0)} & = & \sqrt{ q^{(0)} } \\ z^{(0)} & = & F ( x^{(0)} ) \end{eqnarray} where :math:`F(x) = \R{acos} (x)` for :math:`-` and :math:`F(x) = \R{acosh} (x)` for :math:`+`. For orders :math:`j` greater than zero we have .. math:: :nowrap: \begin{eqnarray} q^{(j)} & = & \mp \sum_{k=0}^j x^{(k)} x^{(j-k)} \\ b^{(j)} & = & \frac{1}{j} \frac{1}{ b^{(0)} } \left( \frac{j}{2} q^{(j)} - \sum_{k=1}^{j-1} k b^{(k)} b^{(j-k)} \right) \\ z^{(j)} & = & \frac{1}{j} \frac{1}{ b^{(0)} } \left( \mp j x^{(j)} - \sum_{k=1}^{j-1} k z^{(k)} b^{(j-k)} \right) \end{eqnarray} If :math:`j = 0`, we note that :math:`F^{(1)} ( x^{(0)} ) = \mp 1 / b^{(0)}` and hence .. math:: :nowrap: \begin{eqnarray} \D{H}{ x^{(j)} } & = & \D{G}{ x^{(j)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ x^{(0)} } + \D{G}{ b^{(j)} } \D{ b^{(j)} }{ q^{(0)} } \D{ q^{(0)} }{ x^{(0)} } \\ & = & \D{G}{ x^{(j)} } \mp \D{G}{ z^{(j)} } \frac{1}{ b^{(0)} } \mp \D{G}{ b^{(j)} } \frac{ x^{(0)} }{ b^{(0)} } \end{eqnarray} If :math:`j > 0`, then for :math:`k = 1, \ldots , j-1` .. math:: :nowrap: \begin{eqnarray} \D{H}{ b^{(0)} } & = & \D{G}{ b^{(0)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ b^{(0)} } + \D{G}{ b^{(j)} } \D{ b^{(j)} }{ b^{(0)} } \\ & = & \D{G}{ b^{(0)} } - \D{G}{ z^{(j)} } \frac{ z^{(j)} }{ b^{(0)} } - \D{G}{ b^{(j)} } \frac{ b^{(j)} }{ b^{(0)} } \\ \D{H}{ x^{(0)} } & = & \D{G}{ x^{(0)} } + \D{G}{ b^{(j)} } \D{ b^{(j)} }{ q^{(j)} } \D{ q^{(j)} }{ x^{(0)} } \\ & = & \D{G}{ x^{(0)} } \mp \D{G}{ b^{(j)} } \frac{ x^{(j)} }{ b^{(0)} } \\ \D{H}{ x^{(j)} } & = & \D{G}{ x^{(j)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ x^{(j)} } + \D{G}{ b^{(j)} } \D{ b^{(j)} }{ q^{(j)} } \D{ q^{(j)} }{ x^{(j)} } \\ & = & \D{G}{ x^{(j)} } \mp \D{G}{ z^{(j)} } \frac{1}{ b^{(0)} } \mp \D{G}{ b^{(j)} } \frac{ x^{(0)} }{ b^{(0)} } \\ \D{H}{ b^{(j - k)} } & = & \D{G}{ b^{(j - k)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ b^{(j - k)} } + \D{G}{ b^{(j)} } \D{ b^{(j)} }{ b^{(j - k)} } \\ & = & \D{G}{ b^{(j - k)} } - \D{G}{ z^{(j)} } \frac{k z^{(k)} }{j b^{(0)} } - \D{G}{ b^{(j)} } \frac{ b^{(k)} }{ b^{(0)} } \\ \D{H}{ x^{(k)} } & = & \D{G}{ x^{(k)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ x^{(k)} } + \D{G}{ b^{(j)} } \D{ b^{(j)} }{ q^{(j)} } \D{ q^{(j)} }{ x^{(k)} } \\ & = & \D{G}{ x^{(k)} } \mp \D{G}{ b^{(j)} } \frac{ x^{(j-k)} }{ b^{(0)} } \\ \D{H}{ z^{(k)} } & = & \D{G}{ z^{(k)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ z^{(k)} } + \D{G}{ b^{(j)} } \D{ b^{(j)} }{ z^{(k)} } \\ & = & \D{G}{ z^{(k)} } - \D{G}{ z^{(j)} } \frac{k b^{(j-k)} }{ j b^{(0)} } \end{eqnarray} {xrst_end acos_reverse} ================================================ FILE: xrst/theory/asin_forward.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin asin_forward} {xrst_spell asinh } Inverse Sine and Hyperbolic Sine Forward Mode Theory #################################################### Derivatives *********** .. math:: :nowrap: \begin{eqnarray} \R{asin}^{(1)} (x) & = & 1 / \sqrt{ 1 - x * x } \\ \R{asinh}^{(1)} (x) & = & 1 / \sqrt{ 1 + x * x } \end{eqnarray} If :math:`F(x)` is :math:`\R{asin} (x)` or :math:`\R{asinh} (x)` the corresponding derivative satisfies the equation .. math:: \sqrt{ 1 \mp x * x } * F^{(1)} (x) - 0 * F (u) = 1 and in the :ref:`standard math function differential equation` , :math:`A(x) = 0`, :math:`B(x) = \sqrt{1 \mp x * x }`, and :math:`D(x) = 1`. We use :math:`a`, :math:`b`, :math:`d` and :math:`z` to denote the Taylor coefficients for :math:`A [ X (t) ]`, :math:`B [ X (t) ]`, :math:`D [ X (t) ]`, and :math:`F [ X(t) ]` respectively. Taylor Coefficients Recursion ***************************** We define :math:`Q(x) = 1 \mp x * x` and let :math:`q` be the corresponding Taylor coefficients for :math:`Q[ X(t) ]`. It follows that .. math:: q^{(j)} = \left\{ \begin{array}{ll} 1 \mp x^{(0)} * x^{(0)} & {\rm if} \; j = 0 \\ \mp \sum_{k=0}^j x^{(k)} x^{(j-k)} & {\rm otherwise} \end{array} \right. It follows that :math:`B[ X(t) ] = \sqrt{ Q[ X(t) ] }` and from the equations for the :ref:`square root` that for :math:`j = 0 , 1, \ldots`, .. math:: :nowrap: \begin{eqnarray} b^{(0)} & = & \sqrt{ q^{(0)} } \\ b^{(j+1)} & = & \frac{1}{j+1} \frac{1}{ b^{(0)} } \left( \frac{j+1}{2} q^{(j+1) } - \sum_{k=1}^j k b^{(k)} b^{(j+1-k)} \right) \end{eqnarray} It now follows from the general :ref:`forward_theory@Standard Math Functions@Taylor Coefficients Recursion Formula` that for :math:`j = 0 , 1, \ldots`, .. math:: :nowrap: \begin{eqnarray} z^{(0)} & = & F ( x^{(0)} ) \\ e^{(j)} & = & d^{(j)} + \sum_{k=0}^{j} a^{(j-k)} * z^{(k)} \\ & = & \left\{ \begin{array}{ll} 1 & {\rm if} \; j = 0 \\ 0 & {\rm otherwise} \end{array} \right. \\ z^{(j+1)} & = & \frac{1}{j+1} \frac{1}{ b^{(0)} } \left( \sum_{k=0}^j e^{(k)} (j+1-k) x^{(j+1-k)} - \sum_{k=1}^j b^{(k)} (j+1-k) z^{(j+1-k)} \right) \\ z^{(j+1)} & = & \frac{1}{j+1} \frac{1}{ b^{(0)} } \left( (j+1) x^{(j+1)} - \sum_{k=1}^j k z^{(k)} b^{(j+1-k)} \right) \end{eqnarray} {xrst_end asin_forward} ================================================ FILE: xrst/theory/asin_reverse.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin asin_reverse} {xrst_spell asinh } Inverse Sine and Hyperbolic Sine Reverse Mode Theory #################################################### We use the reverse theory :ref:`standard math function` definition for the functions :math:`H` and :math:`G`. In addition, we use the forward mode notation in :ref:`asin_forward-name` for .. math:: :nowrap: \begin{eqnarray} Q(t) & = & 1 \mp X(t) * X(t) \\ B(t) & = & \sqrt{ Q(t) } \end{eqnarray} We use :math:`q` and :math:`b` for the *p*-th order Taylor coefficient row vectors corresponding to these functions and replace :math:`z^{(j)}` by .. math:: ( z^{(j)} , b^{(j)} ) in the definition for :math:`G` and :math:`H`. The zero order forward mode formulas for the :ref:`asin` function are .. math:: :nowrap: \begin{eqnarray} q^{(0)} & = & 1 \mp x^{(0)} x^{(0)} \\ b^{(0)} & = & \sqrt{ q^{(0)} } \\ z^{(0)} & = & F( x^{(0)} ) \end{eqnarray} where :math:`F(x) = \R{asin} (x)` for :math:`-` and :math:`F(x) = \R{asinh} (x)` for :math:`+`. For the orders :math:`j` greater than zero we have .. math:: :nowrap: \begin{eqnarray} q^{(j)} & = & \mp \sum_{k=0}^j x^{(k)} x^{(j-k)} \\ b^{(j)} & = & \frac{1}{j} \frac{1}{ b^{(0)} } \left( \frac{j}{2} q^{(j)} - \sum_{k=1}^{j-1} k b^{(k)} b^{(j-k)} \right) \\ z^{(j)} & = & \frac{1}{j} \frac{1}{ b^{(0)} } \left( j x^{(j)} - \sum_{k=1}^{j-1} k z^{(k)} b^{(j-k)} \right) \end{eqnarray} If :math:`j = 0`, we note that :math:`F^{(1)} ( x^{(0)} ) = 1 / b^{(0)}` and hence .. math:: :nowrap: \begin{eqnarray} \D{H}{ x^{(j)} } & = & \D{G}{ x^{(j)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ x^{(0)} } + \D{G}{ b^{(j)} } \D{ b^{(j)} }{ q^{(0)} } \D{ q^{(0)} }{ x^{(0)} } \\ & = & \D{G}{ x^{(j)} } + \D{G}{ z^{(j)} } \frac{1}{ b^{(0)} } \mp \D{G}{ b^{(j)} } \frac{ x^{(0)} }{ b^{(0)} } \end{eqnarray} If :math:`j > 0`, then for :math:`k = 1, \ldots , j-1` .. math:: :nowrap: \begin{eqnarray} \D{H}{ b^{(0)} } & = & \D{G}{ b^{(0)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ b^{(0)} } + \D{G}{ b^{(j)} } \D{ b^{(j)} }{ b^{(0)} } \\ & = & \D{G}{ b^{(0)} } - \D{G}{ z^{(j)} } \frac{ z^{(j)} }{ b^{(0)} } - \D{G}{ b^{(j)} } \frac{ b^{(j)} }{ b^{(0)} } \\ \D{H}{ x^{(0)} } & = & \D{G}{ x^{(0)} } + \D{G}{ b^{(j)} } \D{ b^{(j)} }{ q^{(j)} } \D{ q^{(j)} }{ x^{(0)} } \\ & = & \D{G}{ x^{(0)} } \mp \D{G}{ b^{(j)} } \frac{ x^{(j)} }{ b^{(0)} } \\ \D{H}{ x^{(j)} } & = & \D{G}{ x^{(j)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ x^{(j)} } + \D{G}{ b^{(j)} } \D{ b^{(j)} }{ q^{(j)} } \D{ q^{(j)} }{ x^{(j)} } \\ & = & \D{G}{ x^{(j)} } + \D{G}{ z^{(j)} } \frac{1}{ b^{(0)} } \mp \D{G}{ b^{(j)} } \frac{ x^{(0)} }{ b^{(0)} } \\ \D{H}{ b^{(j - k)} } & = & \D{G}{ b^{(j - k)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ b^{(j - k)} } + \D{G}{ b^{(j)} } \D{ b^{(j)} }{ b^{(j - k)} } \\ & = & \D{G}{ b^{(j - k)} } - \D{G}{ z^{(j)} } \frac{k z^{(k)} }{j b^{(0)} } - \D{G}{ b^{(j)} } \frac{ b^{(k)} }{ b^{(0)} } \\ \D{H}{ x^{(k)} } & = & \D{G}{ x^{(k)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ x^{(k)} } + \D{G}{ b^{(j)} } \D{ b^{(j)} }{ q^{(j)} } \D{ q^{(j)} }{ x^{(k)} } \\ & = & \D{G}{ x^{(k)} } \mp \D{G}{ b^{(j)} } \frac{ x^{(j-k)} }{ b^{(0)} } \\ \D{H}{ z^{(k)} } & = & \D{G}{ z^{(k)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ z^{(k)} } + \D{G}{ b^{(j)} } \D{ b^{(j)} }{ z^{(k)} } \\ & = & \D{G}{ z^{(k)} } - \D{G}{ z^{(j)} } \frac{k b^{(j-k)} }{ j b^{(0)} } \end{eqnarray} {xrst_end asin_reverse} ================================================ FILE: xrst/theory/atan_forward.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin atan_forward} Inverse Tangent and Hyperbolic Tangent Forward Mode Theory ########################################################## Derivatives *********** .. math:: :nowrap: \begin{eqnarray} \R{atan}^{(1)} (x) & = & 1 / ( 1 + x * x ) \\ \R{atanh}^{(1)} (x) & = & 1 / ( 1 - x * x ) \end{eqnarray} If :math:`F(x)` is :math:`\R{atan} (x)` or :math:`\R{atanh} (x)`, the corresponding derivative satisfies the equation .. math:: (1 \pm x * x ) * F^{(1)} (x) - 0 * F (x) = 1 and in the :ref:`standard math function differential equation` , :math:`A(x) = 0`, :math:`B(x) = 1 \pm x * x`, and :math:`D(x) = 1`. We use :math:`a`, :math:`b`, :math:`d` and :math:`z` to denote the Taylor coefficients for :math:`A [ X (t) ]`, :math:`B [ X (t) ]`, :math:`D [ X (t) ]`, and :math:`F [ X(t) ]` respectively. Taylor Coefficients Recursion ***************************** For :math:`j = 0 , 1, \ldots`, .. math:: :nowrap: \begin{eqnarray} z^{(0)} & = & F( x^{(0)} ) \\ b^{(j)} & = & \left\{ \begin{array}{ll} 1 \pm x^{(0)} * x^{(0)} & {\rm if} \; j = 0 \\ \pm \sum_{k=0}^j x^{(k)} x^{(j-k)} & {\rm otherwise} \end{array} \right. \\ e^{(j)} & = & d^{(j)} + \sum_{k=0}^{j} a^{(j-k)} * z^{(k)} \\ & = & \left\{ \begin{array}{ll} 1 & {\rm if} \; j = 0 \\ 0 & {\rm otherwise} \end{array} \right. \\ z^{(j+1)} & = & \frac{1}{j+1} \frac{1}{ b^{(0)} } \left( \sum_{k=0}^j e^{(k)} (j+1-k) x^{(j+1-k)} - \sum_{k=1}^j b^{(k)} (j+1-k) z^{(j+1-k)} \right) \\ z^{(j+1)} & = & \frac{1}{j+1} \frac{1}{ b^{(0)} } \left( (j+1) x^{(j+1)} - \sum_{k=1}^j k z^{(k)} b^{(j+1-k)} \right) \end{eqnarray} {xrst_end atan_forward} ================================================ FILE: xrst/theory/atan_reverse.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin atan_reverse} Inverse Tangent and Hyperbolic Tangent Reverse Mode Theory ########################################################## We use the reverse theory :ref:`standard math function` definition for the functions :math:`H` and :math:`G`. In addition, we use the forward mode notation in :ref:`atan_forward-name` for .. math:: B(t) = 1 \pm X(t) * X(t) We use :math:`b` for the *p*-th order Taylor coefficient row vectors corresponding to :math:`B(t)` and replace :math:`z^{(j)}` by .. math:: ( z^{(j)} , b^{(j)} ) in the definition for :math:`G` and :math:`H`. The zero order forward mode formulas for the :ref:`atan` function are .. math:: :nowrap: \begin{eqnarray} z^{(0)} & = & F ( x^{(0)} ) \\ b^{(0)} & = & 1 \pm x^{(0)} x^{(0)} \end{eqnarray} where :math:`F(x) = \R{atan} (x)` for :math:`+` and :math:`F(x) = \R{atanh} (x)` for :math:`-`. For orders :math:`j` greater than zero we have .. math:: :nowrap: \begin{eqnarray} b^{(j)} & = & \pm \sum_{k=0}^j x^{(k)} x^{(j-k)} \\ z^{(j)} & = & \frac{1}{j} \frac{1}{ b^{(0)} } \left( j x^{(j)} - \sum_{k=1}^{j-1} k z^{(k)} b^{(j-k)} \right) \end{eqnarray} If :math:`j = 0`, we note that :math:`F^{(1)} ( x^{(0)} ) = 1 / b^{(0)}` and hence .. math:: :nowrap: \begin{eqnarray} \D{H}{ x^{(j)} } & = & \D{G}{ x^{(j)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ x^{(0)} } + \D{G}{ b^{(j)} } \D{ b^{(j)} }{ x^{(0)} } \\ & = & \D{G}{ x^{(j)} } + \D{G}{ z^{(j)} } \frac{1}{ b^{(0)} } \pm \D{G}{ b^{(j)} } 2 x^{(0)} \end{eqnarray} If :math:`j > 0`, then for :math:`k = 1, \ldots , j-1` .. math:: :nowrap: \begin{eqnarray} \D{H}{ b^{(0)} } & = & \D{G}{ b^{(0)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ b^{(0)} } \\ & = & \D{G}{ b^{(0)} } - \D{G}{ z^{(j)} } \frac{ z^{(j)} }{ b^{(0)} } \\ \D{H}{ x^{(j)} } & = & \D{G}{ x^{(j)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ x^{(j)} } + \D{G}{ b^{(j)} } \D{ b^{(j)} }{ x^{(j)} } \\ & = & \D{G}{ x^{(j)} } + \D{G}{ z^{(j)} } \frac{1}{ b^{(0)} } \pm \D{G}{ b^{(j)} } 2 x^{(0)} \\ \D{H}{ x^{(0)} } & = & \D{G}{ x^{(0)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ x^{(0)} } + \D{G}{ b^{(j)} } \D{ b^{(j)} }{ x^{(0)} } \\ & = & \D{G}{ x^{(0)} } \pm \D{G}{ b^{(j)} } 2 x^{(j)} \\ \D{H}{ x^{(k)} } & = & \D{G}{ x^{(k)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ x^{(k)} } + \D{G}{ b^{(j)} } \D{ b^{(j)} }{ x^{(k)} } \\ & = & \D{G}{ x^{(k)} } \pm \D{G}{ b^{(j)} } 2 x^{(j-k)} \\ \D{H}{ z^{(k)} } & = & \D{G}{ z^{(k)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ z^{(k)} } + \D{G}{ b^{(j)} } \D{ b^{(j)} }{ z^{(k)} } \\ & = & \D{G}{ z^{(k)} } - \D{G}{ z^{(j)} } \frac{k b^{(j-k)} }{ j b^{(0)} } \\ \D{H}{ b^{(j-k)} } & = & \D{G}{ b^{(j-k)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ b^{(j-k)} } + \D{G}{ b^{(j)} } \D{ b^{(j)} }{ b^{(j-k)} } \\ & = & \D{G}{ b^{(j-k)} } - \D{G}{ z^{(j)} } \frac{k z^{(k)} }{ j b^{(0)} } \end{eqnarray} {xrst_end atan_reverse} ================================================ FILE: xrst/theory/cholesky.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin cholesky_theory app} {xrst_spell diag tr universitat zu } AD Theory for Cholesky Factorization #################################### Reference ********* See section 3.6 of Sebastian F. Walter's Ph.D. thesis, *Structured Higher-Order Algorithmic Differentiation* *in the Forward and Reverse Mode* *with Application in Optimum Experimental Design* , Humboldt-Universitat zu Berlin, 2011. Notation ******** Cholesky Factor =============== We are given a positive definite symmetric matrix :math:`A \in \B{R}^{n \times n}` and a Cholesky factorization .. math:: A = L L^\R{T} where :math:`L \in \B{R}^{n \times n}` is lower triangular. Taylor Coefficient ================== The matrix :math:`A` is a function of a scalar argument :math:`t`. For :math:`k = 0 , \ldots , K`, we use :math:`A_k` for the corresponding Taylor coefficients; i.e., .. math:: A(t) = o( t^K ) + \sum_{k = 0}^K A_k t^k where :math:`o( t^K ) / t^K \rightarrow 0` as :math:`t \rightarrow 0`. We use a similar notation for :math:`L(t)`. Lower Triangular Part ===================== For a square matrix :math:`C`, :math:`\R{lower} (C)` is the lower triangular part of :math:`C`, :math:`\R{diag} (C)` is the diagonal matrix with the same diagonal as :math:`C` and .. math:: \R{low} ( C ) = \R{lower} (C) - \frac{1}{2} \R{diag} (C) Forward Mode ************ For Taylor coefficient order :math:`k = 0 , \ldots , K` the coefficients :math:`A_k \in \B{R}^{n \times n}`, and satisfy the equation .. math:: A_k = \sum_{\ell=0}^k L_\ell L_{k-\ell}^\R{T} In the case where :math:`k=0`, the .. math:: A_0 = L_0 L_0^\R{T} The value of :math:`L_0` can be computed using the Cholesky factorization. In the case where :math:`k > 0`, .. math:: A_k = L_k L_0^\R{T} + L_0 L_k^\R{T} + B_k where .. math:: B_k = \sum_{\ell=1}^{k-1} L_\ell L_{k-\ell}^\R{T} Note that :math:`B_k` is defined in terms of Taylor coefficients of :math:`L(t)` that have order less than :math:`k`. We also note that .. math:: L_0^{-1} ( A_k - B_k ) L_0^\R{-T} = L_0^{-1} L_k + L_k^\R{T} L_0^\R{-T} The first matrix on the right hand side is lower triangular, the second is upper triangular, and the diagonals are equal. It follows that .. math:: L_0^{-1} L_k = \R{low} [ L_0^{-1} ( A_k - B_k ) L_0^\R{-T} ] .. math:: L_k = L_0 \R{low} [ L_0^{-1} ( A_k - B_k ) L_0^\R{-T} ] This expresses :math:`L_k` in term of the Taylor coefficients of :math:`A(t)` and the lower order coefficients of :math:`L(t)`. Lemma 1 ******* We use the notation :math:`\dot{C}` for the derivative of a matrix valued function :math:`C(s)` with respect to a scalar argument :math:`s`. We use the notation :math:`\bar{S}` and :math:`\bar{L}` for the partial derivative of a scalar value function :math:`\bar{F}( S, L)` with respect to a symmetric matrix :math:`S` and an lower triangular matrix :math:`L`. Define the scalar valued function .. math:: \hat{F}( C ) = \bar{F} [ S , \hat{L} (S) ] We use :math:`\hat{S}` for the total derivative of :math:`\hat{F}` with respect to :math:`S`. Suppose that :math:`\hat{L} ( S )` is such that .. math:: \dot{L} = L_0 \R{low} ( L_0^{-1} \dot{S} L_0^\R{-T} ) for any :math:`S(s)`. It follows that .. math:: \hat{S} = \bar{S} + \frac{1}{2} ( M + M^\R{T} ) where .. math:: M = L_0^\R{-T} \R{low}( L_0^\R{T} \bar{L} )^\R{T} L_0^{-1} Proof ===== .. math:: \partial_s \hat{F} [ S(s) , L(s) ] = \R{tr} ( \bar{S}^\R{T} \dot{S} ) + \R{tr} ( \bar{L}^\R{T} \dot{L} ) .. math:: \R{tr} ( \bar{L}^\R{T} \dot{L} ) = \R{tr} [ \bar{L}^\R{T} L_0 \R{low} ( L_0^{-1} \dot{S} L_0^\R{-T} ) ] .. math:: = \R{tr} [ \R{low} ( L_0^{-1} \dot{S} L_0^\R{-T} )^\R{T} L_0^\R{T} \bar{L} ] .. math:: = \R{tr} [ L_0^{-1} \dot{S} L_0^\R{-T} \R{low}( L_0^\R{T} \bar{L} ) ] .. math:: = \R{tr} [ L_0^\R{-T} \R{low}( L_0^\R{T} \bar{L} ) L_0^{-1} \dot{S} ] .. math:: \partial_s \hat{F} [ S(s) , L(s) ] = \R{tr} ( \bar{S}^\R{T} \dot{S} ) + \R{tr} [ L_0^\R{-T} \R{low}( L_0^\R{T} \bar{L} ) L_0^{-1} \dot{S} ] We now consider the :math:`(i, j)` component function, for a symmetric matrix :math:`S(s)`, defined by .. math:: S_{k, \ell} (s) = \left\{ \begin{array}{ll} 1 & \R{if} \; k = i \; \R{and} \; \ell = j \\ 1 & \R{if} \; k = j \; \R{and} \; \ell = i \\ 0 & \R{otherwise} \end{array} \right\} This shows that the formula in the lemma is correct for :math:`\hat{S}_{i,j}` and :math:`\hat{S}_{j,i}`. This completes the proof because the component :math:`(i, j)` was arbitrary. Lemma 2 ******* We use the same assumptions as in Lemma 1 except that the matrix :math:`S` is lower triangular (instead of symmetric). It follows that .. math:: \hat{S} = \bar{S} + \R{lower}(M) where .. math:: M = L_0^\R{-T} \R{low}( L_0^\R{T} \bar{L} )^\R{T} L_0^{-1} The proof of this lemma is identical to Lemma 2 except that component function is defined by .. math:: S_{k, \ell} (s) = \left\{ \begin{array}{ll} 1 & \R{if} \; k = i \; \R{and} \; \ell = j \\ 0 & \R{otherwise} \end{array} \right\} Reverse Mode ************ k Equal To 0 ============ For the case :math:`k = 0`, .. math:: \dot{A}_0 = \dot{L}_0 L_0^\R{T} + L_0 \dot{L}_0^\R{T} .. math:: L_0^{-1} \dot{A}_0 L_0^\R{-T} = L_0^{-1} \dot{L}_0 + \dot{L}_0^\R{T} L_0^\R{-T} .. math:: \R{low} ( L_0^{-1} \dot{A}_0 L_0^\R{-T} ) = L_0^{-1} \dot{L}_0 .. math:: \dot{L}_0 = L_0 \R{low} ( L_0^{-1} \dot{A}_0 L_0^\R{-T} ) It follows from Lemma 1 that .. math:: \bar{A}_0 \stackrel{+}{=} \frac{1}{2} ( M + M^\R{T} ) where .. math:: M = L_0^\R{-T} \R{low} ( L_0^\R{T} \bar{L}_0 )^\R{T} L_0^{-1} and :math:`\bar{A}_0` is the partial before and after is before and after :math:`L_0` is removed from the scalar function dependency. k Greater Than 0 ================ In the case where :math:`k > 0`, .. math:: A_k = L_k L_0^\R{T} + L_0 L_k^\R{T} + B_k where :math:`B_k` is defined in terms of Taylor coefficients of :math:`L(t)` that have order less than :math:`k`. It follows that .. math:: \dot{L}_k L_0^\R{T} + L_0 \dot{L}_k^\R{T} = \dot{A}_k - \dot{B}_k - \dot{L}_0 L_k^\R{T} - L_k \dot{L}_0^\R{T} .. math:: L_0^{-1} \dot{L}_k + \dot{L}_k^\R{T} L_0^\R{-T} = L_0^{-1} ( \dot{A}_k - \dot{B}_k - \dot{L}_0 L_k^\R{T} - L_k \dot{L}_0^\R{T} ) L_0^\R{-T} .. math:: L_0^{-1} \dot{L}_k = \R{low} [ L_0^{-1} ( \dot{A}_k - \dot{B}_k - \dot{L}_0 L_k^\R{T} - L_k \dot{L}_0^\R{T} ) L_0^\R{-T} ] .. math:: \dot{L}_k = L_0 \R{low} [ L_0^{-1} ( \dot{A}_k - \dot{B}_k - \dot{L}_0 L_k^\R{T} - L_k \dot{L}_0^\R{T} ) L_0^\R{-T} ] The matrix :math:`A_k` is symmetric, it follows that .. math:: \bar{A}_k \stackrel{+}{=} \frac{1}{2} ( M_k + M_k^\R{T} ) where .. math:: M_k = L_0^\R{-T} \R{low} ( L_0^\R{T} \bar{L}_k )^\R{T} L_0^{-1} The matrix :math:`B_k` is also symmetric, hence .. math:: \bar{B}_k = - \; \frac{1}{2} ( M_k + M_k^\R{T} ) We define the symmetric matrix :math:`C_k (s)` by .. math:: \dot{C}_k = \dot{L}_0 L_k^\R{T} + L_k \dot{L}_0^\R{T} and remove the dependency on :math:`C_k` with .. math:: \R{tr}( \bar{C}_k^\R{T} \dot{C}_k ) = \R{tr}( \bar{B}_k^\R{T} \dot{C}_k ) = \R{tr}( \bar{B}_k^\R{T} \dot{L}_0 L_k^\R{T} ) + \R{tr}( \bar{B}_k^\R{T} L_k \dot{L}_0^\R{T} ) .. math:: = \R{tr}( L_k^\R{T} \bar{B}_k^\R{T} \dot{L}_0 ) + \R{tr}( L_k^\R{T} \bar{B}_k \dot{L}_0 ) .. math:: = \R{tr}[ L_k^\R{T} ( \bar{B}_k + \bar{B}_k^\R{T} ) \dot{L}_0 ] Thus, removing :math:`C_k` from the dependency results in the following update to :math:`\bar{L}_0`: .. math:: \bar{L}_0 \stackrel{+}{=} \R{lower} [ ( \bar{B}_k + \bar{B}_k^\R{T} ) L_k ] which is the same as .. math:: \bar{L}_0 \stackrel{+}{=} 2 \; \R{lower} [ \bar{B}_k L_k ] We still need to remove :math:`B_k` from the dependency. It follows from its definition that .. math:: \dot{B}_k = \sum_{\ell=1}^{k-1} \dot{L}_\ell L_{k-\ell}^\R{T} + L_\ell \dot{L}_{k-\ell}^\R{T} .. math:: \R{tr}( \bar{B}_k^\R{T} \dot{B}_k ) = \sum_{\ell=1}^{k-1} \R{tr}( \bar{B}_k^\R{T} \dot{L}_\ell L_{k-\ell}^\R{T} ) + \R{tr}( \bar{B}_k^\R{T} L_\ell \dot{L}_{k-\ell}^\R{T} ) .. math:: = \sum_{\ell=1}^{k-1} \R{tr}( L_{k-\ell}^\R{T} \bar{B}_k^\R{T} \dot{L}_\ell ) + \sum_{\ell=1}^{k-1} \R{tr}( L_\ell^\R{T} \bar{B}_k \dot{L}_{k-\ell} ) We now use the fact that :math:`\bar{B}_k` is symmetric to conclude .. math:: \R{tr}( \bar{B}_k^\R{T} \dot{B}_k ) = 2 \sum_{\ell=1}^{k-1} \R{tr}( L_{k-\ell}^\R{T} \bar{B}_k^\R{T} \dot{L}_\ell ) Each of the :math:`\dot{L}_\ell` matrices is lower triangular. Thus, removing :math:`B_k` from the dependency results in the following update for :math:`\ell = 1 , \ldots , k-1`: .. math:: \bar{L}_\ell \stackrel{+}{=} 2 \; \R{lower}( \bar{B}_k L_{k-\ell} ) {xrst_end cholesky_theory} ================================================ FILE: xrst/theory/erf_forward.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin erf_forward} Error Function Forward Taylor Polynomial Theory ############################################### Derivatives *********** Given :math:`X(t)`, we define the function .. math:: Z(t) = \R{erf}[ X(t) ] It follows that .. math:: :nowrap: \begin{eqnarray} \R{erf}^{(1)} ( u ) & = & ( 2 / \sqrt{\pi} ) \exp \left( - u^2 \right) \\ Z^{(1)} (t) & = & \R{erf}^{(1)} [ X(t) ] X^{(1)} (t) = Y(t) X^{(1)} (t) \end{eqnarray} where we define the function .. math:: Y(t) = \frac{2}{ \sqrt{\pi} } \exp \left[ - X(t)^2 \right] Taylor Coefficients Recursion ***************************** Suppose that we are given the Taylor coefficients up to order :math:`j` for the function :math:`X(t)` and :math:`Y(t)`. We need a formula that computes the coefficient of order :math:`j` for :math:`Z(t)`. Using the equation above for :math:`Z^{(1)} (t)`, we have .. math:: :nowrap: \begin{eqnarray} \sum_{k=1}^j k z^{(k)} t^{k-1} & = & \left[ \sum_{k=0}^j y^{(k)} t^k \right] \left[ \sum_{k=1}^j k x^{(k)} t^{k-1} \right] + o( t^{j-1} ) \end{eqnarray} Setting the coefficients of :math:`t^{j-1}` equal, we have .. math:: :nowrap: \begin{eqnarray} j z^{(j)} = \sum_{k=1}^j k x^{(k)} y^{(j-k)} \\ z^{(j)} = \frac{1}{j} \sum_{k=1}^j k x^{(k)} y^{(j-k)} \end{eqnarray} {xrst_end erf_forward} ================================================ FILE: xrst/theory/erf_reverse.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin erf_reverse} Error Function Reverse Mode Theory ################################## Notation ******** We use the reverse theory :ref:`standard math function` definition for the functions :math:`H` and :math:`G`. Positive Orders Z(t) ******************** For order :math:`j > 0`, suppose that :math:`H` is the same as :math:`G`. .. math:: z^{(j)} = \frac{1}{j} \sum_{k=1}^j k x^{(k)} y^{(j-k)} For :math:`k = 1 , \ldots , j`, the partial of :math:`H` with respect to :math:`x^{(k)}` is given by .. math:: \D{H}{ x^{(k)} } = \D{G}{ x^{(k)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ x^{(k)} } = \D{G}{ x^{(k)} } + \D{G}{ z^{(j)} } \frac{k}{j} y^{(j-k)} For :math:`k = 1 , \ldots , j` The partial of :math:`H` with respect to :math:`y^{j-k}`, is given by .. math:: \D{H}{ y^{(j-k)} } = \D{G}{ y^{(j-k)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ y^{(j-k)} } = \D{G}{ y^{(j-k)} } + \D{G}{ z^{(j)} } \frac{k}{j} x^{k} Order Zero Z(t) *************** The :math:`z^{(0)}` coefficient is expressed as a function of the Taylor coefficients for :math:`X(t)` and :math:`Y(t)` as follows: In this case, .. math:: \D{H}{ x^{(0)} } = \D{G}{ x^{(0)} } + \D{G}{ z^{(0)} } \D{ z^{(0)} }{ x^{(0)} } = \D{G}{ x^{(0)} } + \D{G}{ z^{(0)} } y^{(0)} {xrst_end erf_reverse} ================================================ FILE: xrst/theory/exp_forward.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin exp_forward} {xrst_spell expm } Exponential Function Forward Mode Theory ######################################## Derivatives *********** If :math:`F(x)` is :math:`\R{exp} (x)` or :math:`\R{expm1} (x)` the corresponding derivative satisfies the equation .. math:: 1 * F^{(1)} (x) - 1 * F (x) = d^{(0)} = \left\{ \begin{array}{ll} 0 & \R{if} \; F(x) = \R{exp}(x) \\ 1 & \R{if} \; F(x) = \R{expm1}(x) \end{array} \right. where the equation above defines :math:`d^{(0)}`. In the :ref:`standard math function differential equation` , :math:`A(x) = 1`, :math:`B(x) = 1`, and :math:`D(x) = d^{(0)}`. We use :math:`a`, :math:`b`, :math:`d`, and :math:`z` to denote the Taylor coefficients for :math:`A [ X (t) ]`, :math:`B [ X (t) ]`, :math:`D [ X (t) ]`, and :math:`F [ X(t) ]` respectively. Taylor Coefficients Recursion ***************************** For orders :math:`j = 0 , 1, \ldots`, .. math:: :nowrap: \begin{eqnarray} z^{(0)} & = & F ( x^{(0)} ) \\ e^{(0)} & = & d^{(0)} + z^{(0)} \\ e^{(j+1)} & = & d^{(j+1)} + \sum_{k=0}^{j+1} a^{(j+1-k)} * z^{(k)} \\ & = & z^{(j+1)} \\ z^{(j+1)} & = & \frac{1}{j+1} \frac{1}{ b^{(0)} } \left( \sum_{k=1}^{j+1} k x^{(k)} e^{(j+1-k)} - \sum_{k=1}^j k z^{(k)} b^{(j+1-k)} \right) \\ & = & x^{(j+1)} d^{(0)} + \frac{1}{j+1} \sum_{k=1}^{j+1} k x^{(k)} z^{(j+1-k)} \end{eqnarray} {xrst_end exp_forward} ================================================ FILE: xrst/theory/exp_reverse.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin exp_reverse} {xrst_spell expm } Exponential Function Reverse Mode Theory ######################################## We use the reverse theory :ref:`standard math function` definition for the functions :math:`H` and :math:`G`. The zero order forward mode formula for the :ref:`exponential` is .. math:: z^{(0)} = F ( x^{(0)} ) and for :math:`j > 0`, .. math:: z^{(j)} = x^{(j)} d^{(0)} + \frac{1}{j} \sum_{k=1}^{j} k x^{(k)} z^{(j-k)} where .. math:: d^{(0)} = \left\{ \begin{array}{ll} 0 & \R{if} \; F(x) = \R{exp}(x) \\ 1 & \R{if} \; F(x) = \R{expm1}(x) \end{array} \right. For order :math:`j = 0, 1, \ldots` we note that .. math:: :nowrap: \begin{eqnarray} \D{H}{ x^{(j)} } & = & \D{G}{ x^{(j)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ x^{(j)} } \\ & = & \D{G}{ x^{(j)} } + \D{G}{ z^{(j)} } ( d^{(0)} + z^{(0)} ) \end{eqnarray} If :math:`j > 0`, then for :math:`k = 1 , \ldots , j` .. math:: :nowrap: \begin{eqnarray} \D{H}{ x^{(k)} } & = & \D{G}{ x^{(k)} } + \D{G}{ z^{(j)} } \frac{1}{j} k z^{(j-k)} \\ \D{H}{ z^{(j-k)} } & = & \D{G}{ z^{(j-k)} } + \D{G}{ z^{(j)} } \frac{1}{j} k x^{(k)} \end{eqnarray} {xrst_end exp_reverse} ================================================ FILE: xrst/theory/forward_theory.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin forward_theory} The Theory of Forward Mode ########################## Taylor Notation *************** In Taylor notation, each variable corresponds to a function of a single argument which we denote by *t* (see Section 10.2 of :ref:`Bib@Evaluating Derivatives` ). Here and below :math:`X(t)`, :math:`Y(t)`, and *Z* ( *t* ) are scalar valued functions and the corresponding *p*-th order Taylor coefficients row vectors are :math:`x`, :math:`y` and :math:`z`; i.e., .. math:: :nowrap: \begin{eqnarray} X(t) & = & x^{(0)} + x^{(1)} * t + \cdots + x^{(p)} * t^p + o( t^p ) \\ Y(t) & = & y^{(0)} + y^{(1)} * t + \cdots + y^{(p)} * t^p + o( t^p ) \\ Z(t) & = & z^{(0)} + z^{(1)} * t + \cdots + z^{(p)} * t^p + o( t^p ) \end{eqnarray} For the purposes of this section, we are given :math:`x` and :math:`y` and need to determine :math:`z`. Binary Operators **************** Addition ======== .. math:: :nowrap: \begin{eqnarray} Z(t) & = & X(t) + Y(t) \\ \sum_{j=0}^p z^{(j)} * t^j & = & \sum_{j=0}^p x^{(j)} * t^j + \sum_{j=0}^p y^{(j)} * t^j + o( t^p ) \\ z^{(j)} & = & x^{(j)} + y^{(j)} \end{eqnarray} Subtraction =========== .. math:: :nowrap: \begin{eqnarray} Z(t) & = & X(t) - Y(t) \\ \sum_{j=0}^p z^{(j)} * t^j & = & \sum_{j=0}^p x^{(j)} * t^j - \sum_{j=0}^p y^{(j)} * t^j + o( t^p ) \\ z^{(j)} & = & x^{(j)} - y^{(j)} \end{eqnarray} Multiplication ============== .. math:: :nowrap: \begin{eqnarray} Z(t) & = & X(t) * Y(t) \\ \sum_{j=0}^p z^{(j)} * t^j & = & \left( \sum_{j=0}^p x^{(j)} * t^j \right) * \left( \sum_{j=0}^p y^{(j)} * t^j \right) + o( t^p ) \\ z^{(j)} & = & \sum_{k=0}^j x^{(j-k)} * y^{(k)} \end{eqnarray} Division ======== .. math:: :nowrap: \begin{eqnarray} Z(t) & = & X(t) / Y(t) \\ x & = & z * y \\ \sum_{j=0}^p x^{(j)} * t^j & = & \left( \sum_{j=0}^p z^{(j)} * t^j \right) * \left( \sum_{j=0}^p y^{(j)} * t^j \right) + o( t^p ) \\ x^{(j)} & = & \sum_{k=0}^j z^{(j-k)} y^{(k)} \\ z^{(j)} & = & \frac{1}{y^{(0)}} \left( x^{(j)} - \sum_{k=1}^j z^{(j-k)} y^{(k)} \right) \end{eqnarray} Standard Math Functions *********************** Suppose that :math:`F` is a standard math function and .. math:: Z(t) = F[ X(t) ] Differential Equation ===================== All of the standard math functions satisfy a differential equation of the form .. math:: B(u) * F^{(1)} (u) - A(u) * F (u) = D(u) We use :math:`a`, :math:`b` and :math:`d` to denote the *p*-th order Taylor coefficient row vectors for :math:`A [ X (t) ]`, :math:`B [ X (t) ]` and :math:`D [ X (t) ]` respectively. We assume that these coefficients are known functions of :math:`x`, the *p*-th order Taylor coefficients for :math:`X(t)`. Taylor Coefficients Recursion Formula ===================================== Our problem here is to express :math:`z`, the *p*-th order Taylor coefficient row vector for :math:`Z(t)`, in terms of these other known coefficients. It follows from the formulas above that .. math:: :nowrap: \begin{eqnarray} Z^{(1)} (t) & = & F^{(1)} [ X(t) ] * X^{(1)} (t) \\ B[ X(t) ] * Z^{(1)} (t) & = & \{ D[ X(t) ] + A[ X(t) ] * Z(t) \} * X^{(1)} (t) \\ B[ X(t) ] * Z^{(1)} (t) & = & E(t) * X^{(1)} (t) \end{eqnarray} where we define .. math:: E(t) = D[X(t)] + A[X(t)] * Z(t) We can compute the value of :math:`z^{(0)}` using the formula .. math:: z^{(0)} = F ( x^{(0)} ) Suppose by induction (on :math:`j`) that we are given the Taylor coefficients of :math:`E(t)` up to order :math:`j-1`; i.e., :math:`e^{(k)}` for :math:`k = 0 , \ldots , j-1` and the coefficients :math:`z^{(k)}` for :math:`k = 0 , \ldots , j`. We can compute :math:`e^{(j)}` using the formula .. math:: e^{(j)} = d^{(j)} + \sum_{k=0}^j a^{(j-k)} * z^{(k)} We need to complete the induction by finding formulas for :math:`z^{(j+1)}`. It follows from the definition of :math:`E(t)` that .. math:: \left( \sum_{k=0}^j b^{(k)} * t^k \right) * \left( \sum_{k=1}^{j+1} k z^{(k)} * t^{k-1} \right) = \left( \sum_{k=0}^j e^{(k)} * t^k \right) * \left( \sum_{k=1}^{j+1} k x^{(k)} * t^{k-1} \right) + o( t^p ) Setting the left and right side coefficients of :math:`t^j` equal, and using the formula for :ref:`forward_theory@Binary Operators@Multiplication` , we obtain .. math:: :nowrap: \begin{eqnarray} \sum_{k=0}^j b^{(k)} (j+1-k) z^{(j+1-k)} & = & \sum_{k=0}^j e^{(k)} (j+1-k) x^{(j+1-k)} \\ z^{(j+1)} & = & \frac{1}{j+1} \frac{1}{ b^{(0)} } \left( \sum_{k=0}^j e^{(k)} (j+1-k) x^{(j+1-k)} - \sum_{k=1}^j b^{(k)} (j+1-k) z^{(j+1-k)} \right) \\ z^{(j+1)} & = & \frac{1}{j+1} \frac{1}{ b^{(0)} } \left( \sum_{k=1}^{j+1} k x^{(k)} e^{(j+1-k)} - \sum_{k=1}^j k z^{(k)} b^{(j+1-k)} \right) \end{eqnarray} This completes the induction that computes :math:`e^{(j)}` and :math:`z^{(j+1)}`. {xrst_toc_hidden xrst/theory/exp_forward.xrst xrst/theory/log_forward.xrst xrst/theory/sqrt_forward.xrst xrst/theory/sin_cos_forward.xrst xrst/theory/atan_forward.xrst xrst/theory/asin_forward.xrst xrst/theory/acos_forward.xrst xrst/theory/pow_forward.xrst xrst/theory/tan_forward.xrst xrst/theory/erf_forward.xrst } Cases that Apply Recursion Above ================================ .. csv-table:: :widths: auto exp_forward,:ref:`exp_forward-title` log_forward,:ref:`log_forward-title` sqrt_forward,:ref:`sqrt_forward-title` sin_cos_forward,:ref:`sin_cos_forward-title` atan_forward,:ref:`atan_forward-title` asin_forward,:ref:`asin_forward-title` acos_forward,:ref:`acos_forward-title` pow_forward,:ref:`pow_forward-title` Special Cases ============= .. csv-table:: :widths: auto tan_forward,:ref:`tan_forward-title` erf_forward,:ref:`erf_forward-title` {xrst_end forward_theory} ================================================ FILE: xrst/theory/log_forward.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin log_forward} Logarithm Function Forward Mode Theory ###################################### Derivatives *********** If :math:`F(x)` is :math:`\R{log} (x)` or :math:`\R{log1p} (x)` the corresponding derivative satisfies the equation .. math:: ( \bar{b} + x ) * F^{(1)} (x) - 0 * F (x) = 1 where .. math:: \bar{b} = \left\{ \begin{array}{ll} 0 & \R{if} \; F(x) = \R{log}(x) \\ 1 & \R{if} \; F(x) = \R{log1p}(x) \end{array} \right. In the :ref:`standard math function differential equation` , :math:`A(x) = 0`, :math:`B(x) = \bar{b} + x`, and :math:`D(x) = 1`. We use :math:`a`, :math:`b`, :math:`d`, and :math:`z` to denote the Taylor coefficients for :math:`A [ X (t) ]`, :math:`B [ X (t) ]`, :math:`D [ X (t) ]`, and :math:`F [ X(t) ]` respectively. Taylor Coefficients Recursion ***************************** For orders :math:`j = 0 , 1, \ldots`, .. math:: :nowrap: \begin{eqnarray} z^{(0)} & = & F ( x^{(0)} ) \\ e^{(j)} & = & d^{(j)} + \sum_{k=0}^{j} a^{(j-k)} * z^{(k)} \\ & = & \left\{ \begin{array}{ll} 1 & {\rm if} \; j = 0 \\ 0 & {\rm otherwise} \end{array} \right. \\ z^{(j+1)} & = & \frac{1}{j+1} \frac{1}{ b^{(0)} } \left( \sum_{k=1}^{j+1} k x^{(k)} e^{(j+1-k)} - \sum_{k=1}^j k z^{(k)} b^{(j+1-k)} \right) \\ & = & \frac{1}{j+1} \frac{1}{ \bar{b} + x^{(0)} } \left( (j+1) x^{(j+1) } - \sum_{k=1}^j k z^{(k)} x^{(j+1-k)} \right) \end{eqnarray} {xrst_end log_forward} ================================================ FILE: xrst/theory/log_reverse.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin log_reverse} Logarithm Function Reverse Mode Theory ###################################### We use the reverse theory :ref:`standard math function` definition for the functions :math:`H` and :math:`G`. The zero order forward mode formula for the :ref:`logarithm` is .. math:: z^{(0)} = F( x^{(0)} ) and for :math:`j > 0`, .. math:: z^{(j)} = \frac{1}{ \bar{b} + x^{(0)} } \frac{1}{j} \left( j x^{(j)} - \sum_{k=1}^{j-1} k z^{(k)} x^{(j-k)} \right) where .. math:: \bar{b} = \left\{ \begin{array}{ll} 0 & \R{if} \; F(x) = \R{log}(x) \\ 1 & \R{if} \; F(x) = \R{log1p}(x) \end{array} \right. We note that for :math:`j > 0` .. math:: :nowrap: \begin{eqnarray} \D{ z^{(j)} } { x^{(0)} } & = & - \frac{1}{ \bar{b} + x^{(0)} } \frac{1}{ \bar{b} + x^{(0)} } \frac{1}{j} \left( j x^{(j)} - \sum_{k=1}^{j-1} k z^{(k)} x^{(j-k)} \right) \\ & = & - \frac{z^{(j)}}{ \bar{b} + x^{(0)} } \end{eqnarray} Removing the zero order partials are given by .. math:: :nowrap: \begin{eqnarray} \D{H}{ x^{(0)} } & = & \D{G}{ x^{(0)} } + \D{G}{ z^{(0)} } \D{ z^{(0)} }{ x^{(0)} } \\ & = & \D{G}{ x^{(0)} } + \D{G}{ z^{(0)} } \frac{1}{ \bar{b} + x^{(0)} } \end{eqnarray} For orders :math:`j > 0` and for :math:`k = 1 , \ldots , j-1` .. math:: :nowrap: \begin{eqnarray} \D{H}{ x^{(0)} } & = & \D{G}{ x^{(0)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ x^{(0)} } \\ & = & \D{G}{ x^{(0)} } - \D{G}{ z^{(j)} } \frac{ z^{(j)} }{ \bar{b} + x^{(0)} } \\ \D{H}{ x^{(j)} } & = & \D{G}{ x^{(j)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ x^{(j)} } \\ & = & \D{G}{ x^{(j)} } + \D{G}{ z^{(j)} } \frac{1}{ \bar{b} + x^{(0)} } \\ \D{H}{ x^{(j-k)} } & = & \D{G}{ x^{(j-k)} } - \D{G}{ z^{(j)} } \frac{1}{ \bar{b} + x^{(0)} } \frac{k}{j} z^{(k)} \\ \D{H}{ z^{(k)} } & = & \D{G}{ z^{(k)} } - \D{G}{ z^{(j)} } \frac{1}{ \bar{b} + x^{(0)} } \frac{k}{j} x^{(j-k)} \end{eqnarray} {xrst_end log_reverse} ================================================ FILE: xrst/theory/pow_forward.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin pow_forward} Power Function Forward Mode Theory ################################## We consider the operation :math:`F(x) = x^y` where :math:`x` is a variable and :math:`y` is a parameter. Derivatives *********** The corresponding derivative satisfies the equation .. math:: x * F^{(1)} (x) - y F(x) = 0 This is the :ref:`standard math function differential equation` , where :math:`A(x) = y`, :math:`B(x) = x`, and :math:`D(x) = 0`. We use :math:`a`, :math:`b`, :math:`d`, and :math:`z` to denote the Taylor coefficients for :math:`A [ X (t) ]`, :math:`B [ X (t) ]`, :math:`D [ X (t) ]`, and :math:`F [ X(t) ]` respectively. It follows that :math:`b^j = x^j`, :math:`d^j = 0`, .. math:: a^{(j)} = \left\{ \begin{array}{ll} y & \R{if} \; j = 0 \\ 0 & \R{otherwise} \end{array} \right. Taylor Coefficients Recursion ***************************** z^(0) ===== .. math:: z^{(0)} = F ( x^{(0)} ) e^(j) ===== .. math:: :nowrap: \begin{eqnarray} e^{(j)} & = & d^{(j)} + \sum_{k=0}^j a^{(j-k)} * z^{(k)} \\ e^{(j)} & = & y * z^{(j)} \end{eqnarray} z^j === For :math:`j = 0, \ldots , p-1` .. math:: :nowrap: \begin{eqnarray} z^{(j+1)} & = & \frac{1}{j+1} \frac{1}{ b^{(0)} } \left( \sum_{k=1}^{j+1} k x^{(k)} e^{(j+1-k)} - \sum_{k=1}^j k z^{(k)} b^{(j+1-k)} \right) \\ & = & \frac{1}{j+1} \frac{1}{ x^{(0)} } \left( y \sum_{k=1}^{j+1} k x^{(k)} z^{(j+1-k)} - \sum_{k=1}^j k z^{(k)} x^{(j+1-k)} \right) \\ & = & \frac{1}{j+1} \frac{1}{ x^{(0)} } \left( y (j+1) x^{(j+1)} z^{(0)} + \sum_{k=1}^j k ( y x^{(k)} z^{(j+1-k)} - z^{(k)} x^{(j+1-k)} ) \right) \\ & = & y z^{(0)} x^{(j+1)} / x^{(0)} + \frac{1}{j+1} \frac{1}{ x^{(0)} } \sum_{k=1}^j k ( y x^{(k)} z^{(j+1-k)} - z^{(k)} x^{(j+1-k)} ) \end{eqnarray} For :math:`j = 1, \ldots , p` .. math:: :nowrap: \begin{eqnarray} z^{(j)} & = & \left. \left( y z^{(0)} x^{(j)} + \frac{1}{j} \sum_{k=1}^{j-1} k ( y x^{(k)} z^{(j-k)} - z^{(k)} x^{(j-k)} ) \right) \right/ x^{(0)} \end{eqnarray} {xrst_end pow_forward} ================================================ FILE: xrst/theory/pow_reverse.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin pow_reverse} Power Function Reverse Mode Theory ################################## We use the reverse theory :ref:`standard math function` definition for the functions :math:`H` and :math:`G`. The zero order forward mode formula for the :ref:`power` function is .. math:: z^{(0)} = F ( x^{(0)} ) .. math:: :nowrap: \begin{eqnarray} \D{H}{ x^{(0)} } & = & \D{G}{ x^{(0)} } + \D{G}{ z^{(0)} } \D{ z^{(0)} }{ x^{(0)} } \\ \D{ z^{(0)} }{ x^{(0)} } & = & y [ x^{(0)} ]^{y - 1} = y z^{(0)} / x{(0)} \end{eqnarray} All the equations below apply to the case where :math:`j > 0`. For this case, the equation for :math:`z^{(j)}` is .. math:: z^{(j)} = \left. \left( y z^{(0)} x^{(j)} + \frac{1}{j} \sum_{k=1}^{j-1} k ( y x^{(k)} z^{(j-k)} - z^{(k)} x^{(j-k)} ) \right) \right/ x^{(0)} x^j *** .. math:: :nowrap: \begin{eqnarray} \D{H}{ x^{(j)} } & = & \D{G}{ x^{(j)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ x^{(j)} } \\ \D{ z^{(j)} }{ x^{(j)} } & = & y z^{(0)} / x^{(0)} \end{eqnarray} x^k *** For :math:`k = 1 , \ldots , j-1` .. math:: :nowrap: \begin{eqnarray} \D{H}{ x^{(k)} } & = & \D{G}{ x^{(k)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ x^{(k)} } \\ \D{ z^{(j)} }{ x^{(k)} } & = & \frac{1}{j} ( k y - (j-k) ) z^{(j-k)} / x^{(0)} \end{eqnarray} z^k *** For :math:`k = 1 , \ldots , j-1` .. math:: :nowrap: \begin{eqnarray} \D{H}{ z^{(k)} } & = & \D{G}{ z^{(k)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ z^{(k)} } \\ \D{ z^{(j)} }{ z^{(k)} } & = & \frac{1}{j} ( (j-k) y - k ) x^{(j-k)} / x^{(0)} \end{eqnarray} x^0 *** .. math:: :nowrap: \begin{eqnarray} \D{H}{ x^{(0)} } & = & \D{G}{ x^{(0)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ x^{(0)} } \\ \D{ z^{(j)} }{ x^{(0)} } & = & - z^{(j)} / x^{(0)} \end{eqnarray} z^0 *** .. math:: :nowrap: \begin{eqnarray} \D{H}{ z^{(0)} } & = & \D{G}{ z^{(0)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ z^{(0)} } \\ \D{ z^{(j)} }{ z^{(0)} } & = & y x^{(j)} / x^{(0)} \end{eqnarray} {xrst_end pow_reverse} ================================================ FILE: xrst/theory/research.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin research} {xrst_spell kasper kristensen py subgraphs symmertic } Research and Software Engineering Projects Related to CppAD ########################################################### See Also ******** :ref:`wish_list-name` Purpose ******* This is a list of research topics, and software engineering projects, related to CppAD. If you know of published results for any of the research topics, please contact the project manage so a link to the answer can be added to the CppAD documentation. Abs-Normal Form *************** `Abs-normal `_ forms are recent advent in AD and algorithms related to it are an important research topic. The CppAD :ref:`abs_normal_fun-name` routine enables one to create approximations for non-smooth functions that have higher than first order accuracy. This opens the question of designing algorithms to take advantage of this. Atomic Functions **************** Mathematical formulas for, and implementation of, :ref:`atomic functions` that make AD faster and or conserve on memory for special cases; e.g., special functions, sparse matrix operations. Dynamic Parameters ****************** CppAD provides for :ref:`dynamic parameters` in a unique way (they can depend on other dynamic parameters). This opens the question of techniques that take advantage of this. For example, see :ref:`base2ad.cpp-name` which uses this feature. Multi-Threading *************** Interesting algorithms and implementations that take advantage of the CppAD :ref:`multi threading` capability. Optimization ************ There is no paper describing the CppAD :ref:`optimization` of a computational graph. In particular, the optimization of :ref:`conditional expressions` is not well understood. In addition, possible improvements to the optimization would be welcome. For example, detecting places where the distributive law can be used to reduce two multiplies and one addition to one multiply and two additions. Scripting Language ****************** It would be useful to make :ref:`addon-name` packages that connect CppAD to a scripting languages. The package `cppad_swig `_ demonstrates how one could use swig to do this and `cppad_py `_ is an example connection to Python. Sparsity ******** Sparsity Patterns ================= There are forward, reverse and subgraph methods for computing a :ref:`sparsity_pattern-name` . It is unclear which is better for which cases. Coloring Problem ================ Given the :ref:`glossary@Sparsity Pattern` for a Jacobian or Hessian, a graph coloring algorithm is used to determine which row or columns can be evaluated at the same time; see ``color_general.hpp`` and ``color_symmertic.hpp`` Subgraphs ========= The :ref:`subgraph_jac_rev-name` method use subgraphs to compute sparsity patterns and to evaluate sparse derivatives. This is a new technique that should be written up. It was invented by Kasper Kristensen, DTU Technical University of Denmark. See the :ref:`wish_list@Subgraph` wish list items. Speed ***** CppAD provides for some :ref:`speed-name` comparisons between different AD packages. These comparisons could be improved, extended, and written up. {xrst_end research} ================================================ FILE: xrst/theory/reverse_identity.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin reverse_identity} {xrst_spell rclr } An Important Reverse Mode Identity ################################## The theorem and the proof below is a restatement of the results on page 236 of :ref:`Bib@Evaluating Derivatives` . Notation ******** Given a function :math:`f(u, v)` where :math:`u \in \B{R}^n` we use the notation .. math:: \D{f}{u} (u, v) = \left[ \D{f}{u_1} (u, v) , \cdots , \D{f}{u_n} (u, v) \right] Reverse Sweep ************* When using :ref:`reverse mode` we are given a function :math:`F : \B{R}^n \rightarrow \B{R}^m`, a matrix of Taylor coefficients :math:`x \in \B{R}^{n \times p}`, and a weight vector :math:`w \in \B{R}^m`. We define the functions :math:`X : \B{R} \times \B{R}^{n \times p} \rightarrow \B{R}^n`, :math:`W : \B{R} \times \B{R}^{n \times p} \rightarrow \B{R}`, and :math:`W_j : \B{R}^{n \times p} \rightarrow \B{R}` by .. math:: :nowrap: \begin{eqnarray} X(t , x) & = & x^{(0)} + x^{(1)} t + \cdots + x^{(p-1)} t^{p-1} \\ W(t, x) & = & w_0 F_0 [X(t, x)] + \cdots + w_{m-1} F_{m-1} [X(t, x)] \\ W_j (x) & = & \frac{1}{j!} \Dpow{j}{t} W(0, x) \end{eqnarray} where :math:`x^{(j)}` is the *j*-th column of :math:`x \in \B{R}^{n \times p}`. The theorem below implies that .. math:: \D{ W_j }{ x^{(i)} } (x) = \D{ W_{j-i} }{ x^{(0)} } (x) A :ref:`general reverse sweep` calculates the values .. math:: \D{ W_{p-1} }{ x^{(i)} } (x) \hspace{1cm} (i = 0 , \ldots , p-1) But the return values for a reverse sweep are specified in terms of the more useful values .. math:: \D{ W_j }{ x^{(0)} } (x) \hspace{1cm} (j = 0 , \ldots , p-1) Theorem ******* Suppose that :math:`F : \B{R}^n \rightarrow \B{R}^m` is a :math:`p` times continuously differentiable function. Define the functions :math:`Z : \B{R} \times \B{R}^{n \times p} \rightarrow \B{R}^n`, :math:`Y : \B{R} \times \B{R}^{n \times p }\rightarrow \B{R}^m`, and :math:`y^{(j)} : \B{R}^{n \times p }\rightarrow \B{R}^m` by .. math:: :nowrap: \begin{eqnarray} Z(t, x) & = & x^{(0)} + x^{(1)} t + \cdots + x^{(p-1)} t^{p-1} \\ Y(t, x) & = & F [ Z(t, x) ] \\ y^{(j)} (x) & = & \frac{1}{j !} \Dpow{j}{t} Y(0, x) \end{eqnarray} where :math:`x^{(j)}` denotes the *j*-th column of :math:`x \in \B{R}^{n \times p}`. It follows that for all :math:`i, j` such that :math:`i \leq j < p`, .. math:: :nowrap: \begin{eqnarray} \D{ y^{(j)} }{ x^{(i)} } (x) & = & \D{ y^{(j-i)} }{ x^{(0)} } (x) \end{eqnarray} Proof ***** If follows from the definitions that .. math:: \begin{array}{rclr} \D{ y^{(j)} }{ x^{(i)} } (x) & = & \frac{1}{j ! } \D{ }{ x^{(i)} } \left[ \Dpow{j}{t} (F \circ Z) (t, x) \right]_{t=0} \\ & = & \frac{1}{j ! } \left[ \Dpow{j}{t} \D{ }{ x^{(i)} } (F \circ Z) (t, x) \right]_{t=0} \\ & = & \frac{1}{j ! } \left\{ \Dpow{j}{t} \left[ t^i ( F^{(1)} \circ Z ) (t, x) \right] \right\}_{t=0} \end{array} For :math:`k > i`, the *k*-th partial of :math:`t^i` with respect to :math:`t` is zero. Thus, the partial with respect to :math:`t` is given by .. math:: :nowrap: \begin{eqnarray} \Dpow{j}{t} \left[ t^i ( F^{(1)} \circ Z ) (t, x) \right] & = & \sum_{k=0}^i \left( \begin{array}{c} j \\ k \end{array} \right) \frac{ i ! }{ (i - k) ! } t^{i-k} \; \Dpow{j-k}{t} ( F^{(1)} \circ Z ) (t, x) \\ \left\{ \Dpow{j}{t} \left[ t^i ( F^{(1)} \circ Z ) (t, x) \right] \right\}_{t=0} & = & \left( \begin{array}{c} j \\ i \end{array} \right) i ! \Dpow{j-i}{t} ( F^{(1)} \circ Z ) (t, x) \\ & = & \frac{ j ! }{ (j - i) ! } \Dpow{j-i}{t} ( F^{(1)} \circ Z ) (t, x) \\ \D{ y^{(j)} }{ x^{(i)} } (x) & = & \frac{ 1 }{ (j - i) ! } \Dpow{j-i}{t} ( F^{(1)} \circ Z ) (t, x) \end{eqnarray} Applying this formula to the case where :math:`j` is replaced by :math:`j - i` and :math:`i` is replaced by zero, we obtain .. math:: \D{ y^{(j-i)} }{ x^{(0)} } (x) = \frac{ 1 }{ (j - i) ! } \Dpow{j-i}{t} ( F^{(1)} \circ Z ) (t, x) = \D{ y^{(j)} }{ x^{(i)} } (x) which completes the proof {xrst_end reverse_identity} ================================================ FILE: xrst/theory/reverse_theory.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin reverse_theory} The Theory of Reverse Mode ########################## Taylor Notation *************** In Taylor notation, each variable corresponds to a function of a single argument which we denote by *t* (see Section 10.2 of :ref:`Bib@Evaluating Derivatives` ). Here and below :math:`X(t)`, :math:`Y(t)`, and *Z* ( *t* ) are scalar valued functions and the corresponding *p*-th order Taylor coefficients row vectors are :math:`x`, :math:`y` and :math:`z`; i.e., .. math:: :nowrap: \begin{eqnarray} X(t) & = & x^{(0)} + x^{(1)} * t + \cdots + x^{(p)} * t^p + O( t^{p+1} ) \\ Y(t) & = & y^{(0)} + y^{(1)} * t + \cdots + y^{(p)} * t^p + O( t^{p+1} ) \\ Z(t) & = & z^{(0)} + z^{(1)} * t + \cdots + z^{(p)} * t^p + O( t^{p+1} ) \end{eqnarray} For the purposes of this discussion, we are given the *p*-th order Taylor coefficient row vectors :math:`x`, :math:`y`, and :math:`z`. In addition, we are given the partial derivatives of a scalar valued function .. math:: G ( z^{(j)} , \ldots , z^{(0)}, x, y) We need to compute the partial derivatives of the scalar valued function .. math:: H ( z^{(j-1)} , \ldots , z^{(0)}, x, y) = G ( z^{(j)}, z^{(j-1)} , \ldots , z^{(0)}, x , y ) where :math:`z^{(j)}` is expressed as a function of the *j-1*-th order Taylor coefficient row vector for :math:`Z` and the vectors :math:`x`, :math:`y`; i.e., :math:`z^{(j)}` above is a shorthand for .. math:: z^{(j)} ( z^{(j-1)} , \ldots , z^{(0)}, x, y ) If we do not provide a formula for a partial derivative of :math:`H`, then that partial derivative has the same value as for the function :math:`G`. Binary Operators **************** Addition ======== The forward mode formula for :ref:`forward_theory@Binary Operators@Addition` is .. math:: z^{(j)} = x^{(j)} + y^{(j)} If follows that for :math:`k = 0 , \ldots , j` and :math:`l = 0 , \ldots , j-1` .. math:: :nowrap: \begin{eqnarray} \D{H}{ x^{(k)} } & = & \D{G}{ x^{(k)} } + \D{G}{ z^{(k)} } \\ \\ \D{H}{ y^{(k)} } & = & \D{G}{ y^{(k)} } + \D{G}{ z^{(k)} } \\ \D{H}{ z^{(l)} } & = & \D{G}{ z^{(l)} } \end{eqnarray} Subtraction =========== The forward mode formula for :ref:`forward_theory@Binary Operators@Subtraction` is .. math:: z^{(j)} = x^{(j)} - y^{(j)} If follows that for :math:`k = 0 , \ldots , j` .. math:: :nowrap: \begin{eqnarray} \D{H}{ x^{(k)} } & = & \D{G}{ x^{(k)} } - \D{G}{ z^{(k)} } \\ \\ \D{H}{ y^{(k)} } & = & \D{G}{ y^{(k)} } - \D{G}{ z^{(k)} } \end{eqnarray} Multiplication ============== The forward mode formula for :ref:`forward_theory@Binary Operators@Multiplication` is .. math:: z^{(j)} = \sum_{k=0}^j x^{(j-k)} * y^{(k)} If follows that for :math:`k = 0 , \ldots , j` and :math:`l = 0 , \ldots , j-1` .. math:: :nowrap: \begin{eqnarray} \D{H}{ x^{(j-k)} } & = & \D{G}{ x^{(j-k)} } + \sum_{k=0}^j \D{G}{ z^{(j)} } y^{(k)} \\ \D{H}{ y^{(k)} } & = & \D{G}{ y^{(k)} } + \sum_{k=0}^j \D{G}{ z^{(j)} } x^{(j-k)} \end{eqnarray} Division ======== The forward mode formula for :ref:`forward_theory@Binary Operators@Division` is .. math:: z^{(j)} = \frac{1}{y^{(0)}} \left( x^{(j)} - \sum_{k=1}^j z^{(j-k)} y^{(k)} \right) If follows that for :math:`k = 1 , \ldots , j` .. math:: :nowrap: \begin{eqnarray} \D{H}{ x^{(j)} } & = & \D{G}{ x^{(j)} } + \D{G}{ z^{(j)} } \frac{1}{y^{(0)}} \\ \D{H}{ z^{(j-k)} } & = & \D{G}{ z^{(j-k)} } - \D{G}{ z^{(j)} } \frac{1}{y^{(0)}} y^{(k)} \\ \D{H}{ y^{(k)} } & = & \D{G}{ y^{(k)} } - \D{G}{ z^{(j)} } \frac{1}{y^{(0)}} z^{(j-k)} \\ \D{H}{ y^{(0)} } & = & \D{G}{ y^{(0)} } - \D{G}{ z^{(j)} } \frac{1}{y^{(0)}} \frac{1}{y^{(0)}} \left( x^{(j)} - \sum_{k=1}^j z^{(j-k)} y^{(k)} \right) \\ & = & \D{G}{ y^{(0)} } - \D{G}{ z^{(j)} } \frac{1}{y^{(0)}} z^{(j)} \end{eqnarray} Standard Math Functions *********************** The standard math functions have only one argument. Hence we are given the partial derivatives of a scalar valued function .. math:: G ( z^{(j)} , \ldots , z^{(0)}, x) We need to compute the partial derivatives of the scalar valued function .. math:: H ( z^{(j-1)} , \ldots , z^{(0)}, x) = G ( z^{(j)}, z^{(j-1)} , \ldots , z^{(0)}, x) where :math:`z^{(j)}` is expressed as a function of the *j-1*-th order Taylor coefficient row vector for :math:`Z` and the vector :math:`x`; i.e., :math:`z^{(j)}` above is a shorthand for .. math:: z^{(j)} ( z^{(j-1)} , \ldots , z^{(0)}, x ) Contents ******** {xrst_toc_table xrst/theory/exp_reverse.xrst xrst/theory/log_reverse.xrst xrst/theory/sqrt_reverse.xrst xrst/theory/sin_cos_reverse.xrst xrst/theory/atan_reverse.xrst xrst/theory/asin_reverse.xrst xrst/theory/acos_reverse.xrst xrst/theory/tan_reverse.xrst xrst/theory/erf_reverse.xrst xrst/theory/pow_reverse.xrst } {xrst_end reverse_theory} ================================================ FILE: xrst/theory/sin_cos_forward.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin sin_cos_forward} Trigonometric and Hyperbolic Sine and Cosine Forward Theory ########################################################### Differential Equation ********************* The :ref:`standard math function differential equation` is .. math:: B(u) * F^{(1)} (u) - A(u) * F (u) = D(u) In this sections we consider forward mode for the following choices: .. csv-table:: :widths: auto ,,:math:`F(u)`,,:math:`\sin(u)`,,:math:`\cos(u)`,,:math:`\sinh(u)`,,:math:`\cosh(u)` ,,:math:`A(u)`,,:math:`0`,,:math:`0`,,:math:`0`,,:math:`0` ,,:math:`B(u)`,,:math:`1`,,:math:`1`,,:math:`1`,,:math:`1` ,,:math:`D(u)`,,:math:`\cos(u)`,,:math:`- \sin(u)`,,:math:`\cosh(u)`,,:math:`\sinh(u)` We use :math:`a`, :math:`b`, :math:`d` and :math:`f` for the Taylor coefficients of :math:`A [ X (t) ]`, :math:`B [ X (t) ]`, :math:`D [ X (t) ]`, and :math:`F [ X(t) ]` respectively. It now follows from the general :ref:`forward_theory@Standard Math Functions@Taylor Coefficients Recursion Formula` that for :math:`j = 0 , 1, \ldots`, .. math:: :nowrap: \begin{eqnarray} f^{(0)} & = & D ( x^{(0)} ) \\ e^{(j)} & = & d^{(j)} + \sum_{k=0}^{j} a^{(j-k)} * f^{(k)} \\ & = & d^{(j)} \\ f^{(j+1)} & = & \frac{1}{j+1} \frac{1}{ b^{(0)} } \left( \sum_{k=1}^{j+1} k x^{(k)} e^{(j+1-k)} - \sum_{k=1}^j k f^{(k)} b^{(j+1-k)} \right) \\ & = & \frac{1}{j+1} \sum_{k=1}^{j+1} k x^{(k)} d^{(j+1-k)} \end{eqnarray} The formula above generates the order :math:`j+1` coefficient of :math:`F[ X(t) ]` from the lower order coefficients for :math:`X(t)` and :math:`D[ X(t) ]`. {xrst_end sin_cos_forward} ================================================ FILE: xrst/theory/sin_cos_reverse.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin sin_cos_reverse} Trigonometric and Hyperbolic Sine and Cosine Reverse Theory ########################################################### We use the reverse theory :ref:`standard math function` definition for the functions :math:`H` and :math:`G`. In addition, we use the following definitions for :math:`s` and :math:`c` and the integer :math:`\ell` .. csv-table:: :widths: auto Coefficients,,:math:`s`,,:math:`c`,,:math:`\ell` Trigonometric Case,,:math:`\sin [ X(t) ]`,,:math:`\cos [ X(t) ]`,,1 Hyperbolic Case,,:math:`\sinh [ X(t) ]`,,:math:`\cosh [ X(t) ]`,,-1 We use the value .. math:: z^{(j)} = ( s^{(j)} , c^{(j)} ) in the definition for :math:`G` and :math:`H`. The forward mode formulas for the :ref:`sine and cosine` functions are .. math:: :nowrap: \begin{eqnarray} s^{(j)} & = & \frac{1 + \ell}{2} \sin ( x^{(0)} ) + \frac{1 - \ell}{2} \sinh ( x^{(0)} ) \\ c^{(j)} & = & \frac{1 + \ell}{2} \cos ( x^{(0)} ) + \frac{1 - \ell}{2} \cosh ( x^{(0)} ) \end{eqnarray} for the case :math:`j = 0`, and for :math:`j > 0`, .. math:: :nowrap: \begin{eqnarray} s^{(j)} & = & \frac{1}{j} \sum_{k=1}^{j} k x^{(k)} c^{(j-k)} \\ c^{(j)} & = & \ell \frac{1}{j} \sum_{k=1}^{j} k x^{(k)} s^{(j-k)} \end{eqnarray} If :math:`j = 0`, we have the relation .. math:: :nowrap: \begin{eqnarray} \D{H}{ x^{(j)} } & = & \D{G}{ x^{(j)} } + \D{G}{ s^{(j)} } c^{(0)} + \ell \D{G}{ c^{(j)} } s^{(0)} \end{eqnarray} If :math:`j > 0`, then for :math:`k = 1, \ldots , j-1` .. math:: :nowrap: \begin{eqnarray} \D{H}{ x^{(k)} } & = & \D{G}{ x^{(k)} } + \D{G}{ s^{(j)} } \frac{1}{j} k c^{(j-k)} + \ell \D{G}{ c^{(j)} } \frac{1}{j} k s^{(j-k)} \\ \D{H}{ s^{(j-k)} } & = & \D{G}{ s^{(j-k)} } + \ell \D{G}{ c^{(j)} } k x^{(k)} \\ \D{H}{ c^{(j-k)} } & = & \D{G}{ c^{(j-k)} } + \D{G}{ s^{(j)} } k x^{(k)} \end{eqnarray} {xrst_end sin_cos_reverse} ================================================ FILE: xrst/theory/sqrt_forward.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin sqrt_forward} Square Root Function Forward Mode Theory ######################################## If :math:`F(x) = \sqrt{x}` .. math:: F(x) * F^{(1)} (x) - 0 * F (x) = 1/2 and in the :ref:`standard math function differential equation` , :math:`A(x) = 0`, :math:`B(x) = F(x)`, and :math:`D(x) = 1/2`. We use :math:`a`, :math:`b`, :math:`d`, and :math:`z` to denote the Taylor coefficients for :math:`A [ X (t) ]`, :math:`B [ X (t) ]`, :math:`D [ X (t) ]`, and :math:`F [ X(t) ]` respectively. It now follows from the general :ref:`forward_theory@Standard Math Functions@Taylor Coefficients Recursion Formula` that for :math:`j = 0 , 1, \ldots`, .. math:: :nowrap: \begin{eqnarray} z^{(0)} & = & \sqrt { x^{(0)} } \\ e^{(j)} & = & d^{(j)} + \sum_{k=0}^{j} a^{(j-k)} * z^{(k)} \\ & = & \left\{ \begin{array}{ll} 1/2 & {\rm if} \; j = 0 \\ 0 & {\rm otherwise} \end{array} \right. \\ z^{(j+1)} & = & \frac{1}{j+1} \frac{1}{ b^{(0)} } \left( \sum_{k=1}^{j+1} k x^{(k)} e^{(j+1-k)} - \sum_{k=1}^j k z^{(k)} b^{(j+1-k)} \right) \\ & = & \frac{1}{j+1} \frac{1}{ z^{(0)} } \left( \frac{j+1}{2} x^{(j+1) } - \sum_{k=1}^j k z^{(k)} z^{(j+1-k)} \right) \end{eqnarray} {xrst_end sqrt_forward} ================================================ FILE: xrst/theory/sqrt_reverse.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin sqrt_reverse} Square Root Function Reverse Mode Theory ######################################## We use the reverse theory :ref:`standard math function` definition for the functions :math:`H` and :math:`G`. The forward mode formulas for the :ref:`square root` function are .. math:: z^{(j)} = \sqrt { x^{(0)} } for the case :math:`j = 0`, and for :math:`j > 0`, .. math:: z^{(j)} = \frac{1}{j} \frac{1}{ z^{(0)} } \left( \frac{j}{2} x^{(j) } - \sum_{\ell=1}^{j-1} \ell z^{(\ell)} z^{(j-\ell)} \right) If :math:`j = 0`, we have the relation .. math:: :nowrap: \begin{eqnarray} \D{H}{ x^{(j)} } & = & \D{G}{ x^{(j)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ x^{(0)} } \\ & = & \D{G}{ x^{(j)} } + \D{G}{ z^{(j)} } \frac{1}{2 z^{(0)} } \end{eqnarray} If :math:`j > 0`, then for :math:`k = 1, \ldots , j-1` .. math:: :nowrap: \begin{eqnarray} \D{H}{ z^{(0)} } & = & \D{G}{ z^{(0)} } + \D{G} { z^{(j)} } \D{ z^{(j)} }{ z^{(0)} } \\ & = & \D{G}{ z^{(0)} } - \D{G}{ z^{(j)} } \frac{ z^{(j)} }{ z^{(0)} } \\ \D{H}{ x^{(j)} } & = & \D{G}{ x^{(j)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ x^{(j)} } \\ & = & \D{G}{ x^{(j)} } + \D{G}{ z^{(j)} } \frac{1}{ 2 z^{(0)} } \\ \D{H}{ z^{(k)} } & = & \D{G}{ z^{(k)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ z^{(k)} } \\ & = & \D{G}{ z^{(k)} } - \D{G}{ z^{(j)} } \frac{ z^{(j-k)} }{ z^{(0)} } \end{eqnarray} {xrst_end sqrt_reverse} ================================================ FILE: xrst/theory/tan_forward.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin tan_forward} Tangent and Hyperbolic Tangent Forward Taylor Polynomial Theory ############################################################### Derivatives *********** .. math:: :nowrap: \begin{eqnarray} \tan^{(1)} ( u ) & = & [ \cos (u)^2 + \sin (u)^2 ] / \cos (u)^2 \\ & = & 1 + \tan (u)^2 \\ \tanh^{(1)} ( u ) & = & [ \cosh (u)^2 - \sinh (u)^2 ] / \cosh (u)^2 \\ & = & 1 - \tanh (u)^2 \end{eqnarray} If :math:`F(u)` is :math:`\tan (u)` or :math:`\tanh (u)` the corresponding derivative is given by .. math:: F^{(1)} (u) = 1 \pm F(u)^2 Given :math:`X(t)`, we define the function :math:`Z(t) = F[ X(t) ]`. It follows that .. math:: Z^{(1)} (t) = F^{(1)} [ X(t) ] X^{(1)} (t) = [ 1 \pm Y(t) ] X^{(1)} (t) where we define the function :math:`Y(t) = Z(t)^2`. Taylor Coefficients Recursion ***************************** Suppose that we are given the Taylor coefficients up to order :math:`j` for the function :math:`X(t)` and up to order :math:`j-1` for the functions :math:`Y(t)` and :math:`Z(t)`. We need a formula that computes the coefficient of order :math:`j` for :math:`Y(t)` and :math:`Z(t)`. Using the equation above for :math:`Z^{(1)} (t)`, we have .. math:: :nowrap: \begin{eqnarray} \sum_{k=1}^j k z^{(k)} t^{k-1} & = & \sum_{k=1}^j k x^{(k)} t^{k-1} \pm \left[ \sum_{k=0}^{j-1} y^{(k)} t^k \right] \left[ \sum_{k=1}^j k x^{(k)} t^{k-1} \right] + o( t^{j-1} ) \end{eqnarray} Setting the coefficients of :math:`t^{j-1}` equal, we have .. math:: :nowrap: \begin{eqnarray} j z^{(j)} = j x^{(j)} \pm \sum_{k=1}^j k x^{(k)} y^{(j-k)} \\ z^{(j)} = x^{(j)} \pm \frac{1}{j} \sum_{k=1}^j k x^{(k)} y^{(j-k)} \end{eqnarray} Once we have computed :math:`z^{(j)}`, we can compute :math:`y^{(j)}` as follows: .. math:: y^{(j)} = \sum_{k=0}^j z^{(k)} z^{(j-k)} {xrst_end tan_forward} ================================================ FILE: xrst/theory/tan_reverse.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin tan_reverse} Tangent and Hyperbolic Tangent Reverse Mode Theory ################################################## Notation ******** We use the reverse theory :ref:`standard math function` definition for the functions :math:`H` and :math:`G`. In addition, we use the forward mode notation in :ref:`tan_forward-name` for :math:`X(t)`, :math:`Y(t)` and :math:`Z(t)`. Eliminating Y(t) **************** For :math:`j > 0`, the forward mode coefficients are given by .. math:: y^{(j-1)} = \sum_{k=0}^{j-1} z^{(k)} z^{(j-k-1)} Fix :math:`j > 0` and suppose that :math:`H` is the same as :math:`G` except that :math:`y^{(j-1)}` is replaced as a function of the Taylor coefficients for :math:`Z(t)`. To be specific, for :math:`k = 0 , \ldots , j-1`, .. math:: :nowrap: \begin{eqnarray} \D{H}{ z^{(k)} } & = & \D{G}{ z^{(k)} } + \D{G}{ y^{(j-1)} } \D{ y^{(j-1)} }{ z^{(k)} } \\ & = & \D{G}{ z^{(k)} } + \D{G}{ y^{(j-1)} } 2 z^{(j-k-1)} \end{eqnarray} Positive Orders Z(t) ******************** For order :math:`j > 0`, suppose that :math:`H` is the same as :math:`G` except that :math:`z^{(j)}` is expressed as a function of the coefficients for :math:`X(t)`, and the lower order Taylor coefficients for :math:`Y(t)`, :math:`Z(t)`. .. math:: z^{(j)} = x^{(j)} \pm \frac{1}{j} \sum_{k=1}^j k x^{(k)} y^{(j-k)} For :math:`k = 1 , \ldots , j`, the partial of :math:`H` with respect to :math:`x^{(k)}` is given by .. math:: :nowrap: \begin{eqnarray} \D{H}{ x^{(k)} } & = & \D{G}{ x^{(k)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ x^{(k)} } \\ & = & \D{G}{ x^{(k)} } + \D{G}{ z^{(j)} } \left[ \delta ( j - k ) \pm \frac{k}{j} y^{(j-k)} \right] \end{eqnarray} where :math:`\delta ( j - k )` is one if :math:`j = k` and zero otherwise. For :math:`k = 1 , \ldots , j` The partial of :math:`H` with respect to :math:`y^{j-k}`, is given by .. math:: :nowrap: \begin{eqnarray} \D{H}{ y^{(j-k)} } & = & \D{G}{ y^{(j-k)} } + \D{G}{ z^{(j)} } \D{ z^{(j)} }{ y^{(j-k)} } \\ & = & \D{G}{ y^{(j-k)} } \pm \D{G}{ z^{(j)} }\frac{k}{j} x^{k} \end{eqnarray} Order Zero Z(t) *************** The order zero coefficients for the tangent and hyperbolic tangent are .. math:: :nowrap: \begin{eqnarray} z^{(0)} & = & \left\{ \begin{array}{c} \tan ( x^{(0)} ) \\ \tanh ( x^{(0)} ) \end{array} \right. \end{eqnarray} Suppose that :math:`H` is the same as :math:`G` except that :math:`z^{(0)}` is expressed as a function of the Taylor coefficients for :math:`X(t)`. In this case, .. math:: :nowrap: \begin{eqnarray} \D{H}{ x^{(0)} } & = & \D{G}{ x^{(0)} } + \D{G}{ z^{(0)} } \D{ z^{(0)} }{ x^{(0)} } \\ & = & \D{G}{ x^{(0)} } + \D{G}{ z^{(0)} } ( 1 \pm y^{(0)} ) \end{eqnarray} {xrst_end tan_reverse} ================================================ FILE: xrst/theory/taylor_ode.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin taylor_ode} AD Theory for Solving ODE's Using Taylor's Method ################################################# Problem ******* We are given an initial value problem for :math:`y : \B{R} \rightarrow \B{R}^n`; i.e., we know :math:`y(0) \in \B{R}^n` and we know a function :math:`g : \B{R}^n \rightarrow \B{R}^n` such that :math:`y^1 (t) = g[ y(t) ]` where :math:`y^k (t)` is the *k*-th derivative of :math:`y(t)`. z(t) **** We define the function :math:`z : \B{R} \rightarrow \B{R}^n` by :math:`z(t) = g[ y(t) ]`. Given the Taylor coefficients :math:`y^{(k)} (t)` for :math:`k = 0 , \ldots , p`, we can compute :math:`z^{(p)} (t)` using forward mode AD on the function :math:`g(y)`; see :ref:`forward_order-name` . It follows from :math:`y^1 (t) = z(t)` that :math:`y^{p+1} (t) = z^p (t)` .. math:: y^{(p+1)} (t) = z^{(p)} (t) / (k + 1) where :math:`y^{(k)} (t)` is the *k*-th order Taylor coefficient for :math:`y(t)`; i.e., :math:`y^k (t) / k !`. Starting with the known value :math:`y^{(0)} (t)`, this gives a prescription for computing :math:`y^{(k)} (t)` for any :math:`k`. Taylor's Method *************** The *p*-th order Taylor method for approximates .. math:: y( t + \Delta t ) \approx y^{(0)} (t) + y^{(1)} (t) \Delta t + \cdots + y^{(p)} (t) \Delta t^p {xrst_toc_hidden example/general/taylor_ode.cpp } Example ******* The file :ref:`taylor_ode.cpp-name` contains an example and test of this method. {xrst_end taylor_ode} ================================================ FILE: xrst/theory/theory.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin Theory} The Theory of Derivative Calculations ##################################### Contents ******** {xrst_toc_list xrst/introduction.xrst xrst/theory/forward_theory.xrst xrst/theory/reverse_theory.xrst xrst/theory/reverse_identity.xrst xrst/theory/taylor_ode.xrst xrst/theory/research.xrst } {xrst_end Theory} ================================================ FILE: xrst/thread_alloc.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-24 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin thread_alloc} A Fast Multi-Threading Memory Allocator ####################################### Syntax ****** | ``# include `` Purpose ******* The C++ ``new`` and ``delete`` operators are thread safe, but this means that a thread may have to wait for a lock on these operations. Once memory is obtained for a thread, the ``thread_alloc`` memory allocator keeps that memory :ref:`available` for the thread so that it can be re-used without waiting for a lock. All the CppAD memory allocations use this utility. The :ref:`free_available` function should be used to return memory to the system (once it is no longer required by a thread). Include ******* The routines in sections below are defined by ``cppad/thread_alloc.hpp`` . This file is included by ``cppad/cppad.hpp`` , but it can also be included separately with out the rest of the ``CppAD`` . Contents ******** {xrst_toc_table example/utility/thread_alloc.cpp include/cppad/utility/thread_alloc.hpp } {xrst_end thread_alloc} */ ================================================ FILE: xrst/uniform_01_hpp.xrst ================================================ # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later # SPDX-FileCopyrightText: Bradley M. Bell # SPDX-FileContributor: 2003-22 Bradley M. Bell # ---------------------------------------------------------------------------- {xrst_begin uniform_01.hpp} Source: uniform_01 ################## | # ``ifndef CPPAD_UNIFORM_01_HPP`` | # ``define CPPAD_UNIFORM_01_HPP`` {xrst_literal include/cppad/speed/uniform_01.hpp // BEGIN C++ // END C++ } ``# endif`` {xrst_end uniform_01.hpp} ================================================ FILE: xrst.toml ================================================ [project_name] data = 'CppAD' [directory] project_directory = '.' rst_directory = 'build/rst' html_directory = 'build/html' tex_directory = 'build/tex' [root_file] default = 'user_guide.xrst' app = 'appendix/appendix.xrst' dev = 'xrst/devel/devel.xrst' [heading] # level 0, 1, 2, 3, 4, character = [ '#', '*', '=', '-', '.', ] overline = [ false, false, false, false, false, ] [include_all] rst_epilog = '' rst_prolog = ''' .. |space| unicode:: 0xA0 .. |tab| replace:: |space| |space| |space| |space| ''' latex_macro = [ '\newcommand{\W}[1]{ \; #1 \; }', '\newcommand{\R}[1]{ {\rm #1} }', '\newcommand{\B}[1]{ {\bf #1} }', '\newcommand{\D}[2]{ \frac{\partial #1}{\partial #2} }', '\newcommand{\DD}[3]{ \frac{\partial^2 #1}{\partial #2 \partial #3} }', '\newcommand{\Dpow}[2]{ \frac{\partial^{#1}}{\partial {#2}^{#1}} }', '\newcommand{\dpow}[2]{ \frac{ {\rm d}^{#1}}{{\rm d}\, {#2}^{#1}} }', ] # BEGIN_SORT_THIS_LINE_PLUS_4 # Words that occurred in 10 or more pages on 2025-01-06 [project_dictionary] data = [ ''' acos addr af afun alloc asin atan atanh bool cc ccc checkpointing cmake cond config const cpp cppad cygwin dd det dir dvector eigen endif enum eq eqnarray erf eval exp fabs gcc gt hes hpp ident ifndef ind int itr jac ll mm mul multi namespace nc nowrap num obj op pc pkg preprocessor ptr rec rel res resize seq sizeof sqrt std unix var vec vecad ''' ] # END_SORT_THIS_LINE_MINUS_2